@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.
- package/CHANGELOG.md +96 -0
- package/README.md +82 -29
- package/bin/create-aies-kit.js +143 -41
- package/lib/audit.js +367 -0
- package/lib/kit-core/apply.js +143 -0
- package/lib/kit-core/embedded-corpus.js +79 -67
- package/lib/kit-core/evals.js +256 -0
- package/lib/kit-core/generate.js +984 -412
- package/lib/kit-core/install-api.js +154 -106
- package/lib/kit-core/mcp-catalog.js +95 -40
- package/lib/kit-core/options.js +20 -3
- package/lib/kit-core/repo-scan.js +158 -0
- package/package.json +12 -3
package/lib/kit-core/generate.js
CHANGED
|
@@ -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
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
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.
|
|
113
|
-
4.
|
|
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.
|
|
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 +
|
|
135
|
-
2.
|
|
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
|
|
173
|
-
return L
|
|
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
|
|
177
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
188
|
-
-
|
|
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.
|
|
195
|
-
2.
|
|
196
|
-
3.
|
|
197
|
-
4.
|
|
198
|
-
5. No secrets in prompts, logs, or MCP args
|
|
199
|
-
6.
|
|
200
|
-
7.
|
|
201
|
-
|
|
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
|
-
|
|
685
|
+
${verifyBlock(verify)}
|
|
686
|
+
|
|
687
|
+
## Where things live
|
|
204
688
|
|
|
205
689
|
| Need | Path |
|
|
206
690
|
|------|------|
|
|
207
691
|
| Operator guide | \`AIES.md\` |
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
| Stack cookbook | \`${L.cookbookDir}/\`
|
|
216
|
-
| MCP specs |
|
|
217
|
-
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
226
|
-
npx @grvmithas/create-aies-kit enhance
|
|
227
|
-
\`\`\`
|
|
714
|
+
## Workflow
|
|
228
715
|
|
|
229
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
790
|
+
const cmd = name => `\`${commandRef(L, name)}\``;
|
|
245
791
|
|
|
246
792
|
return `# AIES — Operator guide (${label})
|
|
247
793
|
|
|
248
|
-
**
|
|
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
|
|
252
|
-
|
|
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
|
|
802
|
+
## 0. Start here
|
|
258
803
|
|
|
259
|
-
1. **Read this file
|
|
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
|
|
811
|
+
3. **Run the enhance command in your agent** (copy-paste):
|
|
267
812
|
|
|
268
813
|
\`\`\`text
|
|
269
|
-
${invokeEnhanceChat(
|
|
814
|
+
${invokeEnhanceChat(L.ide, L)}
|
|
270
815
|
\`\`\`
|
|
271
816
|
|
|
272
|
-
Prompt path:
|
|
273
|
-
That scan
|
|
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\`
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
838
|
+
## 2. Parallel agents & worktrees
|
|
304
839
|
|
|
305
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
875
|
+
---
|
|
358
876
|
|
|
359
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
373
|
-
Registry: \`${L.hooksFile}\`
|
|
893
|
+
---
|
|
374
894
|
|
|
375
|
-
|
|
895
|
+
## 6. Hooks
|
|
376
896
|
|
|
377
|
-
|
|
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
|
-
##
|
|
382
|
-
|
|
383
|
-
Specs (all catalog entries shipped): \`${L.mcpsDir}/\`
|
|
384
|
-
Config: \`${L.mcpFile}\`
|
|
902
|
+
## 7. MCP
|
|
385
903
|
|
|
386
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
911
|
+
## 8. Cloud & background agents
|
|
396
912
|
|
|
397
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
922
|
+
## 10. Reading path (${profile.maturity})
|
|
432
923
|
|
|
433
924
|
${maturityReadingPath(profile.maturity)}
|
|
434
925
|
|
|
435
|
-
##
|
|
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
|
-
|
|
444
|
-
|
|
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
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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(
|
|
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
|
|
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
|
|
473
|
-
5.
|
|
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
|
|
495
|
-
return
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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(
|
|
1054
|
+
const L = layoutFor(ide, { cookbookId: stack.cookbook ?? null });
|
|
576
1055
|
const recommendedIds = opts.recommendedMcpIds ?? stackMcpIds(profile.stack);
|
|
577
|
-
const
|
|
1056
|
+
const normalized = { ...profile, ide: L.ide, model, dataClass };
|
|
578
1057
|
|
|
579
1058
|
const flags = [
|
|
580
1059
|
`--stack ${profile.stack}`,
|
|
581
|
-
`--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
|
-
|
|
594
|
-
|
|
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
|
-
|
|
598
|
-
|
|
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
|
-
|
|
1090
|
+
const rendered = renderAgent(L, name, corpus(`agents/${name}.agent.md`));
|
|
1091
|
+
if (rendered) files[rendered.path] = rendered.content;
|
|
603
1092
|
}
|
|
604
1093
|
|
|
605
|
-
|
|
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
|
-
|
|
613
|
-
files[
|
|
614
|
-
|
|
615
|
-
|
|
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
|
|
620
|
-
|
|
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
|
-
|
|
632
|
-
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
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.
|
|
1146
|
+
files[L.policyFile] = modelPolicy(normalized);
|
|
658
1147
|
|
|
659
|
-
|
|
660
|
-
files['
|
|
661
|
-
|
|
662
|
-
files[
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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(
|
|
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
|
-
|
|
713
|
-
|
|
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
|
-
|
|
1282
|
+
Sources: ${verify.sources.length ? verify.sources.join(', ') : 'none detected'}
|
|
728
1283
|
|
|
729
|
-
|
|
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.
|
|
744
|
-
2.
|
|
745
|
-
3.
|
|
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
|
-
|
|
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(`
|
|
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
|
-
|
|
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
|
|
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
|
}
|