agentopology 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +450 -0
  3. package/dist/analyzer/__tests__/analyzer.test.d.ts +2 -0
  4. package/dist/analyzer/__tests__/analyzer.test.d.ts.map +1 -0
  5. package/dist/analyzer/__tests__/analyzer.test.js +258 -0
  6. package/dist/analyzer/__tests__/analyzer.test.js.map +1 -0
  7. package/dist/analyzer/index.d.ts +44 -0
  8. package/dist/analyzer/index.d.ts.map +1 -0
  9. package/dist/analyzer/index.js +308 -0
  10. package/dist/analyzer/index.js.map +1 -0
  11. package/dist/bindings/__tests__/bindings.test.d.ts +2 -0
  12. package/dist/bindings/__tests__/bindings.test.d.ts.map +1 -0
  13. package/dist/bindings/__tests__/bindings.test.js +2089 -0
  14. package/dist/bindings/__tests__/bindings.test.js.map +1 -0
  15. package/dist/bindings/claude-code.d.ts +12 -0
  16. package/dist/bindings/claude-code.d.ts.map +1 -0
  17. package/dist/bindings/claude-code.js +2063 -0
  18. package/dist/bindings/claude-code.js.map +1 -0
  19. package/dist/bindings/codex.d.ts +18 -0
  20. package/dist/bindings/codex.d.ts.map +1 -0
  21. package/dist/bindings/codex.js +1633 -0
  22. package/dist/bindings/codex.js.map +1 -0
  23. package/dist/bindings/copilot-cli.d.ts +20 -0
  24. package/dist/bindings/copilot-cli.d.ts.map +1 -0
  25. package/dist/bindings/copilot-cli.js +1515 -0
  26. package/dist/bindings/copilot-cli.js.map +1 -0
  27. package/dist/bindings/cursor.d.ts +17 -0
  28. package/dist/bindings/cursor.d.ts.map +1 -0
  29. package/dist/bindings/cursor.js +1715 -0
  30. package/dist/bindings/cursor.js.map +1 -0
  31. package/dist/bindings/gemini-cli.d.ts +18 -0
  32. package/dist/bindings/gemini-cli.d.ts.map +1 -0
  33. package/dist/bindings/gemini-cli.js +1327 -0
  34. package/dist/bindings/gemini-cli.js.map +1 -0
  35. package/dist/bindings/index.d.ts +20 -0
  36. package/dist/bindings/index.d.ts.map +1 -0
  37. package/dist/bindings/index.js +33 -0
  38. package/dist/bindings/index.js.map +1 -0
  39. package/dist/bindings/kiro.d.ts +17 -0
  40. package/dist/bindings/kiro.d.ts.map +1 -0
  41. package/dist/bindings/kiro.js +1624 -0
  42. package/dist/bindings/kiro.js.map +1 -0
  43. package/dist/bindings/openclaw.d.ts +18 -0
  44. package/dist/bindings/openclaw.d.ts.map +1 -0
  45. package/dist/bindings/openclaw.js +1578 -0
  46. package/dist/bindings/openclaw.js.map +1 -0
  47. package/dist/bindings/types.d.ts +33 -0
  48. package/dist/bindings/types.d.ts.map +1 -0
  49. package/dist/bindings/types.js +23 -0
  50. package/dist/bindings/types.js.map +1 -0
  51. package/dist/cli/index.d.ts +19 -0
  52. package/dist/cli/index.d.ts.map +1 -0
  53. package/dist/cli/index.js +679 -0
  54. package/dist/cli/index.js.map +1 -0
  55. package/dist/docs/__tests__/docs.test.d.ts +2 -0
  56. package/dist/docs/__tests__/docs.test.d.ts.map +1 -0
  57. package/dist/docs/__tests__/docs.test.js +438 -0
  58. package/dist/docs/__tests__/docs.test.js.map +1 -0
  59. package/dist/docs/content.d.ts +16 -0
  60. package/dist/docs/content.d.ts.map +1 -0
  61. package/dist/docs/content.js +3273 -0
  62. package/dist/docs/content.js.map +1 -0
  63. package/dist/docs/index.d.ts +17 -0
  64. package/dist/docs/index.d.ts.map +1 -0
  65. package/dist/docs/index.js +117 -0
  66. package/dist/docs/index.js.map +1 -0
  67. package/dist/exporters/__tests__/exporters.test.d.ts +2 -0
  68. package/dist/exporters/__tests__/exporters.test.d.ts.map +1 -0
  69. package/dist/exporters/__tests__/exporters.test.js +731 -0
  70. package/dist/exporters/__tests__/exporters.test.js.map +1 -0
  71. package/dist/exporters/index.d.ts +13 -0
  72. package/dist/exporters/index.d.ts.map +1 -0
  73. package/dist/exporters/index.js +18 -0
  74. package/dist/exporters/index.js.map +1 -0
  75. package/dist/exporters/json.d.ts +8 -0
  76. package/dist/exporters/json.d.ts.map +1 -0
  77. package/dist/exporters/json.js +25 -0
  78. package/dist/exporters/json.js.map +1 -0
  79. package/dist/exporters/markdown.d.ts +9 -0
  80. package/dist/exporters/markdown.d.ts.map +1 -0
  81. package/dist/exporters/markdown.js +686 -0
  82. package/dist/exporters/markdown.js.map +1 -0
  83. package/dist/exporters/mermaid.d.ts +11 -0
  84. package/dist/exporters/mermaid.d.ts.map +1 -0
  85. package/dist/exporters/mermaid.js +161 -0
  86. package/dist/exporters/mermaid.js.map +1 -0
  87. package/dist/exporters/types.d.ts +22 -0
  88. package/dist/exporters/types.d.ts.map +1 -0
  89. package/dist/exporters/types.js +10 -0
  90. package/dist/exporters/types.js.map +1 -0
  91. package/dist/import/__tests__/import.test.d.ts +2 -0
  92. package/dist/import/__tests__/import.test.d.ts.map +1 -0
  93. package/dist/import/__tests__/import.test.js +781 -0
  94. package/dist/import/__tests__/import.test.js.map +1 -0
  95. package/dist/import/claude-code.d.ts +38 -0
  96. package/dist/import/claude-code.d.ts.map +1 -0
  97. package/dist/import/claude-code.js +897 -0
  98. package/dist/import/claude-code.js.map +1 -0
  99. package/dist/import/index.d.ts +18 -0
  100. package/dist/import/index.d.ts.map +1 -0
  101. package/dist/import/index.js +28 -0
  102. package/dist/import/index.js.map +1 -0
  103. package/dist/import/serializer.d.ts +11 -0
  104. package/dist/import/serializer.d.ts.map +1 -0
  105. package/dist/import/serializer.js +960 -0
  106. package/dist/import/serializer.js.map +1 -0
  107. package/dist/index.d.ts +26 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +21 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/parser/__tests__/parser.test.d.ts +2 -0
  112. package/dist/parser/__tests__/parser.test.d.ts.map +1 -0
  113. package/dist/parser/__tests__/parser.test.js +6514 -0
  114. package/dist/parser/__tests__/parser.test.js.map +1 -0
  115. package/dist/parser/ast.d.ts +708 -0
  116. package/dist/parser/ast.d.ts.map +1 -0
  117. package/dist/parser/ast.js +12 -0
  118. package/dist/parser/ast.js.map +1 -0
  119. package/dist/parser/index.d.ts +253 -0
  120. package/dist/parser/index.d.ts.map +1 -0
  121. package/dist/parser/index.js +1985 -0
  122. package/dist/parser/index.js.map +1 -0
  123. package/dist/parser/lexer.d.ts +100 -0
  124. package/dist/parser/lexer.d.ts.map +1 -0
  125. package/dist/parser/lexer.js +304 -0
  126. package/dist/parser/lexer.js.map +1 -0
  127. package/dist/parser/validator.d.ts +42 -0
  128. package/dist/parser/validator.d.ts.map +1 -0
  129. package/dist/parser/validator.js +2568 -0
  130. package/dist/parser/validator.js.map +1 -0
  131. package/dist/scaffold/__tests__/scaffold.test.d.ts +7 -0
  132. package/dist/scaffold/__tests__/scaffold.test.d.ts.map +1 -0
  133. package/dist/scaffold/__tests__/scaffold.test.js +295 -0
  134. package/dist/scaffold/__tests__/scaffold.test.js.map +1 -0
  135. package/dist/scaffold/incremental.d.ts +43 -0
  136. package/dist/scaffold/incremental.d.ts.map +1 -0
  137. package/dist/scaffold/incremental.js +185 -0
  138. package/dist/scaffold/incremental.js.map +1 -0
  139. package/dist/scaffold/index.d.ts +13 -0
  140. package/dist/scaffold/index.d.ts.map +1 -0
  141. package/dist/scaffold/index.js +13 -0
  142. package/dist/scaffold/index.js.map +1 -0
  143. package/dist/scaffold/manifest.d.ts +30 -0
  144. package/dist/scaffold/manifest.d.ts.map +1 -0
  145. package/dist/scaffold/manifest.js +71 -0
  146. package/dist/scaffold/manifest.js.map +1 -0
  147. package/dist/scaffold/merge.d.ts +52 -0
  148. package/dist/scaffold/merge.d.ts.map +1 -0
  149. package/dist/scaffold/merge.js +134 -0
  150. package/dist/scaffold/merge.js.map +1 -0
  151. package/dist/scaffold/types.d.ts +51 -0
  152. package/dist/scaffold/types.d.ts.map +1 -0
  153. package/dist/scaffold/types.js +10 -0
  154. package/dist/scaffold/types.js.map +1 -0
  155. package/dist/sync/index.d.ts +34 -0
  156. package/dist/sync/index.d.ts.map +1 -0
  157. package/dist/sync/index.js +392 -0
  158. package/dist/sync/index.js.map +1 -0
  159. package/dist/visualizer/index.d.ts +17 -0
  160. package/dist/visualizer/index.d.ts.map +1 -0
  161. package/dist/visualizer/index.js +1976 -0
  162. package/dist/visualizer/index.js.map +1 -0
  163. package/examples/code-review.at +167 -0
  164. package/examples/data-processing.at +296 -0
  165. package/examples/openclaw-assistant.at +142 -0
  166. package/examples/scheduled-monitor.at +193 -0
  167. package/examples/simple-pipeline.at +163 -0
  168. package/package.json +77 -0
  169. package/skill/SKILL.md +403 -0
  170. package/skill/scripts/emit.ts +853 -0
  171. package/skill/scripts/visualize.ts +89 -0
  172. package/spec/grammar.md +1466 -0
  173. package/spec/reserved-keywords.md +147 -0
  174. package/spec/validation.md +535 -0
@@ -0,0 +1,1624 @@
1
+ /**
2
+ * Kiro CLI binding.
3
+ *
4
+ * Generates the `.kiro/` directory structure, `AGENTS.md`, and supporting
5
+ * files from a parsed {@link TopologyAST}.
6
+ *
7
+ * Kiro uses JSON-based agent definitions (`.kiro/agents/<id>.json`),
8
+ * markdown steering documents (`.kiro/steering/`), and MCP configuration
9
+ * (`.kiro/settings/mcp.json`). Unlike Claude Code where agents are markdown
10
+ * files with frontmatter, Kiro agents are plain JSON.
11
+ *
12
+ * @module
13
+ */
14
+ import { deduplicateFiles } from "./types.js";
15
+ // ---------------------------------------------------------------------------
16
+ // Helpers
17
+ // ---------------------------------------------------------------------------
18
+ /** Convert a kebab-case id to Title Case. */
19
+ function toTitle(id) {
20
+ return id
21
+ .split(/[-_]/)
22
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
23
+ .join(" ");
24
+ }
25
+ /** Generate a shell script stub with a header comment. */
26
+ function shellStub(description) {
27
+ return [
28
+ "#!/usr/bin/env bash",
29
+ `# ${description}`,
30
+ "# Auto-generated by agentopology scaffold — edit as needed.",
31
+ "set -euo pipefail",
32
+ "",
33
+ 'echo "TODO: implement this script"',
34
+ "",
35
+ ].join("\n");
36
+ }
37
+ /** Generate an empty directory marker (a .gitkeep file). */
38
+ function gitkeep(dirPath) {
39
+ return { path: `${dirPath}/.gitkeep`, content: "", category: "machine" };
40
+ }
41
+ /**
42
+ * Map topology hook event names to Kiro event names.
43
+ *
44
+ * Kiro uses camelCase event names (preToolUse, postToolUse) instead of
45
+ * PascalCase (PreToolUse, PostToolUse).
46
+ */
47
+ function mapHookEvent(event) {
48
+ const eventMap = {
49
+ PreToolUse: "preToolUse",
50
+ PostToolUse: "postToolUse",
51
+ };
52
+ return eventMap[event] ?? event;
53
+ }
54
+ /**
55
+ * Map topology tool names to Kiro tool names.
56
+ *
57
+ * Kiro uses lowercase tool names with fs_ prefixes for file operations.
58
+ */
59
+ function mapToolName(tool) {
60
+ const toolMap = {
61
+ Read: "read",
62
+ Write: "write",
63
+ Bash: "shell",
64
+ Edit: "write",
65
+ };
66
+ return toolMap[tool] ?? tool.toLowerCase();
67
+ }
68
+ /**
69
+ * Map topology tool names to Kiro hook matcher names.
70
+ *
71
+ * For hook matchers, Kiro uses the fs_read/fs_write/execute_bash convention.
72
+ */
73
+ function mapMatcherName(matcher) {
74
+ const matcherMap = {
75
+ Read: "fs_read",
76
+ Write: "fs_write",
77
+ Bash: "execute_bash",
78
+ Edit: "fs_write",
79
+ };
80
+ return matcherMap[matcher] ?? matcher;
81
+ }
82
+ /** Format a SchemaType to a human-readable string. */
83
+ function formatSchemaType(t) {
84
+ switch (t.kind) {
85
+ case "primitive":
86
+ return t.value;
87
+ case "array":
88
+ return `${formatSchemaType(t.itemType)}[]`;
89
+ case "enum":
90
+ return t.values.join(" | ");
91
+ case "ref":
92
+ return t.name;
93
+ }
94
+ }
95
+ /** Format schema fields as a markdown list. */
96
+ function formatSchemaFields(fields) {
97
+ return fields
98
+ .map((f) => `- \`${f.name}${f.optional ? "?" : ""}\`: ${formatSchemaType(f.type)}`)
99
+ .join("\n");
100
+ }
101
+ // ---------------------------------------------------------------------------
102
+ // Agent JSON generation
103
+ // ---------------------------------------------------------------------------
104
+ /**
105
+ * Generate `.kiro/agents/<id>.json` for each agent node.
106
+ *
107
+ * Each agent produces a single JSON file with name, description, prompt,
108
+ * model, tools, hooks, resources, and MCP configuration.
109
+ *
110
+ * NOTE: The `.kiro/agents/` JSON format is not publicly documented by Kiro.
111
+ * This generates a reasonable agent config structure that may need validation
112
+ * against actual Kiro when the format is finalized.
113
+ */
114
+ function generateAgents(ast) {
115
+ const files = [];
116
+ for (const node of ast.nodes) {
117
+ if (node.type !== "agent")
118
+ continue;
119
+ const agent = node;
120
+ const agentJson = {};
121
+ agentJson.name = agent.id;
122
+ // Description: prefer agent.description, then role description
123
+ const desc = agent.description ??
124
+ ast.roles[agent.role ?? ""] ??
125
+ ast.roles[agent.id] ??
126
+ agent.role;
127
+ if (desc) {
128
+ agentJson.description = desc;
129
+ }
130
+ // Prompt: build from role + inline prompt
131
+ const promptParts = [];
132
+ const roleText = ast.roles[agent.role ?? ""] ?? ast.roles[agent.id] ?? agent.role;
133
+ if (roleText) {
134
+ promptParts.push(`You are the ${toTitle(agent.id)} agent.\n\n## Role\n${roleText}`);
135
+ }
136
+ else {
137
+ promptParts.push(`You are the ${toTitle(agent.id)} agent.`);
138
+ }
139
+ if (agent.prompt) {
140
+ promptParts.push(`\n## Instructions\n\n${agent.prompt}`);
141
+ }
142
+ if (agent.reads && agent.reads.length > 0) {
143
+ promptParts.push(`\n## Reads\n${agent.reads.map((r) => `- ${r}`).join("\n")}`);
144
+ }
145
+ if (agent.writes && agent.writes.length > 0) {
146
+ promptParts.push(`\n## Writes\n${agent.writes.map((w) => `- ${w}`).join("\n")}`);
147
+ }
148
+ if (agent.outputs) {
149
+ const outputLines = Object.entries(agent.outputs)
150
+ .map(([field, values]) => `- ${field}: ${values.join(" | ")}`)
151
+ .join("\n");
152
+ promptParts.push(`\n## Outputs\n${outputLines}`);
153
+ }
154
+ if (agent.skills && agent.skills.length > 0) {
155
+ promptParts.push(`\nSkills: ${agent.skills.join(", ")}`);
156
+ }
157
+ if (agent.behavior) {
158
+ promptParts.push(`\nBehavior: ${agent.behavior}`);
159
+ }
160
+ if (agent.skip) {
161
+ promptParts.push(`\nSkip when: ${agent.skip}`);
162
+ }
163
+ agentJson.prompt = promptParts.join("\n");
164
+ // Model
165
+ if (agent.model) {
166
+ agentJson.model = agent.model;
167
+ }
168
+ // Fields not in Kiro's agent JSON schema — embed in prompt instead
169
+ // (Kiro only supports: name, description, prompt, model, mcpServers,
170
+ // tools, toolAliases, allowedTools, resources, hooks, toolsSettings,
171
+ // includeMcpJson, keyboardShortcut, welcomeMessage)
172
+ const constraints = [];
173
+ if (agent.timeout)
174
+ constraints.push(`Maximum execution time: ${agent.timeout}`);
175
+ if (agent.temperature != null)
176
+ constraints.push(`Temperature: ${agent.temperature}`);
177
+ if (agent.maxTokens != null)
178
+ constraints.push(`Max tokens: ${agent.maxTokens}`);
179
+ if (agent.thinking)
180
+ constraints.push(`Reasoning level: ${agent.thinking}`);
181
+ if (agent.maxTurns != null)
182
+ constraints.push(`Max turns: ${agent.maxTurns}`);
183
+ if (constraints.length > 0) {
184
+ agentJson.prompt += "\n\n## Constraints\n" + constraints.map((c) => `- ${c}`).join("\n");
185
+ }
186
+ // Tools — map topology tool names to Kiro names
187
+ if (agent.tools && agent.tools.length > 0) {
188
+ agentJson.tools = agent.tools.map(mapToolName);
189
+ }
190
+ // Allowed tools (subset that don't require confirmation)
191
+ if (agent.extensions?.kiro?.allowedTools) {
192
+ agentJson.allowedTools = agent.extensions.kiro.allowedTools;
193
+ }
194
+ // MCP servers — inline the full MCP server config from ast.mcpServers
195
+ if (agent.mcpServers && agent.mcpServers.length > 0) {
196
+ const mcpServers = {};
197
+ for (const serverName of agent.mcpServers) {
198
+ const serverConfig = ast.mcpServers[serverName];
199
+ if (serverConfig) {
200
+ const entry = { ...serverConfig };
201
+ // Strip type field for stdio servers (Kiro auto-detects)
202
+ if (entry.command) {
203
+ delete entry.type;
204
+ }
205
+ mcpServers[serverName] = entry;
206
+ }
207
+ }
208
+ if (Object.keys(mcpServers).length > 0) {
209
+ agentJson.mcpServers = mcpServers;
210
+ }
211
+ }
212
+ // Per-agent hooks — object keyed by event name
213
+ if (agent.hooks && agent.hooks.length > 0) {
214
+ const hooksObj = {};
215
+ for (const hook of agent.hooks) {
216
+ const eventName = mapHookEvent(hook.on);
217
+ if (!hooksObj[eventName]) {
218
+ hooksObj[eventName] = [];
219
+ }
220
+ const entry = {
221
+ command: hook.run,
222
+ };
223
+ if (hook.matcher) {
224
+ entry.matcher = mapMatcherName(hook.matcher);
225
+ }
226
+ hooksObj[eventName].push(entry);
227
+ }
228
+ agentJson.hooks = hooksObj;
229
+ }
230
+ // Merge kiro extension fields
231
+ if (agent.extensions?.kiro) {
232
+ const handled = new Set(["allowedTools"]);
233
+ for (const [k, v] of Object.entries(agent.extensions.kiro)) {
234
+ if (handled.has(k))
235
+ continue;
236
+ agentJson[k] = v;
237
+ }
238
+ }
239
+ files.push({
240
+ path: `.kiro/agents/${agent.id}.json`,
241
+ content: JSON.stringify(agentJson, null, 2) + "\n",
242
+ category: "machine",
243
+ });
244
+ }
245
+ return files;
246
+ }
247
+ // ---------------------------------------------------------------------------
248
+ // Per-agent steering docs for Wave 1-7 features
249
+ // ---------------------------------------------------------------------------
250
+ /**
251
+ * Generate `.kiro/steering/agent-<id>.md` for agents with behavioral config
252
+ * that cannot be expressed in JSON (on-fail, retry, circuitBreaker, schemas,
253
+ * rate limits, artifacts, join semantics, output format, log level, variants).
254
+ */
255
+ function generateAgentSteering(ast) {
256
+ const files = [];
257
+ for (const node of ast.nodes) {
258
+ if (node.type !== "agent")
259
+ continue;
260
+ const agent = node;
261
+ const sections = [];
262
+ let hasContent = false;
263
+ // on-fail / retry (Wave 1)
264
+ if (agent.onFail) {
265
+ sections.push(`## Failure Handling`);
266
+ sections.push("");
267
+ sections.push(`On failure: **${agent.onFail}**`);
268
+ sections.push("");
269
+ hasContent = true;
270
+ }
271
+ if (agent.retry != null) {
272
+ if (!sections.some((s) => s.includes("Failure Handling"))) {
273
+ sections.push(`## Failure Handling`);
274
+ sections.push("");
275
+ }
276
+ if (typeof agent.retry === "number") {
277
+ sections.push(`Retry up to **${agent.retry}** times on failure.`);
278
+ }
279
+ else {
280
+ const r = agent.retry;
281
+ sections.push(`Retry up to **${r.max}** times.`);
282
+ if (r.backoff)
283
+ sections.push(`- Backoff: ${r.backoff}`);
284
+ if (r.interval)
285
+ sections.push(`- Interval: ${r.interval}`);
286
+ if (r.maxInterval)
287
+ sections.push(`- Max interval: ${r.maxInterval}`);
288
+ if (r.jitter)
289
+ sections.push(`- Jitter: enabled`);
290
+ if (r.nonRetryable && r.nonRetryable.length > 0) {
291
+ sections.push(`- Non-retryable: ${r.nonRetryable.join(", ")}`);
292
+ }
293
+ }
294
+ sections.push("");
295
+ hasContent = true;
296
+ }
297
+ // circuitBreaker (Wave 5)
298
+ if (agent.circuitBreaker) {
299
+ const cb = agent.circuitBreaker;
300
+ sections.push(`## Circuit Breaker`);
301
+ sections.push("");
302
+ sections.push(`- Threshold: ${cb.threshold} failures`);
303
+ sections.push(`- Window: ${cb.window}`);
304
+ sections.push(`- Cooldown: ${cb.cooldown}`);
305
+ sections.push("");
306
+ hasContent = true;
307
+ }
308
+ // compensates (Wave 5 — saga)
309
+ if (agent.compensates) {
310
+ sections.push(`## Compensation`);
311
+ sections.push("");
312
+ sections.push(`This agent compensates (undoes side effects of) **${agent.compensates}**.`);
313
+ sections.push("");
314
+ hasContent = true;
315
+ }
316
+ // inputSchema / outputSchema (Wave 3)
317
+ if (agent.inputSchema && agent.inputSchema.length > 0) {
318
+ sections.push(`## Input Schema`);
319
+ sections.push("");
320
+ sections.push(formatSchemaFields(agent.inputSchema));
321
+ sections.push("");
322
+ hasContent = true;
323
+ }
324
+ if (agent.outputSchema && agent.outputSchema.length > 0) {
325
+ sections.push(`## Output Schema`);
326
+ sections.push("");
327
+ sections.push(formatSchemaFields(agent.outputSchema));
328
+ sections.push("");
329
+ hasContent = true;
330
+ }
331
+ // rateLimit (Wave 6)
332
+ if (agent.rateLimit) {
333
+ sections.push(`## Rate Limit`);
334
+ sections.push("");
335
+ sections.push(`Rate limit: **${agent.rateLimit}**`);
336
+ sections.push("");
337
+ hasContent = true;
338
+ }
339
+ // produces / consumes (Wave 6 — artifacts)
340
+ if (agent.produces && agent.produces.length > 0) {
341
+ sections.push(`## Produces`);
342
+ sections.push("");
343
+ sections.push(agent.produces.map((a) => `- ${a}`).join("\n"));
344
+ sections.push("");
345
+ hasContent = true;
346
+ }
347
+ if (agent.consumes && agent.consumes.length > 0) {
348
+ sections.push(`## Consumes`);
349
+ sections.push("");
350
+ sections.push(agent.consumes.map((a) => `- ${a}`).join("\n"));
351
+ sections.push("");
352
+ hasContent = true;
353
+ }
354
+ // join (Wave 2)
355
+ if (agent.join) {
356
+ sections.push(`## Join Strategy`);
357
+ sections.push("");
358
+ sections.push(`Wait strategy: **${agent.join}**`);
359
+ sections.push("");
360
+ hasContent = true;
361
+ }
362
+ // outputFormat (Wave 1)
363
+ if (agent.outputFormat) {
364
+ sections.push(`## Output Format`);
365
+ sections.push("");
366
+ sections.push(`Expected output format: **${agent.outputFormat}**`);
367
+ sections.push("");
368
+ hasContent = true;
369
+ }
370
+ // logLevel (Wave 3)
371
+ if (agent.logLevel) {
372
+ sections.push(`## Log Level`);
373
+ sections.push("");
374
+ sections.push(`Log level: **${agent.logLevel}**`);
375
+ sections.push("");
376
+ hasContent = true;
377
+ }
378
+ // variants (Wave 7 — A/B testing)
379
+ if (agent.variants && agent.variants.length > 0) {
380
+ sections.push(`## Prompt Variants`);
381
+ sections.push("");
382
+ for (const v of agent.variants) {
383
+ const parts = [`**${v.id}** (weight: ${v.weight})`];
384
+ if (v.model)
385
+ parts.push(`model: ${v.model}`);
386
+ if (v.temperature != null)
387
+ parts.push(`temperature: ${v.temperature}`);
388
+ sections.push(`- ${parts.join(", ")}`);
389
+ if (v.prompt) {
390
+ sections.push(` > ${v.prompt.replace(/\n/g, "\n > ")}`);
391
+ }
392
+ }
393
+ sections.push("");
394
+ hasContent = true;
395
+ }
396
+ if (!hasContent)
397
+ continue;
398
+ // Determine inclusion mode: fileMatch if agent has reads/writes paths,
399
+ // otherwise always.
400
+ const allPaths = [
401
+ ...(agent.reads ?? []),
402
+ ...(agent.writes ?? []),
403
+ ];
404
+ const headerLines = ["---"];
405
+ if (allPaths.length > 0) {
406
+ headerLines.push("inclusion: fileMatch");
407
+ // Combine all read/write paths into a glob pattern
408
+ const pattern = allPaths.length === 1
409
+ ? allPaths[0].replace(/\/$/, "/**")
410
+ : `{${allPaths.map((p) => p.replace(/\/$/, "/**")).join(",")}}`;
411
+ headerLines.push(`fileMatchPattern: "${pattern}"`);
412
+ }
413
+ else {
414
+ headerLines.push("inclusion: always");
415
+ }
416
+ headerLines.push("---");
417
+ headerLines.push("");
418
+ headerLines.push(`# ${toTitle(agent.id)} — Behavioral Config`);
419
+ headerLines.push("");
420
+ const header = headerLines;
421
+ files.push({
422
+ path: `.kiro/steering/agent-${agent.id}.md`,
423
+ content: header.join("\n") + sections.join("\n") + "\n",
424
+ category: "machine",
425
+ });
426
+ }
427
+ return files;
428
+ }
429
+ // ---------------------------------------------------------------------------
430
+ // Human node steering docs
431
+ // ---------------------------------------------------------------------------
432
+ /**
433
+ * Generate `.kiro/steering/human-<id>.md` for human-in-the-loop pause points.
434
+ */
435
+ function generateHumanSteering(ast) {
436
+ const files = [];
437
+ for (const node of ast.nodes) {
438
+ if (node.type !== "human")
439
+ continue;
440
+ const human = node;
441
+ const sections = [
442
+ "---",
443
+ "inclusion: always",
444
+ "---",
445
+ "",
446
+ `# ${toTitle(human.id)} — Human Pause Point`,
447
+ "",
448
+ ];
449
+ if (human.description) {
450
+ sections.push(human.description);
451
+ sections.push("");
452
+ }
453
+ sections.push("## Behavior");
454
+ sections.push("");
455
+ sections.push("This is a human-in-the-loop pause point. Stop and wait for human input before continuing.");
456
+ sections.push("");
457
+ if (human.timeout) {
458
+ sections.push(`- Timeout: ${human.timeout}`);
459
+ }
460
+ if (human.onTimeout) {
461
+ sections.push(`- On timeout: ${human.onTimeout}`);
462
+ }
463
+ if (human.timeout || human.onTimeout) {
464
+ sections.push("");
465
+ }
466
+ files.push({
467
+ path: `.kiro/steering/human-${human.id}.md`,
468
+ content: sections.join("\n") + "\n",
469
+ category: "machine",
470
+ });
471
+ }
472
+ return files;
473
+ }
474
+ // ---------------------------------------------------------------------------
475
+ // Group node steering docs
476
+ // ---------------------------------------------------------------------------
477
+ /**
478
+ * Generate `.kiro/steering/group-<id>.md` for group chat coordination nodes.
479
+ */
480
+ function generateGroupSteering(ast) {
481
+ const files = [];
482
+ for (const node of ast.nodes) {
483
+ if (node.type !== "group")
484
+ continue;
485
+ const group = node;
486
+ const sections = [
487
+ "---",
488
+ "inclusion: always",
489
+ "---",
490
+ "",
491
+ `# ${toTitle(group.id)} — Group Chat`,
492
+ "",
493
+ ];
494
+ if (group.description) {
495
+ sections.push(group.description);
496
+ sections.push("");
497
+ }
498
+ sections.push("## Coordination");
499
+ sections.push("");
500
+ sections.push(`Members: ${group.members.join(", ")}`);
501
+ if (group.speakerSelection) {
502
+ sections.push(`Speaker selection: ${group.speakerSelection}`);
503
+ }
504
+ if (group.maxRounds != null) {
505
+ sections.push(`Max rounds: ${group.maxRounds}`);
506
+ }
507
+ if (group.termination) {
508
+ sections.push(`Termination: ${group.termination}`);
509
+ }
510
+ if (group.timeout) {
511
+ sections.push(`Timeout: ${group.timeout}`);
512
+ }
513
+ sections.push("");
514
+ files.push({
515
+ path: `.kiro/steering/group-${group.id}.md`,
516
+ content: sections.join("\n") + "\n",
517
+ category: "machine",
518
+ });
519
+ }
520
+ return files;
521
+ }
522
+ // ---------------------------------------------------------------------------
523
+ // Steering documents
524
+ // ---------------------------------------------------------------------------
525
+ /**
526
+ * Generate `.kiro/steering/product.md` — product context with topology
527
+ * description, version, and patterns.
528
+ */
529
+ function generateProductSteering(ast) {
530
+ const sections = [];
531
+ sections.push("---");
532
+ sections.push("inclusion: always");
533
+ sections.push("---");
534
+ sections.push("");
535
+ sections.push("# Product Context");
536
+ sections.push("");
537
+ if (ast.topology.description) {
538
+ sections.push(ast.topology.description);
539
+ sections.push("");
540
+ }
541
+ sections.push(`Version: ${ast.topology.version}`);
542
+ if (ast.topology.patterns.length > 0) {
543
+ sections.push(`Patterns: ${ast.topology.patterns.join(", ")}`);
544
+ }
545
+ sections.push("");
546
+ // Schedules
547
+ if (ast.schedules && ast.schedules.length > 0) {
548
+ sections.push("## Schedules");
549
+ sections.push("");
550
+ for (const job of ast.schedules) {
551
+ const expr = job.cron
552
+ ? `cron: ${job.cron}`
553
+ : job.every
554
+ ? `every: ${job.every}`
555
+ : "";
556
+ const target = job.agent
557
+ ? `agent: ${job.agent}`
558
+ : job.action
559
+ ? `action: ${job.action}`
560
+ : "";
561
+ sections.push(`- **${job.id}**: ${expr}${target ? ` -> ${target}` : ""}${job.enabled === false ? " (disabled)" : ""}`);
562
+ }
563
+ sections.push("");
564
+ }
565
+ // Interfaces
566
+ if (ast.interfaces && ast.interfaces.length > 0) {
567
+ sections.push("## Interfaces");
568
+ sections.push("");
569
+ for (const iface of ast.interfaces) {
570
+ const typePart = iface.type ? ` (${iface.type})` : "";
571
+ const configKeys = Object.keys(iface.config);
572
+ const configPart = configKeys.length > 0
573
+ ? ` — ${configKeys.map((k) => `${k}: ${iface.config[k]}`).join(", ")}`
574
+ : "";
575
+ sections.push(`- **${iface.id}**${typePart}${configPart}`);
576
+ }
577
+ sections.push("");
578
+ }
579
+ // Depth levels
580
+ if (ast.depth && ast.depth.levels && ast.depth.levels.length > 0) {
581
+ sections.push("## Depth Levels");
582
+ sections.push("");
583
+ if (ast.depth.factors.length > 0) {
584
+ sections.push(`Factors: ${ast.depth.factors.join(", ")}`);
585
+ }
586
+ for (const level of ast.depth.levels) {
587
+ let line = `- **Level ${level.level}**: ${level.label}`;
588
+ if (level.omit.length > 0)
589
+ line += ` (omit: ${level.omit.join(", ")})`;
590
+ sections.push(line);
591
+ }
592
+ sections.push("");
593
+ }
594
+ // Composition: params
595
+ if (ast.params && ast.params.length > 0) {
596
+ sections.push("## Parameters");
597
+ sections.push("");
598
+ for (const p of ast.params) {
599
+ const req = p.required ? " (required)" : "";
600
+ const def = p.default != null ? ` = ${p.default}` : "";
601
+ sections.push(`- **${p.name}**: ${p.type}${req}${def}`);
602
+ }
603
+ sections.push("");
604
+ }
605
+ // Composition: interface endpoints
606
+ if (ast.interfaceEndpoints) {
607
+ sections.push("## Interface");
608
+ sections.push("");
609
+ sections.push(`- Entry: ${ast.interfaceEndpoints.entry}`);
610
+ sections.push(`- Exit: ${ast.interfaceEndpoints.exit}`);
611
+ sections.push("");
612
+ }
613
+ // Composition: imports
614
+ if (ast.imports && ast.imports.length > 0) {
615
+ sections.push("## Imports");
616
+ sections.push("");
617
+ for (const imp of ast.imports) {
618
+ let line = `- **${imp.alias}** from \`${imp.source}\``;
619
+ if (imp.sha256)
620
+ line += ` (sha256: ${imp.sha256})`;
621
+ if (imp.registry)
622
+ line += ` [registry: ${imp.registryPackage}@${imp.registryVersion}]`;
623
+ sections.push(line);
624
+ if (Object.keys(imp.params).length > 0) {
625
+ for (const [k, v] of Object.entries(imp.params)) {
626
+ sections.push(` - ${k}: ${v}`);
627
+ }
628
+ }
629
+ }
630
+ sections.push("");
631
+ }
632
+ // Composition: includes
633
+ if (ast.includes && ast.includes.length > 0) {
634
+ sections.push("## Includes");
635
+ sections.push("");
636
+ for (const inc of ast.includes) {
637
+ sections.push(`- ${inc.source}`);
638
+ }
639
+ sections.push("");
640
+ }
641
+ // Defaults
642
+ if (ast.defaults) {
643
+ const d = ast.defaults;
644
+ const items = [];
645
+ if (d.thinking)
646
+ items.push(`thinking: ${d.thinking}`);
647
+ if (d.thinkingBudget)
648
+ items.push(`thinking-budget: ${d.thinkingBudget}`);
649
+ if (d.temperature != null)
650
+ items.push(`temperature: ${d.temperature}`);
651
+ if (d.maxTokens != null)
652
+ items.push(`max-tokens: ${d.maxTokens}`);
653
+ if (d.topP != null)
654
+ items.push(`top-p: ${d.topP}`);
655
+ if (d.topK != null)
656
+ items.push(`top-k: ${d.topK}`);
657
+ if (d.seed != null)
658
+ items.push(`seed: ${d.seed}`);
659
+ if (d.timeout)
660
+ items.push(`timeout: ${d.timeout}`);
661
+ if (d.outputFormat)
662
+ items.push(`output-format: ${d.outputFormat}`);
663
+ if (d.logLevel)
664
+ items.push(`log-level: ${d.logLevel}`);
665
+ if (d.stop && d.stop.length > 0)
666
+ items.push(`stop: ${d.stop.join(", ")}`);
667
+ if (items.length > 0) {
668
+ sections.push("## Defaults");
669
+ sections.push("");
670
+ for (const item of items)
671
+ sections.push(`- ${item}`);
672
+ sections.push("");
673
+ }
674
+ }
675
+ // Artifacts
676
+ if (ast.artifacts && ast.artifacts.length > 0) {
677
+ sections.push("## Artifacts");
678
+ sections.push("");
679
+ for (const art of ast.artifacts) {
680
+ let line = `- **${art.id}** (${art.type})`;
681
+ if (art.path)
682
+ line += ` — path: ${art.path}`;
683
+ if (art.retention)
684
+ line += `, retention: ${art.retention}`;
685
+ if (art.dependsOn && art.dependsOn.length > 0)
686
+ line += `, depends on: ${art.dependsOn.join(", ")}`;
687
+ sections.push(line);
688
+ }
689
+ sections.push("");
690
+ }
691
+ // Observability
692
+ if (ast.observability && ast.observability.enabled) {
693
+ sections.push("## Observability");
694
+ sections.push("");
695
+ sections.push(`- Level: ${ast.observability.level}`);
696
+ sections.push(`- Exporter: ${ast.observability.exporter}`);
697
+ if (ast.observability.endpoint)
698
+ sections.push(`- Endpoint: ${ast.observability.endpoint}`);
699
+ if (ast.observability.service)
700
+ sections.push(`- Service: ${ast.observability.service}`);
701
+ sections.push(`- Sample rate: ${ast.observability.sampleRate}`);
702
+ const cap = ast.observability.capture;
703
+ if (cap) {
704
+ sections.push(`- Capture: prompts=${cap.prompts}, completions=${cap.completions}, tool-args=${cap.toolArgs}, tool-results=${cap.toolResults}`);
705
+ }
706
+ const sp = ast.observability.spans;
707
+ if (sp) {
708
+ sections.push(`- Spans: agents=${sp.agents}, tools=${sp.tools}, gates=${sp.gates}, memory=${sp.memory}`);
709
+ }
710
+ sections.push("");
711
+ }
712
+ // Checkpoint
713
+ if (ast.checkpoint) {
714
+ sections.push("## Checkpoint");
715
+ sections.push("");
716
+ sections.push(`- Backend: ${ast.checkpoint.backend}`);
717
+ sections.push(`- Strategy: ${ast.checkpoint.strategy}`);
718
+ if (ast.checkpoint.connection)
719
+ sections.push(`- Connection: ${ast.checkpoint.connection}`);
720
+ if (ast.checkpoint.ttl)
721
+ sections.push(`- TTL: ${ast.checkpoint.ttl}`);
722
+ if (ast.checkpoint.replay) {
723
+ sections.push(`- Replay: enabled=${ast.checkpoint.replay.enabled}${ast.checkpoint.replay.maxHistory ? `, maxHistory=${ast.checkpoint.replay.maxHistory}` : ""}${ast.checkpoint.replay.branch ? ", branching enabled" : ""}`);
724
+ }
725
+ sections.push("");
726
+ }
727
+ return {
728
+ path: ".kiro/steering/product.md",
729
+ content: sections.join("\n") + "\n",
730
+ category: "machine",
731
+ };
732
+ }
733
+ /**
734
+ * Generate `.kiro/steering/tech.md` — technology context with models,
735
+ * tool configuration, and provider settings.
736
+ */
737
+ function generateTechSteering(ast) {
738
+ const sections = [];
739
+ sections.push("---");
740
+ sections.push("inclusion: always");
741
+ sections.push("---");
742
+ sections.push("");
743
+ sections.push("# Technology");
744
+ sections.push("");
745
+ // Models used
746
+ const models = new Set();
747
+ for (const node of ast.nodes) {
748
+ if (node.type === "orchestrator") {
749
+ models.add(node.model);
750
+ }
751
+ else if (node.type === "agent" && node.model) {
752
+ models.add(node.model);
753
+ }
754
+ }
755
+ if (models.size > 0) {
756
+ sections.push("## Models");
757
+ sections.push("");
758
+ for (const model of models) {
759
+ sections.push(`- ${model}`);
760
+ }
761
+ sections.push("");
762
+ }
763
+ // Providers
764
+ if (ast.providers && ast.providers.length > 0) {
765
+ sections.push("## Providers");
766
+ sections.push("");
767
+ for (const p of ast.providers) {
768
+ const parts = [p.name];
769
+ if (p.baseUrl)
770
+ parts.push(`base: ${p.baseUrl}`);
771
+ if (p.default)
772
+ parts.push("(default)");
773
+ sections.push(`- ${parts.join(" — ")}`);
774
+ }
775
+ sections.push("");
776
+ }
777
+ // Tools
778
+ if (ast.toolDefs.length > 0) {
779
+ sections.push("## Tools");
780
+ sections.push("");
781
+ for (const tool of ast.toolDefs) {
782
+ sections.push(`- **${tool.id}**: ${tool.description}`);
783
+ }
784
+ sections.push("");
785
+ }
786
+ // Skills summary
787
+ if (ast.skills.length > 0) {
788
+ sections.push("## Skills");
789
+ sections.push("");
790
+ for (const skill of ast.skills) {
791
+ const desc = skill.description ? ` — ${skill.description}` : "";
792
+ sections.push(`- **${skill.id}**${desc}`);
793
+ }
794
+ sections.push("");
795
+ }
796
+ // Environment variables
797
+ if (Object.keys(ast.env).length > 0) {
798
+ sections.push("## Environment");
799
+ sections.push("");
800
+ for (const [key, val] of Object.entries(ast.env)) {
801
+ const value = typeof val === "string" ? val : val.value;
802
+ sections.push(`- \`${key}\`: ${value}`);
803
+ }
804
+ sections.push("");
805
+ }
806
+ return {
807
+ path: ".kiro/steering/tech.md",
808
+ content: sections.join("\n") + "\n",
809
+ category: "machine",
810
+ };
811
+ }
812
+ /**
813
+ * Generate `.kiro/steering/structure.md` — file organization with
814
+ * memory layout and read/write patterns.
815
+ */
816
+ function generateStructureSteering(ast) {
817
+ const sections = [];
818
+ sections.push("---");
819
+ sections.push("inclusion: always");
820
+ sections.push("---");
821
+ sections.push("");
822
+ sections.push("# Structure");
823
+ sections.push("");
824
+ // Memory layout
825
+ if (Object.keys(ast.memory).length > 0) {
826
+ sections.push("## Memory Layout");
827
+ sections.push("");
828
+ if (ast.memory.domains) {
829
+ sections.push("- **Domains:** .kiro/domains/");
830
+ }
831
+ if (ast.memory.references) {
832
+ sections.push("- **References:** .kiro/references/");
833
+ }
834
+ if (ast.memory.metrics) {
835
+ sections.push("- **Metrics:** .kiro/metrics.jsonl");
836
+ }
837
+ if (ast.memory.workspace) {
838
+ sections.push("- **Workspace:** .kiro/runs/");
839
+ }
840
+ sections.push("");
841
+ }
842
+ // Read/Write patterns
843
+ const agents = ast.nodes.filter((n) => n.type === "agent");
844
+ const agentsWithAccess = agents.filter((a) => (a.reads && a.reads.length > 0) || (a.writes && a.writes.length > 0));
845
+ if (agentsWithAccess.length > 0) {
846
+ sections.push("## Read/Write Patterns");
847
+ sections.push("");
848
+ for (const agent of agentsWithAccess) {
849
+ sections.push(`### ${toTitle(agent.id)}`);
850
+ if (agent.reads && agent.reads.length > 0) {
851
+ sections.push(`- Reads: ${agent.reads.join(", ")}`);
852
+ }
853
+ if (agent.writes && agent.writes.length > 0) {
854
+ sections.push(`- Writes: ${agent.writes.join(", ")}`);
855
+ }
856
+ sections.push("");
857
+ }
858
+ }
859
+ // Workspace protocol
860
+ const workspace = ast.memory.workspace;
861
+ if (workspace?.structure && Array.isArray(workspace.structure)) {
862
+ sections.push("## Workspace Structure");
863
+ sections.push("");
864
+ for (const item of workspace.structure) {
865
+ sections.push(`- ${item}`);
866
+ }
867
+ sections.push("");
868
+ }
869
+ return {
870
+ path: ".kiro/steering/structure.md",
871
+ content: sections.join("\n") + "\n",
872
+ category: "machine",
873
+ };
874
+ }
875
+ // ---------------------------------------------------------------------------
876
+ // MCP configuration
877
+ // ---------------------------------------------------------------------------
878
+ /**
879
+ * Generate `.kiro/settings/mcp.json` — MCP server configuration.
880
+ *
881
+ * Nearly identical to Claude Code's .mcp.json format: an object with a
882
+ * `mcpServers` key mapping server names to their configuration.
883
+ */
884
+ function generateMcpJson(ast) {
885
+ if (Object.keys(ast.mcpServers).length === 0)
886
+ return null;
887
+ // Collect per-server autonomous status from agent declarations
888
+ // If ANY agent using this server has permissions: autonomous, use ["*"]
889
+ const serverHasAutonomousAgent = new Map();
890
+ const serverDisabledTools = new Map();
891
+ for (const node of ast.nodes) {
892
+ if (node.type !== "agent")
893
+ continue;
894
+ const agent = node;
895
+ if (!agent.mcpServers || agent.mcpServers.length === 0)
896
+ continue;
897
+ for (const serverName of agent.mcpServers) {
898
+ if (agent.permissions === "autonomous") {
899
+ serverHasAutonomousAgent.set(serverName, true);
900
+ }
901
+ // Agent disallowedTools -> disabledTools (only plain names, no patterns)
902
+ if (agent.disallowedTools && agent.disallowedTools.length > 0) {
903
+ if (!serverDisabledTools.has(serverName)) {
904
+ serverDisabledTools.set(serverName, new Set());
905
+ }
906
+ for (const tool of agent.disallowedTools) {
907
+ // Only include plain tool names (no parentheses = no patterns)
908
+ if (!tool.includes("(")) {
909
+ serverDisabledTools.get(serverName).add(tool);
910
+ }
911
+ }
912
+ }
913
+ }
914
+ }
915
+ // Collect topology-level settings deny, filtering out patterns
916
+ const settingsDeny = ast.settings?.deny;
917
+ const mcpConfig = { mcpServers: {} };
918
+ const servers = mcpConfig.mcpServers;
919
+ for (const [name, config] of Object.entries(ast.mcpServers)) {
920
+ const entry = {};
921
+ let hasEnv = false;
922
+ for (const [key, value] of Object.entries(config)) {
923
+ if (key === "args" && Array.isArray(value)) {
924
+ entry.args = value;
925
+ }
926
+ else {
927
+ entry[key] = value;
928
+ }
929
+ if (key === "env")
930
+ hasEnv = true;
931
+ }
932
+ // Strip type field for stdio servers (Kiro auto-detects)
933
+ if (entry.command) {
934
+ delete entry.type;
935
+ }
936
+ // Always include env field
937
+ if (!hasEnv) {
938
+ entry.env = {};
939
+ }
940
+ // disabled: false by default
941
+ entry.disabled = false;
942
+ // autoApprove: ["*"] for autonomous agents, omit for supervised
943
+ if (serverHasAutonomousAgent.get(name)) {
944
+ entry.autoApprove = ["*"];
945
+ }
946
+ // disabledTools: merge agent-level disallowedTools + settings deny (plain names only)
947
+ const disableSet = new Set(serverDisabledTools.get(name) ?? []);
948
+ if (settingsDeny) {
949
+ for (const tool of settingsDeny) {
950
+ // Only include plain tool names (no parentheses = no patterns)
951
+ if (!tool.includes("(")) {
952
+ disableSet.add(tool);
953
+ }
954
+ }
955
+ }
956
+ if (disableSet.size > 0) {
957
+ entry.disabledTools = [...disableSet];
958
+ }
959
+ servers[name] = entry;
960
+ }
961
+ return {
962
+ path: ".kiro/settings/mcp.json",
963
+ content: JSON.stringify(mcpConfig, null, 2) + "\n",
964
+ category: "machine",
965
+ };
966
+ }
967
+ // ---------------------------------------------------------------------------
968
+ // Trigger / prompt files
969
+ // ---------------------------------------------------------------------------
970
+ /**
971
+ * Generate `.kiro/prompts/<trigger-name>.md` — one file per trigger.
972
+ */
973
+ function generatePromptFiles(ast) {
974
+ const files = [];
975
+ for (const trigger of ast.triggers) {
976
+ const sections = [];
977
+ sections.push(`# /${trigger.name}`);
978
+ sections.push("");
979
+ sections.push(trigger.pattern);
980
+ sections.push("");
981
+ if (trigger.argument) {
982
+ sections.push("## Arguments");
983
+ sections.push(`- ${trigger.argument}: extracted from the command pattern`);
984
+ sections.push("");
985
+ }
986
+ // Pipeline (from edges)
987
+ if (ast.edges.length > 0) {
988
+ sections.push("## Pipeline");
989
+ for (const edge of ast.edges) {
990
+ let line = `${edge.from} -> ${edge.to}`;
991
+ if (edge.condition)
992
+ line += ` [when ${edge.condition}]`;
993
+ sections.push(`- ${line}`);
994
+ }
995
+ sections.push("");
996
+ }
997
+ // Agents table
998
+ const agents = ast.nodes.filter((n) => n.type === "agent");
999
+ if (agents.length > 0) {
1000
+ sections.push("## Agents");
1001
+ sections.push("| Agent | Phase | Model | Role |");
1002
+ sections.push("|-------|-------|-------|------|");
1003
+ for (const agent of agents) {
1004
+ const phase = agent.phase != null ? String(agent.phase) : "-";
1005
+ const model = agent.model ?? "-";
1006
+ const role = agent.role ?? "-";
1007
+ sections.push(`| ${agent.id} | ${phase} | ${model} | ${role} |`);
1008
+ }
1009
+ sections.push("");
1010
+ }
1011
+ files.push({
1012
+ path: `.kiro/prompts/${trigger.name}.md`,
1013
+ content: sections.join("\n") + "\n",
1014
+ category: "machine",
1015
+ });
1016
+ }
1017
+ return files;
1018
+ }
1019
+ // ---------------------------------------------------------------------------
1020
+ // AGENTS.md — top-level project context with flow narrative
1021
+ // ---------------------------------------------------------------------------
1022
+ /**
1023
+ * Generate `AGENTS.md` — top-level project context.
1024
+ *
1025
+ * Kiro has no native orchestration, so the flow graph is expressed as
1026
+ * prose in AGENTS.md to give the AI context about the multi-agent workflow.
1027
+ */
1028
+ function generateAgentsMd(ast) {
1029
+ const sections = [];
1030
+ const name = ast.topology.name;
1031
+ sections.push(`# ${toTitle(name)} — Agent Topology`);
1032
+ sections.push("");
1033
+ if (ast.topology.description) {
1034
+ sections.push(ast.topology.description);
1035
+ sections.push("");
1036
+ }
1037
+ sections.push(`Version: ${ast.topology.version}`);
1038
+ if (ast.topology.patterns.length > 0) {
1039
+ sections.push(`Patterns: ${ast.topology.patterns.join(", ")}`);
1040
+ }
1041
+ sections.push("");
1042
+ // Orchestrator
1043
+ const orch = ast.nodes.find((n) => n.type === "orchestrator");
1044
+ if (orch) {
1045
+ sections.push("## Orchestrator");
1046
+ sections.push("");
1047
+ sections.push(`Model: ${orch.model}`);
1048
+ if (orch.handles?.length > 0) {
1049
+ sections.push(`Handles: ${orch.handles.join(", ")}`);
1050
+ }
1051
+ if (orch.generates) {
1052
+ sections.push(`Generates: ${orch.generates}`);
1053
+ }
1054
+ sections.push("");
1055
+ }
1056
+ // Flow (enhanced with Wave 2+ edge attributes)
1057
+ if (ast.edges.length > 0) {
1058
+ sections.push("## Flow");
1059
+ sections.push("");
1060
+ for (const edge of ast.edges) {
1061
+ const arrow = edge.isError ? `-x${edge.errorType ? `[${edge.errorType}]` : ""}->` : "->";
1062
+ let line = `${edge.from} ${arrow} ${edge.to}`;
1063
+ if (edge.condition)
1064
+ line += ` [when ${edge.condition}]`;
1065
+ if (edge.maxIterations)
1066
+ line += ` [max ${edge.maxIterations}]`;
1067
+ if (edge.weight != null)
1068
+ line += ` [weight ${edge.weight}]`;
1069
+ if (edge.race)
1070
+ line += ` [race]`;
1071
+ if (edge.tolerance != null)
1072
+ line += ` [tolerance: ${edge.tolerance}]`;
1073
+ if (edge.wait)
1074
+ line += ` [wait ${edge.wait}]`;
1075
+ if (edge.reflection)
1076
+ line += ` (reflection)`;
1077
+ sections.push(`- ${line}`);
1078
+ }
1079
+ sections.push("");
1080
+ }
1081
+ // Agent summary table
1082
+ const agents = ast.nodes.filter((n) => n.type === "agent");
1083
+ if (agents.length > 0) {
1084
+ sections.push("## Agents");
1085
+ sections.push("");
1086
+ for (const agent of agents) {
1087
+ sections.push(`### ${toTitle(agent.id)}`);
1088
+ sections.push("");
1089
+ const roleText = ast.roles[agent.role ?? ""] ?? ast.roles[agent.id] ?? agent.role;
1090
+ if (roleText) {
1091
+ sections.push(`**Role:** ${roleText}`);
1092
+ sections.push("");
1093
+ }
1094
+ if (agent.description) {
1095
+ sections.push(agent.description);
1096
+ sections.push("");
1097
+ }
1098
+ if (agent.model) {
1099
+ sections.push(`- **Model:** ${agent.model}`);
1100
+ }
1101
+ if (agent.phase != null) {
1102
+ sections.push(`- **Phase:** ${agent.phase}`);
1103
+ }
1104
+ if (agent.tools && agent.tools.length > 0) {
1105
+ sections.push(`- **Tools:** ${agent.tools.join(", ")}`);
1106
+ }
1107
+ if (agent.reads && agent.reads.length > 0) {
1108
+ sections.push(`- **Reads:** ${agent.reads.join(", ")}`);
1109
+ }
1110
+ if (agent.writes && agent.writes.length > 0) {
1111
+ sections.push(`- **Writes:** ${agent.writes.join(", ")}`);
1112
+ }
1113
+ if (agent.behavior) {
1114
+ sections.push(`- **Behavior:** ${agent.behavior}`);
1115
+ }
1116
+ sections.push("");
1117
+ }
1118
+ }
1119
+ // Gates
1120
+ const gates = ast.nodes.filter((n) => n.type === "gate");
1121
+ if (gates.length > 0) {
1122
+ sections.push("## Gates");
1123
+ sections.push("");
1124
+ for (const gate of gates) {
1125
+ sections.push(`### ${toTitle(gate.id)}`);
1126
+ if (gate.after)
1127
+ sections.push(`After: ${gate.after}`);
1128
+ if (gate.before)
1129
+ sections.push(`Before: ${gate.before}`);
1130
+ if (gate.run)
1131
+ sections.push(`Run: ${gate.run}`);
1132
+ if (gate.checks && gate.checks.length > 0) {
1133
+ sections.push(`Checks: ${gate.checks.join(", ")}`);
1134
+ }
1135
+ if (gate.onFail)
1136
+ sections.push(`On fail: ${gate.onFail}`);
1137
+ sections.push("");
1138
+ }
1139
+ }
1140
+ // Human nodes
1141
+ const humans = ast.nodes.filter((n) => n.type === "human");
1142
+ if (humans.length > 0) {
1143
+ sections.push("## Human Pause Points");
1144
+ sections.push("");
1145
+ for (const human of humans) {
1146
+ sections.push(`### ${toTitle(human.id)}`);
1147
+ if (human.description)
1148
+ sections.push(human.description);
1149
+ if (human.timeout)
1150
+ sections.push(`- **Timeout:** ${human.timeout}`);
1151
+ if (human.onTimeout)
1152
+ sections.push(`- **On timeout:** ${human.onTimeout}`);
1153
+ sections.push("");
1154
+ }
1155
+ }
1156
+ // Group nodes
1157
+ const groups = ast.nodes.filter((n) => n.type === "group");
1158
+ if (groups.length > 0) {
1159
+ sections.push("## Group Chats");
1160
+ sections.push("");
1161
+ for (const group of groups) {
1162
+ sections.push(`### ${toTitle(group.id)}`);
1163
+ if (group.description)
1164
+ sections.push(group.description);
1165
+ sections.push(`- **Members:** ${group.members.join(", ")}`);
1166
+ if (group.speakerSelection)
1167
+ sections.push(`- **Speaker selection:** ${group.speakerSelection}`);
1168
+ if (group.maxRounds != null)
1169
+ sections.push(`- **Max rounds:** ${group.maxRounds}`);
1170
+ if (group.termination)
1171
+ sections.push(`- **Termination:** ${group.termination}`);
1172
+ if (group.timeout)
1173
+ sections.push(`- **Timeout:** ${group.timeout}`);
1174
+ sections.push("");
1175
+ }
1176
+ }
1177
+ // Triggers
1178
+ if (ast.triggers.length > 0) {
1179
+ sections.push("## Triggers");
1180
+ sections.push("");
1181
+ for (const trigger of ast.triggers) {
1182
+ sections.push(`### /${trigger.name}`);
1183
+ sections.push(`Pattern: \`${trigger.pattern}\``);
1184
+ if (trigger.argument)
1185
+ sections.push(`Argument: ${trigger.argument}`);
1186
+ sections.push("");
1187
+ }
1188
+ }
1189
+ return {
1190
+ path: "AGENTS.md",
1191
+ content: sections.join("\n") + "\n",
1192
+ category: "agent",
1193
+ };
1194
+ }
1195
+ // ---------------------------------------------------------------------------
1196
+ // Hook scripts
1197
+ // ---------------------------------------------------------------------------
1198
+ /** Generate hook script stubs under .kiro/scripts/. */
1199
+ function generateHookScripts(ast) {
1200
+ const files = [];
1201
+ for (const hook of ast.hooks) {
1202
+ if (!hook.run)
1203
+ continue;
1204
+ const scriptName = hook.run.replace(/^.*\//, "").replace(/\s.*$/, "");
1205
+ files.push({
1206
+ path: `.kiro/scripts/${scriptName}`,
1207
+ content: [
1208
+ "#!/usr/bin/env bash",
1209
+ `# Hook: ${hook.name} — fires on ${mapHookEvent(hook.on)}`,
1210
+ "# Auto-generated by agentopology scaffold — edit as needed.",
1211
+ "set -euo pipefail",
1212
+ "",
1213
+ `echo "TODO: implement ${hook.name} hook"`,
1214
+ "",
1215
+ ].join("\n"),
1216
+ category: "script",
1217
+ });
1218
+ }
1219
+ return files;
1220
+ }
1221
+ // ---------------------------------------------------------------------------
1222
+ // Gate scripts
1223
+ // ---------------------------------------------------------------------------
1224
+ /** Generate gate script stubs under .kiro/scripts/. */
1225
+ function generateGateScripts(ast) {
1226
+ const files = [];
1227
+ for (const node of ast.nodes) {
1228
+ if (node.type !== "gate")
1229
+ continue;
1230
+ const gate = node;
1231
+ if (gate.run) {
1232
+ const scriptName = gate.run.replace(/^.*\//, "").replace(/\s.*$/, "");
1233
+ files.push({
1234
+ path: `.kiro/scripts/${scriptName}`,
1235
+ content: shellStub(`Gate: ${gate.id} — ${gate.run}`),
1236
+ category: "script",
1237
+ });
1238
+ }
1239
+ }
1240
+ return files;
1241
+ }
1242
+ // ---------------------------------------------------------------------------
1243
+ // Tool scripts
1244
+ // ---------------------------------------------------------------------------
1245
+ /** Generate tool script stubs under .kiro/scripts/. */
1246
+ function generateToolScripts(ast) {
1247
+ const files = [];
1248
+ for (const tool of ast.toolDefs) {
1249
+ const ext = tool.lang === "python" ? "py" : tool.lang === "node" ? "js" : "sh";
1250
+ const shebang = tool.lang === "python"
1251
+ ? "#!/usr/bin/env python3"
1252
+ : tool.lang === "node"
1253
+ ? "#!/usr/bin/env node"
1254
+ : "#!/usr/bin/env bash";
1255
+ const lines = [
1256
+ shebang,
1257
+ `# Tool: ${tool.id} — ${tool.description}`,
1258
+ "# Auto-generated by agentopology scaffold — edit as needed.",
1259
+ "",
1260
+ ];
1261
+ if (tool.lang === "bash" || !tool.lang) {
1262
+ lines.push("set -euo pipefail");
1263
+ lines.push("");
1264
+ }
1265
+ if (tool.args && tool.args.length > 0) {
1266
+ lines.push(`# Args: ${tool.args.join(", ")}`);
1267
+ lines.push("");
1268
+ }
1269
+ if (tool.lang === "python") {
1270
+ lines.push(`print("TODO: implement ${tool.id}")`);
1271
+ }
1272
+ else if (tool.lang === "node") {
1273
+ lines.push(`console.log("TODO: implement ${tool.id}");`);
1274
+ }
1275
+ else {
1276
+ lines.push(`echo "TODO: implement ${tool.id}"`);
1277
+ }
1278
+ lines.push("");
1279
+ files.push({
1280
+ path: `.kiro/scripts/${tool.id}.${ext}`,
1281
+ content: lines.join("\n"),
1282
+ category: "script",
1283
+ });
1284
+ }
1285
+ return files;
1286
+ }
1287
+ // ---------------------------------------------------------------------------
1288
+ // Memory directories
1289
+ // ---------------------------------------------------------------------------
1290
+ /** Generate memory structure directories under .kiro/. */
1291
+ function generateMemory(ast) {
1292
+ const files = [];
1293
+ if (ast.memory.domains) {
1294
+ files.push(gitkeep(".kiro/domains"));
1295
+ }
1296
+ if (ast.memory.references) {
1297
+ files.push(gitkeep(".kiro/references"));
1298
+ }
1299
+ if (ast.memory.metrics) {
1300
+ files.push({
1301
+ path: ".kiro/metrics.jsonl",
1302
+ content: "",
1303
+ category: "machine",
1304
+ });
1305
+ }
1306
+ if (ast.memory.workspace) {
1307
+ files.push(gitkeep(".kiro/runs"));
1308
+ }
1309
+ return files;
1310
+ }
1311
+ // ---------------------------------------------------------------------------
1312
+ // Metering script
1313
+ // ---------------------------------------------------------------------------
1314
+ /** Generate metering script stub. */
1315
+ function generateMetering(ast) {
1316
+ if (!ast.metering)
1317
+ return null;
1318
+ const m = ast.metering;
1319
+ const lines = [
1320
+ "#!/usr/bin/env bash",
1321
+ "# Metering / cost tracking script",
1322
+ "# Auto-generated by agentopology scaffold — edit as needed.",
1323
+ "set -euo pipefail",
1324
+ "",
1325
+ `# Track: ${m.track.join(", ")}`,
1326
+ `# Per: ${m.per.join(", ")}`,
1327
+ `# Output: ${m.output} (${m.format})`,
1328
+ `# Pricing: ${m.pricing}`,
1329
+ "",
1330
+ 'echo "TODO: implement metrics collection"',
1331
+ "",
1332
+ ];
1333
+ return {
1334
+ path: "scripts/collect-metrics.sh",
1335
+ content: lines.join("\n"),
1336
+ category: "script",
1337
+ };
1338
+ }
1339
+ // ---------------------------------------------------------------------------
1340
+ // Gate hooks
1341
+ // ---------------------------------------------------------------------------
1342
+ /**
1343
+ * Generate `.kiro/hooks/gate-{id}.kiro.hook` JSON files for each enforced gate.
1344
+ *
1345
+ * Kiro hooks are individual JSON files in `.kiro/hooks/` with trigger metadata.
1346
+ * Each gate with `run:` and non-advisory behavior gets a hook file that
1347
+ * fires when files in the gate's `after` agent's writes paths are edited.
1348
+ */
1349
+ function generateGateHooks(ast) {
1350
+ const files = [];
1351
+ // Build a map of agent writes paths for file pattern matching
1352
+ const agentWrites = new Map();
1353
+ for (const node of ast.nodes) {
1354
+ if (node.type !== "agent")
1355
+ continue;
1356
+ const agent = node;
1357
+ if (agent.writes && agent.writes.length > 0) {
1358
+ agentWrites.set(agent.id, agent.writes);
1359
+ }
1360
+ }
1361
+ for (const node of ast.nodes) {
1362
+ if (node.type !== "gate")
1363
+ continue;
1364
+ const gate = node;
1365
+ if (!gate.run)
1366
+ continue;
1367
+ if (gate.behavior === "advisory")
1368
+ continue;
1369
+ const scriptName = gate.run.replace(/^.*\//, "").replace(/\s.*$/, "");
1370
+ // Derive file patterns from the `after` agent's writes paths
1371
+ const afterWrites = gate.after ? agentWrites.get(gate.after) : undefined;
1372
+ const filePatterns = afterWrites
1373
+ ? afterWrites.map((p) => p.replace(/\/$/, "/**"))
1374
+ : ["workspace/**"];
1375
+ const hookJson = {
1376
+ enabled: true,
1377
+ name: `${toTitle(gate.id)} Gate`,
1378
+ description: `Quality gate that runs after ${gate.after || "any agent"}${gate.before ? ` and before ${gate.before}` : ""}.${gate.checks && gate.checks.length > 0 ? ` Checks: ${gate.checks.join(", ")}.` : ""}`,
1379
+ version: "1",
1380
+ when: {
1381
+ type: "fileEdited",
1382
+ patterns: filePatterns,
1383
+ },
1384
+ then: {
1385
+ type: "runCommand",
1386
+ command: `.kiro/scripts/${scriptName}`,
1387
+ },
1388
+ };
1389
+ files.push({
1390
+ path: `.kiro/hooks/gate-${gate.id}.kiro.hook`,
1391
+ content: JSON.stringify(hookJson, null, 2) + "\n",
1392
+ category: "machine",
1393
+ });
1394
+ }
1395
+ return files;
1396
+ }
1397
+ // ---------------------------------------------------------------------------
1398
+ // Specs directory
1399
+ // ---------------------------------------------------------------------------
1400
+ /**
1401
+ * Generate `.kiro/specs/` directory with Kiro-standard spec files.
1402
+ *
1403
+ * Only generates files when the topology has sufficient content to populate
1404
+ * them meaningfully (description, agents, flow, etc.).
1405
+ *
1406
+ * - `requirements.md` — populated from topology description + agent roles
1407
+ * - `design.md` — populated from flow and architecture
1408
+ * - `tasks.md` — populated from agents and their phases
1409
+ */
1410
+ function generateSpecs(ast) {
1411
+ const files = [];
1412
+ const agents = ast.nodes.filter((n) => n.type === "agent");
1413
+ // Only generate specs if there is meaningful content
1414
+ const hasDescription = !!ast.topology.description;
1415
+ const hasAgents = agents.length > 0;
1416
+ const hasEdges = ast.edges.length > 0;
1417
+ if (!hasDescription && !hasAgents)
1418
+ return files;
1419
+ // --- requirements.md ---
1420
+ if (hasDescription || hasAgents) {
1421
+ const req = [];
1422
+ req.push(`# Requirements — ${toTitle(ast.topology.name)}`);
1423
+ req.push("");
1424
+ if (ast.topology.description) {
1425
+ req.push("## Overview");
1426
+ req.push("");
1427
+ req.push(ast.topology.description);
1428
+ req.push("");
1429
+ }
1430
+ if (Object.keys(ast.roles).length > 0) {
1431
+ req.push("## Agent Roles");
1432
+ req.push("");
1433
+ for (const [role, desc] of Object.entries(ast.roles)) {
1434
+ req.push(`- **${toTitle(role)}**: ${desc}`);
1435
+ }
1436
+ req.push("");
1437
+ }
1438
+ if (agents.length > 0) {
1439
+ req.push("## Functional Requirements");
1440
+ req.push("");
1441
+ for (const agent of agents) {
1442
+ const desc = agent.description ??
1443
+ ast.roles[agent.role ?? ""] ??
1444
+ ast.roles[agent.id] ??
1445
+ "";
1446
+ if (desc) {
1447
+ req.push(`- **${toTitle(agent.id)}**: ${desc}`);
1448
+ }
1449
+ }
1450
+ req.push("");
1451
+ }
1452
+ // Gates as quality requirements
1453
+ const gates = ast.nodes.filter((n) => n.type === "gate");
1454
+ if (gates.length > 0) {
1455
+ req.push("## Quality Gates");
1456
+ req.push("");
1457
+ for (const gate of gates) {
1458
+ let line = `- **${toTitle(gate.id)}**`;
1459
+ if (gate.checks && gate.checks.length > 0) {
1460
+ line += `: checks ${gate.checks.join(", ")}`;
1461
+ }
1462
+ if (gate.onFail)
1463
+ line += ` (on fail: ${gate.onFail})`;
1464
+ req.push(line);
1465
+ }
1466
+ req.push("");
1467
+ }
1468
+ files.push({
1469
+ path: `.kiro/specs/${ast.topology.name}/requirements.md`,
1470
+ content: req.join("\n") + "\n",
1471
+ category: "machine",
1472
+ });
1473
+ }
1474
+ // --- design.md ---
1475
+ if (hasEdges || hasAgents) {
1476
+ const design = [];
1477
+ design.push(`# Design — ${toTitle(ast.topology.name)}`);
1478
+ design.push("");
1479
+ if (ast.topology.patterns.length > 0) {
1480
+ design.push("## Architecture Patterns");
1481
+ design.push("");
1482
+ design.push(ast.topology.patterns.map((p) => `- ${p}`).join("\n"));
1483
+ design.push("");
1484
+ }
1485
+ // Orchestrator
1486
+ const orch = ast.nodes.find((n) => n.type === "orchestrator");
1487
+ if (orch) {
1488
+ design.push("## Orchestrator");
1489
+ design.push("");
1490
+ design.push(`Model: ${orch.model}`);
1491
+ if (orch.handles?.length > 0) {
1492
+ design.push(`Handles: ${orch.handles.join(", ")}`);
1493
+ }
1494
+ design.push("");
1495
+ }
1496
+ if (hasEdges) {
1497
+ design.push("## Flow Graph");
1498
+ design.push("");
1499
+ for (const edge of ast.edges) {
1500
+ const arrow = edge.isError ? `-x->` : "->";
1501
+ let line = `- ${edge.from} ${arrow} ${edge.to}`;
1502
+ if (edge.condition)
1503
+ line += ` [when ${edge.condition}]`;
1504
+ if (edge.maxIterations)
1505
+ line += ` [max ${edge.maxIterations}]`;
1506
+ design.push(line);
1507
+ }
1508
+ design.push("");
1509
+ }
1510
+ // MCP servers as integration points
1511
+ if (Object.keys(ast.mcpServers).length > 0) {
1512
+ design.push("## Integration Points (MCP Servers)");
1513
+ design.push("");
1514
+ for (const [name, config] of Object.entries(ast.mcpServers)) {
1515
+ const stype = config.type ?? "stdio";
1516
+ design.push(`- **${name}** (${stype})`);
1517
+ }
1518
+ design.push("");
1519
+ }
1520
+ files.push({
1521
+ path: `.kiro/specs/${ast.topology.name}/design.md`,
1522
+ content: design.join("\n") + "\n",
1523
+ category: "machine",
1524
+ });
1525
+ }
1526
+ // --- tasks.md ---
1527
+ if (hasAgents) {
1528
+ const tasks = [];
1529
+ tasks.push(`# Tasks — ${toTitle(ast.topology.name)}`);
1530
+ tasks.push("");
1531
+ // Group agents by phase
1532
+ const phased = agents
1533
+ .filter((a) => a.phase != null)
1534
+ .sort((a, b) => (a.phase ?? 0) - (b.phase ?? 0));
1535
+ const unphased = agents.filter((a) => a.phase == null);
1536
+ if (phased.length > 0) {
1537
+ let currentPhase = -1;
1538
+ for (const agent of phased) {
1539
+ if (agent.phase !== currentPhase) {
1540
+ currentPhase = agent.phase;
1541
+ tasks.push(`## Phase ${currentPhase}`);
1542
+ tasks.push("");
1543
+ }
1544
+ const desc = agent.description ??
1545
+ ast.roles[agent.role ?? ""] ??
1546
+ "";
1547
+ tasks.push(`- [ ] **${toTitle(agent.id)}**${desc ? `: ${desc}` : ""}`);
1548
+ if (agent.tools && agent.tools.length > 0) {
1549
+ tasks.push(` - Tools: ${agent.tools.join(", ")}`);
1550
+ }
1551
+ if (agent.mcpServers && agent.mcpServers.length > 0) {
1552
+ tasks.push(` - MCP: ${agent.mcpServers.join(", ")}`);
1553
+ }
1554
+ }
1555
+ tasks.push("");
1556
+ }
1557
+ if (unphased.length > 0) {
1558
+ tasks.push("## Unphased");
1559
+ tasks.push("");
1560
+ for (const agent of unphased) {
1561
+ const desc = agent.description ??
1562
+ ast.roles[agent.role ?? ""] ??
1563
+ "";
1564
+ tasks.push(`- [ ] **${toTitle(agent.id)}**${desc ? `: ${desc}` : ""}`);
1565
+ }
1566
+ tasks.push("");
1567
+ }
1568
+ files.push({
1569
+ path: `.kiro/specs/${ast.topology.name}/tasks.md`,
1570
+ content: tasks.join("\n") + "\n",
1571
+ category: "machine",
1572
+ });
1573
+ }
1574
+ return files;
1575
+ }
1576
+ // ---------------------------------------------------------------------------
1577
+ // Binding export
1578
+ // ---------------------------------------------------------------------------
1579
+ /** Kiro CLI binding — generates .kiro/ directory structure and AGENTS.md. */
1580
+ export const kiroBinding = {
1581
+ name: "kiro",
1582
+ description: "AWS Kiro CLI — generates .kiro/ directory with JSON agents, steering docs, MCP config, and prompts.",
1583
+ scaffold(ast) {
1584
+ const files = [];
1585
+ // 1. Agent JSON files
1586
+ files.push(...generateAgents(ast));
1587
+ // 2. Steering documents
1588
+ files.push(generateProductSteering(ast));
1589
+ files.push(generateTechSteering(ast));
1590
+ files.push(generateStructureSteering(ast));
1591
+ // 2b. Per-agent behavioral steering docs (Wave 1-7)
1592
+ files.push(...generateAgentSteering(ast));
1593
+ // 2c. Human node steering docs (Wave 5)
1594
+ files.push(...generateHumanSteering(ast));
1595
+ // 2d. Group node steering docs (Wave 5)
1596
+ files.push(...generateGroupSteering(ast));
1597
+ // 3. MCP configuration
1598
+ const mcpFile = generateMcpJson(ast);
1599
+ if (mcpFile)
1600
+ files.push(mcpFile);
1601
+ // 4. Prompt files (triggers)
1602
+ files.push(...generatePromptFiles(ast));
1603
+ // 5. AGENTS.md (top-level flow narrative)
1604
+ files.push(generateAgentsMd(ast));
1605
+ // 6. Hook scripts
1606
+ files.push(...generateHookScripts(ast));
1607
+ // 7. Gate scripts
1608
+ files.push(...generateGateScripts(ast));
1609
+ // 8. Gate hook files
1610
+ files.push(...generateGateHooks(ast));
1611
+ // 9. Tool scripts
1612
+ files.push(...generateToolScripts(ast));
1613
+ // 10. Memory directories
1614
+ files.push(...generateMemory(ast));
1615
+ // 11. Metering
1616
+ const meteringFile = generateMetering(ast);
1617
+ if (meteringFile)
1618
+ files.push(meteringFile);
1619
+ // 12. Specs directory (.kiro/specs/)
1620
+ files.push(...generateSpecs(ast));
1621
+ return deduplicateFiles(files);
1622
+ },
1623
+ };
1624
+ //# sourceMappingURL=kiro.js.map