context-engineer 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +88 -0
  2. package/bin/cli.mjs +91 -0
  3. package/lib/copy.mjs +102 -0
  4. package/lib/init.mjs +166 -0
  5. package/lib/prompts.mjs +144 -0
  6. package/lib/update.mjs +198 -0
  7. package/package.json +35 -0
  8. package/templates/checksums.json +68 -0
  9. package/templates/claude/.claude/rules/context-maintenance.md +38 -0
  10. package/templates/claude/.claude/rules/experience-capture.md +46 -0
  11. package/templates/claude/.claude/settings.project.json +22 -0
  12. package/templates/claude/.claude/skills/bootstrap/SKILL.md +223 -0
  13. package/templates/claude/.claude/skills/dev/SKILL.md +119 -0
  14. package/templates/claude/.claude/skills/dev-capture/SKILL.md +111 -0
  15. package/templates/claude/.claude/skills/dev-commit/SKILL.md +90 -0
  16. package/templates/claude/.claude/skills/dev-decompose/SKILL.md +113 -0
  17. package/templates/claude/.claude/skills/dev-deps/SKILL.md +108 -0
  18. package/templates/claude/.claude/skills/dev-execute/SKILL.md +196 -0
  19. package/templates/claude/.claude/skills/dev-prd/SKILL.md +100 -0
  20. package/templates/claude/.claude/skills/dev-quality/SKILL.md +109 -0
  21. package/templates/claude/.claude/skills/dev-requirements/SKILL.md +75 -0
  22. package/templates/claude/.claude/skills/review-context/SKILL.md +120 -0
  23. package/templates/claude/.claude/skills/sync/SKILL.md +107 -0
  24. package/templates/claude/.claude/skills/update-context/SKILL.md +105 -0
  25. package/templates/claude/.claude/workflow/agents/implementer.md +65 -0
  26. package/templates/claude/.claude/workflow/agents/reviewer.md +96 -0
  27. package/templates/claude/.claude/workflow/agents/team-config.md +97 -0
  28. package/templates/claude/.claude/workflow/agents/tester.md +98 -0
  29. package/templates/claude/.claude/workflow/interfaces/phase-contract.md +157 -0
  30. package/templates/claude/CLAUDE.md +50 -0
  31. package/templates/core/.context/_meta/concepts.md +9 -0
  32. package/templates/core/.context/_meta/drift-report.md +16 -0
  33. package/templates/core/.context/_meta/last-sync.json +6 -0
  34. package/templates/core/.context/_meta/schema.md +242 -0
  35. package/templates/core/.context/architecture/api-surface.md +52 -0
  36. package/templates/core/.context/architecture/class-index.md +49 -0
  37. package/templates/core/.context/architecture/data-flow.md +103 -0
  38. package/templates/core/.context/architecture/data-model.md +35 -0
  39. package/templates/core/.context/architecture/decisions/001-template.md +35 -0
  40. package/templates/core/.context/architecture/dependencies.md +35 -0
  41. package/templates/core/.context/architecture/infrastructure.md +42 -0
  42. package/templates/core/.context/architecture/module-graph.md +68 -0
  43. package/templates/core/.context/architecture/overview.md +87 -0
  44. package/templates/core/.context/business/domain-model.md +43 -0
  45. package/templates/core/.context/business/glossary.md +23 -0
  46. package/templates/core/.context/business/overview.md +29 -0
  47. package/templates/core/.context/business/workflows.md +61 -0
  48. package/templates/core/.context/constitution.md +84 -0
  49. package/templates/core/.context/conventions/code-style.md +47 -0
  50. package/templates/core/.context/conventions/error-handling.md +50 -0
  51. package/templates/core/.context/conventions/git.md +46 -0
  52. package/templates/core/.context/conventions/patterns.md +41 -0
  53. package/templates/core/.context/conventions/testing.md +49 -0
  54. package/templates/core/.context/experience/debugging.md +21 -0
  55. package/templates/core/.context/experience/incidents.md +26 -0
  56. package/templates/core/.context/experience/lessons.md +23 -0
  57. package/templates/core/.context/experience/performance.md +29 -0
  58. package/templates/core/.context/index.md +93 -0
  59. package/templates/core/.context/progress/backlog.md +23 -0
  60. package/templates/core/.context/progress/status.md +30 -0
  61. package/templates/core/.context/workflow/artifacts/.gitkeep +0 -0
  62. package/templates/core/.context/workflow/config.md +35 -0
  63. package/templates/core/AGENTS.md +53 -0
  64. package/templates/core/scripts/compact-experience.sh +83 -0
  65. package/templates/core/scripts/detect-drift.sh +388 -0
  66. package/templates/core/scripts/extract-structure.sh +757 -0
  67. package/templates/core/scripts/sync-context.sh +510 -0
  68. package/templates/cursor/.cursor/rules/always.mdc +18 -0
  69. package/templates/cursor/.cursor/rules/backend.mdc +16 -0
  70. package/templates/cursor/.cursor/rules/database.mdc +16 -0
  71. package/templates/cursor/.cursor/rules/frontend.mdc +13 -0
  72. package/templates/cursor/.cursorrules +23 -0
  73. package/templates/github/.github/copilot-instructions.md +15 -0
  74. package/templates/github/.github/workflows/context-drift.yml +73 -0
@@ -0,0 +1,757 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # extract-structure.sh — Extract project structure for context engineering
4
+ #
5
+ # Capabilities:
6
+ # 1. Namespace/module listing with file counts
7
+ # 2. Class/struct/interface definitions with inheritance
8
+ # 3. Import/include dependency graph (module-level)
9
+ # 4. Public function/method signatures
10
+ #
11
+ # Usage: bash scripts/extract-structure.sh [OPTIONS]
12
+ #
13
+ # Options:
14
+ # --all Run all extractions (default)
15
+ # --class-index Extract class/struct/interface index
16
+ # --module-graph Extract module dependency graph
17
+ # --namespaces Extract namespace/directory mapping
18
+ # --functions Extract public function signatures
19
+ # --src-dir DIR Source directory to scan (default: auto-detect)
20
+ # --output-dir DIR Output directory (default: .context/architecture)
21
+ #
22
+ # Supported languages:
23
+ # C/C++: class/struct, #include, namespace
24
+ # C#: class/interface, using, namespace
25
+ # TypeScript: class/interface, import, export
26
+ # Python: class, import, def
27
+ # Rust: struct/trait/impl, use, mod
28
+ # Go: struct/interface, import, package
29
+ # Java: class/interface, import, package
30
+
31
+ set -euo pipefail
32
+
33
+ CONTEXT_DIR=".context"
34
+ OUTPUT_DIR="$CONTEXT_DIR/architecture"
35
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
36
+ SRC_DIR=""
37
+
38
+ # ============================================================
39
+ # Language Detection
40
+ # ============================================================
41
+
42
+ detect_languages() {
43
+ local langs=()
44
+
45
+ # C/C++
46
+ if find . -maxdepth 5 -name "*.cpp" -o -name "*.h" -o -name "*.hpp" -o -name "*.cc" 2>/dev/null | head -1 | grep -q .; then
47
+ langs+=("cpp")
48
+ fi
49
+
50
+ # C#
51
+ if find . -maxdepth 5 -name "*.cs" 2>/dev/null | head -1 | grep -q .; then
52
+ langs+=("csharp")
53
+ fi
54
+
55
+ # TypeScript/JavaScript
56
+ if find . -maxdepth 5 -name "*.ts" -o -name "*.tsx" 2>/dev/null | head -1 | grep -q .; then
57
+ langs+=("typescript")
58
+ elif find . -maxdepth 5 -name "*.js" -o -name "*.jsx" 2>/dev/null | head -1 | grep -q .; then
59
+ langs+=("javascript")
60
+ fi
61
+
62
+ # Python
63
+ if find . -maxdepth 5 -name "*.py" 2>/dev/null | head -1 | grep -q .; then
64
+ langs+=("python")
65
+ fi
66
+
67
+ # Rust
68
+ if find . -maxdepth 5 -name "*.rs" 2>/dev/null | head -1 | grep -q .; then
69
+ langs+=("rust")
70
+ fi
71
+
72
+ # Go
73
+ if find . -maxdepth 5 -name "*.go" 2>/dev/null | head -1 | grep -q .; then
74
+ langs+=("go")
75
+ fi
76
+
77
+ # Java
78
+ if find . -maxdepth 5 -name "*.java" 2>/dev/null | head -1 | grep -q .; then
79
+ langs+=("java")
80
+ fi
81
+
82
+ echo "${langs[*]}"
83
+ }
84
+
85
+ # ============================================================
86
+ # Source Directory Detection
87
+ # ============================================================
88
+
89
+ detect_src_dir() {
90
+ # Common source directory names
91
+ for dir in src source lib app Sources pkg cmd internal; do
92
+ if [ -d "$dir" ]; then
93
+ echo "$dir"
94
+ return
95
+ fi
96
+ done
97
+ # Fallback: current directory
98
+ echo "."
99
+ }
100
+
101
+ # ============================================================
102
+ # Namespace / Directory Mapping
103
+ # ============================================================
104
+
105
+ extract_namespaces() {
106
+ local langs="$1"
107
+ echo "Extracting namespace/directory mapping..."
108
+
109
+ local output="$OUTPUT_DIR/module-graph.md"
110
+
111
+ # We'll append the namespace table to the module-graph if it exists
112
+ # For now, output to stdout for integration
113
+ echo ""
114
+ echo "## Namespace / Directory Mapping"
115
+ echo ""
116
+ echo "| Namespace / Package | Directory | File Count |"
117
+ echo "|---------------------|-----------|------------|"
118
+
119
+ if [[ "$langs" == *"cpp"* ]]; then
120
+ # C++: Extract namespaces from source files
121
+ find "$SRC_DIR" -name "*.h" -o -name "*.hpp" -o -name "*.cpp" -o -name "*.cc" 2>/dev/null | while read -r file; do
122
+ grep -oP 'namespace\s+\K[a-zA-Z_][a-zA-Z0-9_:]*' "$file" 2>/dev/null
123
+ done | sort -u | while read -r ns; do
124
+ local dir
125
+ dir=$(find "$SRC_DIR" -name "*.h" -o -name "*.hpp" -o -name "*.cpp" -o -name "*.cc" 2>/dev/null | xargs grep -l "namespace $ns" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || echo "unknown")
126
+ local count
127
+ count=$(find "$SRC_DIR" -name "*.h" -o -name "*.hpp" -o -name "*.cpp" -o -name "*.cc" 2>/dev/null | xargs grep -l "namespace $ns" 2>/dev/null | wc -l || echo "0")
128
+ echo "| $ns | $dir | $count |"
129
+ done
130
+ fi
131
+
132
+ if [[ "$langs" == *"csharp"* ]]; then
133
+ # C#: Extract namespaces
134
+ find "$SRC_DIR" -name "*.cs" 2>/dev/null | while read -r file; do
135
+ grep -oP 'namespace\s+\K[a-zA-Z_][a-zA-Z0-9_.]*' "$file" 2>/dev/null
136
+ done | sort -u | while read -r ns; do
137
+ local count
138
+ count=$(find "$SRC_DIR" -name "*.cs" 2>/dev/null | xargs grep -l "namespace $ns" 2>/dev/null | wc -l || echo "0")
139
+ local dir
140
+ dir=$(find "$SRC_DIR" -name "*.cs" 2>/dev/null | xargs grep -l "namespace $ns" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || echo "unknown")
141
+ echo "| $ns | $dir | $count |"
142
+ done
143
+ fi
144
+
145
+ if [[ "$langs" == *"python"* ]]; then
146
+ # Python: Extract packages (directories with __init__.py)
147
+ find "$SRC_DIR" -name "__init__.py" 2>/dev/null | while read -r init; do
148
+ local dir
149
+ dir=$(dirname "$init")
150
+ local pkg
151
+ pkg=$(echo "$dir" | tr '/' '.')
152
+ local count
153
+ count=$(find "$dir" -maxdepth 1 -name "*.py" 2>/dev/null | wc -l || echo "0")
154
+ echo "| $pkg | $dir | $count |"
155
+ done
156
+ fi
157
+
158
+ if [[ "$langs" == *"go"* ]]; then
159
+ # Go: Extract packages
160
+ find "$SRC_DIR" -name "*.go" 2>/dev/null | while read -r file; do
161
+ grep -oP 'package\s+\K[a-zA-Z_][a-zA-Z0-9_]*' "$file" 2>/dev/null
162
+ done | sort -u | while read -r pkg; do
163
+ local dir
164
+ dir=$(find "$SRC_DIR" -name "*.go" 2>/dev/null | xargs grep -l "package $pkg" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || echo "unknown")
165
+ local count
166
+ count=$(find "$SRC_DIR" -name "*.go" 2>/dev/null | xargs grep -l "package $pkg" 2>/dev/null | wc -l || echo "0")
167
+ echo "| $pkg | $dir | $count |"
168
+ done
169
+ fi
170
+
171
+ if [[ "$langs" == *"java"* ]]; then
172
+ # Java: Extract packages
173
+ find "$SRC_DIR" -name "*.java" 2>/dev/null | while read -r file; do
174
+ grep -oP 'package\s+\K[a-zA-Z_][a-zA-Z0-9_.]*' "$file" 2>/dev/null
175
+ done | sort -u | while read -r pkg; do
176
+ local count
177
+ count=$(find "$SRC_DIR" -name "*.java" 2>/dev/null | xargs grep -l "package $pkg" 2>/dev/null | wc -l || echo "0")
178
+ local dir
179
+ dir=$(find "$SRC_DIR" -name "*.java" 2>/dev/null | xargs grep -l "package $pkg" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || echo "unknown")
180
+ echo "| $pkg | $dir | $count |"
181
+ done
182
+ fi
183
+
184
+ if [[ "$langs" == *"rust"* ]]; then
185
+ # Rust: Extract modules from mod declarations and directory structure
186
+ find "$SRC_DIR" -name "*.rs" 2>/dev/null | while read -r file; do
187
+ local dir
188
+ dir=$(dirname "$file")
189
+ local modname
190
+ modname=$(basename "$file" .rs)
191
+ if [ "$modname" != "mod" ] && [ "$modname" != "lib" ] && [ "$modname" != "main" ]; then
192
+ echo "| $modname | $dir | 1 |"
193
+ fi
194
+ done | sort -u
195
+ fi
196
+
197
+ if [[ "$langs" == *"typescript"* ]] || [[ "$langs" == *"javascript"* ]]; then
198
+ # TypeScript/JavaScript: Extract by directory structure
199
+ find "$SRC_DIR" -maxdepth 3 -type d 2>/dev/null | while read -r dir; do
200
+ local count
201
+ count=$(find "$dir" -maxdepth 1 \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) 2>/dev/null | wc -l || echo "0")
202
+ if [ "$count" -gt 0 ]; then
203
+ echo "| $(basename "$dir") | $dir | $count |"
204
+ fi
205
+ done
206
+ fi
207
+ }
208
+
209
+ # ============================================================
210
+ # Class / Struct / Interface Extraction
211
+ # ============================================================
212
+
213
+ extract_classes() {
214
+ local langs="$1"
215
+ echo "Extracting class/struct/interface definitions..."
216
+
217
+ echo ""
218
+ echo "## Core Classes by Module"
219
+ echo ""
220
+
221
+ if [[ "$langs" == *"cpp"* ]]; then
222
+ echo "### C/C++ Classes"
223
+ echo ""
224
+ echo "| Class | File | Type | Base Class |"
225
+ echo "|-------|------|------|-----------|"
226
+ find "$SRC_DIR" \( -name "*.h" -o -name "*.hpp" \) 2>/dev/null | sort | while read -r file; do
227
+ # Extract class/struct declarations with optional inheritance
228
+ grep -nP '^\s*(class|struct)\s+([A-Z][a-zA-Z0-9_]+)(\s*:\s*(public|protected|private)\s+([A-Z][a-zA-Z0-9_:]+))?' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
229
+ local type name base
230
+ type=$(echo "$match" | grep -oP '^\s*\K(class|struct)')
231
+ name=$(echo "$match" | grep -oP '(class|struct)\s+\K[A-Z][a-zA-Z0-9_]+')
232
+ base=$(echo "$match" | grep -oP '(public|protected|private)\s+\K[A-Z][a-zA-Z0-9_:]+' || echo "—")
233
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $type | $base |"
234
+ done
235
+ done
236
+ echo ""
237
+ fi
238
+
239
+ if [[ "$langs" == *"csharp"* ]]; then
240
+ echo "### C# Classes"
241
+ echo ""
242
+ echo "| Class | File | Type | Base Class |"
243
+ echo "|-------|------|------|-----------|"
244
+ find "$SRC_DIR" -name "*.cs" 2>/dev/null | sort | while read -r file; do
245
+ grep -nP '^\s*(public|internal|protected)?\s*(abstract|sealed|static|partial)?\s*(class|interface|struct|record|enum)\s+([A-Z][a-zA-Z0-9_<>]+)(\s*:\s*([A-Z][a-zA-Z0-9_<>,\s]+))?' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
246
+ local type name base
247
+ type=$(echo "$match" | grep -oP '(class|interface|struct|record|enum)')
248
+ name=$(echo "$match" | grep -oP '(class|interface|struct|record|enum)\s+\K[A-Z][a-zA-Z0-9_<>]+')
249
+ base=$(echo "$match" | grep -oP ':\s*\K[A-Z][a-zA-Z0-9_<>]+' || echo "—")
250
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $type | $base |"
251
+ done
252
+ done
253
+ echo ""
254
+ fi
255
+
256
+ if [[ "$langs" == *"python"* ]]; then
257
+ echo "### Python Classes"
258
+ echo ""
259
+ echo "| Class | File | Base Class |"
260
+ echo "|-------|------|-----------|"
261
+ find "$SRC_DIR" -name "*.py" 2>/dev/null | sort | while read -r file; do
262
+ grep -nP '^\s*class\s+([A-Z][a-zA-Z0-9_]+)\s*(\(([^)]*)\))?\s*:' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
263
+ local name base
264
+ name=$(echo "$match" | grep -oP 'class\s+\K[A-Z][a-zA-Z0-9_]+')
265
+ base=$(echo "$match" | grep -oP '\(\K[^)]+' || echo "—")
266
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $base |"
267
+ done
268
+ done
269
+ echo ""
270
+ fi
271
+
272
+ if [[ "$langs" == *"typescript"* ]] || [[ "$langs" == *"javascript"* ]]; then
273
+ echo "### TypeScript/JavaScript Classes"
274
+ echo ""
275
+ echo "| Class | File | Type | Base/Implements |"
276
+ echo "|-------|------|------|----------------|"
277
+ find "$SRC_DIR" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" 2>/dev/null | sort | while read -r file; do
278
+ grep -nP '^\s*(export\s+)?(abstract\s+)?(class|interface)\s+([A-Z][a-zA-Z0-9_<>]+)(\s+(extends|implements)\s+([A-Z][a-zA-Z0-9_<>,\s]+))?' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
279
+ local type name base
280
+ type=$(echo "$match" | grep -oP '(class|interface)')
281
+ name=$(echo "$match" | grep -oP '(class|interface)\s+\K[A-Z][a-zA-Z0-9_<>]+')
282
+ base=$(echo "$match" | grep -oP '(extends|implements)\s+\K[A-Z][a-zA-Z0-9_<>]+' || echo "—")
283
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $type | $base |"
284
+ done
285
+ done
286
+ echo ""
287
+ fi
288
+
289
+ if [[ "$langs" == *"rust"* ]]; then
290
+ echo "### Rust Types"
291
+ echo ""
292
+ echo "| Type | File | Kind |"
293
+ echo "|------|------|------|"
294
+ find "$SRC_DIR" -name "*.rs" 2>/dev/null | sort | while read -r file; do
295
+ grep -nP '^\s*pub\s+(struct|enum|trait)\s+([A-Z][a-zA-Z0-9_<>]+)' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
296
+ local kind name
297
+ kind=$(echo "$match" | grep -oP '(struct|enum|trait)')
298
+ name=$(echo "$match" | grep -oP '(struct|enum|trait)\s+\K[A-Z][a-zA-Z0-9_<>]+')
299
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $kind |"
300
+ done
301
+ done
302
+ echo ""
303
+ fi
304
+
305
+ if [[ "$langs" == *"go"* ]]; then
306
+ echo "### Go Types"
307
+ echo ""
308
+ echo "| Type | File | Kind |"
309
+ echo "|------|------|------|"
310
+ find "$SRC_DIR" -name "*.go" -not -name "*_test.go" 2>/dev/null | sort | while read -r file; do
311
+ grep -nP '^\s*type\s+([A-Z][a-zA-Z0-9_]+)\s+(struct|interface)' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
312
+ local kind name
313
+ kind=$(echo "$match" | grep -oP '(struct|interface)$')
314
+ name=$(echo "$match" | grep -oP 'type\s+\K[A-Z][a-zA-Z0-9_]+')
315
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $kind |"
316
+ done
317
+ done
318
+ echo ""
319
+ fi
320
+
321
+ if [[ "$langs" == *"java"* ]]; then
322
+ echo "### Java Classes"
323
+ echo ""
324
+ echo "| Class | File | Type | Base/Implements |"
325
+ echo "|-------|------|------|----------------|"
326
+ find "$SRC_DIR" -name "*.java" 2>/dev/null | sort | while read -r file; do
327
+ grep -nP '^\s*(public|protected)?\s*(abstract|final)?\s*(class|interface|enum|record)\s+([A-Z][a-zA-Z0-9_<>]+)(\s+(extends|implements)\s+([A-Z][a-zA-Z0-9_<>,\s]+))?' "$file" 2>/dev/null | while IFS=: read -r lineno match; do
328
+ local type name base
329
+ type=$(echo "$match" | grep -oP '(class|interface|enum|record)')
330
+ name=$(echo "$match" | grep -oP '(class|interface|enum|record)\s+\K[A-Z][a-zA-Z0-9_<>]+')
331
+ base=$(echo "$match" | grep -oP '(extends|implements)\s+\K[A-Z][a-zA-Z0-9_<>]+' || echo "—")
332
+ [ -n "$name" ] && echo "| $name | $file:$lineno | $type | $base |"
333
+ done
334
+ done
335
+ echo ""
336
+ fi
337
+ }
338
+
339
+ # ============================================================
340
+ # Module Dependency Graph
341
+ # ============================================================
342
+
343
+ extract_module_deps() {
344
+ local langs="$1"
345
+ echo "Extracting module dependency graph..."
346
+
347
+ echo ""
348
+ echo "## Module Dependencies (Import/Include Analysis)"
349
+ echo ""
350
+
351
+ if [[ "$langs" == *"cpp"* ]]; then
352
+ echo "### C/C++ Include Dependencies"
353
+ echo ""
354
+ echo "| Source Directory | Includes From |"
355
+ echo "|-----------------|--------------|"
356
+ # Analyze #include patterns at directory level
357
+ find "$SRC_DIR" \( -name "*.cpp" -o -name "*.cc" -o -name "*.h" -o -name "*.hpp" \) 2>/dev/null | while read -r file; do
358
+ local src_dir
359
+ src_dir=$(dirname "$file")
360
+ grep -oP '#include\s*["<]\K[^">]+' "$file" 2>/dev/null | while read -r inc; do
361
+ local inc_dir
362
+ inc_dir=$(dirname "$inc")
363
+ if [ "$inc_dir" != "." ] && [ "$inc_dir" != "$src_dir" ]; then
364
+ echo "$src_dir → $inc_dir"
365
+ fi
366
+ done
367
+ done | sort -u | head -50 | while read -r dep; do
368
+ local src tgt
369
+ src=$(echo "$dep" | cut -d'→' -f1 | xargs)
370
+ tgt=$(echo "$dep" | cut -d'→' -f2 | xargs)
371
+ echo "| $src | $tgt |"
372
+ done
373
+ echo ""
374
+ fi
375
+
376
+ if [[ "$langs" == *"csharp"* ]]; then
377
+ echo "### C# Namespace Dependencies"
378
+ echo ""
379
+ echo "| Namespace | Uses Namespaces |"
380
+ echo "|-----------|----------------|"
381
+ find "$SRC_DIR" -name "*.cs" 2>/dev/null | while read -r file; do
382
+ local ns
383
+ ns=$(grep -oP 'namespace\s+\K[a-zA-Z_][a-zA-Z0-9_.]*' "$file" 2>/dev/null | head -1)
384
+ [ -z "$ns" ] && continue
385
+ local usings
386
+ usings=$(grep -oP 'using\s+\K[a-zA-Z_][a-zA-Z0-9_.]*' "$file" 2>/dev/null | grep -v "^System" | sort -u | tr '\n' ', ' | sed 's/,$//')
387
+ [ -n "$usings" ] && echo "| $ns | $usings |"
388
+ done | sort -u | head -50
389
+ echo ""
390
+ fi
391
+
392
+ if [[ "$langs" == *"typescript"* ]] || [[ "$langs" == *"javascript"* ]]; then
393
+ echo "### TypeScript/JavaScript Import Dependencies"
394
+ echo ""
395
+ echo "| Source Module | Imports From |"
396
+ echo "|--------------|-------------|"
397
+ find "$SRC_DIR" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" 2>/dev/null | while read -r file; do
398
+ local src_mod
399
+ src_mod=$(dirname "$file")
400
+ grep -oP "from\s+['\"]\.{1,2}/\K[^'\"]*" "$file" 2>/dev/null | while read -r imp; do
401
+ local imp_dir
402
+ imp_dir=$(dirname "$imp" 2>/dev/null)
403
+ if [ "$imp_dir" != "." ]; then
404
+ echo "$src_mod → $imp_dir"
405
+ fi
406
+ done
407
+ done | sort -u | head -50 | while read -r dep; do
408
+ local src tgt
409
+ src=$(echo "$dep" | cut -d'→' -f1 | xargs)
410
+ tgt=$(echo "$dep" | cut -d'→' -f2 | xargs)
411
+ echo "| $src | $tgt |"
412
+ done
413
+ echo ""
414
+ fi
415
+
416
+ if [[ "$langs" == *"python"* ]]; then
417
+ echo "### Python Import Dependencies"
418
+ echo ""
419
+ echo "| Module | Imports From |"
420
+ echo "|--------|-------------|"
421
+ find "$SRC_DIR" -name "*.py" 2>/dev/null | while read -r file; do
422
+ local src_mod
423
+ src_mod=$(dirname "$file" | tr '/' '.')
424
+ grep -oP '^\s*(from\s+\K[a-zA-Z_][a-zA-Z0-9_.]*|import\s+\K[a-zA-Z_][a-zA-Z0-9_.]*)' "$file" 2>/dev/null | while read -r imp; do
425
+ # Only show project-internal imports (skip stdlib and third-party)
426
+ if [ -d "$(echo "$imp" | tr '.' '/')" ] || [ -f "$(echo "$imp" | tr '.' '/').py" ]; then
427
+ echo "$src_mod → $imp"
428
+ fi
429
+ done
430
+ done | sort -u | head -50 | while read -r dep; do
431
+ local src tgt
432
+ src=$(echo "$dep" | cut -d'→' -f1 | xargs)
433
+ tgt=$(echo "$dep" | cut -d'→' -f2 | xargs)
434
+ echo "| $src | $tgt |"
435
+ done
436
+ echo ""
437
+ fi
438
+
439
+ if [[ "$langs" == *"go"* ]]; then
440
+ echo "### Go Package Dependencies"
441
+ echo ""
442
+ echo "| Package | Imports |"
443
+ echo "|---------|--------|"
444
+ # Get the module name from go.mod
445
+ local go_module=""
446
+ [ -f "go.mod" ] && go_module=$(head -1 go.mod | awk '{print $2}')
447
+ find "$SRC_DIR" -name "*.go" -not -name "*_test.go" 2>/dev/null | while read -r file; do
448
+ local pkg
449
+ pkg=$(grep -oP 'package\s+\K[a-zA-Z_]+' "$file" 2>/dev/null | head -1)
450
+ [ -z "$pkg" ] && continue
451
+ local imports
452
+ imports=$(grep -oP "\"${go_module}/\K[^\"]*" "$file" 2>/dev/null | sort -u | tr '\n' ', ' | sed 's/,$//')
453
+ [ -n "$imports" ] && echo "| $pkg | $imports |"
454
+ done | sort -u | head -50
455
+ echo ""
456
+ fi
457
+ }
458
+
459
+ # ============================================================
460
+ # Public Function Signatures
461
+ # ============================================================
462
+
463
+ extract_functions() {
464
+ local langs="$1"
465
+ echo "Extracting public function signatures..."
466
+
467
+ echo ""
468
+ echo "## Public Function Signatures"
469
+ echo ""
470
+
471
+ if [[ "$langs" == *"cpp"* ]]; then
472
+ echo "### C/C++ Public Methods (from headers)"
473
+ echo ""
474
+ echo "| Class | Method | File |"
475
+ echo "|-------|--------|------|"
476
+ find "$SRC_DIR" \( -name "*.h" -o -name "*.hpp" \) 2>/dev/null | sort | while read -r file; do
477
+ # Extract public method declarations (simplified — after "public:" until "private:" or "protected:" or end of class)
478
+ grep -nP '^\s*(virtual\s+)?\S+\s+\S+\s*\([^;]*\)\s*(const)?\s*(override)?\s*(=\s*0)?\s*;' "$file" 2>/dev/null | head -20 | while IFS=: read -r lineno match; do
479
+ local method
480
+ method=$(echo "$match" | sed 's/^\s*//' | sed 's/\s*;$//')
481
+ echo "| — | \`$method\` | $file:$lineno |"
482
+ done
483
+ done | head -100
484
+ echo ""
485
+ fi
486
+
487
+ if [[ "$langs" == *"csharp"* ]]; then
488
+ echo "### C# Public Methods"
489
+ echo ""
490
+ echo "| Class | Method | File |"
491
+ echo "|-------|--------|------|"
492
+ find "$SRC_DIR" -name "*.cs" 2>/dev/null | sort | while read -r file; do
493
+ grep -nP '^\s*public\s+(static\s+)?(async\s+)?(virtual\s+)?(override\s+)?\S+\s+\S+\s*\(' "$file" 2>/dev/null | head -20 | while IFS=: read -r lineno match; do
494
+ local method
495
+ method=$(echo "$match" | sed 's/^\s*//' | grep -oP 'public\s+\K.*')
496
+ local class
497
+ class=$(head -n "$lineno" "$file" 2>/dev/null | grep -oP '(class|struct)\s+\K[A-Z][a-zA-Z0-9_]+' | tail -1 || echo "—")
498
+ echo "| $class | \`$method\` | $file:$lineno |"
499
+ done
500
+ done | head -100
501
+ echo ""
502
+ fi
503
+
504
+ if [[ "$langs" == *"python"* ]]; then
505
+ echo "### Python Public Functions/Methods"
506
+ echo ""
507
+ echo "| Module | Function | File |"
508
+ echo "|--------|----------|------|"
509
+ find "$SRC_DIR" -name "*.py" 2>/dev/null | sort | while read -r file; do
510
+ # Public functions (not starting with _)
511
+ grep -nP '^\s*def\s+([a-z][a-zA-Z0-9_]*)\s*\(' "$file" 2>/dev/null | grep -v '^\s*def\s+_' | head -20 | while IFS=: read -r lineno match; do
512
+ local func
513
+ func=$(echo "$match" | grep -oP 'def\s+\K[a-zA-Z0-9_]+\s*\([^)]*\)')
514
+ local mod
515
+ mod=$(basename "$file" .py)
516
+ echo "| $mod | \`$func\` | $file:$lineno |"
517
+ done
518
+ done | head -100
519
+ echo ""
520
+ fi
521
+
522
+ if [[ "$langs" == *"go"* ]]; then
523
+ echo "### Go Exported Functions"
524
+ echo ""
525
+ echo "| Package | Function | File |"
526
+ echo "|---------|----------|------|"
527
+ find "$SRC_DIR" -name "*.go" -not -name "*_test.go" 2>/dev/null | sort | while read -r file; do
528
+ grep -nP '^\s*func\s+(\([^)]+\)\s+)?[A-Z][a-zA-Z0-9_]*\s*\(' "$file" 2>/dev/null | head -20 | while IFS=: read -r lineno match; do
529
+ local func
530
+ func=$(echo "$match" | sed 's/^\s*func\s*//' | sed 's/\s*{$//')
531
+ local pkg
532
+ pkg=$(grep -oP 'package\s+\K[a-zA-Z_]+' "$file" 2>/dev/null | head -1)
533
+ echo "| $pkg | \`$func\` | $file:$lineno |"
534
+ done
535
+ done | head -100
536
+ echo ""
537
+ fi
538
+
539
+ if [[ "$langs" == *"rust"* ]]; then
540
+ echo "### Rust Public Functions"
541
+ echo ""
542
+ echo "| Module | Function | File |"
543
+ echo "|--------|----------|------|"
544
+ find "$SRC_DIR" -name "*.rs" 2>/dev/null | sort | while read -r file; do
545
+ grep -nP '^\s*pub\s+(async\s+)?fn\s+[a-z_][a-zA-Z0-9_]*' "$file" 2>/dev/null | head -20 | while IFS=: read -r lineno match; do
546
+ local func
547
+ func=$(echo "$match" | grep -oP 'pub\s+(async\s+)?fn\s+\K[a-zA-Z0-9_]+[^{]*' | sed 's/\s*{$//')
548
+ local mod
549
+ mod=$(basename "$file" .rs)
550
+ echo "| $mod | \`$func\` | $file:$lineno |"
551
+ done
552
+ done | head -100
553
+ echo ""
554
+ fi
555
+
556
+ if [[ "$langs" == *"java"* ]]; then
557
+ echo "### Java Public Methods"
558
+ echo ""
559
+ echo "| Class | Method | File |"
560
+ echo "|-------|--------|------|"
561
+ find "$SRC_DIR" -name "*.java" 2>/dev/null | sort | while read -r file; do
562
+ grep -nP '^\s*public\s+(static\s+)?\S+\s+\S+\s*\(' "$file" 2>/dev/null | head -20 | while IFS=: read -r lineno match; do
563
+ local method
564
+ method=$(echo "$match" | sed 's/^\s*//' | grep -oP 'public\s+\K.*')
565
+ local class
566
+ class=$(grep -oP '(class|interface)\s+\K[A-Z][a-zA-Z0-9_]+' "$file" 2>/dev/null | head -1 || echo "—")
567
+ echo "| $class | \`$method\` | $file:$lineno |"
568
+ done
569
+ done | head -100
570
+ echo ""
571
+ fi
572
+ }
573
+
574
+ # ============================================================
575
+ # Generate class-index.md
576
+ # ============================================================
577
+
578
+ generate_class_index() {
579
+ local langs="$1"
580
+ local output="$OUTPUT_DIR/class-index.md"
581
+
582
+ echo "Generating $output..."
583
+
584
+ {
585
+ echo "# Class Index"
586
+ echo ""
587
+ echo "<!-- AUTO-GENERATED: Do not edit manually. Run scripts/extract-structure.sh to regenerate. -->"
588
+ echo "<!-- generated-from: source code analysis -->"
589
+ echo "<!-- generated-at: $TIMESTAMP -->"
590
+ echo ""
591
+ echo "## Summary"
592
+ echo ""
593
+ echo "Auto-generated index of core classes, interfaces, and key data structures per module."
594
+ echo ""
595
+ extract_classes "$langs"
596
+ } > "$output"
597
+
598
+ echo " → Generated $output"
599
+ }
600
+
601
+ # ============================================================
602
+ # Generate module-graph.md (partial — dependencies + namespaces)
603
+ # ============================================================
604
+
605
+ generate_module_graph() {
606
+ local langs="$1"
607
+ local output="$OUTPUT_DIR/module-graph.md"
608
+ local temp_file="${output}.tmp"
609
+ local marker="<!-- AUTO-GENERATED SECTIONS BELOW -->"
610
+
611
+ echo "Generating module dependency data for $output..."
612
+
613
+ # If file exists and has the auto-generated marker, preserve human-curated sections above it
614
+ if [ -f "$output" ] && grep -q "$marker" "$output" 2>/dev/null; then
615
+ # Keep everything up to and including the marker line
616
+ sed "/$marker/q" "$output" > "$temp_file"
617
+ {
618
+ cat "$temp_file"
619
+ echo ""
620
+ extract_module_deps "$langs"
621
+ extract_namespaces "$langs"
622
+ echo ""
623
+ echo "## Circular Dependencies"
624
+ echo ""
625
+ echo "None detected. <!-- Update if circular dependencies are found -->"
626
+ } > "$output"
627
+ rm -f "$temp_file"
628
+ else
629
+ # First time: generate full file with template
630
+ {
631
+ echo "# Module Graph"
632
+ echo ""
633
+ echo "<!-- confidence: medium -->"
634
+ echo "<!-- source: auto-extracted via scripts/extract-structure.sh -->"
635
+ echo "<!-- last-updated: $(date +%Y-%m-%d) -->"
636
+ echo ""
637
+ echo "## Summary"
638
+ echo ""
639
+ echo "Module dependency graph and namespace mapping. Auto-generated sections are marked below."
640
+ echo ""
641
+ echo "## Subsystems"
642
+ echo ""
643
+ echo "<!-- HUMAN-CURATED: Fill in module responsibilities and key classes -->"
644
+ echo ""
645
+ echo "| Module | Directory | Responsibility | Key Classes |"
646
+ echo "|--------|-----------|---------------|-------------|"
647
+ echo "| [TO BE FILLED] | | | |"
648
+ echo ""
649
+ echo "## Dependency Graph"
650
+ echo ""
651
+ echo "<!-- HUMAN-CURATED: Draw ASCII dependency diagram based on the data below -->"
652
+ echo ""
653
+ echo '```'
654
+ echo "[Draw based on Module Dependencies section below]"
655
+ echo '```'
656
+ echo ""
657
+ echo "## Module Interfaces"
658
+ echo ""
659
+ echo "<!-- HUMAN-CURATED: Document key public interfaces per module -->"
660
+ echo ""
661
+ echo "### [Module Name]"
662
+ echo ""
663
+ echo "**Public Interface**:"
664
+ echo '- `ClassName::method(params)` — One-line description'
665
+ echo ""
666
+ echo "**Depends on**: [Module1, Module2]"
667
+ echo "**Depended by**: [Module3, Module4]"
668
+ echo ""
669
+ echo "---"
670
+ echo ""
671
+ echo "$marker"
672
+ echo ""
673
+ extract_module_deps "$langs"
674
+ extract_namespaces "$langs"
675
+ echo ""
676
+ echo "## Circular Dependencies"
677
+ echo ""
678
+ echo "None detected. <!-- Update if circular dependencies are found -->"
679
+ } > "$output"
680
+ fi
681
+
682
+ echo " → Generated $output"
683
+ }
684
+
685
+ # ============================================================
686
+ # Main
687
+ # ============================================================
688
+
689
+ echo "========================================="
690
+ echo " Structure Extraction"
691
+ echo "========================================="
692
+ echo ""
693
+
694
+ LANGS=$(detect_languages)
695
+ echo "Detected languages: $LANGS"
696
+
697
+ if [ -z "$LANGS" ]; then
698
+ echo "Error: No supported source files found."
699
+ exit 1
700
+ fi
701
+
702
+ # Detect or use provided source directory
703
+ if [ -z "$SRC_DIR" ]; then
704
+ SRC_DIR=$(detect_src_dir)
705
+ fi
706
+ echo "Source directory: $SRC_DIR"
707
+ echo ""
708
+
709
+ # Parse arguments
710
+ TARGET="${1:---all}"
711
+ shift || true
712
+
713
+ while [ $# -gt 0 ]; do
714
+ case "$1" in
715
+ --src-dir)
716
+ SRC_DIR="$2"
717
+ shift 2
718
+ ;;
719
+ --output-dir)
720
+ OUTPUT_DIR="$2"
721
+ shift 2
722
+ ;;
723
+ *)
724
+ shift
725
+ ;;
726
+ esac
727
+ done
728
+
729
+ case "$TARGET" in
730
+ --all)
731
+ generate_class_index "$LANGS"
732
+ generate_module_graph "$LANGS"
733
+ ;;
734
+ --class-index)
735
+ generate_class_index "$LANGS"
736
+ ;;
737
+ --module-graph)
738
+ generate_module_graph "$LANGS"
739
+ ;;
740
+ --namespaces)
741
+ extract_namespaces "$LANGS"
742
+ ;;
743
+ --functions)
744
+ extract_functions "$LANGS"
745
+ ;;
746
+ *)
747
+ echo "Usage: bash scripts/extract-structure.sh [--all | --class-index | --module-graph | --namespaces | --functions]"
748
+ echo ""
749
+ echo "Options:"
750
+ echo " --src-dir DIR Source directory to scan (default: auto-detect)"
751
+ echo " --output-dir DIR Output directory (default: .context/architecture)"
752
+ exit 1
753
+ ;;
754
+ esac
755
+
756
+ echo ""
757
+ echo "Done. Review the generated files and fill in human-curated sections."