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