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,897 @@
1
+ /**
2
+ * Claude Code importer — reads .claude/ directory structure and constructs
3
+ * a TopologyAST suitable for serialization to .at format.
4
+ *
5
+ * @module
6
+ */
7
+ import { extractSectionUntilKnownHeading, CLAUDE_CODE_STRUCTURAL_HEADINGS } from "../sync/index.js";
8
+ function parseFrontmatter(content) {
9
+ const fields = {};
10
+ let body = content;
11
+ if (!content.startsWith("---")) {
12
+ return { fields, body };
13
+ }
14
+ const endIdx = content.indexOf("---", 3);
15
+ if (endIdx === -1)
16
+ return { fields, body };
17
+ const yaml = content.slice(3, endIdx).trim();
18
+ body = content.slice(endIdx + 3).trim();
19
+ let currentKey = null;
20
+ let currentList = null;
21
+ for (const line of yaml.split("\n")) {
22
+ const trimmed = line.trim();
23
+ if (!trimmed)
24
+ continue;
25
+ // List item continuation
26
+ if (trimmed.startsWith("- ") && currentKey && currentList !== null) {
27
+ currentList.push(trimmed.slice(2).trim());
28
+ continue;
29
+ }
30
+ // Flush previous list
31
+ if (currentKey && currentList !== null) {
32
+ fields[currentKey] = currentList;
33
+ currentKey = null;
34
+ currentList = null;
35
+ }
36
+ const colonIdx = trimmed.indexOf(":");
37
+ if (colonIdx === -1)
38
+ continue;
39
+ const key = trimmed.slice(0, colonIdx).trim();
40
+ const rawValue = trimmed.slice(colonIdx + 1).trim();
41
+ if (rawValue === "" || rawValue === undefined) {
42
+ // Start of a list
43
+ currentKey = key;
44
+ currentList = [];
45
+ continue;
46
+ }
47
+ // Boolean
48
+ if (rawValue === "true") {
49
+ fields[key] = true;
50
+ continue;
51
+ }
52
+ if (rawValue === "false") {
53
+ fields[key] = false;
54
+ continue;
55
+ }
56
+ // Unquote
57
+ const value = rawValue.replace(/^["']|["']$/g, "");
58
+ fields[key] = value;
59
+ }
60
+ // Flush last list
61
+ if (currentKey && currentList !== null) {
62
+ fields[currentKey] = currentList;
63
+ }
64
+ return { fields, body };
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // Section extraction helpers
68
+ // ---------------------------------------------------------------------------
69
+ /** Known structural headings that delimit sections in AGENT.md body. */
70
+ const AGENT_KNOWN_HEADINGS = [
71
+ ...CLAUDE_CODE_STRUCTURAL_HEADINGS,
72
+ "## Instructions",
73
+ "## Circuit Breaker",
74
+ "## Input Schema",
75
+ "## Output Schema",
76
+ "## Variants",
77
+ "## Artifacts",
78
+ "## Model Configuration",
79
+ "## Scale",
80
+ ];
81
+ /** Known structural headings in SKILL.md body. */
82
+ const SKILL_KNOWN_HEADINGS = [
83
+ "## Orchestrator",
84
+ "## Flow",
85
+ "## Gates",
86
+ "## Triggers",
87
+ "## Depth Levels",
88
+ "## Batch",
89
+ "## Environments",
90
+ "## Memory",
91
+ "## Metering",
92
+ "## Schedules",
93
+ "## Interfaces",
94
+ "## Parameters",
95
+ "## Interface",
96
+ "## Imports",
97
+ "## Includes",
98
+ "## Error Handling",
99
+ "## Fallback Chain",
100
+ "## How to Use",
101
+ ];
102
+ /** Extract a markdown section by heading, stopping at any known heading. */
103
+ function extractSection(body, heading) {
104
+ return extractSectionUntilKnownHeading(body, heading, AGENT_KNOWN_HEADINGS.filter((h) => h !== heading));
105
+ }
106
+ /** Extract a markdown section from SKILL.md, stopping at known skill headings. */
107
+ function extractSkillSection(body, heading) {
108
+ return extractSectionUntilKnownHeading(body, heading, SKILL_KNOWN_HEADINGS.filter((h) => h !== heading));
109
+ }
110
+ /** Parse bullet list items from a section: "- item" → ["item"] */
111
+ function parseBulletList(text) {
112
+ if (!text)
113
+ return [];
114
+ return text
115
+ .split("\n")
116
+ .filter((line) => line.trim().startsWith("- "))
117
+ .map((line) => line.trim().slice(2).trim());
118
+ }
119
+ /** Parse output enum lines: "- field: val1 | val2" → OutputsMap */
120
+ function parseOutputLines(text) {
121
+ if (!text)
122
+ return undefined;
123
+ const outputs = {};
124
+ for (const line of text.split("\n")) {
125
+ const trimmed = line.trim();
126
+ if (!trimmed.startsWith("- "))
127
+ continue;
128
+ const content = trimmed.slice(2);
129
+ const colonIdx = content.indexOf(":");
130
+ if (colonIdx === -1)
131
+ continue;
132
+ const field = content.slice(0, colonIdx).trim();
133
+ const values = content.slice(colonIdx + 1).trim().split(/\s*\|\s*/);
134
+ if (field && values.length > 0) {
135
+ outputs[field] = values;
136
+ }
137
+ }
138
+ return Object.keys(outputs).length > 0 ? outputs : undefined;
139
+ }
140
+ // ---------------------------------------------------------------------------
141
+ // Inline pattern extractors
142
+ // ---------------------------------------------------------------------------
143
+ function extractInlinePattern(body, pattern) {
144
+ const match = pattern.exec(body);
145
+ return match ? match[1] : null;
146
+ }
147
+ function extractTimeout(body) {
148
+ return extractInlinePattern(body, /You have a maximum of (\S+) to complete/) ?? undefined;
149
+ }
150
+ function extractOnFail(body) {
151
+ return extractInlinePattern(body, /If you fail: (.+)/) ?? undefined;
152
+ }
153
+ function extractRetry(body) {
154
+ const simple = extractInlinePattern(body, /Retry up to (\d+) times/);
155
+ if (simple)
156
+ return parseInt(simple, 10);
157
+ const structured = body.match(/Retry strategy: max (\d+) attempts,\s*(\w+) backoff,\s*interval (\S+)/);
158
+ if (structured) {
159
+ const config = {
160
+ max: parseInt(structured[1], 10),
161
+ backoff: structured[2],
162
+ interval: structured[3],
163
+ };
164
+ const maxInt = body.match(/max interval (\S+)/);
165
+ if (maxInt)
166
+ config.maxInterval = maxInt[1].replace(/,$/, "");
167
+ if (/with jitter/.test(body))
168
+ config.jitter = true;
169
+ const nonRetryable = body.match(/non-retryable: (.+)/);
170
+ if (nonRetryable) {
171
+ config.nonRetryable = nonRetryable[1].split(/,\s*/);
172
+ }
173
+ return config;
174
+ }
175
+ return undefined;
176
+ }
177
+ function extractThinking(body) {
178
+ const match = body.match(/Use (\w+) level reasoning(?: \(budget: (\d+) tokens\))?/);
179
+ if (!match)
180
+ return {};
181
+ const result = { thinking: match[1] };
182
+ if (match[2])
183
+ result.thinkingBudget = parseInt(match[2], 10);
184
+ return result;
185
+ }
186
+ // ---------------------------------------------------------------------------
187
+ // Reverse permission mapping
188
+ // ---------------------------------------------------------------------------
189
+ function reversePermissionMode(mode) {
190
+ if (!mode)
191
+ return undefined;
192
+ switch (mode) {
193
+ case "plan": return "supervised";
194
+ case "bypassPermissions": return "unrestricted";
195
+ case "askUser": return "interactive";
196
+ case "auto": return "autonomous";
197
+ case "confirm": return "confirm";
198
+ default: return mode;
199
+ }
200
+ }
201
+ // ---------------------------------------------------------------------------
202
+ // AGENT.md parser
203
+ // ---------------------------------------------------------------------------
204
+ const KNOWN_FRONTMATTER_KEYS = new Set([
205
+ "name", "description", "model", "maxTurns", "tools", "disallowed-tools",
206
+ "mcpServers", "background", "permissionMode", "isolation", "sandbox",
207
+ "fallback-chain", "type", "members",
208
+ ]);
209
+ export function parseAgentMd(agentId, content) {
210
+ const { fields, body } = parseFrontmatter(content);
211
+ // Detect node type
212
+ if (fields.type === "group" || body.includes("(Group Chat)")) {
213
+ const group = {
214
+ type: "group",
215
+ id: agentId,
216
+ label: agentId,
217
+ members: fields.members ?? [],
218
+ description: fields.description,
219
+ };
220
+ // Try to extract group-specific fields from body
221
+ const speakerMatch = body.match(/Speaker selection:\s*(.+)/);
222
+ if (speakerMatch)
223
+ group.speakerSelection = speakerMatch[1].trim();
224
+ const roundsMatch = body.match(/Max rounds:\s*(\d+)/);
225
+ if (roundsMatch)
226
+ group.maxRounds = parseInt(roundsMatch[1], 10);
227
+ const termMatch = body.match(/Termination:\s*(.+)/);
228
+ if (termMatch)
229
+ group.termination = termMatch[1].trim();
230
+ const timeoutMatch = body.match(/Timeout:\s*(\S+)/);
231
+ if (timeoutMatch)
232
+ group.timeout = timeoutMatch[1];
233
+ return group;
234
+ }
235
+ if (body.includes("(Human-in-the-Loop)")) {
236
+ const human = {
237
+ type: "human",
238
+ id: agentId,
239
+ label: agentId,
240
+ };
241
+ const descMatch = body.match(/\n\n(.+?)(?:\n\n|$)/s);
242
+ if (descMatch)
243
+ human.description = descMatch[1].trim();
244
+ const timeoutMatch = body.match(/Timeout:\s*(\S+)/);
245
+ if (timeoutMatch)
246
+ human.timeout = timeoutMatch[1];
247
+ const onTimeoutMatch = body.match(/On timeout:\s*(.+)/);
248
+ if (onTimeoutMatch)
249
+ human.onTimeout = onTimeoutMatch[1].trim();
250
+ return human;
251
+ }
252
+ // Regular agent
253
+ const agent = {
254
+ type: "agent",
255
+ id: agentId,
256
+ label: agentId,
257
+ };
258
+ // Frontmatter fields
259
+ if (fields.model)
260
+ agent.model = fields.model;
261
+ if (fields.description)
262
+ agent.description = fields.description;
263
+ if (fields.maxTurns)
264
+ agent.maxTurns = parseInt(fields.maxTurns, 10);
265
+ if (fields.tools)
266
+ agent.tools = fields.tools;
267
+ if (fields["disallowed-tools"])
268
+ agent.disallowedTools = fields["disallowed-tools"];
269
+ if (fields.mcpServers)
270
+ agent.mcpServers = fields.mcpServers;
271
+ if (fields.background === true)
272
+ agent.background = true;
273
+ if (fields.permissionMode)
274
+ agent.permissions = reversePermissionMode(fields.permissionMode);
275
+ if (fields.isolation)
276
+ agent.isolation = fields.isolation;
277
+ if (fields.sandbox !== undefined) {
278
+ agent.sandbox = fields.sandbox;
279
+ }
280
+ if (fields["fallback-chain"]) {
281
+ const fc = fields["fallback-chain"];
282
+ agent.fallbackChain = typeof fc === "string" ? fc.split(/,\s*/) : fc;
283
+ }
284
+ // Extensions: any unknown frontmatter keys
285
+ const extensions = {};
286
+ for (const [key, value] of Object.entries(fields)) {
287
+ if (!KNOWN_FRONTMATTER_KEYS.has(key)) {
288
+ extensions[key] = value;
289
+ }
290
+ }
291
+ if (Object.keys(extensions).length > 0) {
292
+ agent.extensions = { "claude-code": extensions };
293
+ }
294
+ // Body sections
295
+ agent.role = extractSection(body, "## Role")?.trim() ?? undefined;
296
+ // If role matches a single line, it's a role reference, not inline
297
+ // Keep it as the role key for now
298
+ const prompt = extractSection(body, "## Instructions");
299
+ if (prompt)
300
+ agent.prompt = prompt;
301
+ agent.reads = parseBulletList(extractSection(body, "## Reads"));
302
+ if (agent.reads.length === 0)
303
+ agent.reads = undefined;
304
+ agent.writes = parseBulletList(extractSection(body, "## Writes"));
305
+ if (agent.writes.length === 0)
306
+ agent.writes = undefined;
307
+ agent.outputs = parseOutputLines(extractSection(body, "## Outputs"));
308
+ // Scale
309
+ const scaleSection = extractSection(body, "## Scale");
310
+ if (scaleSection) {
311
+ const mode = scaleSection.match(/Mode:\s*(\S+)/);
312
+ const by = scaleSection.match(/By:\s*(.+)/);
313
+ const min = scaleSection.match(/Min:\s*(\d+)/);
314
+ const max = scaleSection.match(/Max:\s*(\d+)/);
315
+ const batchSize = scaleSection.match(/Batch size:\s*(\d+)/);
316
+ if (mode && by && min && max) {
317
+ agent.scale = {
318
+ mode: mode[1],
319
+ by: by[1].trim(),
320
+ min: parseInt(min[1], 10),
321
+ max: parseInt(max[1], 10),
322
+ batchSize: batchSize ? parseInt(batchSize[1], 10) : null,
323
+ };
324
+ }
325
+ }
326
+ // Circuit breaker
327
+ const cbSection = extractSection(body, "## Circuit Breaker");
328
+ if (cbSection) {
329
+ const threshold = cbSection.match(/Failure threshold:\s*(\d+)/);
330
+ const window = cbSection.match(/Window:\s*(\S+)/);
331
+ const cooldown = cbSection.match(/Cooldown:\s*(\S+)/);
332
+ if (threshold && window && cooldown) {
333
+ agent.circuitBreaker = {
334
+ threshold: parseInt(threshold[1], 10),
335
+ window: window[1],
336
+ cooldown: cooldown[1],
337
+ };
338
+ }
339
+ }
340
+ // Model configuration
341
+ const modelConfig = extractSection(body, "## Model Configuration");
342
+ if (modelConfig) {
343
+ const temp = modelConfig.match(/Temperature:\s*([\d.]+)/);
344
+ if (temp)
345
+ agent.temperature = parseFloat(temp[1]);
346
+ const maxTokens = modelConfig.match(/Max tokens:\s*(\d+)/);
347
+ if (maxTokens)
348
+ agent.maxTokens = parseInt(maxTokens[1], 10);
349
+ const topP = modelConfig.match(/Top-p:\s*([\d.]+)/);
350
+ if (topP)
351
+ agent.topP = parseFloat(topP[1]);
352
+ const topK = modelConfig.match(/Top-k:\s*(\d+)/);
353
+ if (topK)
354
+ agent.topK = parseInt(topK[1], 10);
355
+ const stopMatch = modelConfig.match(/Stop sequences:\s*(.+)/);
356
+ if (stopMatch)
357
+ agent.stop = stopMatch[1].split(/,\s*/);
358
+ const seed = modelConfig.match(/Seed:\s*(\d+)/);
359
+ if (seed)
360
+ agent.seed = parseInt(seed[1], 10);
361
+ }
362
+ // Input/Output schema
363
+ const inputSchema = extractSection(body, "## Input Schema");
364
+ if (inputSchema)
365
+ agent.inputSchema = parseSchemaFields(inputSchema);
366
+ const outputSchema = extractSection(body, "## Output Schema");
367
+ if (outputSchema)
368
+ agent.outputSchema = parseSchemaFields(outputSchema);
369
+ // Artifacts
370
+ const artifactsSection = extractSection(body, "## Artifacts");
371
+ if (artifactsSection) {
372
+ const producesMatch = artifactsSection.match(/Produces:\s*(.+)/);
373
+ if (producesMatch)
374
+ agent.produces = producesMatch[1].split(/,\s*/);
375
+ const consumesMatch = artifactsSection.match(/Consumes:\s*(.+)/);
376
+ if (consumesMatch)
377
+ agent.consumes = consumesMatch[1].split(/,\s*/);
378
+ }
379
+ // Inline patterns
380
+ agent.timeout = extractTimeout(body);
381
+ agent.onFail = extractOnFail(body);
382
+ agent.retry = extractRetry(body);
383
+ const thinkingInfo = extractThinking(body);
384
+ if (thinkingInfo.thinking)
385
+ agent.thinking = thinkingInfo.thinking;
386
+ if (thinkingInfo.thinkingBudget)
387
+ agent.thinkingBudget = thinkingInfo.thinkingBudget;
388
+ const outputFormat = extractInlinePattern(body, /Output format:\s*(\S+)/);
389
+ if (outputFormat)
390
+ agent.outputFormat = outputFormat;
391
+ const logLevel = extractInlinePattern(body, /Log level:\s*(\S+)/);
392
+ if (logLevel)
393
+ agent.logLevel = logLevel;
394
+ const join = extractInlinePattern(body, /Wait strategy:\s*(\S+)/);
395
+ if (join)
396
+ agent.join = join;
397
+ const compensates = extractInlinePattern(body, /compensates.*?:\s*(\S+)/);
398
+ if (compensates)
399
+ agent.compensates = compensates;
400
+ const invocation = extractInlinePattern(body, /Invocation:\s*(\S+)/);
401
+ if (invocation)
402
+ agent.invocation = invocation;
403
+ const skillsInline = extractInlinePattern(body, /Skills:\s*(.+)/);
404
+ if (skillsInline)
405
+ agent.skills = skillsInline.split(/,\s*/);
406
+ const behavior = extractInlinePattern(body, /Behavior:\s*(\S+)/);
407
+ if (behavior)
408
+ agent.behavior = behavior;
409
+ const skip = extractInlinePattern(body, /Skip when:\s*(.+)/);
410
+ if (skip)
411
+ agent.skip = skip;
412
+ const rateLimit = extractInlinePattern(body, /Rate limit:\s*(.+)/);
413
+ if (rateLimit)
414
+ agent.rateLimit = rateLimit;
415
+ return agent;
416
+ }
417
+ function parseSchemaFields(text) {
418
+ const fields = [];
419
+ for (const line of text.split("\n")) {
420
+ const match = line.trim().match(/^-\s+\*\*(\w+?)(\?)?\*\*.*?:\s*(.+)/);
421
+ if (!match)
422
+ continue;
423
+ const optional = match[2] === "?";
424
+ fields.push({
425
+ name: match[1],
426
+ type: parseSchemaTypeStr(match[3].trim()),
427
+ optional,
428
+ });
429
+ }
430
+ return fields;
431
+ }
432
+ function parseSchemaTypeStr(typeStr) {
433
+ if (typeStr.endsWith("[]")) {
434
+ return { kind: "array", itemType: parseSchemaTypeStr(typeStr.slice(0, -2)) };
435
+ }
436
+ if (typeStr.includes(" | ")) {
437
+ return { kind: "enum", values: typeStr.split(/\s*\|\s*/) };
438
+ }
439
+ const primitives = ["string", "number", "integer", "boolean", "object"];
440
+ if (primitives.includes(typeStr)) {
441
+ return { kind: "primitive", value: typeStr };
442
+ }
443
+ return { kind: "ref", name: typeStr };
444
+ }
445
+ export function parseSkillMd(content) {
446
+ const { fields, body } = parseFrontmatter(content);
447
+ const result = {
448
+ isTopology: !!fields.topology,
449
+ name: (fields.topology || fields.name),
450
+ version: fields.version,
451
+ description: fields.description,
452
+ patterns: fields.patterns,
453
+ edges: [],
454
+ gates: [],
455
+ triggers: [],
456
+ agentIds: new Set(),
457
+ };
458
+ // Orchestrator
459
+ const orchSection = extractSkillSection(body, "## Orchestrator");
460
+ if (orchSection) {
461
+ const modelMatch = orchSection.match(/Model:\s*(\S+)/);
462
+ const handlesMatch = orchSection.match(/Handles:\s*(.+)/);
463
+ if (modelMatch) {
464
+ const orch = {
465
+ type: "orchestrator",
466
+ id: "orchestrator",
467
+ label: "Orchestrator",
468
+ model: modelMatch[1],
469
+ handles: handlesMatch ? handlesMatch[1].split(/,\s*/) : [],
470
+ };
471
+ // Outputs
472
+ const outputsSection = extractSection(orchSection, "### Outputs");
473
+ if (outputsSection) {
474
+ orch.outputs = parseOutputLines(outputsSection);
475
+ }
476
+ const genMatch = orchSection.match(/Generates:\s*(.+)/);
477
+ if (genMatch)
478
+ orch.generates = genMatch[1].trim();
479
+ result.orchestrator = orch;
480
+ }
481
+ }
482
+ // Flow
483
+ const flowSection = extractSkillSection(body, "## Flow");
484
+ if (flowSection) {
485
+ for (const line of flowSection.split("\n")) {
486
+ const trimmed = line.trim();
487
+ if (!trimmed.startsWith("- "))
488
+ continue;
489
+ const edgeLine = trimmed.slice(2).trim();
490
+ const edge = parseEdgeLine(edgeLine);
491
+ if (edge) {
492
+ result.edges.push(edge);
493
+ result.agentIds.add(edge.from);
494
+ result.agentIds.add(edge.to);
495
+ }
496
+ }
497
+ }
498
+ // Gates
499
+ const gatesSection = extractSkillSection(body, "## Gates");
500
+ if (gatesSection) {
501
+ // Split by ### headings
502
+ const gateParts = gatesSection.split(/^### /m).filter((s) => s.trim());
503
+ for (const part of gateParts) {
504
+ const lines = part.split("\n");
505
+ const gateId = lines[0].trim().toLowerCase().replace(/\s+/g, "-");
506
+ const gate = {
507
+ type: "gate",
508
+ id: gateId,
509
+ label: gateId,
510
+ };
511
+ for (const line of lines.slice(1)) {
512
+ const trimmed = line.trim();
513
+ if (trimmed.startsWith("After:"))
514
+ gate.after = trimmed.slice(6).trim();
515
+ if (trimmed.startsWith("Before:"))
516
+ gate.before = trimmed.slice(7).trim();
517
+ if (trimmed.startsWith("Run:"))
518
+ gate.run = trimmed.slice(4).trim();
519
+ if (trimmed.startsWith("Checks:"))
520
+ gate.checks = trimmed.slice(7).trim().split(/,\s*/);
521
+ if (trimmed.startsWith("On fail:"))
522
+ gate.onFail = trimmed.slice(8).trim();
523
+ if (trimmed.startsWith("Retry:"))
524
+ gate.retry = parseInt(trimmed.slice(6).trim(), 10);
525
+ if (trimmed.startsWith("Timeout:"))
526
+ gate.timeout = trimmed.slice(8).trim();
527
+ }
528
+ result.gates.push(gate);
529
+ }
530
+ }
531
+ // Triggers
532
+ const triggersSection = extractSkillSection(body, "## Triggers");
533
+ if (triggersSection) {
534
+ const triggerParts = triggersSection.split(/^### /m).filter((s) => s.trim());
535
+ for (const part of triggerParts) {
536
+ const lines = part.split("\n");
537
+ const name = lines[0].trim().replace(/^\//, "");
538
+ let pattern = "";
539
+ let argument;
540
+ for (const line of lines.slice(1)) {
541
+ const trimmed = line.trim();
542
+ const patternMatch = trimmed.match(/^Pattern:\s*`?(.+?)`?\s*$/);
543
+ if (patternMatch)
544
+ pattern = patternMatch[1];
545
+ const argMatch = trimmed.match(/^Argument:\s*(\S+)/);
546
+ if (argMatch)
547
+ argument = argMatch[1];
548
+ }
549
+ if (name && pattern) {
550
+ result.triggers.push({ name, pattern, argument });
551
+ }
552
+ }
553
+ }
554
+ // Extract additional topology-level metadata from body text
555
+ const domainMatch = body.match(/^Domain:\s*(.+)/m);
556
+ if (domainMatch)
557
+ result.domain = domainMatch[1].trim();
558
+ const foundationsMatch = body.match(/^Foundations:\s*(.+)/m);
559
+ if (foundationsMatch)
560
+ result.foundations = foundationsMatch[1].split(/,\s*/);
561
+ const advancedMatch = body.match(/^Advanced:\s*(.+)/m);
562
+ if (advancedMatch)
563
+ result.advanced = advancedMatch[1].split(/,\s*/);
564
+ const timeoutMatch = body.match(/^Timeout:\s*(\S+)/m);
565
+ if (timeoutMatch)
566
+ result.timeout = timeoutMatch[1];
567
+ const errorMatch = body.match(/handled by:\s*\*\*(\S+)\*\*/);
568
+ if (errorMatch)
569
+ result.errorHandler = errorMatch[1];
570
+ if (/Durable:\s*yes/i.test(body))
571
+ result.durable = true;
572
+ return result;
573
+ }
574
+ function parseEdgeLine(line) {
575
+ // Match: from -> to [attrs]... (one or more bracket groups)
576
+ // Also: from -x-> to, from -x[type]-> to
577
+ const match = line.match(/^(\S+)\s+(->|-x(?:\[(\w+)\])?->)\s+(\S+)((?:\s+\[[^\]]+\])*)$/);
578
+ if (!match)
579
+ return null;
580
+ const edge = {
581
+ from: match[1],
582
+ to: match[4],
583
+ condition: null,
584
+ maxIterations: null,
585
+ };
586
+ // Error edge
587
+ if (match[2].startsWith("-x")) {
588
+ edge.isError = true;
589
+ if (match[3])
590
+ edge.errorType = match[3];
591
+ }
592
+ // Parse attributes — merge all bracket groups into one string
593
+ const bracketGroups = match[5]?.trim();
594
+ if (bracketGroups) {
595
+ // Extract contents from all [...] groups and merge
596
+ const allAttrs = Array.from(bracketGroups.matchAll(/\[([^\]]+)\]/g))
597
+ .map((m) => m[1])
598
+ .join(", ");
599
+ const whenMatch = allAttrs.match(/when\s+(.+?)(?:,\s*(?:max|per|race|tolerance|wait|weight|reflection)|$)/);
600
+ if (whenMatch)
601
+ edge.condition = whenMatch[1].trim();
602
+ const maxMatch = allAttrs.match(/max\s+(\d+)/);
603
+ if (maxMatch)
604
+ edge.maxIterations = parseInt(maxMatch[1], 10);
605
+ const perMatch = allAttrs.match(/per\s+(\S+)/);
606
+ if (perMatch)
607
+ edge.per = perMatch[1];
608
+ if (/\brace\b/.test(allAttrs))
609
+ edge.race = true;
610
+ const tolMatch = allAttrs.match(/tolerance:\s*(\S+)/);
611
+ if (tolMatch)
612
+ edge.tolerance = tolMatch[1].includes("%") ? tolMatch[1] : parseInt(tolMatch[1], 10);
613
+ const waitMatch = allAttrs.match(/wait\s+(\S+)/);
614
+ if (waitMatch)
615
+ edge.wait = waitMatch[1];
616
+ const weightMatch = allAttrs.match(/weight\s+([\d.]+)/);
617
+ if (weightMatch)
618
+ edge.weight = parseFloat(weightMatch[1]);
619
+ if (/\breflection\b/.test(allAttrs))
620
+ edge.reflection = true;
621
+ }
622
+ return edge;
623
+ }
624
+ // ---------------------------------------------------------------------------
625
+ // settings.json parser
626
+ // ---------------------------------------------------------------------------
627
+ export function parseSettingsJson(content) {
628
+ const json = JSON.parse(content);
629
+ const settings = {};
630
+ const hooks = [];
631
+ if (json.allow)
632
+ settings.allow = json.allow;
633
+ if (json.deny)
634
+ settings.deny = json.deny;
635
+ if (json.ask)
636
+ settings.ask = json.ask;
637
+ // Parse hooks
638
+ if (json.hooks) {
639
+ for (const [event, handlers] of Object.entries(json.hooks)) {
640
+ if (!Array.isArray(handlers))
641
+ continue;
642
+ for (const handler of handlers) {
643
+ const hookList = handler.hooks ?? [];
644
+ const matcher = handler.matcher ?? "";
645
+ for (const hookEntry of hookList) {
646
+ // Skip gate enforcement hooks (generated, not user-defined)
647
+ if (hookEntry.command && /gate-[\w-]+\.sh/.test(hookEntry.command))
648
+ continue;
649
+ const command = hookEntry.command ?? "";
650
+ // Extract script name for hook id
651
+ const nameMatch = command.match(/([^/]+?)(?:\.sh)?$/);
652
+ const name = nameMatch ? nameMatch[1] : event.toLowerCase();
653
+ hooks.push({
654
+ name,
655
+ on: event,
656
+ matcher,
657
+ run: command,
658
+ type: hookEntry.type,
659
+ timeout: hookEntry.timeout,
660
+ });
661
+ }
662
+ }
663
+ }
664
+ }
665
+ return { settings, hooks };
666
+ }
667
+ // ---------------------------------------------------------------------------
668
+ // .mcp.json parser
669
+ // ---------------------------------------------------------------------------
670
+ export function parseMcpJson(content) {
671
+ const json = JSON.parse(content);
672
+ return json.mcpServers ?? {};
673
+ }
674
+ // ---------------------------------------------------------------------------
675
+ // Command file parser
676
+ // ---------------------------------------------------------------------------
677
+ export function parseCommandMd(filename, content) {
678
+ const name = filename.replace(/\.md$/, "");
679
+ const { body } = parseFrontmatter(content);
680
+ // Look for pattern in body: /command-name or just the first line
681
+ const patternMatch = body.match(/^#\s+\/(.+)/m);
682
+ const pattern = patternMatch ? `/${patternMatch[1].trim()}` : `/${name}`;
683
+ // Look for argument
684
+ const argMatch = body.match(/## Arguments\s*\n-\s*(\w+)/);
685
+ const argument = argMatch ? argMatch[1] : undefined;
686
+ return { name, pattern, argument };
687
+ }
688
+ // ---------------------------------------------------------------------------
689
+ // Action script parser
690
+ // ---------------------------------------------------------------------------
691
+ export function parseActionScript(filename, content) {
692
+ // Extract id from filename: action-<id>.sh → <id>
693
+ const idMatch = filename.match(/action-(.+?)\.sh$/);
694
+ if (!idMatch)
695
+ return null;
696
+ const id = idMatch[1];
697
+ const action = {
698
+ type: "action",
699
+ id,
700
+ label: id,
701
+ };
702
+ // Parse comment headers
703
+ const descMatch = content.match(/# Action:\s*\S+\s*—\s*(.+)/);
704
+ if (descMatch)
705
+ action.description = descMatch[1].trim();
706
+ if (content.includes("# External source:")) {
707
+ action.kind = "external";
708
+ const sourceMatch = content.match(/# External source:\s*(.+)/);
709
+ if (sourceMatch)
710
+ action.source = sourceMatch[1].trim();
711
+ }
712
+ else if (content.includes("git action")) {
713
+ action.kind = "git";
714
+ }
715
+ else if (content.includes("report action")) {
716
+ action.kind = "report";
717
+ }
718
+ else {
719
+ action.kind = "inline";
720
+ }
721
+ const timeoutMatch = content.match(/# Timeout:\s*(\S+)/);
722
+ if (timeoutMatch)
723
+ action.timeout = timeoutMatch[1];
724
+ const onFailMatch = content.match(/# On failure:\s*(.+)/);
725
+ if (onFailMatch)
726
+ action.onFail = onFailMatch[1].trim();
727
+ return action;
728
+ }
729
+ // ---------------------------------------------------------------------------
730
+ // Main import function
731
+ // ---------------------------------------------------------------------------
732
+ export function importClaudeCode(files, topologyName) {
733
+ const nodes = [];
734
+ const roles = {};
735
+ // 1. Parse AGENT.md files
736
+ for (const file of files) {
737
+ const match = file.path.match(/(?:^|[\\/])agents[\\/]([^\\/]+)[\\/]AGENT\.md$/);
738
+ if (!match)
739
+ continue;
740
+ const agentId = match[1];
741
+ const node = parseAgentMd(agentId, file.content);
742
+ if (node) {
743
+ // Extract role description into roles map and set agent.role to the key
744
+ if (node.type === "agent" && node.role) {
745
+ roles[agentId] = node.role;
746
+ node.role = agentId;
747
+ }
748
+ nodes.push(node);
749
+ }
750
+ }
751
+ // 2. Parse SKILL.md files for topology metadata
752
+ let topologyMeta = null;
753
+ const additionalSkills = [];
754
+ for (const file of files) {
755
+ const match = file.path.match(/(?:^|[\\/])skills[\\/]([^\\/]+)[\\/]SKILL\.md$/);
756
+ if (!match)
757
+ continue;
758
+ const skillResult = parseSkillMd(file.content);
759
+ if (skillResult.isTopology && !topologyMeta) {
760
+ topologyMeta = skillResult;
761
+ }
762
+ else {
763
+ // Non-topology skill
764
+ const { fields } = parseFrontmatter(file.content);
765
+ additionalSkills.push({
766
+ id: match[1],
767
+ description: fields.description ?? "",
768
+ disableModelInvocation: fields["disable-model-invocation"],
769
+ userInvocable: fields["user-invocable"],
770
+ context: fields.context,
771
+ agent: fields.agent,
772
+ allowedTools: fields["allowed-tools"],
773
+ });
774
+ }
775
+ }
776
+ // 3. Parse settings.json
777
+ let settings = {};
778
+ let hooks = [];
779
+ const settingsFile = files.find((f) => f.path.endsWith("settings.json") && f.path.includes(".claude"));
780
+ if (settingsFile) {
781
+ const parsed = parseSettingsJson(settingsFile.content);
782
+ settings = parsed.settings;
783
+ hooks = parsed.hooks;
784
+ }
785
+ // 4. Parse .mcp.json
786
+ let mcpServers = {};
787
+ const mcpFile = files.find((f) => f.path.endsWith(".mcp.json"));
788
+ if (mcpFile) {
789
+ mcpServers = parseMcpJson(mcpFile.content);
790
+ }
791
+ // 5. Parse command files for triggers (only if no SKILL.md triggers)
792
+ let triggers = topologyMeta?.triggers ?? [];
793
+ if (triggers.length === 0) {
794
+ for (const file of files) {
795
+ const match = file.path.match(/(?:^|[\\/])commands[\\/](.+\.md)$/);
796
+ if (!match)
797
+ continue;
798
+ const trigger = parseCommandMd(match[1], file.content);
799
+ if (trigger)
800
+ triggers.push(trigger);
801
+ }
802
+ }
803
+ // 6. Parse action scripts
804
+ for (const file of files) {
805
+ const match = file.path.match(/(?:^|[\\/])scripts[\\/](action-.+\.sh)$/);
806
+ if (!match)
807
+ continue;
808
+ const action = parseActionScript(match[1], file.content);
809
+ if (action) {
810
+ // Don't add if already in nodes (from SKILL.md orchestrator handles)
811
+ if (!nodes.some((n) => n.id === action.id)) {
812
+ nodes.push(action);
813
+ }
814
+ }
815
+ }
816
+ // 7. Build orchestrator
817
+ if (topologyMeta?.orchestrator) {
818
+ nodes.unshift(topologyMeta.orchestrator);
819
+ }
820
+ // 8. Add gates from SKILL.md
821
+ if (topologyMeta?.gates) {
822
+ nodes.push(...topologyMeta.gates);
823
+ }
824
+ // 9. Build edges — from SKILL.md or generate linear flow
825
+ let edges = topologyMeta?.edges ?? [];
826
+ if (edges.length === 0) {
827
+ // Generate linear flow from agent ordering
828
+ const agentNodes = nodes.filter((n) => n.type === "agent");
829
+ for (let i = 0; i < agentNodes.length - 1; i++) {
830
+ edges.push({
831
+ from: agentNodes[i].id,
832
+ to: agentNodes[i + 1].id,
833
+ condition: null,
834
+ maxIterations: null,
835
+ });
836
+ }
837
+ }
838
+ // 10. Add action nodes for orchestrator handles
839
+ if (topologyMeta?.orchestrator) {
840
+ for (const handleId of topologyMeta.orchestrator.handles) {
841
+ if (!nodes.some((n) => n.id === handleId)) {
842
+ nodes.push({
843
+ type: "action",
844
+ id: handleId,
845
+ label: handleId,
846
+ kind: "inline",
847
+ });
848
+ }
849
+ }
850
+ }
851
+ // Build topology meta
852
+ const meta = {
853
+ name: topologyMeta?.name ?? topologyName,
854
+ version: topologyMeta?.version ?? "1.0.0",
855
+ description: topologyMeta?.description ?? "",
856
+ patterns: topologyMeta?.patterns ?? [],
857
+ foundations: topologyMeta?.foundations,
858
+ advanced: topologyMeta?.advanced,
859
+ domain: topologyMeta?.domain,
860
+ timeout: topologyMeta?.timeout,
861
+ errorHandler: topologyMeta?.errorHandler,
862
+ durable: topologyMeta?.durable,
863
+ };
864
+ const ast = {
865
+ topology: meta,
866
+ nodes,
867
+ edges,
868
+ depth: { factors: [], levels: [] },
869
+ memory: {},
870
+ batch: {},
871
+ environments: {},
872
+ triggers,
873
+ hooks,
874
+ settings,
875
+ mcpServers,
876
+ metering: null,
877
+ skills: additionalSkills,
878
+ toolDefs: [],
879
+ roles,
880
+ context: {},
881
+ env: {},
882
+ providers: [],
883
+ schedules: [],
884
+ interfaces: [],
885
+ defaults: null,
886
+ schemas: [],
887
+ observability: null,
888
+ params: [],
889
+ interfaceEndpoints: null,
890
+ imports: [],
891
+ includes: [],
892
+ checkpoint: null,
893
+ artifacts: [],
894
+ };
895
+ return ast;
896
+ }
897
+ //# sourceMappingURL=claude-code.js.map