@grvmithas/create-aies-kit 1.3.0 → 2.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.
@@ -4,10 +4,14 @@ import {
4
4
  MCP_SPECS,
5
5
  buildRecommendedMcpConfig,
6
6
  detectRepoSignals,
7
+ renderCodexMcpToml,
8
+ renderMcpServers,
7
9
  stackMcpIds,
8
10
  } from './mcp-catalog.js';
9
11
  import { describeInstallLayout, layoutFor } from './install-api.js';
10
- import { SITE_URL, STACK_LABELS, STACKS } from './options.js';
12
+ import { KIT_VERSION, SITE_URL, STACK_LABELS, STACKS, normalizeIde } from './options.js';
13
+ import { detectVerifyCommands } from './repo-scan.js';
14
+ import { renderEvalFiles, renderGatesWorkflow } from './evals.js';
11
15
 
12
16
  export { buildInstallQuery, describeInstallLayout, getInstallCommands, layoutFor } from './install-api.js';
13
17
 
@@ -36,7 +40,58 @@ const CORE_PROMPTS = [
36
40
  'create-agent',
37
41
  ];
38
42
 
39
- const CORE_AGENTS = ['planner', 'coder', 'reviewer', 'prompt-engineer'];
43
+ const CORE_AGENTS = ['planner', 'coder', 'reviewer', 'security', 'testing', 'prompt-engineer', 'parallel-implementer'];
44
+
45
+ /** Skills preloaded into Claude Code subagents via the `skills` frontmatter field. */
46
+ const AGENT_SKILLS = {
47
+ planner: ['planning', 'architecture'],
48
+ coder: ['testing'],
49
+ reviewer: ['code-review'],
50
+ security: ['security'],
51
+ testing: ['testing'],
52
+ 'prompt-engineer': ['prompt'],
53
+ 'parallel-implementer': ['testing'],
54
+ };
55
+
56
+ const GUIDES = [
57
+ 'human-in-the-loop',
58
+ 'evals-as-quality-gates',
59
+ 'mcp-when-and-how',
60
+ 'skills-vs-prompts-vs-agents',
61
+ 'how-to-set-up-planning',
62
+ 'parallel-agents-and-worktrees',
63
+ 'native-agent-hooks',
64
+ 'agent-skills-standard',
65
+ 'ide-native-subagents',
66
+ ];
67
+
68
+ const GUARD_CMD = 'node hooks/aies-guard.mjs';
69
+ const DONE_CMD = 'node hooks/aies-done.mjs';
70
+ const EGRESS_CMD = 'node hooks/aies-egress-check.mjs';
71
+ /** Seconds a definition-of-done run may take before the runtime kills the hook. */
72
+ const DONE_TIMEOUT = 900;
73
+
74
+ const CLAUDE_MATCHERS = ['Bash', 'Edit|Write|MultiEdit|NotebookEdit', 'Read'];
75
+
76
+ const OPENCODE_GUARD_PLUGIN = `// AIES guard adapter for opencode — runs hooks/aies-guard.mjs before every tool call.
77
+ // A non-zero exit (deny) or a failure to run the guard blocks the tool: the adapter fails closed.
78
+ import { spawnSync } from 'node:child_process';
79
+ import { join } from 'node:path';
80
+
81
+ export const AiesGuard = async ({ directory }) => ({
82
+ 'tool.execute.before': async (input, output) => {
83
+ const payload = JSON.stringify({ tool_name: input.tool, tool_input: output.args, cwd: directory });
84
+ const result = spawnSync(process.execPath, [join(directory, 'hooks', 'aies-guard.mjs'), '--target', 'opencode'], {
85
+ input: payload,
86
+ encoding: 'utf8',
87
+ timeout: 15000,
88
+ });
89
+ if (result.status !== 0) {
90
+ throw new Error(result.stderr?.trim() || 'AIES guard could not run; the action is blocked (fail closed).');
91
+ }
92
+ },
93
+ });
94
+ `;
40
95
 
41
96
  function corpus(key) {
42
97
  const body = EMBEDDED_CORPUS[key];
@@ -50,33 +105,402 @@ function stackMeta(stack) {
50
105
  return STACKS.find(s => s.id === stack) ?? { id: stack, label: stack, skillKey: null, cookbook: null };
51
106
  }
52
107
 
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+/, '');
108
+ /** Minimal frontmatter parser for the flat `key: value` blocks the AIES corpus uses. */
109
+ export function parseFrontmatter(md) {
110
+ const text = md.replace(/^\uFEFF/, '');
111
+ const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
112
+ if (!match) return { data: {}, body: text };
113
+ /** @type {Record<string, string>} */
114
+ const data = {};
115
+ for (const line of match[1].split(/\r?\n/)) {
116
+ const kv = line.match(/^([A-Za-z_][\w-]*):\s*(.*)$/);
117
+ if (!kv) continue;
118
+ let value = kv[2].trim();
119
+ if (value.startsWith('"')) {
120
+ try {
121
+ value = JSON.parse(value);
122
+ } catch {
123
+ value = value.slice(1, -1);
124
+ }
125
+ }
126
+ data[kv[1]] = value;
127
+ }
128
+ return { data, body: text.slice(match[0].length).replace(/^\s+/, '') };
58
129
  }
59
130
 
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.`;
131
+ function yamlScalar(value) {
132
+ if (typeof value === 'boolean' || typeof value === 'number') return String(value);
133
+ const s = String(value);
134
+ return /^[\w./@-][\w ./@,()-]*$/.test(s) && !/^(true|false|null|yes|no)$/i.test(s) ? s : JSON.stringify(s);
64
135
  }
65
136
 
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
- ---
137
+ /** Render a YAML frontmatter block. Arrays → block lists, plain objects → one nested level. */
138
+ function frontmatter(obj) {
139
+ const lines = ['---'];
140
+ for (const [key, value] of Object.entries(obj)) {
141
+ if (value === undefined || value === null) continue;
142
+ if (Array.isArray(value)) {
143
+ lines.push(`${key}:`);
144
+ for (const item of value) lines.push(` - ${yamlScalar(item)}`);
145
+ } else if (typeof value === 'object') {
146
+ lines.push(`${key}:`);
147
+ for (const [k, v] of Object.entries(value)) lines.push(` ${k}: ${yamlScalar(v)}`);
148
+ } else {
149
+ lines.push(`${key}: ${yamlScalar(value)}`);
150
+ }
151
+ }
152
+ lines.push('---', '');
153
+ return lines.join('\n');
154
+ }
74
155
 
75
- ${cleaned}
76
- `;
77
- } else {
78
- files[`${L.skillsDir}/${name}.skill.md`] = body;
156
+ function tomlString(value) {
157
+ return JSON.stringify(String(value));
158
+ }
159
+
160
+ /** TOML literal multi-line string: no escape processing, so regexes and backslashes survive. */
161
+ function tomlLiteralBlock(text) {
162
+ return `'''\n${text.replace(/'''/g, "'\u2019'")}\n'''`;
163
+ }
164
+
165
+ function firstSentence(text, max = 300) {
166
+ const t = text.replace(/\s+/g, ' ').trim();
167
+ const cut = t.match(/^(.{20,}?[.!?])(\s|$)/)?.[1] ?? t;
168
+ return cut.slice(0, max);
169
+ }
170
+
171
+ function promptDescription(src, name) {
172
+ const purpose = src.match(/\*\*Purpose:\*\*\s*(.+)/)?.[1] ?? src.match(/## Purpose\s*\n+([^\n]+)/)?.[1];
173
+ return firstSentence(purpose ?? `AIES ${name.replace(/-/g, ' ')} prompt`, 200);
174
+ }
175
+
176
+ function skillMarkdown(name, src) {
177
+ const { data, body } = parseFrontmatter(src);
178
+ const description = data.description
179
+ ?? firstSentence(src.match(/## Purpose\s*\n+([^\n]+)/)?.[1] ?? `AIES ${name} procedure.`, 1000);
180
+ return `${frontmatter({ name, description: description.slice(0, 1024) })}\n${body}`;
181
+ }
182
+
183
+ function isReadOnly(data) {
184
+ return data.privilege === 'read-only' || !/\b(Edit|Write)\b/.test(data.tools ?? '');
185
+ }
186
+
187
+ /**
188
+ * Render one agent definition into the native format of a target.
189
+ * @returns {{ path: string, content: string } | null}
190
+ */
191
+ export function renderAgent(L, name, src) {
192
+ if (!L.agentFormat) return null;
193
+ const { data, body } = parseFrontmatter(src);
194
+ const agentName = data.name ?? name;
195
+ const description = data.description ?? firstSentence(body, 300);
196
+ const readonly = isReadOnly(data);
197
+ const dir = L.agentsDir;
198
+
199
+ switch (L.agentFormat) {
200
+ case 'claude':
201
+ return {
202
+ path: `${dir}/${agentName}.md`,
203
+ content: `${frontmatter({
204
+ name: agentName,
205
+ description,
206
+ tools: data.tools,
207
+ model: data.model ?? 'inherit',
208
+ skills: AGENT_SKILLS[agentName],
209
+ isolation: data.isolation,
210
+ })}\n${body}`,
211
+ };
212
+ case 'cursor':
213
+ return {
214
+ path: `${dir}/${agentName}.md`,
215
+ content: `${frontmatter({ name: agentName, description, model: 'inherit', readonly })}\n${body}`,
216
+ };
217
+ case 'copilot':
218
+ return {
219
+ path: `${dir}/${agentName}.agent.md`,
220
+ content: `${frontmatter({
221
+ name: agentName,
222
+ description,
223
+ tools: readonly ? ['read', 'search'] : ['read', 'search', 'edit', 'execute'],
224
+ })}\n${body}`,
225
+ };
226
+ case 'gemini':
227
+ return {
228
+ path: `${dir}/${agentName}.md`,
229
+ content: `${frontmatter({
230
+ name: agentName,
231
+ description,
232
+ kind: 'local',
233
+ tools: readonly ? ['read_file', 'grep_search'] : undefined,
234
+ max_turns: 30,
235
+ })}\n${body}`,
236
+ };
237
+ case 'opencode':
238
+ return {
239
+ path: `${dir}/${agentName}.md`,
240
+ content: `${frontmatter({
241
+ description,
242
+ mode: 'subagent',
243
+ permission: { edit: readonly ? 'deny' : 'allow', bash: 'ask' },
244
+ })}\n${body}`,
245
+ };
246
+ case 'codex':
247
+ return {
248
+ path: `${dir}/${agentName}.toml`,
249
+ content: [
250
+ `name = ${tomlString(agentName)}`,
251
+ `description = ${tomlString(description)}`,
252
+ `developer_instructions = ${tomlLiteralBlock(
253
+ readonly
254
+ ? `READ-ONLY ROLE: never edit files or run state-changing commands; report findings to the parent.\n\n${body.trim()}`
255
+ : body.trim(),
256
+ )}`,
257
+ '',
258
+ ].join('\n'),
259
+ };
260
+ default:
261
+ return null;
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Render one AIDLC prompt as a native slash command / prompt file.
267
+ * @returns {{ path: string, content: string }}
268
+ */
269
+ export function renderCommand(L, name, src) {
270
+ const body = parseFrontmatter(src).body;
271
+ const description = promptDescription(src, name);
272
+ const dir = L.commandsDir;
273
+ switch (L.commandFormat) {
274
+ case 'described-md':
275
+ return { path: `${dir}/${name}.md`, content: `${frontmatter({ description })}\n${body}` };
276
+ case 'copilot-prompt':
277
+ return { path: `${dir}/${name}.prompt.md`, content: `${frontmatter({ description, agent: 'agent' })}\n${body}` };
278
+ case 'gemini-toml':
279
+ return {
280
+ path: `${dir}/${name}.toml`,
281
+ content: `description = ${tomlString(description)}\nprompt = ${tomlLiteralBlock(body.trim())}\n`,
282
+ };
283
+ case 'kiro-steering':
284
+ return { path: `${dir}/aies-${name}.md`, content: `${frontmatter({ inclusion: 'manual' })}${body}` };
285
+ case 'skill':
286
+ return {
287
+ path: `${dir}/${name}/SKILL.md`,
288
+ content: `${frontmatter({ name, description: `${description} Run when the user invokes /${name}.`.slice(0, 1024) })}\n${body}`,
289
+ };
290
+ case 'continue-prompt':
291
+ return { path: `${dir}/${name}.prompt`, content: `${frontmatter({ name, description, invokable: true })}\n${body}` };
292
+ case 'plain-md':
293
+ default:
294
+ return { path: `${dir}/${name}.md`, content: body };
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Native hook wiring: the guard (pre-tool), the definition-of-done gate (completion event, where
300
+ * the runtime documents a blocking contract), and — for confidential data — the egress self-test
301
+ * at session start. Returns null when the target has no project hooks.
302
+ */
303
+ export function renderHookConfig(L, { egressCheck = false } = {}) {
304
+ const sessionStart = (cmd, timeout) => (egressCheck ? { SessionStart: [{ hooks: [{ type: 'command', command: cmd, timeout }] }] } : {});
305
+ switch (L.hookFormat) {
306
+ case 'claude':
307
+ return {
308
+ $schema: 'https://json.schemastore.org/claude-code-settings.json',
309
+ permissions: {
310
+ deny: ['Read(./.env)', 'Read(./.env.local)', 'Read(./.env.production)', 'Read(./**/*.pem)', 'Read(./**/id_rsa)'],
311
+ },
312
+ hooks: {
313
+ PreToolUse: CLAUDE_MATCHERS.map(matcher => ({
314
+ matcher,
315
+ hooks: [
316
+ {
317
+ type: 'command',
318
+ command: 'node "$CLAUDE_PROJECT_DIR/hooks/aies-guard.mjs" --target claude',
319
+ timeout: 15,
320
+ onFailure: 'block',
321
+ },
322
+ ],
323
+ })),
324
+ Stop: [{ hooks: [{ type: 'command', command: 'node "$CLAUDE_PROJECT_DIR/hooks/aies-done.mjs" --target claude', timeout: DONE_TIMEOUT }] }],
325
+ ...sessionStart('node "$CLAUDE_PROJECT_DIR/hooks/aies-egress-check.mjs" --target claude', 15),
326
+ },
327
+ };
328
+ case 'cursor':
329
+ return {
330
+ version: 1,
331
+ hooks: {
332
+ beforeShellExecution: [{ command: `${GUARD_CMD} --target cursor`, timeout: 15, failClosed: true }],
333
+ beforeReadFile: [{ command: `${GUARD_CMD} --target cursor --event read`, timeout: 15, failClosed: true }],
334
+ preToolUse: [{ command: `${GUARD_CMD} --target cursor`, timeout: 15, failClosed: true }],
335
+ stop: [{ command: `${DONE_CMD} --target cursor`, timeout: DONE_TIMEOUT }],
336
+ },
337
+ };
338
+ case 'codex':
339
+ return {
340
+ hooks: {
341
+ PreToolUse: [
342
+ { matcher: 'Bash', hooks: [{ type: 'command', command: `${GUARD_CMD} --target codex --event shell`, timeout: 15 }] },
343
+ { matcher: 'Edit|Write', hooks: [{ type: 'command', command: `${GUARD_CMD} --target codex --event write`, timeout: 15 }] },
344
+ ],
345
+ Stop: [{ hooks: [{ type: 'command', command: `${DONE_CMD} --target codex`, timeout: DONE_TIMEOUT }] }],
346
+ ...sessionStart(`${EGRESS_CMD} --target codex`, 15),
347
+ },
348
+ };
349
+ case 'gemini':
350
+ return {
351
+ hooks: {
352
+ BeforeTool: [
353
+ { matcher: 'run_shell_command', hooks: [{ type: 'command', command: `${GUARD_CMD} --target gemini --event shell`, timeout: 15000 }] },
354
+ { matcher: 'write_file|replace', hooks: [{ type: 'command', command: `${GUARD_CMD} --target gemini --event write`, timeout: 15000 }] },
355
+ { matcher: 'read_file|read_many_files', hooks: [{ type: 'command', command: `${GUARD_CMD} --target gemini --event read`, timeout: 15000 }] },
356
+ ],
357
+ AfterAgent: [{ hooks: [{ type: 'command', command: `${DONE_CMD} --target gemini`, timeout: DONE_TIMEOUT * 1000 }] }],
358
+ ...sessionStart(`${EGRESS_CMD} --target gemini`, 15000),
359
+ },
360
+ };
361
+ case 'devin':
362
+ return {
363
+ PreToolUse: [{ hooks: [{ type: 'command', command: `${GUARD_CMD} --target devin`, timeout: 15 }] }],
364
+ Stop: [{ hooks: [{ type: 'command', command: `${DONE_CMD} --target devin`, timeout: DONE_TIMEOUT }] }],
365
+ ...sessionStart(`${EGRESS_CMD} --target devin`, 15),
366
+ };
367
+ case 'opencode':
368
+ return OPENCODE_GUARD_PLUGIN;
369
+ case 'copilot':
370
+ return {
371
+ version: 1,
372
+ hooks: {
373
+ preToolUse: [
374
+ {
375
+ type: 'command',
376
+ bash: `${GUARD_CMD} --target copilot`,
377
+ powershell: `${GUARD_CMD} --target copilot`,
378
+ timeoutSec: 15,
379
+ },
380
+ ],
381
+ },
382
+ };
383
+ case 'kiro':
384
+ return {
385
+ version: 'v1',
386
+ hooks: [
387
+ ...['shell', 'write', 'read'].map(category => ({
388
+ name: `aies-guard-${category}`,
389
+ description: `AIES deterministic ${category} policy (see hooks/README.md)`,
390
+ trigger: 'PreToolUse',
391
+ matcher: category,
392
+ action: { type: 'command', command: `${GUARD_CMD} --target kiro --event ${category}` },
393
+ })),
394
+ ],
395
+ };
396
+ default:
397
+ return null;
398
+ }
399
+ }
400
+
401
+ /** Empty, valid MCP config for the target — servers are enabled by a human from mcps/recommended.json. */
402
+ export function renderMcpConfig(L, recommendedIds) {
403
+ switch (L.mcpFormat) {
404
+ case 'portable':
405
+ case 'cursor':
406
+ return JSON.stringify({ mcpServers: {} }, null, 2);
407
+ case 'gemini':
408
+ return JSON.stringify({ context: { fileName: ['AGENTS.md', 'GEMINI.md'] }, mcpServers: {} }, null, 2);
409
+ case 'opencode':
410
+ return JSON.stringify({ $schema: 'https://opencode.ai/config.json', mcp: {} }, null, 2);
411
+ case 'codex':
412
+ return [
413
+ '# AIES — Codex project config. MCP servers stay commented until a human approves them.',
414
+ '# Guide: mcps/README.md · recommendations: mcps/recommended.json',
415
+ '# Subagents in .codex/agents/ inherit these; agent TOML files cannot override them.',
416
+ 'sandbox_mode = "workspace-write"',
417
+ 'approval_policy = "on-request"',
418
+ '',
419
+ renderCodexMcpToml(recommendedIds),
420
+ ].join('\n');
421
+ default:
422
+ return null;
423
+ }
424
+ }
425
+
426
+ /** Definition-of-done commands: the detected lint, typecheck, and test commands (build is left to CI). */
427
+ function doneConfig(verify) {
428
+ const commands = ['lint', 'typecheck', 'test'].map(k => verify?.commands?.[k]).filter(Boolean);
429
+ return JSON.stringify(
430
+ {
431
+ $comment: commands.length
432
+ ? 'Commands the agent must pass before it may finish. Human-maintained: the guard denies agent edits. See hooks/README.md.'
433
+ : 'No verify commands detected. Add the commands the agent must pass before finishing; until then the gate allows completion.',
434
+ commands,
435
+ timeoutSeconds: DONE_TIMEOUT,
436
+ maxConsecutiveBlocks: 3,
437
+ },
438
+ null,
439
+ 2,
440
+ );
441
+ }
442
+
443
+ function egressConfig() {
444
+ return JSON.stringify(
445
+ {
446
+ $comment: 'AC-06 egress self-test. Every probe must fail inside the agent sandbox. List hosts the sandbox must allow in expectReachable.',
447
+ dnsProbes: ['example.com'],
448
+ probes: [
449
+ { host: 'example.com', port: 443 },
450
+ { host: '1.1.1.1', port: 443 },
451
+ ],
452
+ expectReachable: [],
453
+ timeoutMs: 2500,
454
+ },
455
+ null,
456
+ 2,
457
+ );
458
+ }
459
+
460
+ function setupSteps(stack) {
461
+ const node = ['react', 'nextjs', 'react-native', 'nodejs', 'nestjs'].includes(stack);
462
+ const python = ['python', 'fastapi'].includes(stack);
463
+ const steps = [' - uses: actions/checkout@v4'];
464
+ if (node) {
465
+ steps.push(' - uses: actions/setup-node@v4', ' with:', ' node-version: 22', ' cache: npm', ' - run: npm ci');
466
+ } else if (python) {
467
+ steps.push(' - uses: actions/setup-python@v5', ' with:', " python-version: '3.12'", ' - run: pip install -r requirements.txt || pip install -e .');
468
+ } else if (stack === 'java') {
469
+ steps.push(' - uses: actions/setup-java@v4', ' with:', ' distribution: temurin', ' java-version: 21', ' - run: ./mvnw -B -q dependency:go-offline || ./gradlew --no-daemon dependencies');
470
+ } else if (stack === 'dotnet') {
471
+ steps.push(' - uses: actions/setup-dotnet@v4', ' with:', ' dotnet-version: 9.0.x', ' - run: dotnet restore');
79
472
  }
473
+ return steps.join('\n');
474
+ }
475
+
476
+ function copilotSetupSteps(stack) {
477
+ return `# Prepares the GitHub Copilot cloud agent environment. The job name must stay "copilot-setup-steps".
478
+ name: Copilot Setup Steps
479
+
480
+ on:
481
+ workflow_dispatch:
482
+ push:
483
+ paths: [.github/workflows/copilot-setup-steps.yml]
484
+ pull_request:
485
+ paths: [.github/workflows/copilot-setup-steps.yml]
486
+
487
+ jobs:
488
+ copilot-setup-steps:
489
+ runs-on: ubuntu-latest
490
+ timeout-minutes: 20
491
+ permissions:
492
+ contents: read
493
+ steps:
494
+ ${setupSteps(stack)}
495
+ `;
496
+ }
497
+
498
+ function installCommand(stack) {
499
+ if (['react', 'nextjs', 'react-native', 'nodejs', 'nestjs'].includes(stack)) return 'npm ci';
500
+ if (['python', 'fastapi'].includes(stack)) return 'pip install -r requirements.txt';
501
+ if (stack === 'java') return './mvnw -B -q dependency:go-offline';
502
+ if (stack === 'dotnet') return 'dotnet restore';
503
+ return '';
80
504
  }
81
505
 
82
506
  function modelPolicy(profile) {
@@ -86,6 +510,18 @@ function modelPolicy(profile) {
86
510
 
87
511
  Generated for: **${model}** · data class **${dataClass}**
88
512
 
513
+ ## Route by task class, not by vendor loyalty
514
+
515
+ | Task class | Model tier | Reasoning effort | Why |
516
+ |------------|-----------|------------------|-----|
517
+ | Planning, architecture, root-cause analysis | Frontier | high | Errors here multiply across every downstream task |
518
+ | Implementation against an approved plan | Frontier or strong mid-tier | medium | Plan carries the hard reasoning; cost scales with volume |
519
+ | Review / security pass | Frontier, **different family than the author** when available | high | Correlated blind spots defeat same-model review |
520
+ | Exploration, search, summarization subagents | Fast / small tier | low | Read-heavy, high-volume, low blast radius |
521
+ | Bulk mechanical edits (renames, codemods) | Fast tier + deterministic tooling | low | Prefer codemods; model only for the long tail |
522
+
523
+ Subagent definitions default to \`model: inherit\`. Override per agent only with a measured reason (eval delta or cost), and record it in the PR.
524
+
89
525
  ## Provider posture
90
526
 
91
527
  | Choice | Guidance |
@@ -101,16 +537,17 @@ ${
101
537
  dataClass === 'public'
102
538
  ? '- Public data may use cloud models with org-approved logging.\n- Still ban secrets in prompts.'
103
539
  : 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.'
540
+ ? '- 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 an allowlist.\n- Cloud/background agents only on approved runners; no production credentials in agent environments.\n- Require HITL for any tool that can write external systems.'
541
+ : '- Internal data: use org-approved providers only.\n- Redact secrets and customer identifiers from prompts and logs.\n- MCP: enable only servers from `mcps/recommended.json` after handbook review.'
106
542
  }
107
543
 
108
544
  ## Non-negotiables
109
545
 
110
546
  1. Human review is accountable — model output is advisory
111
547
  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
548
+ 3. Parallel agents: one task contract per worktree (\`plans/_TASK_CONTRACT.md\`), at most 4 at once
549
+ 4. MCP config stays empty until a human enables reviewed servers
550
+ 5. Eval gates for AI-touched paths before production
114
551
 
115
552
  Online: ${SITE_URL}/docs/handbook/mcp-when-and-how
116
553
  `;
@@ -127,18 +564,18 @@ function maturityReadingPath(maturity) {
127
564
  if (maturity === 'intermediate') {
128
565
  return `1. Enhance repo → ephemeral plan → cookbook generate/review
129
566
  2. Wire \`guides/evals-as-quality-gates.md\` into CI
130
- 3. Use \`hooks/\` before commit; enable MCP only from recommended stubs
567
+ 3. Read \`guides/native-agent-hooks.md\`; keep the guard on; enable MCP only from \`mcps/recommended.json\`
131
568
  4. Online: ${SITE_URL}/workflows`;
132
569
  }
133
570
  if (maturity === 'advanced') {
134
- return `1. Enhance repo + MCP handbook + agent hooks
135
- 2. Compose agents (planner → coder → reviewer) with budgets
571
+ return `1. Enhance repo + \`guides/ide-native-subagents.md\` + \`guides/native-agent-hooks.md\`
572
+ 2. Run 2–4 agents in parallel with \`guides/parallel-agents-and-worktrees.md\` and \`scripts/aies-worktree.mjs\`
136
573
  3. Security skill before write tools; story-scoped plans/reviews
137
574
  4. Online: ${SITE_URL}/docs/04-agent-engineering/README`;
138
575
  }
139
576
  return `1. Enhance repo + SoA mapping (${SITE_URL}/portal)
140
577
  2. Migration bible: ${SITE_URL}/docs/handbook/ai-migration-bible-path
141
- 3. Eval CI + MCP allowlists + hooks + segregation of duties
578
+ 3. Eval CI + MCP allowlists + guard hooks + segregation of duties; cloud agents PR-only
142
579
  4. Attach \`plans/{STORY_ID}-plan.md\` + \`reviews/{STORY_ID}-code-review.md\` on PRs — then stop maintaining them`;
143
580
  }
144
581
 
@@ -169,296 +606,367 @@ function pathBasename(p) {
169
606
  return parts[parts.length - 1] ?? p;
170
607
  }
171
608
 
172
- function skillPath(L, name) {
173
- return L.skillStyle === 'cursor-skill' ? `${L.skillsDir}/${name}/SKILL.md` : `${L.skillsDir}/${name}.skill.md`;
609
+ function commandRef(L, name) {
610
+ return renderCommand(L, name, '').path;
611
+ }
612
+
613
+ function invokeEnhanceChat(ide, L) {
614
+ const path = commandRef(L, 'enhance-repo');
615
+ switch (ide) {
616
+ case 'cursor':
617
+ return '/enhance-repo Scan this repo and enhance the AIES harness. Follow the XML instructions exactly.';
618
+ case 'claude-code':
619
+ return '/enhance-repo';
620
+ case 'copilot':
621
+ return '/enhance-repo (or: #file:.github/prompts/enhance-repo.prompt.md Scan this repo and enhance the AIES harness.)';
622
+ case 'gemini-cli':
623
+ return '/aies:enhance-repo';
624
+ case 'opencode':
625
+ return '/enhance-repo';
626
+ case 'windsurf':
627
+ return '/enhance-repo';
628
+ case 'kiro':
629
+ return '#aies-enhance-repo Scan this repo and enhance the AIES harness.';
630
+ default:
631
+ return `Read ${path} and execute it against this repository — scan first, then patch AIES artifacts only.`;
632
+ }
633
+ }
634
+
635
+ export const VERIFY_PLACEHOLDER = '(fill in)';
636
+
637
+ function verifyBlock(verify) {
638
+ const commands = verify?.commands ?? {};
639
+ const required = ['lint', 'typecheck', 'test'];
640
+ const keys = [...required, ...(commands.build ? ['build'] : [])];
641
+ const width = Math.max(...keys.map(k => (commands[k] ?? '').length), 0);
642
+ const lines = keys.map(k => (commands[k] ? `${commands[k].padEnd(width)} # ${k}` : `# ${k.padEnd(9)} → ${VERIFY_PLACEHOLDER}`));
643
+ const missing = required.filter(k => !commands[k]);
644
+ const lead = verify?.sources?.length
645
+ ? `Detected from ${verify.sources.join(', ')}. Confirm these match CI.`
646
+ : 'No verification commands detected.';
647
+ const tail = missing.length
648
+ ? ` Replace each \`${VERIFY_PLACEHOLDER}\` line with this repo's exact command, or \`none\` with a reason; \`audit\` fails until you do.`
649
+ : '';
650
+ return `Run every command below and report each result before claiming done. ${lead}${tail}
651
+
652
+ \`\`\`bash
653
+ ${lines.join('\n')}
654
+ \`\`\``;
174
655
  }
175
656
 
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}
657
+ function buildAgentsMd(profile, label, L, verify) {
658
+ return `# AGENTS.md — ${label}
180
659
 
181
- This repository uses the **AI Engineering Standard (AIES)** harness.
660
+ > Canonical agent instructions (AGENTS.md standard, Agentic AI Foundation). Read natively by Codex, Cursor,
661
+ > GitHub Copilot, Gemini CLI (via \`.gemini/settings.json\`), Kiro, Devin Desktop, opencode, Zed, and Junie.
662
+ > Tool-specific files (\`CLAUDE.md\`, \`.cursor/rules/\`, \`.github/copilot-instructions.md\`, …) add deltas only.
663
+ > Generated by AIES kit ${KIT_VERSION}. Run \`npx @grvmithas/create-aies-kit@latest enhance\` to refresh.
182
664
 
183
665
  ## Profile
184
666
 
185
667
  - Stack: ${label}
186
668
  - Role: ${profile.role}
187
- - Maturity: ${profile.maturity} (docs reading path only)
188
- - Primary IDE selection: ${profile.ide}
189
- - Model: ${profile.model ?? 'undecided'}
669
+ - Primary agent runtime: ${profile.ide}
670
+ - Model: ${profile.model ?? 'undecided'} (see \`MODEL_POLICY.md\` for routing by task class)
190
671
  - Data class: ${profile.dataClass ?? 'internal'}
191
672
 
192
673
  ## Non-negotiables
193
674
 
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
675
+ 1. Multi-file work requires an approved \`plans/{STORY_ID}-plan.md\` (ephemeral; attach it every session).
676
+ 2. Parallel work: one task per worktree with a filled \`plans/{TASK_ID}-contract.md\`; never edit outside its Allowed paths.
677
+ 3. Never push to protected branches, force-push, \`reset --hard\`, or bypass hooks (\`--no-verify\`). The guard hook enforces this; do not route around a denial — stop and ask.
678
+ 4. External effects (publish, deploy, apply, destructive SQL) are human-run after approval.
679
+ 5. No secrets in prompts, logs, commits, or MCP args. Never print \`.env\` files.
680
+ 6. Respect this repo's lint, typecheck, format, and test configs — never weaken them to pass.
681
+ 7. Write \`reviews/{STORY_ID}-code-review.md\` on AI-touched PRs. A human owns every merge.
682
+
683
+ ## Verify before claiming done
202
684
 
203
- ## Where to look
685
+ ${verifyBlock(verify)}
686
+
687
+ ## Where things live
204
688
 
205
689
  | Need | Path |
206
690
  |------|------|
207
691
  | 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
692
+ | Skills (Agent Skills standard) | \`.agents/skills/<name>/SKILL.md\` |
693
+ | Slash commands / prompts | \`${L.commandsDir}/\` |
694
+ | Subagents | \`${L.agentsDir ?? 'n/a for this runtime — use the roles below'}\` |
695
+ | Plans · task contracts · reviews | \`plans/\` · \`plans/_TASK_CONTRACT.md\` · \`reviews/\` |
696
+ | Guard hook · CI gates | \`hooks/aies-guard.mjs\` · \`templates/ci/aies-gates.yml\` |
697
+ | Evals (golden set + gate) | \`evals/\` |
698
+ | Parallel worktrees | \`node scripts/aies-worktree.mjs\` |
699
+ | Stack cookbook | \`${L.cookbookDir}/\` |
700
+ | MCP specs (recommended only) · config | \`mcps/\` · \`mcps/recommended.json\` |
701
+ | Model & data policy | \`MODEL_POLICY.md\` |
702
+
703
+ ## Roles
704
+
705
+ | Role | Authority | Use when |
706
+ |------|-----------|----------|
707
+ | planner | read-only | Decompose an approved story into a dependency-ordered plan |
708
+ | coder | workspace-write | Implement an approved plan, nothing more |
709
+ | parallel-implementer | workspace-write, own worktree | Execute one task contract in isolation |
710
+ | reviewer | read-only | Severity-calibrated review with file:line evidence |
711
+ | security | read-only | AuthN/AuthZ, data access, secrets, supply chain |
712
+ | testing | workspace-write | Tests and coverage for changed behavior |
224
713
 
225
- \`\`\`bash
226
- npx @grvmithas/create-aies-kit enhance
227
- \`\`\`
714
+ ## Workflow
228
715
 
229
- Then paste into agent chat:
716
+ Enhance → plan (approve) → [split into task contracts + worktrees if parallel] → code → verify → review report → PR → human merge in dependency order.
717
+ `;
718
+ }
230
719
 
231
- \`\`\`text
232
- ${enhanceChat}
233
- \`\`\`
720
+ function buildToolDelta(profile, L) {
721
+ const pointer = 'Follow `AGENTS.md` (canonical). This file adds runtime-specific notes only.';
722
+ switch (L.ide) {
723
+ case 'claude-code':
724
+ return `@AGENTS.md
234
725
 
235
- ## Workflow
726
+ # Claude Code notes
727
+
728
+ ${pointer}
729
+
730
+ - Subagents: \`.claude/agents/\` — delegate review to \`reviewer\` and security to \`security\`; they are read-only.
731
+ - Slash commands: \`.claude/commands/\` (\`/story-kickoff\`, \`/implementation-plan\`, \`/coding\`, \`/enhance-repo\`, …).
732
+ - Skills: \`.claude/skills/\` mirrors \`.agents/skills/\` (regenerate with \`enhance\`; edit the \`.agents\` copy).
733
+ - Hooks + permission denies: \`.claude/settings.json\` runs \`hooks/aies-guard.mjs\` (with \`onFailure: "block"\`) before every Bash, Edit/Write, and Read call.
734
+ - \`CLAUDE.local.md\` replaces the \`@AGENTS.md\` import for that user — keep personal notes elsewhere or re-import \`@AGENTS.md\`.
735
+ - Parallel work: \`parallel-implementer\` runs with \`isolation: worktree\`; for long tasks prefer \`node scripts/aies-worktree.mjs new <task>\` and a separate session per worktree.
736
+ `;
737
+ case 'cursor':
738
+ return `${frontmatter({ description: 'AIES harness — Cursor-specific deltas to AGENTS.md', alwaysApply: true })}
739
+ ${pointer}
740
+
741
+ - Subagents: \`.cursor/agents/\` (reviewer/security/planner are \`readonly\`).
742
+ - Commands: \`.cursor/commands/\` — type \`/\` in Agent chat.
743
+ - Skills: discovered from \`.agents/skills/\`.
744
+ - Hooks: \`.cursor/hooks.json\` runs \`hooks/aies-guard.mjs\` (\`failClosed: true\`) before shell commands, tool calls, and file reads; a denial is final — ask the human.
745
+ - Parallel work: one Cloud Agent or local worktree per task contract; never two agents on one branch.
746
+ `;
747
+ case 'copilot':
748
+ return `# Copilot instructions
749
+
750
+ ${pointer}
236
751
 
237
- Enhance → \`plans/{STORY_ID}-plan.md\` (approve) → coding with plan attached → verify (lint/test) → \`reviews/{STORY_ID}-code-review.md\` → PR
752
+ - Custom agents: \`.github/agents/*.agent.md\`. Prompt files: \`.github/prompts/*.prompt.md\` (type \`/\` in chat).
753
+ - Skills: discovered from \`.agents/skills/\`.
754
+ - Hooks: \`.github/hooks/aies-guard.json\` (CLI, VS Code, cloud agent).
755
+ - Cloud agent environment: \`.github/workflows/copilot-setup-steps.yml\`. Cloud agent PRs are drafts a human reviews.
238
756
  `;
757
+ case 'kiro':
758
+ return `${frontmatter({ inclusion: 'always' })}# AIES — Kiro steering
759
+
760
+ ${pointer}
761
+
762
+ - Prompts are manual steering files: reference \`#aies-coding\`, \`#aies-implementation-plan\`, … in chat.
763
+ - Skills: \`.kiro/skills/\` mirrors \`.agents/skills/\` (Kiro does not read \`.agents/\`).
764
+ - Hooks: \`.kiro/hooks/aies-guard.json\` runs \`hooks/aies-guard.mjs\` before shell, write, and read tools.
765
+ `;
766
+ case 'windsurf':
767
+ return `${frontmatter({ trigger: 'always_on', description: 'AIES harness deltas' })}
768
+ ${pointer}
769
+
770
+ - Commands are skills (workflows are deprecated): \`.devin/skills/<name>/SKILL.md\` — invoke with \`/story-kickoff\`, \`/coding\`, …
771
+ - Skills: discovered from \`.agents/skills/\`.
772
+ - Hooks: \`.devin/hooks.v1.json\` runs \`hooks/aies-guard.mjs\` before every tool call. Hooks are off in Restricted Mode — keep branch protection on.
773
+ - MCP: \`.devin/mcp_config.json\` (empty until a human approves servers).
774
+ `;
775
+ case 'continue':
776
+ return `${frontmatter({ name: 'AIES harness', alwaysApply: true })}
777
+ ${pointer}
778
+
779
+ - Prompts: \`.continue/prompts/\` (invokable as slash commands).
780
+ - Skills: open the relevant \`.agents/skills/<name>/SKILL.md\` and attach it.
781
+ `;
782
+ default:
783
+ return null;
784
+ }
239
785
  }
240
786
 
241
- function buildAiesReadme({ profile, label, L, stack, flags, now, recommendedIds }) {
787
+ function buildAiesReadme({ profile, label, L, stack, flags, recommendedIds }) {
242
788
  const model = profile.model ?? 'undecided';
243
789
  const dataClass = profile.dataClass ?? 'internal';
244
- const enhancePrompt = `${L.promptsDir}/enhance-repo.prompt.md`;
790
+ const cmd = name => `\`${commandRef(L, name)}\``;
245
791
 
246
792
  return `# AIES — Operator guide (${label})
247
793
 
248
- **Generated:** ${now}
794
+ **Kit:** ${KIT_VERSION} · upgrade with \`npx @grvmithas/create-aies-kit@latest enhance\` (your edits are kept; see §12)
249
795
  **Profile:** ${profile.role} · ${profile.ide} · ${profile.maturity} · ${label} · model **${model}** · data **${dataClass}**
250
796
 
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\`).
797
+ This install is **always complete** for the selected runtime. Every file is written to a path that runtime loads natively.
798
+ \`AGENTS.md\` and \`.agents/skills/\` are the cross-tool canon; teammates on other tools get the same rules and skills.
254
799
 
255
800
  ---
256
801
 
257
- ## 0. Start from the word go (do this first)
802
+ ## 0. Start here
258
803
 
259
- 1. **Read this file** (AIES.md).
260
- 2. **Refresh kit signals** (MCP recommendations from repo scan):
804
+ 1. **Read this file**, then \`AGENTS.md\`.
805
+ 2. **Refresh kit signals** (MCP recommendations from a repo scan):
261
806
 
262
807
  \`\`\`bash
263
- npx @grvmithas/create-aies-kit enhance
808
+ npx @grvmithas/create-aies-kit@latest enhance
264
809
  \`\`\`
265
810
 
266
- 3. **Run the enhance prompt in your IDE agent chat** (copy-paste):
811
+ 3. **Run the enhance command in your agent** (copy-paste):
267
812
 
268
813
  \`\`\`text
269
- ${invokeEnhanceChat(profile.ide, enhancePrompt)}
814
+ ${invokeEnhanceChat(L.ide, L)}
270
815
  \`\`\`
271
816
 
272
- Prompt path: \`${enhancePrompt}\`
273
- That scan rewrites harness files to match **this** repo’s lint, style guides, FE/BE layout, and guardrails.
817
+ Prompt path: ${cmd('enhance-repo')}
818
+ That scan fills \`AGENTS.md\` with this repo's real lint/test commands, layout, and guardrails.
274
819
 
275
- 4. Create an **ephemeral** plan: \`plans/{STORY_ID}-plan.md\` (see §1) — approve before multi-file coding.
820
+ 4. Create an **ephemeral** plan: \`plans/{STORY_ID}-plan.md\` — approve before multi-file coding.
276
821
  5. Code with the plan attached → write \`reviews/{STORY_ID}-code-review.md\` → open PR with both attached.
277
822
 
278
- **Re-install kit (force):**
279
-
280
- \`\`\`bash
281
- npx @grvmithas/create-aies-kit ${flags} --force
282
- \`\`\`
823
+ **Re-install (force):** \`npx @grvmithas/create-aies-kit@latest ${flags} --force\`
283
824
 
284
825
  ---
285
826
 
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.
827
+ ## 1. Ephemeral plans, task contracts & review reports
289
828
 
290
829
  | Artifact | Path | After merge |
291
830
  |----------|------|-------------|
292
831
  | Plan | \`plans/{STORY_ID}-plan.md\` | Attach to PR; archive or delete — **do not maintain** |
832
+ | Task contract (parallel work) | \`plans/{TASK_ID}-contract.md\` | Lives in the task's worktree; dies with the branch |
293
833
  | Code review | \`reviews/{STORY_ID}-code-review.md\` | Attach to PR; freeze — file issues for leftovers |
294
834
  | Index only | \`implementation_plan.md\` | Points here — not the active plan |
295
835
 
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
836
+ ---
302
837
 
303
- ### Temporary memory
838
+ ## 2. Parallel agents & worktrees
304
839
 
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\`)
840
+ One task · one branch · one worktree · one base commit. At most **4** agents at once.
308
841
 
309
- ### Anti-patterns
842
+ \`\`\`bash
843
+ node scripts/aies-worktree.mjs new JIRA-123-api # branch agent/JIRA-123-api, ports + DB schema isolated
844
+ # → fill plans/JIRA-123-api-contract.md (Allowed paths, Acceptance, Stop conditions), start an agent in that folder
845
+ node scripts/aies-worktree.mjs check JIRA-123-api # fails if the agent touched files outside the contract
846
+ node scripts/aies-worktree.mjs overlap # shared files between branches → decides merge order
847
+ node scripts/aies-worktree.mjs remove JIRA-123-api # after the PR merges
848
+ \`\`\`
310
849
 
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
850
+ Merge serially in dependency order and re-run verification after **each** merge. Guide: \`guides/parallel-agents-and-worktrees.md\`.
314
851
 
315
852
  ---
316
853
 
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/\` |
854
+ ## 3. Commands (prompts)
330
855
 
331
- **Rule:** Prefer cookbook prompts for stack-idiomatic work; prefer AIDLC prompts for lifecycle gates.
856
+ | Situation | Command |
857
+ |-----------|---------|
858
+ | New story / kickoff | ${cmd('story-kickoff')} |
859
+ | Build the plan | ${cmd('implementation-plan')} |
860
+ | Implement approved plan | ${cmd('coding')} |
861
+ | Security pass | ${cmd('security-review')} |
862
+ | Adapt harness to this repo | ${cmd('enhance-repo')} |
863
+ | Author a prompt / skill / agent | ${cmd('create-prompt')} · ${cmd('create-skill')} · ${cmd('create-agent')} |
864
+ | Stack generate / review / debug | \`${L.cookbookDir}/prompts/\` |
332
865
 
333
866
  ---
334
867
 
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.
868
+ ## 4. Skills (Agent Skills standard)
354
869
 
355
- ---
870
+ Canonical: \`.agents/skills/<name>/SKILL.md\` — \`name\` matches the folder, \`description\` says what + when, body < 500 lines.
871
+ ${L.skillMirrorDir ? `Mirrored to \`${L.skillMirrorDir}/\` because ${L.ide} reads skills there. Edit the \`.agents\` copy; \`enhance\` re-syncs.\n` : ''}
872
+ Core: planning · debug · code-review · testing · architecture · context · devops · evaluation · security · prompt · documentation, plus the ${label} stack skill.
873
+ Agents load skills on demand from their descriptions; you can also invoke one explicitly by name.
356
874
 
357
- ## 4. Agents
875
+ ---
358
876
 
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 |
877
+ ## 5. Subagents
365
878
 
366
- Compose agents; do not give them merge authority.
879
+ ${L.agentsDir ? `Native definitions in \`${L.agentsDir}/\`:
367
880
 
368
- ---
881
+ | Agent | Authority | Use when |
882
+ |-------|-----------|----------|
883
+ | planner | read-only | Complex multi-file planning |
884
+ | coder | workspace-write | Execution against an approved plan |
885
+ | parallel-implementer | workspace-write in its own worktree | One task contract, isolated |
886
+ | reviewer | read-only | Severity-calibrated review |
887
+ | security | read-only | Threat / AuthZ / secrets pass |
888
+ | testing | workspace-write | Tests for changed behavior |
889
+ | prompt-engineer | workspace-write | Designing prompts/skills |
369
890
 
370
- ## 5. Agent hooks (evidence gates)
891
+ Read-only agents cannot edit files. No agent has merge authority.` : `${profile.ide} has no project-level subagent files; the roles in \`AGENTS.md\` apply to the main agent.`}
371
892
 
372
- Scripts: \`hooks/*.hook.sh\` · Operator notes: \`hooks/README.md\` · Full contract: \`hooks/CONTRACT.md\`
373
- Registry: \`${L.hooksFile}\`
893
+ ---
374
894
 
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.
895
+ ## 6. Hooks
376
896
 
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.
897
+ ${L.hooksFile ? `\`${L.hooksFile}\` wires \`hooks/aies-guard.mjs\` into the runtime: destructive git, force pushes, pushes to protected branches, pipe-to-shell, destructive SQL, deploy/publish, \`sudo\`, and printing secrets are **denied before execution**. Edits to the agent's own guardrails (hook, permission, MCP, and CI config) and reads of secret files are denied too, and a guard crash denies the action.${L.ide === 'codex' ? ' Codex skips new or changed hooks until you trust them: run `/hooks` once after install.' : ''}` : `${profile.ide} has no project-level hook config. \`hooks/aies-guard.mjs\` still works from CI or git hooks; rely on branch protection for enforcement.`}
898
+ CI gates: copy \`templates/ci/aies-gates.yml\` to \`.github/workflows/\` — it runs \`node hooks/aies-guard.mjs --self-test\`, the harness audit, this repo's verify commands, and the evals in \`evals/\`. See \`hooks/README.md\`.
378
899
 
379
900
  ---
380
901
 
381
- ## 6. MCP connections (relevant + safe)
382
-
383
- Specs (all catalog entries shipped): \`${L.mcpsDir}/\`
384
- Config: \`${L.mcpFile}\`
902
+ ## 7. MCP
385
903
 
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\`.
904
+ Specs: \`mcps/\` · Config: ${L.mcpFile ? `\`${L.mcpFile}\` (empty by default)` : 'user-level only for this runtime — see `mcps/README.md`'} · Ready-to-paste: \`mcps/recommended.json\`
390
905
 
391
- **Never** commit tokens. Prefer read-only scopes first.
906
+ Recommended for this repo: **${recommendedIds.join(', ')}**. Prefer vendor-hosted Streamable HTTP servers with OAuth (no tokens in the repo) and read-only endpoints first.
907
+ \`mcps/\` holds specs for the recommended servers only; the full catalog is at ${SITE_URL}/docs/mcps/README. \`enhance\` adds specs when the repo scan recommends new servers.
392
908
 
393
909
  ---
394
910
 
395
- ## 7. Root instruction files (always present)
911
+ ## 8. Cloud & background agents
396
912
 
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}** |
913
+ ${L.ide === 'copilot' ? '- `.github/workflows/copilot-setup-steps.yml` prepares the Copilot cloud agent environment.\n' : ''}${L.ide === 'cursor' ? '- `.cursor/environment.json` gives Cursor Cloud Agents the install step.\n' : ''}- Cloud agents work on their own branch and open **draft** PRs; the same guard, contracts, and review rules apply.
914
+ - Never give cloud agents production credentials. Template workflows for headless review live in \`templates/ci/\`.
404
915
 
405
916
  ---
406
917
 
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.
918
+ ## 9. Layout (${profile.ide})
428
919
 
429
- ---
920
+ ${describeLayoutTable(L)}
430
921
 
431
- ## 9. Reading path (${profile.maturity})
922
+ ## 10. Reading path (${profile.maturity})
432
923
 
433
924
  ${maturityReadingPath(profile.maturity)}
434
925
 
435
- ## 10. Role next workflow
436
-
437
- ${SITE_URL}/workflows/${roleNext(profile.role)}
438
-
439
- ## 11. Online
926
+ ## 11. Next
440
927
 
928
+ - Role workflow: ${SITE_URL}/workflows/${roleNext(profile.role)}
441
929
  - Quick start: ${SITE_URL}/quickstart
442
930
  - 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
931
+
932
+ ## 12. Upgrades and your edits
933
+
934
+ Edit any generated file freely. \`.aies-kit.json\` records what the kit wrote, so \`enhance\` (or re-running install):
935
+
936
+ | Your file is… | The kit… |
937
+ |---------------|----------|
938
+ | Untouched since the kit wrote it | Updates it in place |
939
+ | Edited by you | Leaves it alone and writes the new version to \`<file>.aies-new\` for you to merge, then delete |
940
+ | No longer part of the kit, untouched | Deletes it |
941
+ | No longer part of the kit, edited | Leaves it in place |
942
+
943
+ \`--force\` replaces edited files with the kit version. Commit \`.aies-kit.json\`; add \`*.aies-new\` and \`hooks/logs/\` to \`.gitignore\`.
445
944
  `;
446
945
  }
447
946
 
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
- }
947
+ function describeLayoutTable(L) {
948
+ const rows = [
949
+ ['`AGENTS.md`', 'Canonical instructions (cross-tool)'],
950
+ ['`AIES.md`', 'This operator guide'],
951
+ ['`.agents/skills/`', 'Skills (Agent Skills standard)'],
952
+ [`\`${L.commandsDir}/\``, 'Commands / prompts (native)'],
953
+ ...(L.agentsDir ? [[`\`${L.agentsDir}/\``, 'Subagents (native)']] : []),
954
+ ...(L.skillMirrorDir ? [[`\`${L.skillMirrorDir}/\``, 'Skill mirror for this runtime']] : []),
955
+ ...(L.rulesFile ? [[`\`${L.rulesFile}\``, 'Runtime-specific deltas']] : []),
956
+ ...(L.hooksFile ? [[`\`${L.hooksFile}\``, 'Native hook wiring']] : []),
957
+ ...(L.mcpFile ? [[`\`${L.mcpFile}\``, 'MCP config (empty)']] : []),
958
+ ['`hooks/`', 'Guard script (native hook enforcement)'],
959
+ ['`evals/`', 'Harness evals + product eval cases (promptfoo)'],
960
+ ['`.aies-kit.json`', 'Hashes of kit-written files — lets upgrades keep your edits'],
961
+ ['`plans/` · `reviews/`', 'Ephemeral plans, task contracts, review reports'],
962
+ ['`scripts/aies-worktree.mjs`', 'Parallel-agent worktree helper'],
963
+ [`\`${L.cookbookDir}/\` · \`guides/\` · \`mcps/\` · \`templates/\``, 'Shared reference (repo root)'],
964
+ ['`MODEL_POLICY.md`', 'Model routing + data policy'],
965
+ ];
966
+ return ['| Path | Purpose |', '|------|---------|', ...rows.map(([a, b]) => `| ${a} | ${b} |`)].join('\n');
459
967
  }
460
968
 
461
- function implementationPlanIndex(promptsDir) {
969
+ function implementationPlanIndex() {
462
970
  return `# Implementation plans (ephemeral index)
463
971
 
464
972
  > **Not a living document.** Plans exist only for the duration of one story/task.
@@ -467,118 +975,89 @@ function implementationPlanIndex(promptsDir) {
467
975
  ## Create a plan for this story
468
976
 
469
977
  1. Copy \`plans/_TEMPLATE.md\` → \`plans/{STORY_ID}-plan.md\`
470
- 2. Fill it with \`${promptsDir}/implementation-plan.prompt.md\` + the planning skill
978
+ 2. Fill it with the implementation-plan command + the planning skill
471
979
  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)
980
+ 4. **Attach** \`plans/{STORY_ID}-plan.md\` in every agent session for that story
981
+ 5. Splitting across parallel agents? One \`plans/{TASK_ID}-contract.md\` per task via \`node scripts/aies-worktree.mjs new {TASK_ID}\`
982
+ 6. Attach the plan to the PR; when the PR merges, **stop maintaining it** (archive or delete)
474
983
 
475
984
  ## Naming
476
985
 
477
986
  | Artifact | Path |
478
987
  |----------|------|
479
988
  | Plan | \`plans/{STORY_ID}-plan.md\` |
989
+ | Task contract | \`plans/{TASK_ID}-contract.md\` |
480
990
  | Code review report | \`reviews/{STORY_ID}-code-review.md\` |
481
991
 
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
992
+ Examples: \`plans/JIRA-1234-plan.md\`, \`plans/JIRA-1234-api-contract.md\`, \`reviews/JIRA-1234-code-review.md\`
489
993
 
490
994
  See \`plans/README.md\` and \`reviews/README.md\`.
491
995
  `;
492
996
  }
493
997
 
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
- };
998
+ function claudeCiTemplate() {
999
+ return `# Template — copy to .github/workflows/ only after adding the ANTHROPIC_API_KEY secret.
1000
+ # Headless Claude Code review on PRs: read-only, comments only, never pushes.
1001
+ name: AI PR review (Claude Code)
1002
+
1003
+ on:
1004
+ pull_request:
1005
+ types: [opened, synchronize, ready_for_review]
1006
+
1007
+ permissions:
1008
+ contents: read
1009
+ pull-requests: write
1010
+
1011
+ jobs:
1012
+ review:
1013
+ if: github.event.pull_request.draft == false
1014
+ runs-on: ubuntu-latest
1015
+ timeout-minutes: 15
1016
+ steps:
1017
+ - uses: actions/checkout@v4
1018
+ - uses: anthropics/claude-code-action@v1
1019
+ with:
1020
+ anthropic_api_key: \${{ secrets.ANTHROPIC_API_KEY }}
1021
+ prompt: |
1022
+ Review this PR using .agents/skills/code-review/SKILL.md and AGENTS.md.
1023
+ Post findings as review comments with file:line evidence. Do not modify files.
1024
+ `;
1025
+ }
1026
+
1027
+ /**
1028
+ * Files earlier kit versions installed that this version no longer generates, with the content the
1029
+ * kit wrote. An upgrade deletes them only when the copy on disk is byte-identical (untouched).
1030
+ */
1031
+ function retiredFiles(files) {
1032
+ const retired = {};
1033
+ for (const key of Object.keys(EMBEDDED_CORPUS)) {
1034
+ if ((key.startsWith('hooks/') && key.endsWith('.hook.sh')) || key === 'hooks/CONTRACT.md') retired[key] = EMBEDDED_CORPUS[key];
1035
+ }
1036
+ for (const spec of MCP_SPECS) {
1037
+ const rel = `mcps/${spec.file}`;
1038
+ if (!(rel in files) && EMBEDDED_CORPUS[rel]) retired[rel] = EMBEDDED_CORPUS[rel];
1039
+ }
1040
+ return retired;
563
1041
  }
564
1042
 
565
1043
  /**
566
1044
  * @param {object} profile
567
- * @param {{ recommendedMcpIds?: string[] }} [opts]
1045
+ * @param {{ recommendedMcpIds?: string[], verify?: { commands: Record<string, string>, sources: string[] } }} [opts]
568
1046
  */
569
1047
  export function generateStarterKit(profile, opts = {}) {
1048
+ const ide = normalizeIde(profile.ide);
570
1049
  const stack = stackMeta(profile.stack);
571
1050
  const label = STACK_LABELS[profile.stack] ?? profile.stack;
572
1051
  const now = new Date().toISOString();
573
1052
  const model = profile.model ?? 'undecided';
574
1053
  const dataClass = profile.dataClass ?? 'internal';
575
- const L = layoutFor(profile.ide, { cookbookId: stack.cookbook ?? null });
1054
+ const L = layoutFor(ide, { cookbookId: stack.cookbook ?? null });
576
1055
  const recommendedIds = opts.recommendedMcpIds ?? stackMcpIds(profile.stack);
577
- const recommended = buildRecommendedMcpConfig(recommendedIds);
1056
+ const normalized = { ...profile, ide: L.ide, model, dataClass };
578
1057
 
579
1058
  const flags = [
580
1059
  `--stack ${profile.stack}`,
581
- `--ide ${profile.ide}`,
1060
+ `--ide ${L.ide}`,
582
1061
  `--role ${profile.role}`,
583
1062
  `--maturity ${profile.maturity}`,
584
1063
  `--goal ${profile.goal}`,
@@ -590,143 +1069,221 @@ export function generateStarterKit(profile, opts = {}) {
590
1069
  /** @type {Record<string, string>} */
591
1070
  const files = {};
592
1071
 
593
- for (const name of CORE_PROMPTS) {
594
- files[`${L.promptsDir}/${name}.prompt.md`] = corpus(`aidlc/${name}.prompt.md`);
1072
+ // Cross-tool canon
1073
+ const skills = CORE_SKILLS.map(name => [name, corpus(`skills/${name}.skill.md`)]);
1074
+ if (stack.cookbook) {
1075
+ const skillKey = cookbookKeys(stack.cookbook).find(k => k.includes('/skills/') && k.endsWith('.skill.md'));
1076
+ if (skillKey) skills.push([pathBasename(skillKey).replace(/\.skill\.md$/, ''), corpus(skillKey)]);
595
1077
  }
596
-
597
- for (const name of CORE_SKILLS) {
598
- writeSkill(files, L, name, corpus(`skills/${name}.skill.md`));
1078
+ for (const [name, src] of skills) {
1079
+ const md = skillMarkdown(name, src);
1080
+ files[`${L.skillsDir}/${name}/SKILL.md`] = md;
1081
+ if (L.skillMirrorDir) files[`${L.skillMirrorDir}/${name}/SKILL.md`] = md;
599
1082
  }
600
1083
 
1084
+ // Native commands + agents
1085
+ for (const name of CORE_PROMPTS) {
1086
+ const rendered = renderCommand(L, name, corpus(`aidlc/${name}.prompt.md`));
1087
+ files[rendered.path] = rendered.content;
1088
+ }
601
1089
  for (const name of CORE_AGENTS) {
602
- files[`${L.agentsDir}/${name}.agent.md`] = corpus(`agents/${name}.agent.md`);
1090
+ const rendered = renderAgent(L, name, corpus(`agents/${name}.agent.md`));
1091
+ if (rendered) files[rendered.path] = rendered.content;
603
1092
  }
604
1093
 
605
- files[`${L.templatesDir}/system-prompt.template.md`] = corpus('templates/system-prompt.template.md');
1094
+ // Hooks
1095
+ files['hooks/aies-guard.mjs'] = corpus('kit/aies-guard.mjs');
1096
+ files['hooks/aies-done.mjs'] = corpus('kit/aies-done.mjs');
1097
+ files['hooks/aies-done.json'] = doneConfig(opts.verify);
1098
+ files['hooks/aies-egress-check.mjs'] = corpus('kit/aies-egress-check.mjs');
1099
+ files['hooks/aies-egress.json'] = egressConfig();
1100
+ files['hooks/README.md'] = corpus('kit/hooks-README.md');
1101
+ const hookConfig = renderHookConfig(L, { egressCheck: dataClass === 'confidential' });
1102
+ if (hookConfig) files[L.hooksFile] = typeof hookConfig === 'string' ? hookConfig : JSON.stringify(hookConfig, null, 2);
606
1103
 
1104
+ // Plans, contracts, reviews, worktrees
607
1105
  files['plans/README.md'] = corpus('kit/plans-README.md');
608
1106
  files['plans/_TEMPLATE.md'] = corpus('kit/plan-TEMPLATE.md');
1107
+ files['plans/_TASK_CONTRACT.md'] = corpus('kit/task-contract-TEMPLATE.md');
609
1108
  files['reviews/README.md'] = corpus('kit/reviews-README.md');
610
1109
  files['reviews/_TEMPLATE.md'] = corpus('kit/review-TEMPLATE.md');
1110
+ files['scripts/aies-worktree.mjs'] = corpus('kit/aies-worktree.mjs');
611
1111
 
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
-
1112
+ // Shared reference
1113
+ files[`${L.templatesDir}/system-prompt.template.md`] = corpus('templates/system-prompt.template.md');
1114
+ files[`${L.templatesDir}/ci/claude-pr-review.yml`] = claudeCiTemplate();
1115
+ files[`${L.templatesDir}/ci/aies-gates.yml`] = renderGatesWorkflow(normalized, setupSteps(profile.stack), opts.verify);
1116
+ Object.assign(files, renderEvalFiles(normalized));
618
1117
  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));
1118
+ for (const key of cookbookKeys(stack.cookbook)) {
1119
+ files[`${L.cookbookDir}/${key.slice(`cookbook/${stack.cookbook}/`.length)}`] = corpus(key);
628
1120
  }
629
1121
  }
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
-
1122
+ for (const guide of GUIDES) {
1123
+ files[`${L.guidesDir}/${guide}.md`] = corpus(`handbook/${guide}.md`);
1124
+ }
637
1125
  files[`${L.mcpsDir}/README.md`] = corpus('mcps/README.md');
638
- for (const spec of MCP_SPECS) {
1126
+ for (const spec of MCP_SPECS.filter(s => recommendedIds.includes(s.id))) {
639
1127
  files[`${L.mcpsDir}/${spec.file}`] = corpus(`mcps/${spec.file}`);
640
1128
  }
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,
1129
+ files[`${L.mcpsDir}/recommended.json`] = JSON.stringify(
1130
+ {
1131
+ $comment: `Reviewed candidates for ${L.mcpFile ?? 'your user-level MCP config'}. Copy one entry at a time after security review. Remote servers authenticate with OAuth on first use — no tokens in this repo.`,
1132
+ format: L.mcpFormat ?? 'portable',
1133
+ servers: L.mcpFormat === 'codex' ? renderMcpServers('portable', recommendedIds) : renderMcpServers(L.mcpFormat ?? 'portable', recommendedIds),
1134
+ catalog: buildRecommendedMcpConfig(recommendedIds),
653
1135
  },
654
- };
655
- files[L.mcpFile] = JSON.stringify(mcpDoc, null, 2);
1136
+ null,
1137
+ 2,
1138
+ );
1139
+ const mcpConfig = renderMcpConfig(L, recommendedIds);
1140
+ if (mcpConfig && L.mcpFile && L.mcpFile === L.hooksFile && hookConfig) {
1141
+ files[L.mcpFile] = JSON.stringify({ ...JSON.parse(mcpConfig), ...hookConfig }, null, 2);
1142
+ } else if (mcpConfig && L.mcpFile) {
1143
+ files[L.mcpFile] = mcpConfig;
1144
+ }
656
1145
 
657
- files[L.hooksFile] = JSON.stringify(agentHooksRegistry(L), null, 2);
1146
+ files[L.policyFile] = modelPolicy(normalized);
658
1147
 
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;
1148
+ // Instructions: AGENTS.md canonical + runtime delta
1149
+ files['AGENTS.md'] = buildAgentsMd(normalized, label, L, opts.verify);
1150
+ const delta = buildToolDelta(normalized, L);
1151
+ if (delta && L.rulesFile) files[L.rulesFile] = delta;
664
1152
 
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;
1153
+ // Cloud agent environments
1154
+ if (L.ide === 'copilot') files['.github/workflows/copilot-setup-steps.yml'] = copilotSetupSteps(profile.stack);
1155
+ if (L.ide === 'cursor' && installCommand(profile.stack)) {
1156
+ files['.cursor/environment.json'] = JSON.stringify({ install: installCommand(profile.stack) }, null, 2);
695
1157
  }
696
1158
 
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
- });
1159
+ files['implementation_plan.md'] = implementationPlanIndex();
1160
+ files['AIES.md'] = buildAiesReadme({ profile: normalized, label, L, stack, flags, recommendedIds });
708
1161
 
709
1162
  return {
710
1163
  stack: profile.stack,
711
1164
  generatedAt: now,
712
- profile: { ...profile, model, dataClass },
713
- layout: describeInstallLayout(profile.ide, { cookbookId: stack.cookbook ?? null }),
1165
+ kitVersion: KIT_VERSION,
1166
+ profile: normalized,
1167
+ layout: describeInstallLayout(L.ide, { cookbookId: stack.cookbook ?? null }),
714
1168
  files,
1169
+ retired: retiredFiles(files),
715
1170
  };
716
1171
  }
717
1172
 
1173
+ export const AGENT_PLUGINS_SCHEMA = 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json';
1174
+ export const AGENT_PLUGINS_MCP_SCHEMA = 'https://agent-plugins.org/schemas/1.0.0/mcp.schema.json';
1175
+ const PLUGIN_DESCRIPTION = 'AI Engineering Standard harness: skills, AIDLC commands as skills, subagents, the AIES guard, and the definition-of-done gate.';
1176
+
1177
+ /**
1178
+ * Agent Plugins 1.0 bundle for org-wide distribution.
1179
+ * Portable core (root plugin.json + skills/ + mcp.json) is read natively by Cursor and Kiro.
1180
+ * Overlays: .claude-plugin/ (agents + guard + done-gate hooks) and .codex-plugin/ (manifest only;
1181
+ * Codex repo installs carry the hooks). Claude hooks live at a non-default path so Codex, whose
1182
+ * plugin default is hooks/hooks.json, never loads Claude-format hooks.
1183
+ */
1184
+ export function generatePluginBundle(profile, { name = 'aies' } = {}) {
1185
+ const L = layoutFor('claude-code');
1186
+ const files = {};
1187
+ const meta = { version: KIT_VERSION, description: PLUGIN_DESCRIPTION, homepage: SITE_URL, license: 'MIT' };
1188
+
1189
+ files['plugin.json'] = JSON.stringify({ $schema: AGENT_PLUGINS_SCHEMA, name, ...meta, keywords: ['aies', 'agent-harness', 'guardrails'] }, null, 2);
1190
+ files['mcp.json'] = JSON.stringify({ $schema: AGENT_PLUGINS_MCP_SCHEMA, mcpServers: {} }, null, 2);
1191
+ for (const n of CORE_SKILLS) files[`skills/${n}/SKILL.md`] = skillMarkdown(n, corpus(`skills/${n}.skill.md`));
1192
+ for (const n of CORE_PROMPTS) {
1193
+ const r = renderCommand({ ...L, commandFormat: 'skill', commandsDir: 'skills' }, n, corpus(`aidlc/${n}.prompt.md`));
1194
+ files[r.path] = r.content;
1195
+ }
1196
+
1197
+ files['.claude-plugin/plugin.json'] = JSON.stringify({ name, ...meta, hooks: './hooks/claude-hooks.json' }, null, 2);
1198
+ files['.claude-plugin/marketplace.json'] = JSON.stringify(
1199
+ {
1200
+ name: `${name}-marketplace`,
1201
+ owner: { name: 'Your platform team' },
1202
+ plugins: [{ name, source: './', description: 'AIES harness' }],
1203
+ },
1204
+ null,
1205
+ 2,
1206
+ );
1207
+ for (const n of CORE_AGENTS) {
1208
+ const r = renderAgent({ ...L, agentsDir: 'agents' }, n, corpus(`agents/${n}.agent.md`));
1209
+ if (r) files[r.path] = r.content;
1210
+ }
1211
+ files['hooks/aies-guard.mjs'] = corpus('kit/aies-guard.mjs');
1212
+ files['hooks/aies-done.mjs'] = corpus('kit/aies-done.mjs');
1213
+ files['hooks/claude-hooks.json'] = JSON.stringify(
1214
+ {
1215
+ hooks: {
1216
+ PreToolUse: [
1217
+ {
1218
+ matcher: CLAUDE_MATCHERS.join('|'),
1219
+ hooks: [{ type: 'command', command: 'node "${CLAUDE_PLUGIN_ROOT}/hooks/aies-guard.mjs" --target claude', timeout: 15, onFailure: 'block' }],
1220
+ },
1221
+ ],
1222
+ Stop: [{ hooks: [{ type: 'command', command: 'node "${CLAUDE_PLUGIN_ROOT}/hooks/aies-done.mjs" --target claude', timeout: DONE_TIMEOUT }] }],
1223
+ },
1224
+ },
1225
+ null,
1226
+ 2,
1227
+ );
1228
+
1229
+ files['.codex-plugin/plugin.json'] = JSON.stringify({ name, ...meta }, null, 2);
1230
+
1231
+ files['README.md'] = `# ${name} — Agent Plugins 1.0 bundle
1232
+
1233
+ One package, four runtimes. Version ${KIT_VERSION}. Regenerate with \`npx @grvmithas/create-aies-kit@latest plugin\`.
1234
+
1235
+ | Runtime | Reads | Gets |
1236
+ |---|---|---|
1237
+ | Cursor, Kiro | \`plugin.json\` (Agent Plugins 1.0) | ${CORE_SKILLS.length} skills, ${CORE_PROMPTS.length} AIDLC commands as skills, \`mcp.json\` |
1238
+ | Claude Code | \`.claude-plugin/plugin.json\` | the same skills, ${CORE_AGENTS.length} subagents, guard (PreToolUse) and definition-of-done gate (Stop) |
1239
+ | Codex | \`.codex-plugin/plugin.json\` | the same skills; hooks come from the per-repo kit install (\`.codex/hooks.json\`) |
1240
+
1241
+ ## Install
1242
+
1243
+ \`\`\`text
1244
+ # Claude Code
1245
+ /plugin marketplace add <org>/<repo>
1246
+ /plugin install ${name}@${name}-marketplace
1247
+ \`\`\`
1248
+
1249
+ Cursor and Kiro: add the git repository through the team marketplace or the plugin settings; both validate \`plugin.json\` against the Agent Plugins schema.
1250
+
1251
+ ## Definition-of-done gate
1252
+
1253
+ The Stop hook reads \`hooks/aies-done.json\` from the **project**, not the plugin. Projects without it are not gated. Create it with the repo kit (\`npx @grvmithas/create-aies-kit@latest\`) or by hand:
1254
+
1255
+ \`\`\`json
1256
+ { "commands": ["npm run lint", "npm test"], "timeoutSeconds": ${DONE_TIMEOUT}, "maxConsecutiveBlocks": 3 }
1257
+ \`\`\`
1258
+
1259
+ ## Supply chain
1260
+
1261
+ Pin the plugin by commit SHA in your marketplace, review every update as a code change, and keep \`mcp.json\` empty unless security has approved a server. The manifest schema is closed: unknown top-level fields are ignored by conforming clients.
1262
+ `;
1263
+ return { kitVersion: KIT_VERSION, profile, files };
1264
+ }
1265
+
718
1266
  /**
719
1267
  * Enhance mode: rescan repo, refresh MCP recommendations + AIES.md section, keep corpus.
720
1268
  * Requires an existing profile — recovered from AIES.md markers or passed flags.
721
1269
  */
722
1270
  export function enhanceStarterKit(cwd, profile) {
723
1271
  const { signals, recommendedMcpIds } = detectRepoSignals(cwd, fs);
1272
+ const verify = detectVerifyCommands(cwd, fs);
724
1273
  const mergedIds = [...new Set([...stackMcpIds(profile.stack), ...recommendedMcpIds])];
725
- const bundle = generateStarterKit(profile, { recommendedMcpIds: mergedIds });
1274
+ const bundle = generateStarterKit(profile, { recommendedMcpIds: mergedIds, verify });
1275
+ const L = layoutFor(profile.ide, { cookbookId: stackMeta(profile.stack).cookbook });
1276
+ const verifyRows = ['lint', 'typecheck', 'test', 'build'].map(k => `| ${k} | ${verify.commands[k] ? `\`${verify.commands[k]}\`` : 'not detected'} |`);
1277
+
1278
+ bundle.files['AIES_REPO_SCAN.md'] = `# AIES repo scan
1279
+
1280
+ ## Verification commands
726
1281
 
727
- const scanReport = `# AIES repo scan
1282
+ Sources: ${verify.sources.length ? verify.sources.join(', ') : 'none detected'}
728
1283
 
729
- Generated: ${new Date().toISOString()}
1284
+ | Check | Command |
1285
+ |-------|---------|
1286
+ ${verifyRows.join('\n')}
730
1287
 
731
1288
  ## Signals detected
732
1289
 
@@ -740,13 +1297,12 @@ ${mergedIds.map(id => `- ${id}`).join('\n')}
740
1297
 
741
1298
  ## Next
742
1299
 
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
1300
+ 1. Confirm the verification commands in \`AGENTS.md\` match CI; fill any \`${VERIFY_PLACEHOLDER}\` line
1301
+ 2. Review \`mcps/recommended.json\` and enable approved servers in \`${L.mcpFile ?? 'your user-level MCP config'}\`
1302
+ 3. Run \`${commandRef(L, 'enhance-repo')}\` in your agent for conventions the scan cannot infer
1303
+ 4. Create \`plans/{STORY_ID}-plan.md\` before multi-file coding
746
1304
  `;
747
-
748
- bundle.files['AIES_REPO_SCAN.md'] = scanReport;
749
- bundle.scan = { signals, recommendedMcpIds: mergedIds };
1305
+ bundle.scan = { signals, recommendedMcpIds: mergedIds, verify };
750
1306
  return bundle;
751
1307
  }
752
1308
 
@@ -754,19 +1310,27 @@ export function renderBashInstallScript(bundle) {
754
1310
  const lines = [
755
1311
  '#!/usr/bin/env bash',
756
1312
  'set -euo pipefail',
757
- 'echo "AIES kit — scaffolding IDE-native harness into $(pwd)"',
1313
+ `echo "AIES kit ${KIT_VERSION} — scaffolding IDE-native harness into $(pwd)"`,
1314
+ '',
1315
+ '# Never overwrite an existing file: a differing copy is written to <file>.aies-new for review.',
1316
+ 'aies_write() {',
1317
+ ' tmp="$(mktemp)"; cat > "$tmp"',
1318
+ ' if [ ! -e "$1" ]; then mv "$tmp" "$1"',
1319
+ ' elif cmp -s "$tmp" "$1"; then rm -f "$tmp"',
1320
+ ' else mv "$tmp" "$1.aies-new"; echo "kept your $1 — new version in $1.aies-new"; fi',
1321
+ '}',
758
1322
  '',
759
1323
  ];
760
1324
  for (const [relPath, content] of Object.entries(bundle.files)) {
761
1325
  const dir = relPath.includes('/') ? relPath.slice(0, relPath.lastIndexOf('/')) : '';
762
1326
  if (dir) lines.push(`mkdir -p "${dir}"`);
763
1327
  const delim = `AIES_EOF_${Buffer.from(relPath).toString('hex').slice(0, 12)}`;
764
- lines.push(`cat > "${relPath}" <<'${delim}'`);
1328
+ lines.push(`aies_write "${relPath}" <<'${delim}'`);
765
1329
  lines.push(content.replace(/\r\n/g, '\n'));
766
1330
  lines.push(delim);
767
1331
  lines.push('');
768
1332
  }
769
- lines.push('echo "Done. Read AIES.md — then: npx @grvmithas/create-aies-kit enhance"');
1333
+ lines.push('echo "Done. Read AIES.md — then: npx @grvmithas/create-aies-kit@latest enhance"');
770
1334
  lines.push(`echo "Open: ${SITE_URL}/quickstart"`);
771
1335
  return lines.join('\n');
772
1336
  }
@@ -774,7 +1338,17 @@ export function renderBashInstallScript(bundle) {
774
1338
  export function renderPowerShellInstallScript(bundle) {
775
1339
  const lines = [
776
1340
  '$ErrorActionPreference = "Stop"',
777
- 'Write-Host "AIES kit — scaffolding IDE-native harness into $(Get-Location)"',
1341
+ `Write-Host "AIES kit ${KIT_VERSION} - scaffolding IDE-native harness into $(Get-Location)"`,
1342
+ '',
1343
+ '# Never overwrite an existing file: a differing copy is written to <file>.aies-new for review.',
1344
+ 'function Write-AiesFile([string]$Rel, [string]$B64) {',
1345
+ ' $path = Join-Path (Get-Location) $Rel',
1346
+ ' $text = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($B64))',
1347
+ ' if (-not (Test-Path -LiteralPath $path)) { [System.IO.File]::WriteAllText($path, $text); return }',
1348
+ ' if (([System.IO.File]::ReadAllText($path) -replace "`r`n", "`n") -eq ($text -replace "`r`n", "`n")) { return }',
1349
+ ' [System.IO.File]::WriteAllText("$path.aies-new", $text)',
1350
+ ' Write-Host "kept your $Rel - new version in $Rel.aies-new"',
1351
+ '}',
778
1352
  '',
779
1353
  ];
780
1354
  for (const [relPath, content] of Object.entries(bundle.files)) {
@@ -784,11 +1358,9 @@ export function renderPowerShellInstallScript(bundle) {
784
1358
  lines.push(`New-Item -ItemType Directory -Force -Path "${dir.replace(/\//g, '\\')}" | Out-Null`);
785
1359
  }
786
1360
  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
- );
1361
+ lines.push(`Write-AiesFile "${psPath}" "${b64}"`);
790
1362
  }
791
- lines.push('Write-Host "Done. Read AIES.md — then: npx @grvmithas/create-aies-kit enhance"');
1363
+ lines.push('Write-Host "Done. Read AIES.md - then: npx @grvmithas/create-aies-kit@latest enhance"');
792
1364
  lines.push(`Write-Host "Open: ${SITE_URL}/quickstart"`);
793
1365
  return lines.join('\n');
794
1366
  }