source.go
269 lines1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
package commands
import (
"bytes"
"flag"
"fmt"
"io/fs"
"log"
"os"
"path/filepath"
"congo.gg"
)
const goVersion = "1.25.0"
const sourceUsage = `Extract Congo CLI source code for distribution
Usage:
congo source [flags]
Flags:
--dir <path> Output directory (default: congo-src)
`
func Source() {
f := flag.NewFlagSet("source", flag.ExitOnError)
f.Usage = func() { fmt.Print(sourceUsage) }
dir := f.String("dir", "congo-src", "Output directory")
f.Parse(os.Args[2:])
if _, err := os.Stat(*dir); err == nil {
log.Fatalf("directory %q already exists", *dir)
}
fmt.Printf("Extracting Congo source to %s...\n", *dir)
// Extract CLI source files (cmd/commands/*.go), rewriting imports
// so the flat layout doesn't have an import cycle.
if err := extractSource(congo.SourceFS, "cmd/commands", *dir); err != nil {
log.Fatalf("extract source: %v", err)
}
// Extract internal scaffold helpers (cmd/internal/*.go),
// flattened into the same directory as package main.
if err := extractInternal(congo.SourceFS, *dir); err != nil {
log.Fatalf("extract internal: %v", err)
}
// Extract scaffold templates (res/scaffold/).
if err := extractFS(congo.ScaffoldFS, ".", *dir); err != nil {
log.Fatalf("extract scaffold: %v", err)
}
// Copy framework source into res/scaffold/pkg/ so the flat
// layout's vendor.go can extract from scaffoldFS.
if err := extractFS(congo.SourceFS, "pkg", filepath.Join(*dir, "res", "scaffold")); err != nil {
log.Fatalf("extract pkg: %v", err)
}
// Write res/claude-context.md.
resDir := filepath.Join(*dir, "res")
os.MkdirAll(resDir, 0755)
if err := os.WriteFile(filepath.Join(resDir, "claude-context.md"), []byte(congo.ClaudeContext), 0644); err != nil {
log.Fatalf("write claude-context: %v", err)
}
// Generate embed.go for the flat layout (package main, local vars).
embedGo := `package main
import "embed"
//go:embed all:res/scaffold
var scaffoldFS embed.FS
//go:embed res/claude-context.md
var claudeContext string
//go:embed *.go
var sourceFS embed.FS
`
if err := os.WriteFile(filepath.Join(*dir, "embed.go"), []byte(embedGo), 0644); err != nil {
log.Fatalf("write embed.go: %v", err)
}
// Generate main.go for the flat layout.
mainGo := `package main
import (
"fmt"
"os"
)
var version = "dev"
const usage = ` + "`" + `Congo — Go framework CLI
Usage:
congo <command> [arguments]
Commands:
init <name> Create a new project
new <name> Add an app to existing project
dev Run development server
build Build for production
claude Launch Claude Code with framework context
source Extract Congo source code
version Print version
Run 'congo <command> --help' for command-specific usage.
` + "`" + `
func main() {
if len(os.Args) < 2 {
fmt.Print(usage)
os.Exit(1)
}
switch os.Args[1] {
case "init":
Init()
case "new":
New()
case "dev":
Dev()
case "build":
Build()
case "claude":
Claude()
case "source":
Source()
case "version", "--version", "-v":
fmt.Printf("congo %s\n", version)
case "help", "--help", "-h":
fmt.Print(usage)
default:
fmt.Fprintf(os.Stderr, "unknown command %q\n\nRun 'congo help' for usage.\n", os.Args[1])
os.Exit(1)
}
}
`
if err := os.WriteFile(filepath.Join(*dir, "main.go"), []byte(mainGo), 0644); err != nil {
log.Fatalf("write main.go: %v", err)
}
// Generate go.mod.
gomod := fmt.Sprintf("module congo.gg\n\ngo %s\n", goVersion)
if err := os.WriteFile(filepath.Join(*dir, "go.mod"), []byte(gomod), 0644); err != nil {
log.Fatalf("write go.mod: %v", err)
}
// Generate Makefile.
makefile := "build:\n\tgo build -o congo .\n\ninstall: build\n\tcp congo /usr/local/bin/congo\n"
if err := os.WriteFile(filepath.Join(*dir, "Makefile"), []byte(makefile), 0644); err != nil {
log.Fatalf("write Makefile: %v", err)
}
fmt.Printf("Done. Build with:\n cd %s && go build -o congo .\n", *dir)
}
// extractSource copies cmd/commands/*.go files into targetDir, rewriting
// congo.gg package references to local vars for the flat layout.
func extractSource(fsys fs.FS, root, targetDir string) error {
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
}
name := filepath.Base(path)
// Skip files that don't belong in the flat layout.
switch name {
case "launch.go", "connect.go", "logs.go", "status.go", "destroy.go": // Require pkg/platform (not in flat layout)
return nil
}
content, err := fs.ReadFile(fsys, path)
if err != nil {
return err
}
content = rewriteForFlat(content)
os.MkdirAll(targetDir, 0755)
return os.WriteFile(filepath.Join(targetDir, name), content, 0644)
})
}
// extractInternal copies cmd/internal/*.go from SourceFS,
// rewriting them to package main for the flat layout.
func extractInternal(fsys fs.FS, targetDir string) error {
const root = "cmd/internal"
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return err
}
content, err := fs.ReadFile(fsys, path)
if err != nil {
return err
}
// Rewrite package scaffold → package main
content = bytes.ReplaceAll(content, []byte("package scaffold"), []byte("package main"))
// Rewrite congo.gg references
content = bytes.ReplaceAll(content, []byte("congo.SourceFS"), []byte("scaffoldFS"))
content = bytes.ReplaceAll(content, []byte("congo.ScaffoldFS"), []byte("scaffoldFS"))
content = bytes.ReplaceAll(content, []byte("\t\"congo.gg\"\n"), nil)
// Fix the pkg root path for the flat layout.
content = bytes.ReplaceAll(content, []byte(`pkgRoot = "pkg/"`), []byte(`pkgRoot = "res/scaffold/pkg/"`))
filename := filepath.Base(path)
os.MkdirAll(targetDir, 0755)
return os.WriteFile(filepath.Join(targetDir, filename), content, 0644)
})
}
// rewriteForFlat rewrites a cmd/commands/*.go file for the flat layout.
func rewriteForFlat(content []byte) []byte {
// Package
content = bytes.ReplaceAll(content, []byte("package commands"), []byte("package main"))
// Congo root package references
content = bytes.ReplaceAll(content, []byte("congo.SourceFS"), []byte("scaffoldFS"))
content = bytes.ReplaceAll(content, []byte("congo.ScaffoldFS"), []byte("scaffoldFS"))
content = bytes.ReplaceAll(content, []byte("congo.ClaudeContext"), []byte("claudeContext"))
content = bytes.ReplaceAll(content, []byte("\t\"congo.gg\"\n"), nil)
// Source extraction: in flat layout, files are at root in sourceFS
content = bytes.ReplaceAll(content, []byte(`scaffoldFS, "cmd/commands"`), []byte(`sourceFS, "."`))
// Internal scaffold import → removed (now in same package)
content = bytes.ReplaceAll(content, []byte("\t\"congo.gg/cmd/internal\"\n"), nil)
// scaffold.X calls → X (same package in flat layout)
content = bytes.ReplaceAll(content, []byte("scaffold.ExtractPackages"), []byte("ExtractPackages"))
content = bytes.ReplaceAll(content, []byte("scaffold.RenderProject"), []byte("RenderProject"))
content = bytes.ReplaceAll(content, []byte("scaffold.RenderApp"), []byte("RenderApp"))
content = bytes.ReplaceAll(content, []byte("scaffold.Data"), []byte("Data"))
return content
}
// extractFS writes all files from an embed.FS into targetDir,
// preserving directory structure.
func extractFS(fsys fs.FS, root, targetDir string) error {
return fs.WalkDir(fsys, root, func(path string, d fs.DirEntry, err error) error {
if err != nil || path == "." {
return err
}
outPath := filepath.Join(targetDir, path)
if d.IsDir() {
return os.MkdirAll(outPath, 0755)
}
content, err := fs.ReadFile(fsys, path)
if err != nil {
return err
}
os.MkdirAll(filepath.Dir(outPath), 0755)
return os.WriteFile(outPath, content, 0644)
})
}