@grvmithas/create-aies-kit 1.0.0 → 1.3.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/README.md +36 -25
- package/bin/create-aies-kit.js +119 -41
- package/lib/kit-core/embedded-corpus.js +138 -0
- package/lib/kit-core/generate.js +794 -0
- package/lib/kit-core/install-api.js +173 -0
- package/lib/kit-core/mcp-catalog.js +170 -0
- package/lib/{options.js → kit-core/options.js} +25 -10
- package/package.json +10 -4
- package/lib/scaffold.js +0 -352
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { EMBEDDED_CORPUS } from './embedded-corpus.js';
|
|
3
|
+
import {
|
|
4
|
+
MCP_SPECS,
|
|
5
|
+
buildRecommendedMcpConfig,
|
|
6
|
+
detectRepoSignals,
|
|
7
|
+
stackMcpIds,
|
|
8
|
+
} from './mcp-catalog.js';
|
|
9
|
+
import { describeInstallLayout, layoutFor } from './install-api.js';
|
|
10
|
+
import { SITE_URL, STACK_LABELS, STACKS } from './options.js';
|
|
11
|
+
|
|
12
|
+
export { buildInstallQuery, describeInstallLayout, getInstallCommands, layoutFor } from './install-api.js';
|
|
13
|
+
|
|
14
|
+
const CORE_SKILLS = [
|
|
15
|
+
'planning',
|
|
16
|
+
'debug',
|
|
17
|
+
'devops',
|
|
18
|
+
'code-review',
|
|
19
|
+
'evaluation',
|
|
20
|
+
'security',
|
|
21
|
+
'testing',
|
|
22
|
+
'architecture',
|
|
23
|
+
'context',
|
|
24
|
+
'prompt',
|
|
25
|
+
'documentation',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const CORE_PROMPTS = [
|
|
29
|
+
'story-kickoff',
|
|
30
|
+
'implementation-plan',
|
|
31
|
+
'coding',
|
|
32
|
+
'security-review',
|
|
33
|
+
'enhance-repo',
|
|
34
|
+
'create-prompt',
|
|
35
|
+
'create-skill',
|
|
36
|
+
'create-agent',
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const CORE_AGENTS = ['planner', 'coder', 'reviewer', 'prompt-engineer'];
|
|
40
|
+
|
|
41
|
+
function corpus(key) {
|
|
42
|
+
const body = EMBEDDED_CORPUS[key];
|
|
43
|
+
if (!body) {
|
|
44
|
+
return `<!-- Missing embedded corpus: ${key}. Re-run: npm run kit:embed -->\n`;
|
|
45
|
+
}
|
|
46
|
+
return body;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function stackMeta(stack) {
|
|
50
|
+
return STACKS.find(s => s.id === stack) ?? { id: stack, label: stack, skillKey: null, cookbook: null };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function stripFrontmatter(md) {
|
|
54
|
+
if (!md.startsWith('---')) return md;
|
|
55
|
+
const end = md.indexOf('\n---', 3);
|
|
56
|
+
if (end === -1) return md;
|
|
57
|
+
return md.slice(end + 4).replace(/^\s+/, '');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function skillDescription(name, body) {
|
|
61
|
+
const purpose = body.match(/## Purpose\s*\n+([^\n]+)/);
|
|
62
|
+
if (purpose) return purpose[1].trim().slice(0, 180);
|
|
63
|
+
return `AIES ${name} skill — use when this procedure applies.`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeSkill(files, L, name, body) {
|
|
67
|
+
if (L.skillStyle === 'cursor-skill') {
|
|
68
|
+
const cleaned = stripFrontmatter(body);
|
|
69
|
+
const desc = skillDescription(name, body).replace(/"/g, "'").replace(/\r?\n/g, ' ');
|
|
70
|
+
files[`${L.skillsDir}/${name}/SKILL.md`] = `---
|
|
71
|
+
name: ${name}
|
|
72
|
+
description: "${desc}"
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
${cleaned}
|
|
76
|
+
`;
|
|
77
|
+
} else {
|
|
78
|
+
files[`${L.skillsDir}/${name}.skill.md`] = body;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function modelPolicy(profile) {
|
|
83
|
+
const model = profile.model ?? 'undecided';
|
|
84
|
+
const dataClass = profile.dataClass ?? 'internal';
|
|
85
|
+
return `# Model & data policy (AIES)
|
|
86
|
+
|
|
87
|
+
Generated for: **${model}** · data class **${dataClass}**
|
|
88
|
+
|
|
89
|
+
## Provider posture
|
|
90
|
+
|
|
91
|
+
| Choice | Guidance |
|
|
92
|
+
|--------|----------|
|
|
93
|
+
| claude / gpt / gemini | Pin model IDs in CI and agent configs; record versions in PRs that change prompts |
|
|
94
|
+
| multi | Route via an org gateway; do not hard-code vendor SDKs in feature code without an abstraction |
|
|
95
|
+
| local | Keep offline evals; never assume cloud tool APIs exist |
|
|
96
|
+
| undecided | Default to least privilege; pick a primary model before production |
|
|
97
|
+
|
|
98
|
+
## Data class rules (${dataClass})
|
|
99
|
+
|
|
100
|
+
${
|
|
101
|
+
dataClass === 'public'
|
|
102
|
+
? '- Public data may use cloud models with org-approved logging.\n- Still ban secrets in prompts.'
|
|
103
|
+
: dataClass === 'confidential'
|
|
104
|
+
? '- Confidential/regulated: prefer private endpoints / VPC; disable training opt-in.\n- No production PII in prompts, traces, or MCP tool args.\n- MCP servers stay empty until security approves allowlist.\n- Require HITL for any tool that can write external systems.'
|
|
105
|
+
: '- Internal data: use org-approved providers only.\n- Redact secrets and customer identifiers from prompts and logs.\n- MCP: enable only from \`_aiesRecommended\` after handbook review.'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
## Non-negotiables
|
|
109
|
+
|
|
110
|
+
1. Human review is accountable — model output is advisory
|
|
111
|
+
2. Multi-file work uses approved \`plans/{STORY_ID}-plan.md\` (ephemeral)
|
|
112
|
+
3. Keep \`mcpServers: {}\` until a human enables approved stubs
|
|
113
|
+
4. Eval gates for AI-touched paths before production
|
|
114
|
+
|
|
115
|
+
Online: ${SITE_URL}/docs/handbook/mcp-when-and-how
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function maturityReadingPath(maturity) {
|
|
120
|
+
if (maturity === 'beginner') {
|
|
121
|
+
return `1. \`guides/skills-vs-prompts-vs-agents.md\` + \`guides/human-in-the-loop.md\`
|
|
122
|
+
2. Run enhance (CLI + agent chat invoke in §0)
|
|
123
|
+
3. Create \`plans/{STORY_ID}-plan.md\` from \`plans/_TEMPLATE.md\`
|
|
124
|
+
4. Cookbook generate → coding → \`reviews/{STORY_ID}-code-review.md\`
|
|
125
|
+
5. Online: ${SITE_URL}/docs/handbook/README`;
|
|
126
|
+
}
|
|
127
|
+
if (maturity === 'intermediate') {
|
|
128
|
+
return `1. Enhance repo → ephemeral plan → cookbook generate/review
|
|
129
|
+
2. Wire \`guides/evals-as-quality-gates.md\` into CI
|
|
130
|
+
3. Use \`hooks/\` before commit; enable MCP only from recommended stubs
|
|
131
|
+
4. Online: ${SITE_URL}/workflows`;
|
|
132
|
+
}
|
|
133
|
+
if (maturity === 'advanced') {
|
|
134
|
+
return `1. Enhance repo + MCP handbook + agent hooks
|
|
135
|
+
2. Compose agents (planner → coder → reviewer) with budgets
|
|
136
|
+
3. Security skill before write tools; story-scoped plans/reviews
|
|
137
|
+
4. Online: ${SITE_URL}/docs/04-agent-engineering/README`;
|
|
138
|
+
}
|
|
139
|
+
return `1. Enhance repo + SoA mapping (${SITE_URL}/portal)
|
|
140
|
+
2. Migration bible: ${SITE_URL}/docs/handbook/ai-migration-bible-path
|
|
141
|
+
3. Eval CI + MCP allowlists + hooks + segregation of duties
|
|
142
|
+
4. Attach \`plans/{STORY_ID}-plan.md\` + \`reviews/{STORY_ID}-code-review.md\` on PRs — then stop maintaining them`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function roleNext(role) {
|
|
146
|
+
const map = {
|
|
147
|
+
'product-manager': 'idea',
|
|
148
|
+
architect: 'architecture',
|
|
149
|
+
frontend: 'ai-coding',
|
|
150
|
+
backend: 'ai-coding',
|
|
151
|
+
fullstack: 'implementation-planning',
|
|
152
|
+
qa: 'testing',
|
|
153
|
+
devops: 'devops',
|
|
154
|
+
'engineering-manager': 'project-kickoff',
|
|
155
|
+
'ai-engineer': 'implementation-planning',
|
|
156
|
+
};
|
|
157
|
+
return map[role] ?? 'implementation-planning';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function cookbookKeys(cookbookId) {
|
|
161
|
+
const prefix = `cookbook/${cookbookId}/`;
|
|
162
|
+
return Object.keys(EMBEDDED_CORPUS)
|
|
163
|
+
.filter(k => k.startsWith(prefix))
|
|
164
|
+
.sort();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function pathBasename(p) {
|
|
168
|
+
const parts = p.split('/');
|
|
169
|
+
return parts[parts.length - 1] ?? p;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function skillPath(L, name) {
|
|
173
|
+
return L.skillStyle === 'cursor-skill' ? `${L.skillsDir}/${name}/SKILL.md` : `${L.skillsDir}/${name}.skill.md`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function buildRootInstructions(profile, label, L) {
|
|
177
|
+
const enhancePrompt = `${L.promptsDir}/enhance-repo.prompt.md`;
|
|
178
|
+
const enhanceChat = invokeEnhanceChat(profile.ide, enhancePrompt);
|
|
179
|
+
return `# AIES project instructions — ${label}
|
|
180
|
+
|
|
181
|
+
This repository uses the **AI Engineering Standard (AIES)** harness.
|
|
182
|
+
|
|
183
|
+
## Profile
|
|
184
|
+
|
|
185
|
+
- Stack: ${label}
|
|
186
|
+
- Role: ${profile.role}
|
|
187
|
+
- Maturity: ${profile.maturity} (docs reading path only)
|
|
188
|
+
- Primary IDE selection: ${profile.ide}
|
|
189
|
+
- Model: ${profile.model ?? 'undecided'}
|
|
190
|
+
- Data class: ${profile.dataClass ?? 'internal'}
|
|
191
|
+
|
|
192
|
+
## Non-negotiables
|
|
193
|
+
|
|
194
|
+
1. Read \`AIES.md\` before coding with AI
|
|
195
|
+
2. Multi-file work requires approved \`plans/{STORY_ID}-plan.md\` (ephemeral — not a forever root plan)
|
|
196
|
+
3. Prefer installed skills/prompts/cookbook over ad-hoc chat
|
|
197
|
+
4. Human owns merges — AI is advisory; write \`reviews/{STORY_ID}-code-review.md\` on AI-touched PRs
|
|
198
|
+
5. No secrets in prompts, logs, or MCP args
|
|
199
|
+
6. Keep MCP \`mcpServers\` empty until a human enables stubs from \`_aiesRecommended\`
|
|
200
|
+
7. Respect this repo’s lint, typecheck, format, and style-guide configs — never weaken them
|
|
201
|
+
8. Honor agent hooks under \`hooks/\` for plan/code/review gates
|
|
202
|
+
|
|
203
|
+
## Where to look
|
|
204
|
+
|
|
205
|
+
| Need | Path |
|
|
206
|
+
|------|------|
|
|
207
|
+
| Operator guide | \`AIES.md\` |
|
|
208
|
+
| Plan index | \`implementation_plan.md\` |
|
|
209
|
+
| Active plan | \`plans/{STORY_ID}-plan.md\` |
|
|
210
|
+
| Review report | \`reviews/{STORY_ID}-code-review.md\` |
|
|
211
|
+
| Agent hooks | \`hooks/\` · \`${L.hooksFile}\` |
|
|
212
|
+
| Skills | \`${L.skillsDir}/\` |
|
|
213
|
+
| Prompts | \`${L.promptsDir}/\` |
|
|
214
|
+
| Agents | \`${L.agentsDir}/\` |
|
|
215
|
+
| Stack cookbook | \`${L.cookbookDir}/\` (repo root) |
|
|
216
|
+
| MCP specs | \`${L.mcpsDir}/\` |
|
|
217
|
+
| MCP config | \`${L.mcpFile}\` |
|
|
218
|
+
| Guides | \`${L.guidesDir}/\` |
|
|
219
|
+
| Policy | \`${L.policyFile}\` |
|
|
220
|
+
|
|
221
|
+
**Layout rule:** IDE folders hold **prompts, skills, and agents** only. Cookbook/guides/mcps/templates stay at repo root.
|
|
222
|
+
|
|
223
|
+
## First commands after install
|
|
224
|
+
|
|
225
|
+
\`\`\`bash
|
|
226
|
+
npx @grvmithas/create-aies-kit enhance
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
Then paste into agent chat:
|
|
230
|
+
|
|
231
|
+
\`\`\`text
|
|
232
|
+
${enhanceChat}
|
|
233
|
+
\`\`\`
|
|
234
|
+
|
|
235
|
+
## Workflow
|
|
236
|
+
|
|
237
|
+
Enhance → \`plans/{STORY_ID}-plan.md\` (approve) → coding with plan attached → verify (lint/test) → \`reviews/{STORY_ID}-code-review.md\` → PR
|
|
238
|
+
`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function buildAiesReadme({ profile, label, L, stack, flags, now, recommendedIds }) {
|
|
242
|
+
const model = profile.model ?? 'undecided';
|
|
243
|
+
const dataClass = profile.dataClass ?? 'internal';
|
|
244
|
+
const enhancePrompt = `${L.promptsDir}/enhance-repo.prompt.md`;
|
|
245
|
+
|
|
246
|
+
return `# AIES — Operator guide (${label})
|
|
247
|
+
|
|
248
|
+
**Generated:** ${now}
|
|
249
|
+
**Profile:** ${profile.role} · ${profile.ide} · ${profile.maturity} · ${label} · model **${model}** · data **${dataClass}**
|
|
250
|
+
|
|
251
|
+
This install is **always complete**. Maturity only changes the reading path — not which files were written.
|
|
252
|
+
There is **no** \`.aies/\` folder.
|
|
253
|
+
**IDE folders** hold prompts, skills, and agents. **Cookbook, guides, MCP specs, and templates** live at repo root (never under \`.github/cookbook\`).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 0. Start from the word go (do this first)
|
|
258
|
+
|
|
259
|
+
1. **Read this file** (AIES.md).
|
|
260
|
+
2. **Refresh kit signals** (MCP recommendations from repo scan):
|
|
261
|
+
|
|
262
|
+
\`\`\`bash
|
|
263
|
+
npx @grvmithas/create-aies-kit enhance
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
3. **Run the enhance prompt in your IDE agent chat** (copy-paste):
|
|
267
|
+
|
|
268
|
+
\`\`\`text
|
|
269
|
+
${invokeEnhanceChat(profile.ide, enhancePrompt)}
|
|
270
|
+
\`\`\`
|
|
271
|
+
|
|
272
|
+
Prompt path: \`${enhancePrompt}\`
|
|
273
|
+
That scan rewrites harness files to match **this** repo’s lint, style guides, FE/BE layout, and guardrails.
|
|
274
|
+
|
|
275
|
+
4. Create an **ephemeral** plan: \`plans/{STORY_ID}-plan.md\` (see §1) — approve before multi-file coding.
|
|
276
|
+
5. Code with the plan attached → write \`reviews/{STORY_ID}-code-review.md\` → open PR with both attached.
|
|
277
|
+
|
|
278
|
+
**Re-install kit (force):**
|
|
279
|
+
|
|
280
|
+
\`\`\`bash
|
|
281
|
+
npx @grvmithas/create-aies-kit ${flags} --force
|
|
282
|
+
\`\`\`
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## 1. Ephemeral plans & review reports (not forever-docs)
|
|
287
|
+
|
|
288
|
+
**Plans and review reports are temporary.** Scope ends when the story/PR completes. Keeping a single living \`implementation_plan.md\` forever is an anti-pattern.
|
|
289
|
+
|
|
290
|
+
| Artifact | Path | After merge |
|
|
291
|
+
|----------|------|-------------|
|
|
292
|
+
| Plan | \`plans/{STORY_ID}-plan.md\` | Attach to PR; archive or delete — **do not maintain** |
|
|
293
|
+
| Code review | \`reviews/{STORY_ID}-code-review.md\` | Attach to PR; freeze — file issues for leftovers |
|
|
294
|
+
| Index only | \`implementation_plan.md\` | Points here — not the active plan |
|
|
295
|
+
|
|
296
|
+
### Create / fill a plan
|
|
297
|
+
|
|
298
|
+
1. Copy \`plans/_TEMPLATE.md\` → \`plans/{STORY_ID}-plan.md\`
|
|
299
|
+
2. Attach \`${L.promptsDir}/implementation-plan.prompt.md\` (or planning skill)
|
|
300
|
+
3. Fill tasks with verification commands from **this** repo (see enhance output)
|
|
301
|
+
4. Human approval before multi-file coding
|
|
302
|
+
|
|
303
|
+
### Temporary memory
|
|
304
|
+
|
|
305
|
+
- Attach the plan file every agent turn for that story
|
|
306
|
+
- Optional sticky/context include by \`STORY_ID\`
|
|
307
|
+
- Never dump the full plan into forever rules (\`CLAUDE.md\` / \`AGENTS.md\`)
|
|
308
|
+
|
|
309
|
+
### Anti-patterns
|
|
310
|
+
|
|
311
|
+
- Forever-editing one root plan for every feature
|
|
312
|
+
- Coding multi-file work from chat with no \`plans/{STORY_ID}-plan.md\`
|
|
313
|
+
- Skipping \`reviews/{STORY_ID}-code-review.md\` on AI-touched PRs
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## 2. How to use the right prompts
|
|
318
|
+
|
|
319
|
+
| Situation | Prompt |
|
|
320
|
+
|-----------|--------|
|
|
321
|
+
| New story / kickoff | \`${L.promptsDir}/story-kickoff.prompt.md\` |
|
|
322
|
+
| Build the plan | \`${L.promptsDir}/implementation-plan.prompt.md\` |
|
|
323
|
+
| Implement approved plan | \`${L.promptsDir}/coding.prompt.md\` |
|
|
324
|
+
| Security pass | \`${L.promptsDir}/security-review.prompt.md\` |
|
|
325
|
+
| Adapt harness to this repo | \`${enhancePrompt}\` — see §0 chat invoke |
|
|
326
|
+
| Author a new prompt | \`${L.promptsDir}/create-prompt.prompt.md\` |
|
|
327
|
+
| Author a new skill | \`${L.promptsDir}/create-skill.prompt.md\` |
|
|
328
|
+
| Author a new agent | \`${L.promptsDir}/create-agent.prompt.md\` |
|
|
329
|
+
| Stack generate / review / debug | \`${L.cookbookDir}/prompts/\` |
|
|
330
|
+
|
|
331
|
+
**Rule:** Prefer cookbook prompts for stack-idiomatic work; prefer AIDLC prompts for lifecycle gates.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## 3. How to use the right skills
|
|
336
|
+
|
|
337
|
+
| Situation | Skill |
|
|
338
|
+
|-----------|--------|
|
|
339
|
+
| Multi-step plan | \`${skillPath(L, 'planning')}\` |
|
|
340
|
+
| Bug / incident | \`${skillPath(L, 'debug')}\` |
|
|
341
|
+
| PR review | \`${skillPath(L, 'code-review')}\` |
|
|
342
|
+
| Tests | \`${skillPath(L, 'testing')}\` |
|
|
343
|
+
| Architecture / ADR | \`${skillPath(L, 'architecture')}\` |
|
|
344
|
+
| Context packing | \`${skillPath(L, 'context')}\` |
|
|
345
|
+
| CI/CD / release | \`${skillPath(L, 'devops')}\` |
|
|
346
|
+
| Eval gates | \`${skillPath(L, 'evaluation')}\` |
|
|
347
|
+
| Threat / AuthZ | \`${skillPath(L, 'security')}\` |
|
|
348
|
+
| Write better prompts | \`${skillPath(L, 'prompt')}\` |
|
|
349
|
+
| Docs | \`${skillPath(L, 'documentation')}\` |
|
|
350
|
+
| Stack-specific | skill under \`${L.skillsDir}/\` matching ${label} |
|
|
351
|
+
|
|
352
|
+
**Cursor:** skills are \`.cursor/skills/{name}/SKILL.md\` (auto-discoverable).
|
|
353
|
+
**Copilot / VS Code:** open the \`.skill.md\` file and attach it.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## 4. Agents
|
|
358
|
+
|
|
359
|
+
| Agent | Path | Use when |
|
|
360
|
+
|-------|------|----------|
|
|
361
|
+
| Planner | \`${L.agentsDir}/planner.agent.md\` | Complex multi-file planning |
|
|
362
|
+
| Coder | \`${L.agentsDir}/coder.agent.md\` | Execution against an approved plan |
|
|
363
|
+
| Reviewer | \`${L.agentsDir}/reviewer.agent.md\` | Severity-calibrated review |
|
|
364
|
+
| Prompt engineer | \`${L.agentsDir}/prompt-engineer.agent.md\` | Designing new prompts/skills |
|
|
365
|
+
|
|
366
|
+
Compose agents; do not give them merge authority.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 5. Agent hooks (evidence gates)
|
|
371
|
+
|
|
372
|
+
Scripts: \`hooks/*.hook.sh\` · Operator notes: \`hooks/README.md\` · Full contract: \`hooks/CONTRACT.md\`
|
|
373
|
+
Registry: \`${L.hooksFile}\`
|
|
374
|
+
|
|
375
|
+
Use hooks so agents cannot skip plan approval, lint evidence, or high-effect tools. Wire evidence to **this repo’s** verify commands after enhance.
|
|
376
|
+
|
|
377
|
+
Typical loop: \`plans/{STORY_ID}-plan.md\` → pre-code hook → coding → lint/test evidence → \`reviews/{STORY_ID}-code-review.md\` → pre-review/pre-commit → PR.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 6. MCP connections (relevant + safe)
|
|
382
|
+
|
|
383
|
+
Specs (all catalog entries shipped): \`${L.mcpsDir}/\`
|
|
384
|
+
Config: \`${L.mcpFile}\`
|
|
385
|
+
|
|
386
|
+
- \`mcpServers\` starts **empty** (safe default).
|
|
387
|
+
- \`_aiesRecommended\` lists enable-ready stubs for this stack/profile: **${recommendedIds.join(', ')}**
|
|
388
|
+
- After \`enhance\`, recommendations update from repo signals (Docker, Playwright, Postgres, etc.).
|
|
389
|
+
- To enable: copy a stub from \`_aiesRecommended\` → \`mcpServers\`, set env secrets outside git, re-read \`${L.guidesDir}/mcp-when-and-how.md\`.
|
|
390
|
+
|
|
391
|
+
**Never** commit tokens. Prefer read-only scopes first.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 7. Root instruction files (always present)
|
|
396
|
+
|
|
397
|
+
| File | Role |
|
|
398
|
+
|------|------|
|
|
399
|
+
| \`CLAUDE.md\` | Claude Code / multi-IDE contract at repo root |
|
|
400
|
+
| \`AGENTS.md\` | Agent-oriented project contract (Cursor/Codex-compatible) |
|
|
401
|
+
| \`copilot-instructions.md\` | Copilot-oriented instructions at repo root |
|
|
402
|
+
| \`.github/copilot-instructions.md\` | Same content for GitHub Copilot discovery |
|
|
403
|
+
| \`${L.rulesFile}\` | Primary IDE rules for **${profile.ide}** |
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 8. Layout (${profile.ide})
|
|
408
|
+
|
|
409
|
+
| Path | Purpose |
|
|
410
|
+
|------|---------|
|
|
411
|
+
| \`AIES.md\` | This operator guide |
|
|
412
|
+
| \`implementation_plan.md\` | Ephemeral-plan **index** (not the active plan) |
|
|
413
|
+
| \`plans/{STORY_ID}-plan.md\` | Active plan — temporary |
|
|
414
|
+
| \`reviews/{STORY_ID}-code-review.md\` | Review report — temporary |
|
|
415
|
+
| \`hooks/\` | Agent evidence-gate scripts |
|
|
416
|
+
| \`${L.hooksFile}\` | Hook registry (agentHooks) |
|
|
417
|
+
| \`${L.promptsDir}/\` | AIDLC + create/enhance prompts (**IDE**) |
|
|
418
|
+
| \`${L.skillsDir}/\` | Skills (**IDE**) |
|
|
419
|
+
| \`${L.agentsDir}/\` | Agent definitions (**IDE**) |
|
|
420
|
+
| \`${L.cookbookDir}/\` | Full stack cookbook (**repo root**) |
|
|
421
|
+
| \`${L.guidesDir}/\` | Handbook slices (**repo root**) |
|
|
422
|
+
| \`${L.mcpsDir}/\` | MCP integration specs (**repo root**) |
|
|
423
|
+
| \`${L.templatesDir}/\` | System prompt template (**repo root**) |
|
|
424
|
+
| \`${L.mcpFile}\` | MCP config (empty \`mcpServers\` by default) |
|
|
425
|
+
| \`${L.policyFile}\` | Model + data policy |
|
|
426
|
+
|
|
427
|
+
**Anti-pattern:** Do not nest cookbook/guides/mcps under \`.github\` — that folder is for Copilot discovery (instructions, prompts, skills, agents) and GitHub metadata only.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## 9. Reading path (${profile.maturity})
|
|
432
|
+
|
|
433
|
+
${maturityReadingPath(profile.maturity)}
|
|
434
|
+
|
|
435
|
+
## 10. Role next workflow
|
|
436
|
+
|
|
437
|
+
${SITE_URL}/workflows/${roleNext(profile.role)}
|
|
438
|
+
|
|
439
|
+
## 11. Online
|
|
440
|
+
|
|
441
|
+
- Quick start: ${SITE_URL}/quickstart
|
|
442
|
+
- Cookbook: ${SITE_URL}/docs/cookbook/${stack.cookbook ?? 'README'}/README
|
|
443
|
+
- MCP handbook: ${SITE_URL}/docs/handbook/mcp-when-and-how
|
|
444
|
+
- Workflows: ${SITE_URL}/workflows
|
|
445
|
+
`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function invokeEnhanceChat(ide, enhancePrompt) {
|
|
449
|
+
switch (ide) {
|
|
450
|
+
case 'cursor':
|
|
451
|
+
return `@${enhancePrompt} Scan this repo and enhance the AIES harness. Follow the XML instructions exactly.`;
|
|
452
|
+
case 'copilot':
|
|
453
|
+
return `#file:${enhancePrompt} Scan this repo and enhance the AIES harness. Follow the XML instructions exactly.`;
|
|
454
|
+
case 'claude-code':
|
|
455
|
+
return `Read ${enhancePrompt} and execute it against this repository.`;
|
|
456
|
+
default:
|
|
457
|
+
return `Open ${enhancePrompt} and execute it — scan first, then patch AIES artifacts only.`;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function implementationPlanIndex(promptsDir) {
|
|
462
|
+
return `# Implementation plans (ephemeral index)
|
|
463
|
+
|
|
464
|
+
> **Not a living document.** Plans exist only for the duration of one story/task.
|
|
465
|
+
> Maintaining a forever-updated root plan is an **anti-pattern**.
|
|
466
|
+
|
|
467
|
+
## Create a plan for this story
|
|
468
|
+
|
|
469
|
+
1. Copy \`plans/_TEMPLATE.md\` → \`plans/{STORY_ID}-plan.md\`
|
|
470
|
+
2. Fill it with \`${promptsDir}/implementation-plan.prompt.md\` + the planning skill
|
|
471
|
+
3. Get human approval before multi-file coding
|
|
472
|
+
4. **Attach** \`plans/{STORY_ID}-plan.md\` in every agent chat for that story
|
|
473
|
+
5. Attach the plan to the PR; when the PR merges, **stop maintaining it** (archive or delete)
|
|
474
|
+
|
|
475
|
+
## Naming
|
|
476
|
+
|
|
477
|
+
| Artifact | Path |
|
|
478
|
+
|----------|------|
|
|
479
|
+
| Plan | \`plans/{STORY_ID}-plan.md\` |
|
|
480
|
+
| Code review report | \`reviews/{STORY_ID}-code-review.md\` |
|
|
481
|
+
|
|
482
|
+
Examples: \`plans/JIRA-1234-plan.md\`, \`reviews/JIRA-1234-code-review.md\`
|
|
483
|
+
|
|
484
|
+
## Temporary memory
|
|
485
|
+
|
|
486
|
+
- Prefer attaching the plan file each session (durable, reviewable)
|
|
487
|
+
- Optional: IDE sticky/context include keyed by story id
|
|
488
|
+
- Do **not** paste the full plan into \`CLAUDE.md\` / \`AGENTS.md\` / forever rules
|
|
489
|
+
|
|
490
|
+
See \`plans/README.md\` and \`reviews/README.md\`.
|
|
491
|
+
`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function agentHooksRegistry(L) {
|
|
495
|
+
return {
|
|
496
|
+
version: '2',
|
|
497
|
+
description: 'AIES agent hooks — evidence gates for AI-assisted development',
|
|
498
|
+
hooksDir: 'hooks',
|
|
499
|
+
artifacts: {
|
|
500
|
+
plan: 'plans/{STORY_ID}-plan.md',
|
|
501
|
+
review: 'reviews/{STORY_ID}-code-review.md',
|
|
502
|
+
},
|
|
503
|
+
hooks: [
|
|
504
|
+
{
|
|
505
|
+
id: 'pre-planning',
|
|
506
|
+
phase: 'before-plan',
|
|
507
|
+
script: 'hooks/pre-planning.hook.sh',
|
|
508
|
+
requiredEvidence: ['requirements-path', 'risk-tier'],
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
id: 'pre-code',
|
|
512
|
+
phase: 'before-code',
|
|
513
|
+
script: 'hooks/pre-code.hook.sh',
|
|
514
|
+
requiredEvidence: ['approved-plan-path', 'human-approval'],
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
id: 'post-code',
|
|
518
|
+
phase: 'after-code',
|
|
519
|
+
script: 'hooks/post-code.hook.sh',
|
|
520
|
+
requiredEvidence: ['verify-evidence'],
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
id: 'pre-review',
|
|
524
|
+
phase: 'before-review',
|
|
525
|
+
script: 'hooks/pre-review.hook.sh',
|
|
526
|
+
requiredEvidence: ['format-evidence', 'lint-evidence'],
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
id: 'pre-commit',
|
|
530
|
+
phase: 'before-commit',
|
|
531
|
+
script: 'hooks/pre-commit.hook.sh',
|
|
532
|
+
requiredEvidence: ['test-evidence', 'secret-scan-evidence'],
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
id: 'pre-tool-call',
|
|
536
|
+
phase: 'before-tool',
|
|
537
|
+
script: 'hooks/pre-tool-call.hook.sh',
|
|
538
|
+
requiredEvidence: ['tool-effect', 'approval-if-high'],
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
id: 'budget-guard',
|
|
542
|
+
phase: 'budget',
|
|
543
|
+
script: 'hooks/budget-guard.hook.sh',
|
|
544
|
+
requiredEvidence: ['budget-profile', 'budget-remaining'],
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
id: 'deployment',
|
|
548
|
+
phase: 'before-deploy',
|
|
549
|
+
script: 'hooks/deployment.hook.sh',
|
|
550
|
+
requiredEvidence: ['release-manifest', 'approval', 'release-evidence'],
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
agentGuidance: {
|
|
554
|
+
beforeMultiFileCoding: `Attach plans/{STORY_ID}-plan.md and confirm human approval`,
|
|
555
|
+
beforePr: `Write reviews/{STORY_ID}-code-review.md; run repo lint/typecheck/test`,
|
|
556
|
+
mcp: `Keep mcpServers empty until human copies from ${L.mcpFile} _aiesRecommended`,
|
|
557
|
+
},
|
|
558
|
+
_aies: {
|
|
559
|
+
hooksGuide: `${SITE_URL}/docs/handbook/hooks-in-practice`,
|
|
560
|
+
contract: 'hooks/CONTRACT.md',
|
|
561
|
+
},
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* @param {object} profile
|
|
567
|
+
* @param {{ recommendedMcpIds?: string[] }} [opts]
|
|
568
|
+
*/
|
|
569
|
+
export function generateStarterKit(profile, opts = {}) {
|
|
570
|
+
const stack = stackMeta(profile.stack);
|
|
571
|
+
const label = STACK_LABELS[profile.stack] ?? profile.stack;
|
|
572
|
+
const now = new Date().toISOString();
|
|
573
|
+
const model = profile.model ?? 'undecided';
|
|
574
|
+
const dataClass = profile.dataClass ?? 'internal';
|
|
575
|
+
const L = layoutFor(profile.ide, { cookbookId: stack.cookbook ?? null });
|
|
576
|
+
const recommendedIds = opts.recommendedMcpIds ?? stackMcpIds(profile.stack);
|
|
577
|
+
const recommended = buildRecommendedMcpConfig(recommendedIds);
|
|
578
|
+
|
|
579
|
+
const flags = [
|
|
580
|
+
`--stack ${profile.stack}`,
|
|
581
|
+
`--ide ${profile.ide}`,
|
|
582
|
+
`--role ${profile.role}`,
|
|
583
|
+
`--maturity ${profile.maturity}`,
|
|
584
|
+
`--goal ${profile.goal}`,
|
|
585
|
+
`--model ${model}`,
|
|
586
|
+
`--data-class ${dataClass}`,
|
|
587
|
+
'--yes',
|
|
588
|
+
].join(' ');
|
|
589
|
+
|
|
590
|
+
/** @type {Record<string, string>} */
|
|
591
|
+
const files = {};
|
|
592
|
+
|
|
593
|
+
for (const name of CORE_PROMPTS) {
|
|
594
|
+
files[`${L.promptsDir}/${name}.prompt.md`] = corpus(`aidlc/${name}.prompt.md`);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
for (const name of CORE_SKILLS) {
|
|
598
|
+
writeSkill(files, L, name, corpus(`skills/${name}.skill.md`));
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
for (const name of CORE_AGENTS) {
|
|
602
|
+
files[`${L.agentsDir}/${name}.agent.md`] = corpus(`agents/${name}.agent.md`);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
files[`${L.templatesDir}/system-prompt.template.md`] = corpus('templates/system-prompt.template.md');
|
|
606
|
+
|
|
607
|
+
files['plans/README.md'] = corpus('kit/plans-README.md');
|
|
608
|
+
files['plans/_TEMPLATE.md'] = corpus('kit/plan-TEMPLATE.md');
|
|
609
|
+
files['reviews/README.md'] = corpus('kit/reviews-README.md');
|
|
610
|
+
files['reviews/_TEMPLATE.md'] = corpus('kit/review-TEMPLATE.md');
|
|
611
|
+
|
|
612
|
+
files['hooks/README.md'] = corpus('kit/hooks-README.md');
|
|
613
|
+
files['hooks/CONTRACT.md'] = corpus('hooks/CONTRACT.md');
|
|
614
|
+
for (const key of Object.keys(EMBEDDED_CORPUS).filter(k => k.startsWith('hooks/') && k.endsWith('.hook.sh'))) {
|
|
615
|
+
files[key] = corpus(key);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (stack.cookbook) {
|
|
619
|
+
const keys = cookbookKeys(stack.cookbook);
|
|
620
|
+
for (const key of keys) {
|
|
621
|
+
const rel = key.slice(`cookbook/${stack.cookbook}/`.length);
|
|
622
|
+
files[`${L.cookbookDir}/${rel}`] = corpus(key);
|
|
623
|
+
}
|
|
624
|
+
const skillKey = keys.find(k => k.includes('/skills/') && k.endsWith('.skill.md'));
|
|
625
|
+
if (skillKey) {
|
|
626
|
+
const skillName = pathBasename(skillKey).replace(/\.skill\.md$/, '');
|
|
627
|
+
writeSkill(files, L, skillName, corpus(skillKey));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
files[`${L.guidesDir}/human-in-the-loop.md`] = corpus('handbook/human-in-the-loop.md');
|
|
632
|
+
files[`${L.guidesDir}/evals-as-quality-gates.md`] = corpus('handbook/evals-as-quality-gates.md');
|
|
633
|
+
files[`${L.guidesDir}/mcp-when-and-how.md`] = corpus('handbook/mcp-when-and-how.md');
|
|
634
|
+
files[`${L.guidesDir}/skills-vs-prompts-vs-agents.md`] = corpus('handbook/skills-vs-prompts-vs-agents.md');
|
|
635
|
+
files[`${L.guidesDir}/how-to-set-up-planning.md`] = corpus('handbook/how-to-set-up-planning.md');
|
|
636
|
+
|
|
637
|
+
files[`${L.mcpsDir}/README.md`] = corpus('mcps/README.md');
|
|
638
|
+
for (const spec of MCP_SPECS) {
|
|
639
|
+
files[`${L.mcpsDir}/${spec.file}`] = corpus(`mcps/${spec.file}`);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
files[L.policyFile] = modelPolicy(profile);
|
|
643
|
+
|
|
644
|
+
const mcpDoc = {
|
|
645
|
+
mcpServers: {},
|
|
646
|
+
_aiesRecommended: recommended,
|
|
647
|
+
_aies: {
|
|
648
|
+
note: 'mcpServers stays empty until a human copies stubs from _aiesRecommended and sets secrets.',
|
|
649
|
+
guide: `${SITE_URL}/docs/handbook/mcp-when-and-how`,
|
|
650
|
+
dataClass,
|
|
651
|
+
recommendedIds,
|
|
652
|
+
specsDir: L.mcpsDir,
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
files[L.mcpFile] = JSON.stringify(mcpDoc, null, 2);
|
|
656
|
+
|
|
657
|
+
files[L.hooksFile] = JSON.stringify(agentHooksRegistry(L), null, 2);
|
|
658
|
+
|
|
659
|
+
const rootInstructions = buildRootInstructions(profile, label, L);
|
|
660
|
+
files['CLAUDE.md'] = rootInstructions;
|
|
661
|
+
files['AGENTS.md'] = rootInstructions;
|
|
662
|
+
files['copilot-instructions.md'] = rootInstructions;
|
|
663
|
+
files['.github/copilot-instructions.md'] = rootInstructions;
|
|
664
|
+
|
|
665
|
+
if (profile.ide === 'cursor') {
|
|
666
|
+
files[L.rulesFile] = `---
|
|
667
|
+
description: AIES project harness rules
|
|
668
|
+
globs:
|
|
669
|
+
alwaysApply: true
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
${rootInstructions}
|
|
673
|
+
`;
|
|
674
|
+
} else if (profile.ide === 'claude-code') {
|
|
675
|
+
// CLAUDE.md already written as rootInstructions
|
|
676
|
+
} else if (profile.ide === 'copilot') {
|
|
677
|
+
// .github/copilot-instructions.md already written — do not nest cookbook under .github
|
|
678
|
+
} else if (profile.ide === 'continue') {
|
|
679
|
+
files[L.rulesFile] = rootInstructions;
|
|
680
|
+
files['.continue/config.snippet.json'] = JSON.stringify(
|
|
681
|
+
{
|
|
682
|
+
comment: 'Merge into Continue config — do not overwrite blindly',
|
|
683
|
+
contextProviders: [
|
|
684
|
+
{ name: 'file', params: { path: 'implementation_plan.md' } },
|
|
685
|
+
{ name: 'file', params: { path: L.cookbookDir } },
|
|
686
|
+
{ name: 'file', params: { path: L.skillsDir } },
|
|
687
|
+
{ name: 'file', params: { path: L.promptsDir } },
|
|
688
|
+
],
|
|
689
|
+
},
|
|
690
|
+
null,
|
|
691
|
+
2,
|
|
692
|
+
);
|
|
693
|
+
} else {
|
|
694
|
+
files[L.rulesFile] = rootInstructions;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
files['implementation_plan.md'] = implementationPlanIndex(L.promptsDir);
|
|
698
|
+
|
|
699
|
+
files['AIES.md'] = buildAiesReadme({
|
|
700
|
+
profile: { ...profile, model, dataClass },
|
|
701
|
+
label,
|
|
702
|
+
L,
|
|
703
|
+
stack,
|
|
704
|
+
flags,
|
|
705
|
+
now,
|
|
706
|
+
recommendedIds,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
return {
|
|
710
|
+
stack: profile.stack,
|
|
711
|
+
generatedAt: now,
|
|
712
|
+
profile: { ...profile, model, dataClass },
|
|
713
|
+
layout: describeInstallLayout(profile.ide, { cookbookId: stack.cookbook ?? null }),
|
|
714
|
+
files,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Enhance mode: rescan repo, refresh MCP recommendations + AIES.md section, keep corpus.
|
|
720
|
+
* Requires an existing profile — recovered from AIES.md markers or passed flags.
|
|
721
|
+
*/
|
|
722
|
+
export function enhanceStarterKit(cwd, profile) {
|
|
723
|
+
const { signals, recommendedMcpIds } = detectRepoSignals(cwd, fs);
|
|
724
|
+
const mergedIds = [...new Set([...stackMcpIds(profile.stack), ...recommendedMcpIds])];
|
|
725
|
+
const bundle = generateStarterKit(profile, { recommendedMcpIds: mergedIds });
|
|
726
|
+
|
|
727
|
+
const scanReport = `# AIES repo scan
|
|
728
|
+
|
|
729
|
+
Generated: ${new Date().toISOString()}
|
|
730
|
+
|
|
731
|
+
## Signals detected
|
|
732
|
+
|
|
733
|
+
\`\`\`json
|
|
734
|
+
${JSON.stringify(signals, null, 2)}
|
|
735
|
+
\`\`\`
|
|
736
|
+
|
|
737
|
+
## Recommended MCP ids
|
|
738
|
+
|
|
739
|
+
${mergedIds.map(id => `- ${id}`).join('\n')}
|
|
740
|
+
|
|
741
|
+
## Next
|
|
742
|
+
|
|
743
|
+
1. Review \`${layoutFor(profile.ide, { cookbookId: stackMeta(profile.stack).cookbook }).mcpFile}\` → \`_aiesRecommended\`
|
|
744
|
+
2. Run \`${layoutFor(profile.ide, { cookbookId: stackMeta(profile.stack).cookbook }).promptsDir}/enhance-repo.prompt.md\` in your IDE agent
|
|
745
|
+
3. Fill \`implementation_plan.md\` before multi-file coding
|
|
746
|
+
`;
|
|
747
|
+
|
|
748
|
+
bundle.files['AIES_REPO_SCAN.md'] = scanReport;
|
|
749
|
+
bundle.scan = { signals, recommendedMcpIds: mergedIds };
|
|
750
|
+
return bundle;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export function renderBashInstallScript(bundle) {
|
|
754
|
+
const lines = [
|
|
755
|
+
'#!/usr/bin/env bash',
|
|
756
|
+
'set -euo pipefail',
|
|
757
|
+
'echo "AIES kit — scaffolding IDE-native harness into $(pwd)"',
|
|
758
|
+
'',
|
|
759
|
+
];
|
|
760
|
+
for (const [relPath, content] of Object.entries(bundle.files)) {
|
|
761
|
+
const dir = relPath.includes('/') ? relPath.slice(0, relPath.lastIndexOf('/')) : '';
|
|
762
|
+
if (dir) lines.push(`mkdir -p "${dir}"`);
|
|
763
|
+
const delim = `AIES_EOF_${Buffer.from(relPath).toString('hex').slice(0, 12)}`;
|
|
764
|
+
lines.push(`cat > "${relPath}" <<'${delim}'`);
|
|
765
|
+
lines.push(content.replace(/\r\n/g, '\n'));
|
|
766
|
+
lines.push(delim);
|
|
767
|
+
lines.push('');
|
|
768
|
+
}
|
|
769
|
+
lines.push('echo "Done. Read AIES.md — then: npx @grvmithas/create-aies-kit enhance"');
|
|
770
|
+
lines.push(`echo "Open: ${SITE_URL}/quickstart"`);
|
|
771
|
+
return lines.join('\n');
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export function renderPowerShellInstallScript(bundle) {
|
|
775
|
+
const lines = [
|
|
776
|
+
'$ErrorActionPreference = "Stop"',
|
|
777
|
+
'Write-Host "AIES kit — scaffolding IDE-native harness into $(Get-Location)"',
|
|
778
|
+
'',
|
|
779
|
+
];
|
|
780
|
+
for (const [relPath, content] of Object.entries(bundle.files)) {
|
|
781
|
+
const dir = relPath.includes('/') ? relPath.slice(0, relPath.lastIndexOf('/')) : '';
|
|
782
|
+
const psPath = relPath.replace(/\//g, '\\');
|
|
783
|
+
if (dir) {
|
|
784
|
+
lines.push(`New-Item -ItemType Directory -Force -Path "${dir.replace(/\//g, '\\')}" | Out-Null`);
|
|
785
|
+
}
|
|
786
|
+
const b64 = Buffer.from(content, 'utf8').toString('base64');
|
|
787
|
+
lines.push(
|
|
788
|
+
`[System.IO.File]::WriteAllText((Join-Path (Get-Location) "${psPath}"), [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("${b64}")))`,
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
lines.push('Write-Host "Done. Read AIES.md — then: npx @grvmithas/create-aies-kit enhance"');
|
|
792
|
+
lines.push(`Write-Host "Open: ${SITE_URL}/quickstart"`);
|
|
793
|
+
return lines.join('\n');
|
|
794
|
+
}
|