@anhth2/spec-driven-dev-plugin 0.5.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.
- package/ARCHITECTURE.md +243 -0
- package/bin/build.js +230 -0
- package/bin/index.js +311 -0
- package/commands/debug.md +374 -0
- package/commands/debug.tmpl +77 -0
- package/commands/define-product.md +451 -0
- package/commands/define-product.tmpl +154 -0
- package/commands/fix-bug.md +379 -0
- package/commands/fix-bug.tmpl +82 -0
- package/commands/generate-bdd.md +591 -0
- package/commands/generate-bdd.tmpl +294 -0
- package/commands/generate-code.md +395 -0
- package/commands/generate-code.tmpl +98 -0
- package/commands/generate-prd.md +488 -0
- package/commands/generate-prd.tmpl +191 -0
- package/commands/generate-tech-docs.md +362 -0
- package/commands/generate-tech-docs.tmpl +65 -0
- package/commands/generate-tests.md +377 -0
- package/commands/generate-tests.tmpl +80 -0
- package/commands/refine-prd.md +408 -0
- package/commands/refine-prd.tmpl +111 -0
- package/commands/review-code.md +354 -0
- package/commands/review-code.tmpl +57 -0
- package/commands/review-context.md +646 -0
- package/commands/review-context.tmpl +349 -0
- package/commands/review-tech-docs.md +518 -0
- package/commands/review-tech-docs.tmpl +221 -0
- package/commands/run-tests.md +343 -0
- package/commands/run-tests.tmpl +46 -0
- package/commands/setup-ai-first.md +278 -0
- package/commands/setup-ai-first.tmpl +197 -0
- package/commands/smoke-test.md +366 -0
- package/commands/smoke-test.tmpl +69 -0
- package/commands/validate-traces.md +529 -0
- package/commands/validate-traces.tmpl +232 -0
- package/core/FRAMEWORK_VERSION +1 -0
- package/core/commands/debug.md +374 -0
- package/core/commands/define-product.md +451 -0
- package/core/commands/fix-bug.md +379 -0
- package/core/commands/generate-bdd.md +591 -0
- package/core/commands/generate-code.md +395 -0
- package/core/commands/generate-prd.md +488 -0
- package/core/commands/generate-tech-docs.md +362 -0
- package/core/commands/generate-tests.md +377 -0
- package/core/commands/refine-prd.md +408 -0
- package/core/commands/review-code.md +354 -0
- package/core/commands/review-context.md +646 -0
- package/core/commands/review-tech-docs.md +518 -0
- package/core/commands/run-tests.md +343 -0
- package/core/commands/setup-ai-first.md +278 -0
- package/core/commands/smoke-test.md +366 -0
- package/core/commands/validate-traces.md +529 -0
- package/core/hooks/data-guard.js +141 -0
- package/core/hooks/settings.json +18 -0
- package/core/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/core/modules/angular/module.yaml +6 -0
- package/core/modules/angular/stack-profile.yaml +38 -0
- package/core/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/core/modules/context-engineering/module.yaml +9 -0
- package/core/modules/context-engineering/stack-profile.yaml +61 -0
- package/core/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/core/modules/dotnet/module.yaml +6 -0
- package/core/modules/dotnet/stack-profile.yaml +50 -0
- package/core/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/core/modules/golang/module.yaml +6 -0
- package/core/modules/golang/stack-profile.yaml +40 -0
- package/core/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/core/modules/java-spring/module.yaml +15 -0
- package/core/modules/java-spring/stack-profile.yaml +28 -0
- package/core/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/core/modules/nextjs/module.yaml +14 -0
- package/core/modules/nextjs/stack-profile.yaml +74 -0
- package/core/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/core/modules/php-laravel/module.yaml +15 -0
- package/core/modules/php-laravel/stack-profile.yaml +56 -0
- package/core/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/core/modules/react/module.yaml +14 -0
- package/core/modules/react/stack-profile.yaml +63 -0
- package/core/rules/data-protection.md +80 -0
- package/core/rules/workflow.md +44 -0
- package/core/skills/code/SKILL.md +526 -0
- package/core/skills/debug/SKILL.md +584 -0
- package/core/skills/discovery/SKILL.md +363 -0
- package/core/skills/prd/SKILL.md +456 -0
- package/core/skills/setup-ai-first/SKILL.md +160 -0
- package/core/skills/spec/SKILL.md +361 -0
- package/core/skills/test/SKILL.md +862 -0
- package/core/steps/context-loader.md +163 -0
- package/core/steps/gate.md +81 -0
- package/core/steps/report-footer.md +53 -0
- package/core/steps/spawn-agent.md +123 -0
- package/core/templates/architecture.template.md +113 -0
- package/core/templates/feature.template +259 -0
- package/core/templates/platform-guide.template.md +145 -0
- package/core/templates/prd.template.md +312 -0
- package/core/templates/product-definition.template.md +168 -0
- package/core/templates/project-context.yaml +78 -0
- package/hooks/data-guard.js +141 -0
- package/hooks/settings.json +18 -0
- package/modules/angular/architecture-snippets/component-patterns.md +187 -0
- package/modules/angular/module.yaml +6 -0
- package/modules/angular/stack-profile.yaml +38 -0
- package/modules/context-engineering/architecture-snippets/context-design.md +119 -0
- package/modules/context-engineering/module.yaml +9 -0
- package/modules/context-engineering/stack-profile.yaml +61 -0
- package/modules/dotnet/architecture-snippets/clean-arch.md +160 -0
- package/modules/dotnet/module.yaml +6 -0
- package/modules/dotnet/stack-profile.yaml +50 -0
- package/modules/golang/architecture-snippets/domain-layout.md +283 -0
- package/modules/golang/module.yaml +6 -0
- package/modules/golang/stack-profile.yaml +40 -0
- package/modules/java-spring/architecture-snippets/layered-arch.md +201 -0
- package/modules/java-spring/module.yaml +15 -0
- package/modules/java-spring/stack-profile.yaml +28 -0
- package/modules/nextjs/architecture-snippets/app-router-patterns.md +269 -0
- package/modules/nextjs/module.yaml +14 -0
- package/modules/nextjs/stack-profile.yaml +74 -0
- package/modules/php-laravel/architecture-snippets/service-repository.md +302 -0
- package/modules/php-laravel/module.yaml +15 -0
- package/modules/php-laravel/stack-profile.yaml +56 -0
- package/modules/react/architecture-snippets/hooks-query-patterns.md +254 -0
- package/modules/react/module.yaml +14 -0
- package/modules/react/stack-profile.yaml +63 -0
- package/package.json +42 -0
- package/rules/data-protection.md +80 -0
- package/rules/workflow.md +44 -0
- package/scripts/init.sh +49 -0
- package/scripts/upgrade.sh +94 -0
- package/skills/code/SKILL.md +526 -0
- package/skills/code/SKILL.tmpl +176 -0
- package/skills/debug/SKILL.md +584 -0
- package/skills/debug/SKILL.tmpl +262 -0
- package/skills/discovery/SKILL.md +363 -0
- package/skills/discovery/SKILL.tmpl +147 -0
- package/skills/prd/SKILL.md +456 -0
- package/skills/prd/SKILL.tmpl +188 -0
- package/skills/setup-ai-first/SKILL.md +160 -0
- package/skills/setup-ai-first/SKILL.tmpl +107 -0
- package/skills/spec/SKILL.md +361 -0
- package/skills/spec/SKILL.tmpl +174 -0
- package/skills/test/SKILL.md +862 -0
- package/skills/test/SKILL.tmpl +296 -0
- package/steps/context-loader.md +163 -0
- package/steps/gate.md +81 -0
- package/steps/report-footer.md +53 -0
- package/steps/spawn-agent.md +123 -0
- package/templates/architecture.template.md +113 -0
- package/templates/feature.template +259 -0
- package/templates/platform-guide.template.md +145 -0
- package/templates/prd.template.md +312 -0
- package/templates/product-definition.template.md +168 -0
- package/templates/project-context.yaml +78 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Framework Architecture
|
|
2
|
+
|
|
3
|
+
> **Nguyên tắc**: Nhìn file này để hiểu toàn bộ framework trước khi đọc bất kỳ file chi tiết nào.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Layer Diagram
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
╔══════════════════════════════════════════════════════════════════════╗
|
|
11
|
+
║ SPEC-DRIVEN DEV FRAMEWORK v0.3 ║
|
|
12
|
+
╚══════════════════════════════════════════════════════════════════════╝
|
|
13
|
+
|
|
14
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
15
|
+
│ LAYER 0 — PROTECTION (luôn chạy trước) │
|
|
16
|
+
│ │
|
|
17
|
+
│ hooks/data-guard.js → PreToolUse hook: chặn đọc file nhạy cảm │
|
|
18
|
+
│ rules/data-protection.md → Quy tắc declarative cho AI agent │
|
|
19
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
20
|
+
│ safe ↓ blocked → ✋ STOP
|
|
21
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
22
|
+
│ LAYER 1 — ENTRY POINT (user trigger) │
|
|
23
|
+
│ │
|
|
24
|
+
│ commands/*.tmpl skills/*/SKILL.tmpl │
|
|
25
|
+
│ (slash commands) (auto-trigger by description match) │
|
|
26
|
+
│ /generate-bdd detect: "tạo BDD", "sinh feature" │
|
|
27
|
+
│ /generate-code detect: "viết code", "implement" │
|
|
28
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
29
|
+
│ both built by bin/build.js
|
|
30
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
31
|
+
│ LAYER 2 — SHARED STEPS (DRY, injected) │
|
|
32
|
+
│ │
|
|
33
|
+
│ steps/gate.md → resolve target file + CHECKPOINT │
|
|
34
|
+
│ steps/context-loader.md → load project config + rules │
|
|
35
|
+
│ steps/report-footer.md → standard output format │
|
|
36
|
+
│ │
|
|
37
|
+
│ Injected at build time via {{include:steps/X.md}} │
|
|
38
|
+
│ Source: *.tmpl → Output: *.md (gitignored) │
|
|
39
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
40
|
+
│ context loaded
|
|
41
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
42
|
+
│ LAYER 3 — PROJECT CONTEXT (read from consumer project) │
|
|
43
|
+
│ │
|
|
44
|
+
│ .agent/project-context.yaml → paths, tech_stack, domains │
|
|
45
|
+
│ CLAUDE.md → architecture, coding standards │
|
|
46
|
+
│ rules/data-protection.md → what AI must never access │
|
|
47
|
+
│ .agent/modules/{stack}/ → stack rules (plug-in, optional) │
|
|
48
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
49
|
+
│ context-aware
|
|
50
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
51
|
+
│ LAYER 4 — EXECUTION (command logic) │
|
|
52
|
+
│ │
|
|
53
|
+
│ DISCOVERY PRD / BDD CODE / TEST DEBUG │
|
|
54
|
+
│ /define-product /generate-prd /generate-code /fix-bug │
|
|
55
|
+
│ /refine-prd /generate-tests /debug │
|
|
56
|
+
│ /generate-bdd /run-tests /validate- │
|
|
57
|
+
│ /generate-tech-docs /smoke-test traces │
|
|
58
|
+
│ /review-code /setup-ai-first │
|
|
59
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
60
|
+
│
|
|
61
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
62
|
+
│ LAYER 5 — OUTPUT (artifacts in consumer proj) │
|
|
63
|
+
│ │
|
|
64
|
+
│ specs/product-definition/ specs/prd/ specs/bdd/ │
|
|
65
|
+
│ tech-docs/ src/ .trace/ .agent/review/ │
|
|
66
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Data Flow — Một command chạy như thế nào
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
User types: /generate-bdd specs/prd/payment/PAY-01.md
|
|
75
|
+
│
|
|
76
|
+
▼
|
|
77
|
+
[L0] data-guard.js checks tool calls in real-time
|
|
78
|
+
→ nếu AI cố đọc .env / *.key → BLOCK + warn
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
[L1] commands/generate-bdd.md được Claude đọc
|
|
82
|
+
(assembled từ generate-bdd.tmpl + injected steps)
|
|
83
|
+
│
|
|
84
|
+
▼
|
|
85
|
+
[L2] gate.md → resolve file path từ $ARGUMENTS
|
|
86
|
+
context-loader.md → đọc project-context.yaml, CLAUDE.md,
|
|
87
|
+
rules/data-protection.md,
|
|
88
|
+
modules/{stack}/stack-profile.yaml
|
|
89
|
+
│
|
|
90
|
+
▼
|
|
91
|
+
[L3] Claude biết: tech_stack, domains, architecture rules,
|
|
92
|
+
sensitive files to avoid, stack-specific patterns
|
|
93
|
+
│
|
|
94
|
+
▼
|
|
95
|
+
[L4] generate-bdd logic:
|
|
96
|
+
→ đọc PRD → extract UC/BR/AC
|
|
97
|
+
→ apply BDD rules R1-R10
|
|
98
|
+
→ viết specs/bdd/{domain}/{UC-ID}.feature
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
[L5] Output: specs/bdd/payment/PAY-01-UC1.feature
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Build System
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Source (committed to git) Build output (gitignored)
|
|
110
|
+
────────────────────────── ─────────────────────────
|
|
111
|
+
commands/*.tmpl ──┐
|
|
112
|
+
skills/**/SKILL.tmpl ──┤ node bin/build.js → commands/*.md
|
|
113
|
+
steps/*.md (shared) ──┘ skills/**/SKILL.md
|
|
114
|
+
|
|
115
|
+
Trigger:
|
|
116
|
+
npm run build ← manual
|
|
117
|
+
prepublishOnly hook ← auto before npm publish
|
|
118
|
+
bin/index.js install ← auto when user runs npx
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Module Plug-in System
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Available modules (modules/):
|
|
127
|
+
java-spring, angular, react, nextjs,
|
|
128
|
+
dotnet, golang, php-laravel, context-engineering
|
|
129
|
+
|
|
130
|
+
Usage:
|
|
131
|
+
npx @anhth2/spec-driven-dev --module java-spring
|
|
132
|
+
└─ copies modules/java-spring/ → consumer/.agent/modules/java-spring/
|
|
133
|
+
|
|
134
|
+
At runtime, context-loader.md reads:
|
|
135
|
+
.agent/modules/{tech_stack.module}/stack-profile.yaml
|
|
136
|
+
.agent/modules/{tech_stack.module}/architecture-snippets/
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Hook System — Data Protection
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
Consumer project setup:
|
|
145
|
+
.claude/settings.json ← registers hook (provided as template)
|
|
146
|
+
hooks/data-guard.js ← copied from this package on install
|
|
147
|
+
|
|
148
|
+
Runtime:
|
|
149
|
+
Every tool use (Read, Write, Edit, Bash)
|
|
150
|
+
│
|
|
151
|
+
▼
|
|
152
|
+
data-guard.js checks:
|
|
153
|
+
.env* / *.key / *.pem / *secret* / *password* / *credential*
|
|
154
|
+
application-prod.* / appsettings.Production.*
|
|
155
|
+
│
|
|
156
|
+
safe → allow blocked → exit(2) + warn user
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Future — Sub-Agent Pattern (v2 roadmap)
|
|
162
|
+
|
|
163
|
+
Khi một số command quá nặng cho single context window:
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
Main session (orchestrator — lightweight, chỉ coordinate)
|
|
167
|
+
│
|
|
168
|
+
├─ spawn spec-agent ──→ /refine-prd analysis (own context window)
|
|
169
|
+
│ └─ returns: findings.yaml
|
|
170
|
+
│
|
|
171
|
+
├─ spawn codegen-agent ──→ /generate-code UC1 (own context window)
|
|
172
|
+
│ └─ returns: src/ changes
|
|
173
|
+
│
|
|
174
|
+
└─ spawn test-agent ──→ /generate-tests UC1 (own context window)
|
|
175
|
+
└─ returns: test files
|
|
176
|
+
|
|
177
|
+
Benefits:
|
|
178
|
+
- Main session không bị bloat bởi large file reads
|
|
179
|
+
- Mỗi agent focus vào 1 task, ít hallucination hơn
|
|
180
|
+
- Parallel execution cho multiple UCs
|
|
181
|
+
|
|
182
|
+
Implementation path:
|
|
183
|
+
1. Tạo steps/spawn-agent.md — pattern + handoff payload format
|
|
184
|
+
2. Tạo commands/orchestrate-feature.tmpl — top-level orchestrator
|
|
185
|
+
3. Sub-agent commands nhận input qua $ARGUMENTS (JSON payload)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Directory Map
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
spec-driven-dev/
|
|
194
|
+
├── ARCHITECTURE.md ← Đọc đây trước ◀◀◀
|
|
195
|
+
├── bin/
|
|
196
|
+
│ ├── build.js ← assembles *.tmpl → *.md
|
|
197
|
+
│ └── index.js ← npm installer + hook installer
|
|
198
|
+
├── commands/
|
|
199
|
+
│ └── *.tmpl ← slash commands (14 commands)
|
|
200
|
+
├── hooks/
|
|
201
|
+
│ ├── data-guard.js ← PreToolUse sensitive file protection
|
|
202
|
+
│ └── settings.json ← hook registration template
|
|
203
|
+
├── modules/
|
|
204
|
+
│ └── {stack}/ ← 8 stacks: java-spring, angular, react,
|
|
205
|
+
│ ├── module.yaml nextjs, dotnet, golang, php-laravel,
|
|
206
|
+
│ ├── stack-profile.yaml context-engineering
|
|
207
|
+
│ └── architecture-snippets/
|
|
208
|
+
├── rules/
|
|
209
|
+
│ ├── data-protection.md ← what AI must NEVER read/write
|
|
210
|
+
│ └── workflow.md ← general AI behavior rules
|
|
211
|
+
├── skills/
|
|
212
|
+
│ └── {name}/SKILL.tmpl ← Claude plugin skills (7 skills)
|
|
213
|
+
├── steps/
|
|
214
|
+
│ ├── gate.md ← shared: file resolve + checkpoint
|
|
215
|
+
│ ├── context-loader.md ← shared: load all project context
|
|
216
|
+
│ └── report-footer.md ← shared: standard output format
|
|
217
|
+
└── templates/
|
|
218
|
+
├── project-context.yaml ← consumer project config template
|
|
219
|
+
├── architecture.template.md
|
|
220
|
+
└── platform-guide.template.md
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Maintenance Guide
|
|
226
|
+
|
|
227
|
+
| Muốn thay đổi gì | Sửa file nào |
|
|
228
|
+
|------------------|-------------|
|
|
229
|
+
| Logic của 1 command cụ thể | `commands/{name}.tmpl` |
|
|
230
|
+
| Logic của 1 skill cụ thể | `skills/{name}/SKILL.tmpl` |
|
|
231
|
+
| Gate / checkpoint pattern | `steps/gate.md` |
|
|
232
|
+
| Context loading | `steps/context-loader.md` |
|
|
233
|
+
| Report format | `steps/report-footer.md` |
|
|
234
|
+
| Sensitive file patterns | `hooks/data-guard.js` + `rules/data-protection.md` |
|
|
235
|
+
| Stack-specific rules | `modules/{stack}/stack-profile.yaml` |
|
|
236
|
+
| Project setup template | `templates/project-context.yaml` |
|
|
237
|
+
| Build system | `bin/build.js` |
|
|
238
|
+
| Installer | `bin/index.js` |
|
|
239
|
+
|
|
240
|
+
Sau khi sửa bất kỳ `.tmpl` hoặc `steps/*.md`:
|
|
241
|
+
```bash
|
|
242
|
+
npm run build # regenerate tất cả *.md
|
|
243
|
+
```
|
package/bin/build.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* build.js — Assembles templates → .md files
|
|
5
|
+
*
|
|
6
|
+
* Processes:
|
|
7
|
+
* commands/*.tmpl → commands/*.md
|
|
8
|
+
* skills/**\/SKILL.tmpl → skills/**\/SKILL.md
|
|
9
|
+
*
|
|
10
|
+
* Replaces {{include:steps/gate.md}}, {{include:steps/context-loader.md}},
|
|
11
|
+
* and {{include:steps/report-footer.md}} with the actual file contents.
|
|
12
|
+
*
|
|
13
|
+
* Usage: node bin/build.js
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const ROOT = path.join(__dirname, '..');
|
|
20
|
+
const COMMANDS_DIR = path.join(ROOT, 'commands');
|
|
21
|
+
const SKILLS_DIR = path.join(ROOT, 'skills');
|
|
22
|
+
|
|
23
|
+
// Cache for step file contents
|
|
24
|
+
const stepCache = {};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load a step file by name, caching the result.
|
|
28
|
+
* @param {string} stepPath - e.g. "steps/gate.md"
|
|
29
|
+
* @returns {string} file contents
|
|
30
|
+
*/
|
|
31
|
+
function loadStep(stepPath) {
|
|
32
|
+
if (stepCache[stepPath]) return stepCache[stepPath];
|
|
33
|
+
|
|
34
|
+
const fullPath = path.join(ROOT, stepPath);
|
|
35
|
+
if (!fs.existsSync(fullPath)) {
|
|
36
|
+
throw new Error(`Step file not found: ${fullPath}`);
|
|
37
|
+
}
|
|
38
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
39
|
+
stepCache[stepPath] = content;
|
|
40
|
+
return content;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Process a single .tmpl file: resolve all {{include:...}} markers
|
|
45
|
+
* and write the output as a .md file (replacing .tmpl extension with .md).
|
|
46
|
+
*
|
|
47
|
+
* @param {string} tmplFile - full path to the .tmpl file
|
|
48
|
+
* @param {string} label - display label for logging (e.g. "generate-code")
|
|
49
|
+
* @returns {{ label: string, success: boolean, error?: string }}
|
|
50
|
+
*/
|
|
51
|
+
function buildTemplate(tmplFile, label) {
|
|
52
|
+
const outFile = tmplFile.replace(/\.tmpl$/, '.md');
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
let content = fs.readFileSync(tmplFile, 'utf8');
|
|
56
|
+
|
|
57
|
+
// Replace all {{include:...}} markers
|
|
58
|
+
content = content.replace(/\{\{include:([^}]+)\}\}/g, (match, includePath) => {
|
|
59
|
+
const trimmed = includePath.trim();
|
|
60
|
+
return loadStep(trimmed);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
fs.writeFileSync(outFile, content, 'utf8');
|
|
64
|
+
return { label, success: true };
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return { label, success: false, error: err.message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Recursively find all files matching a predicate under a directory.
|
|
72
|
+
* @param {string} dir
|
|
73
|
+
* @param {(filename: string) => boolean} predicate
|
|
74
|
+
* @returns {string[]} absolute paths
|
|
75
|
+
*/
|
|
76
|
+
function findFiles(dir, predicate) {
|
|
77
|
+
if (!fs.existsSync(dir)) return [];
|
|
78
|
+
const results = [];
|
|
79
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
80
|
+
const fullPath = path.join(dir, entry.name);
|
|
81
|
+
if (entry.isDirectory()) {
|
|
82
|
+
results.push(...findFiles(fullPath, predicate));
|
|
83
|
+
} else if (predicate(entry.name)) {
|
|
84
|
+
results.push(fullPath);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return results;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
let allSucceeded = 0;
|
|
93
|
+
let allFailed = 0;
|
|
94
|
+
|
|
95
|
+
// ── 1. Build commands/*.tmpl → commands/*.md ──────────────────────────────────
|
|
96
|
+
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log('Building commands from templates...');
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
const commandTmplFiles = fs.readdirSync(COMMANDS_DIR)
|
|
102
|
+
.filter(f => f.endsWith('.tmpl'))
|
|
103
|
+
.map(f => path.join(COMMANDS_DIR, f));
|
|
104
|
+
|
|
105
|
+
if (commandTmplFiles.length === 0) {
|
|
106
|
+
console.log(' (no .tmpl files found in commands/)');
|
|
107
|
+
} else {
|
|
108
|
+
const commandResults = commandTmplFiles.map(f => {
|
|
109
|
+
const basename = path.basename(f, '.tmpl');
|
|
110
|
+
return buildTemplate(f, basename);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
for (const r of commandResults) {
|
|
114
|
+
if (r.success) {
|
|
115
|
+
console.log(` ✅ ${r.label}.tmpl → ${r.label}.md`);
|
|
116
|
+
allSucceeded++;
|
|
117
|
+
} else {
|
|
118
|
+
console.log(` ❌ ${r.label}.tmpl → FAILED: ${r.error}`);
|
|
119
|
+
allFailed++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── 2. Build skills/**/SKILL.tmpl → skills/**/SKILL.md ───────────────────────
|
|
125
|
+
|
|
126
|
+
console.log('');
|
|
127
|
+
console.log('Building skills from templates...');
|
|
128
|
+
console.log('');
|
|
129
|
+
|
|
130
|
+
const skillTmplFiles = findFiles(SKILLS_DIR, name => name === 'SKILL.tmpl');
|
|
131
|
+
|
|
132
|
+
if (skillTmplFiles.length === 0) {
|
|
133
|
+
console.log(' (no SKILL.tmpl files found in skills/)');
|
|
134
|
+
} else {
|
|
135
|
+
const skillResults = skillTmplFiles.map(f => {
|
|
136
|
+
// Use the parent directory name as the label (e.g., "code", "debug")
|
|
137
|
+
const skillName = path.basename(path.dirname(f));
|
|
138
|
+
return buildTemplate(f, `skills/${skillName}/SKILL`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
for (const r of skillResults) {
|
|
142
|
+
if (r.success) {
|
|
143
|
+
console.log(` ✅ ${r.label}.tmpl → ${r.label}.md`);
|
|
144
|
+
allSucceeded++;
|
|
145
|
+
} else {
|
|
146
|
+
console.log(` ❌ ${r.label}.tmpl → FAILED: ${r.error}`);
|
|
147
|
+
allFailed++;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── 3. Populate core/ directory (distributable) ──────────────────────────────
|
|
153
|
+
//
|
|
154
|
+
// core/ mirrors what gets installed into .agent/ in consumer projects.
|
|
155
|
+
// It is gitignored but included in the npm package (via "files" whitelist).
|
|
156
|
+
|
|
157
|
+
const CORE_DIR = path.join(ROOT, 'core');
|
|
158
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
|
159
|
+
const VERSION = pkg.version;
|
|
160
|
+
|
|
161
|
+
console.log('');
|
|
162
|
+
console.log('Populating core/ (distributable) ...');
|
|
163
|
+
console.log('');
|
|
164
|
+
|
|
165
|
+
// Clean and recreate core/
|
|
166
|
+
if (fs.existsSync(CORE_DIR)) {
|
|
167
|
+
fs.rmSync(CORE_DIR, { recursive: true });
|
|
168
|
+
}
|
|
169
|
+
fs.mkdirSync(CORE_DIR, { recursive: true });
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Recursively copy src → dest, optionally filtering files by name.
|
|
173
|
+
* @param {string} src
|
|
174
|
+
* @param {string} dest
|
|
175
|
+
* @param {((name: string) => boolean) | null} fileFilter null = copy all files
|
|
176
|
+
*/
|
|
177
|
+
function copyDirToCore(src, dest, fileFilter) {
|
|
178
|
+
if (!fs.existsSync(src)) return;
|
|
179
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
180
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
181
|
+
const srcPath = path.join(src, entry.name);
|
|
182
|
+
const destPath = path.join(dest, entry.name);
|
|
183
|
+
if (entry.isDirectory()) {
|
|
184
|
+
copyDirToCore(srcPath, destPath, fileFilter);
|
|
185
|
+
} else if (!fileFilter || fileFilter(entry.name)) {
|
|
186
|
+
fs.copyFileSync(srcPath, destPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const coreMappings = [
|
|
192
|
+
// Built command .md files (not .tmpl sources)
|
|
193
|
+
{ src: COMMANDS_DIR, dest: path.join(CORE_DIR, 'commands'), filter: f => f.endsWith('.md') },
|
|
194
|
+
// Step files (gate, context-loader, spawn-agent, report-footer)
|
|
195
|
+
{ src: path.join(ROOT, 'steps'), dest: path.join(CORE_DIR, 'steps'), filter: null },
|
|
196
|
+
// Hook scripts
|
|
197
|
+
{ src: path.join(ROOT, 'hooks'), dest: path.join(CORE_DIR, 'hooks'), filter: null },
|
|
198
|
+
// Data-protection and workflow rules
|
|
199
|
+
{ src: path.join(ROOT, 'rules'), dest: path.join(CORE_DIR, 'rules'), filter: null },
|
|
200
|
+
// PRD / architecture templates
|
|
201
|
+
{ src: path.join(ROOT, 'templates'), dest: path.join(CORE_DIR, 'templates'), filter: null },
|
|
202
|
+
// Stack module profiles
|
|
203
|
+
{ src: path.join(ROOT, 'modules'), dest: path.join(CORE_DIR, 'modules'), filter: null },
|
|
204
|
+
// Built skill .md files (not .tmpl sources)
|
|
205
|
+
{ src: SKILLS_DIR, dest: path.join(CORE_DIR, 'skills'), filter: f => !f.endsWith('.tmpl') },
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
for (const { src, dest, filter } of coreMappings) {
|
|
209
|
+
copyDirToCore(src, dest, filter);
|
|
210
|
+
const rel = path.relative(ROOT, dest);
|
|
211
|
+
console.log(` ✅ ${rel}/`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Write FRAMEWORK_VERSION so upgrade.sh can compare installed vs latest
|
|
215
|
+
fs.writeFileSync(path.join(CORE_DIR, 'FRAMEWORK_VERSION'), VERSION + '\n', 'utf8');
|
|
216
|
+
console.log(` ✅ core/FRAMEWORK_VERSION (v${VERSION})`);
|
|
217
|
+
|
|
218
|
+
// ── Summary ───────────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
const total = allSucceeded + allFailed;
|
|
221
|
+
console.log('');
|
|
222
|
+
|
|
223
|
+
if (allFailed === 0) {
|
|
224
|
+
console.log(`Build complete: ${allSucceeded}/${total} templates assembled.`);
|
|
225
|
+
} else {
|
|
226
|
+
console.log(`Build finished with errors: ${allSucceeded}/${total} succeeded.`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
console.log('');
|