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,679 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AgenTopology CLI.
4
+ *
5
+ * Commands:
6
+ * agentopology validate <file.at> — parse and validate
7
+ * agentopology scaffold <file.at> --target <binding> — generate files
8
+ * agentopology scaffold <file.at> --target <binding> --dry-run — preview only
9
+ * agentopology sync <file.at> --target <binding> --dir <path> — sync prompts back
10
+ * agentopology visualize <file.at> — generate HTML visualization
11
+ * agentopology targets — list bindings
12
+ * agentopology docs [topic] — language reference
13
+ * agentopology docs --all — all docs (LLM ingestion)
14
+ * agentopology docs --search <term> — search docs
15
+ *
16
+ * @module
17
+ */
18
+ import * as fs from "node:fs";
19
+ import * as path from "node:path";
20
+ import { execSync } from "node:child_process";
21
+ import { parse } from "../parser/index.js";
22
+ import { validate } from "../parser/validator.js";
23
+ import { bindings } from "../bindings/index.js";
24
+ import { syncFromPlatform } from "../sync/index.js";
25
+ import { generateVisualization } from "../visualizer/index.js";
26
+ import { exporters } from "../exporters/index.js";
27
+ import { analyze } from "../analyzer/index.js";
28
+ import { listTopics, getTopic, getAllTopics, searchTopics } from "../docs/index.js";
29
+ import { importFromPlatform } from "../import/index.js";
30
+ import { readManifest, writeManifest, hashContent } from "../scaffold/manifest.js";
31
+ import { computeIncrementalPlan, executeActions } from "../scaffold/incremental.js";
32
+ // ---------------------------------------------------------------------------
33
+ // ANSI colors (no external deps)
34
+ // ---------------------------------------------------------------------------
35
+ const isColorSupported = process.env.NO_COLOR === undefined && process.stdout.isTTY;
36
+ const c = {
37
+ red: (s) => (isColorSupported ? `\x1b[31m${s}\x1b[0m` : s),
38
+ green: (s) => (isColorSupported ? `\x1b[32m${s}\x1b[0m` : s),
39
+ yellow: (s) => (isColorSupported ? `\x1b[33m${s}\x1b[0m` : s),
40
+ cyan: (s) => (isColorSupported ? `\x1b[36m${s}\x1b[0m` : s),
41
+ bold: (s) => (isColorSupported ? `\x1b[1m${s}\x1b[0m` : s),
42
+ dim: (s) => (isColorSupported ? `\x1b[2m${s}\x1b[0m` : s),
43
+ };
44
+ // ---------------------------------------------------------------------------
45
+ // Helpers
46
+ // ---------------------------------------------------------------------------
47
+ function usage() {
48
+ console.log(`
49
+ ${c.bold("agentopology")} — AgenTopology CLI
50
+
51
+ ${c.bold("Usage:")}
52
+ agentopology validate <file.at>
53
+ agentopology scaffold <file.at> --target <binding> [--dry-run] [--force] [--prune] [--output <dir>]
54
+ agentopology sync <file.at> --target <binding> --dir <path>
55
+ agentopology visualize <file.at> [--output <dir>]
56
+ agentopology export <file.at> --format <markdown|mermaid|json> [--output <dir>]
57
+ agentopology info <file.at>
58
+ agentopology import --target <binding> --dir <path> [--name <topology-name>] [--output <dir>]
59
+ agentopology targets
60
+ agentopology docs [topic]
61
+ agentopology docs --all
62
+ agentopology docs --search <term>
63
+
64
+ ${c.bold("Commands:")}
65
+ validate Parse an .at file and run all 29 validation rules.
66
+ scaffold Generate project files for a target platform.
67
+ sync Sync prompt content from platform files back into .at source.
68
+ visualize Generate an interactive HTML visualization of the topology.
69
+ export Export topology as Markdown documentation or Mermaid diagram.
70
+ info Analyze topology: detect patterns, compute layers, suggest improvements.
71
+ import Reverse-engineer platform files into an .at topology file.
72
+ targets List available binding targets.
73
+ docs Language reference — show documentation for .at syntax and features.
74
+
75
+ ${c.bold("Options:")}
76
+ --target <name> Binding target (e.g. claude-code, codex, gemini-cli, copilot-cli, kiro)
77
+ --format <name> Export format (markdown, mermaid, json).
78
+ --dir <path> Directory to read platform files from (used with sync, import).
79
+ --name <name> Topology name for the generated .at file (used with import).
80
+ --output, -o <dir> Output directory for generated files (scaffold, visualize, export).
81
+ --dry-run Preview generated files without writing to disk.
82
+ --force Overwrite all files, ignoring manifest and conflicts.
83
+ --prune Delete files that were previously scaffolded but are no longer generated.
84
+ --all Show all documentation topics (for LLM ingestion).
85
+ --search <term> Search across all documentation topics.
86
+ --help, -h Show this help message.
87
+ `);
88
+ }
89
+ function readFile(filePath) {
90
+ const resolved = path.resolve(filePath);
91
+ if (!fs.existsSync(resolved)) {
92
+ console.error(c.red(`Error: file not found: ${resolved}`));
93
+ process.exit(1);
94
+ }
95
+ return fs.readFileSync(resolved, "utf-8");
96
+ }
97
+ /** Recursively create directories and write a file. */
98
+ function writeFile(basePath, relativePath, content) {
99
+ const fullPath = path.join(basePath, relativePath);
100
+ const dir = path.dirname(fullPath);
101
+ fs.mkdirSync(dir, { recursive: true });
102
+ fs.writeFileSync(fullPath, content, "utf-8");
103
+ }
104
+ function parseArgs(argv) {
105
+ const args = argv.slice(2); // skip node and script path
106
+ const result = {
107
+ command: undefined,
108
+ file: undefined,
109
+ target: undefined,
110
+ format: undefined,
111
+ dir: undefined,
112
+ output: undefined,
113
+ name: undefined,
114
+ dryRun: false,
115
+ force: false,
116
+ prune: false,
117
+ all: false,
118
+ search: undefined,
119
+ help: false,
120
+ };
121
+ let i = 0;
122
+ while (i < args.length) {
123
+ const arg = args[i];
124
+ if (arg === "--help" || arg === "-h") {
125
+ result.help = true;
126
+ i++;
127
+ continue;
128
+ }
129
+ if (arg === "--dry-run") {
130
+ result.dryRun = true;
131
+ i++;
132
+ continue;
133
+ }
134
+ if (arg === "--force") {
135
+ result.force = true;
136
+ i++;
137
+ continue;
138
+ }
139
+ if (arg === "--prune") {
140
+ result.prune = true;
141
+ i++;
142
+ continue;
143
+ }
144
+ if (arg === "--all") {
145
+ result.all = true;
146
+ i++;
147
+ continue;
148
+ }
149
+ if (arg === "--search" && i + 1 < args.length) {
150
+ result.search = args[i + 1];
151
+ i += 2;
152
+ continue;
153
+ }
154
+ if (arg === "--target" && i + 1 < args.length) {
155
+ result.target = args[i + 1];
156
+ i += 2;
157
+ continue;
158
+ }
159
+ if (arg === "--format" && i + 1 < args.length) {
160
+ result.format = args[i + 1];
161
+ i += 2;
162
+ continue;
163
+ }
164
+ if (arg === "--dir" && i + 1 < args.length) {
165
+ result.dir = args[i + 1];
166
+ i += 2;
167
+ continue;
168
+ }
169
+ if ((arg === "--output" || arg === "-o") && i + 1 < args.length) {
170
+ result.output = args[i + 1];
171
+ i += 2;
172
+ continue;
173
+ }
174
+ if (arg === "--name" && i + 1 < args.length) {
175
+ result.name = args[i + 1];
176
+ i += 2;
177
+ continue;
178
+ }
179
+ // Positional arguments
180
+ if (!result.command) {
181
+ result.command = arg;
182
+ }
183
+ else if (!result.file) {
184
+ result.file = arg;
185
+ }
186
+ i++;
187
+ }
188
+ return result;
189
+ }
190
+ // ---------------------------------------------------------------------------
191
+ // Commands
192
+ // ---------------------------------------------------------------------------
193
+ function cmdValidate(filePath) {
194
+ const source = readFile(filePath);
195
+ let ast;
196
+ try {
197
+ ast = parse(source);
198
+ }
199
+ catch (err) {
200
+ console.error(c.red(`Parse error: ${err.message}`));
201
+ process.exit(1);
202
+ }
203
+ console.log(c.bold(`Validating ${path.basename(filePath)} (${ast.topology.name} v${ast.topology.version})`));
204
+ console.log("");
205
+ const results = validate(ast);
206
+ if (results.length === 0) {
207
+ console.log(c.green(" All 29 validation rules passed."));
208
+ console.log("");
209
+ return;
210
+ }
211
+ const errors = results.filter((r) => r.level === "error");
212
+ const warnings = results.filter((r) => r.level === "warning");
213
+ for (const result of results) {
214
+ const prefix = result.level === "error"
215
+ ? c.red(` ERROR [${result.rule}]`)
216
+ : c.yellow(` WARN [${result.rule}]`);
217
+ const linePart = result.line ? ` line ${result.line}:` : "";
218
+ const nodePart = result.node ? c.dim(` (${result.node})`) : "";
219
+ console.log(`${prefix}${linePart} ${result.message}${nodePart}`);
220
+ }
221
+ console.log("");
222
+ console.log(` ${c.red(`${errors.length} error(s)`)}, ${c.yellow(`${warnings.length} warning(s)`)}`);
223
+ console.log("");
224
+ if (errors.length > 0) {
225
+ process.exit(1);
226
+ }
227
+ }
228
+ function cmdScaffold(filePath, targetName, dryRun, outputDir, force, prune) {
229
+ const source = readFile(filePath);
230
+ let ast;
231
+ try {
232
+ ast = parse(source);
233
+ }
234
+ catch (err) {
235
+ console.error(c.red(`Parse error: ${err.message}`));
236
+ process.exit(1);
237
+ }
238
+ const binding = bindings[targetName];
239
+ if (!binding) {
240
+ console.error(c.red(`Unknown target: "${targetName}"`));
241
+ console.error(`Available targets: ${Object.keys(bindings).join(", ")}`);
242
+ process.exit(1);
243
+ }
244
+ console.log(c.bold(`Scaffolding ${ast.topology.name} for ${binding.description}`));
245
+ console.log("");
246
+ const files = binding.scaffold(ast);
247
+ if (files.length === 0) {
248
+ console.log(c.yellow(" No files generated."));
249
+ return;
250
+ }
251
+ const basePath = outputDir ? path.resolve(outputDir) : process.cwd();
252
+ if (dryRun) {
253
+ console.log(c.cyan(" Dry run — files that would be generated:"));
254
+ console.log("");
255
+ const manifest = readManifest(basePath, targetName);
256
+ if (manifest && !force) {
257
+ const actions = computeIncrementalPlan(basePath, targetName, files, manifest);
258
+ for (const action of actions) {
259
+ switch (action.type) {
260
+ case "create":
261
+ console.log(` ${c.green("+")} ${action.path}`);
262
+ break;
263
+ case "update":
264
+ console.log(` ${c.yellow("~")} ${action.path} (${action.detail})`);
265
+ break;
266
+ case "delete":
267
+ console.log(` ${c.red("-")} ${action.path}`);
268
+ break;
269
+ case "unchanged":
270
+ console.log(` ${c.dim("=")} ${action.path}`);
271
+ break;
272
+ case "conflict":
273
+ console.log(` ${c.bold("!")} ${action.path} (${action.detail})`);
274
+ break;
275
+ }
276
+ }
277
+ }
278
+ else {
279
+ for (const file of files) {
280
+ console.log(` ${c.green("+")} ${file.path} (${file.content.length} bytes)`);
281
+ }
282
+ }
283
+ console.log("");
284
+ console.log(` ${files.length} file(s) would be generated.`);
285
+ }
286
+ else {
287
+ const manifest = readManifest(basePath, targetName);
288
+ if (manifest && !force) {
289
+ // INCREMENTAL MODE
290
+ const actions = computeIncrementalPlan(basePath, targetName, files, manifest);
291
+ const result = executeActions(basePath, actions, { prune: !!prune, force: !!force });
292
+ for (const action of actions) {
293
+ switch (action.type) {
294
+ case "create":
295
+ console.log(` ${c.green("+")} ${action.path}`);
296
+ break;
297
+ case "update":
298
+ console.log(` ${c.yellow("~")} ${action.path}`);
299
+ break;
300
+ case "delete":
301
+ if (prune)
302
+ console.log(` ${c.red("-")} ${action.path}`);
303
+ break;
304
+ case "unchanged": break;
305
+ case "conflict":
306
+ console.log(` ${c.bold("!")} ${action.path} (preserved — user edited)`);
307
+ break;
308
+ }
309
+ }
310
+ console.log("");
311
+ console.log(` ${result.created} created, ${result.updated} updated, ${result.unchanged} unchanged, ${result.conflicts} conflicts${prune ? `, ${result.deleted} deleted` : ""}`);
312
+ }
313
+ else {
314
+ // FIRST RUN or --force — write everything
315
+ for (const file of files) {
316
+ writeFile(basePath, file.path, file.content);
317
+ console.log(` ${c.green("+")} ${file.path}`);
318
+ }
319
+ console.log("");
320
+ console.log(` ${c.bold(`${files.length}`)} file(s) written to ${basePath}`);
321
+ }
322
+ // Always write manifest after successful scaffold
323
+ const newManifest = {
324
+ source: path.basename(filePath),
325
+ sourceHash: hashContent(source),
326
+ target: targetName,
327
+ generatedAt: new Date().toISOString(),
328
+ files: {},
329
+ };
330
+ for (const file of files) {
331
+ newManifest.files[file.path] = {
332
+ hash: hashContent(file.content),
333
+ category: file.category || "machine",
334
+ };
335
+ }
336
+ writeManifest(basePath, targetName, newManifest);
337
+ }
338
+ console.log("");
339
+ }
340
+ function readDirRecursive(dirPath) {
341
+ const resolved = path.resolve(dirPath);
342
+ if (!fs.existsSync(resolved)) {
343
+ console.error(c.red(`Error: directory not found: ${resolved}`));
344
+ process.exit(1);
345
+ }
346
+ const files = [];
347
+ function walk(dir, prefix) {
348
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
349
+ const rel = path.join(prefix, entry.name);
350
+ const full = path.join(dir, entry.name);
351
+ if (entry.isDirectory()) {
352
+ walk(full, rel);
353
+ }
354
+ else {
355
+ files.push({ path: rel, content: fs.readFileSync(full, "utf-8") });
356
+ }
357
+ }
358
+ }
359
+ walk(resolved, "");
360
+ return files;
361
+ }
362
+ function cmdSync(filePath, targetName, dirPath) {
363
+ const atSource = readFile(filePath);
364
+ const files = readDirRecursive(dirPath);
365
+ const updated = syncFromPlatform(atSource, files, targetName);
366
+ const resolved = path.resolve(filePath);
367
+ fs.writeFileSync(resolved, updated, "utf-8");
368
+ console.log(c.green(` Updated ${path.basename(filePath)} with prompt blocks from ${targetName} files.`));
369
+ }
370
+ function cmdVisualize(filePath, outputDir) {
371
+ const source = readFile(filePath);
372
+ let ast;
373
+ try {
374
+ ast = parse(source);
375
+ }
376
+ catch (err) {
377
+ console.error(c.red(`Parse error: ${err.message}`));
378
+ process.exit(1);
379
+ }
380
+ const html = generateVisualization(ast);
381
+ const resolved = path.resolve(filePath);
382
+ const stem = path.basename(resolved, ".at");
383
+ const outDir = outputDir ? path.resolve(outputDir) : path.dirname(resolved);
384
+ const outFile = path.join(outDir, `${stem}-topology.html`);
385
+ try {
386
+ fs.mkdirSync(outDir, { recursive: true });
387
+ fs.writeFileSync(outFile, html, "utf-8");
388
+ }
389
+ catch (err) {
390
+ console.error(c.red(`Error: Cannot write "${outFile}"`));
391
+ console.error(err.message);
392
+ process.exit(1);
393
+ }
394
+ console.log(c.green(` Visualization written to ${outFile}`));
395
+ // Try to open in the default browser (non-fatal if it fails).
396
+ const openCmd = process.platform === "darwin" ? "open" : "xdg-open";
397
+ try {
398
+ execSync(`${openCmd} "${outFile}"`, { stdio: "ignore" });
399
+ }
400
+ catch {
401
+ // Non-fatal — the file was written successfully regardless.
402
+ }
403
+ }
404
+ function cmdExport(filePath, formatName, outputDir) {
405
+ const source = readFile(filePath);
406
+ let ast;
407
+ try {
408
+ ast = parse(source);
409
+ }
410
+ catch (err) {
411
+ console.error(c.red(`Parse error: ${err.message}`));
412
+ process.exit(1);
413
+ }
414
+ const exporter = exporters[formatName];
415
+ if (!exporter) {
416
+ console.error(c.red(`Unknown format: "${formatName}"`));
417
+ console.error(`Available formats: ${Object.keys(exporters).join(", ")}`);
418
+ process.exit(1);
419
+ }
420
+ console.log(c.bold(`Exporting ${ast.topology.name} as ${exporter.description}`));
421
+ console.log("");
422
+ const files = exporter.export(ast);
423
+ const resolved = path.resolve(filePath);
424
+ const basePath = outputDir ? path.resolve(outputDir) : path.dirname(resolved);
425
+ for (const file of files) {
426
+ writeFile(basePath, file.path, file.content);
427
+ console.log(` ${c.green("+")} ${file.path}`);
428
+ }
429
+ console.log("");
430
+ console.log(` ${c.bold(`${files.length}`)} file(s) written to ${basePath}`);
431
+ console.log("");
432
+ }
433
+ function cmdInfo(filePath) {
434
+ const source = readFile(filePath);
435
+ let ast;
436
+ try {
437
+ ast = parse(source);
438
+ }
439
+ catch (err) {
440
+ console.error(c.red(`Parse error: ${err.message}`));
441
+ process.exit(1);
442
+ }
443
+ const result = analyze(ast);
444
+ const { summary, patterns, layers, suggestions } = result;
445
+ // Summary
446
+ console.log(c.bold(`Topology: ${summary.name} v${summary.version}`));
447
+ if (summary.description) {
448
+ console.log(c.dim(` ${summary.description}`));
449
+ }
450
+ console.log("");
451
+ const counts = summary.nodeCount;
452
+ const parts = [];
453
+ if (counts.agents)
454
+ parts.push(`${counts.agents} agent${counts.agents !== 1 ? "s" : ""}`);
455
+ if (counts.actions)
456
+ parts.push(`${counts.actions} action${counts.actions !== 1 ? "s" : ""}`);
457
+ if (counts.gates)
458
+ parts.push(`${counts.gates} gate${counts.gates !== 1 ? "s" : ""}`);
459
+ if (counts.groups)
460
+ parts.push(`${counts.groups} group${counts.groups !== 1 ? "s" : ""}`);
461
+ if (counts.humans)
462
+ parts.push(`${counts.humans} human${counts.humans !== 1 ? "s" : ""}`);
463
+ if (counts.orchestrators)
464
+ parts.push(`${counts.orchestrators} orchestrator${counts.orchestrators !== 1 ? "s" : ""}`);
465
+ console.log(` ${parts.join(", ")}`);
466
+ const condEdges = ast.edges.filter((e) => e.condition).length;
467
+ const loopEdges = ast.edges.filter((e) => e.maxIterations).length;
468
+ const edgeParts = [`${summary.edgeCount} edge${summary.edgeCount !== 1 ? "s" : ""}`];
469
+ if (condEdges)
470
+ edgeParts.push(`${condEdges} conditional`);
471
+ if (loopEdges)
472
+ edgeParts.push(`${loopEdges} loop${loopEdges !== 1 ? "s" : ""}`);
473
+ console.log(` ${edgeParts.join(", ")}`);
474
+ if (summary.declaredPatterns.length > 0) {
475
+ console.log(` Declared patterns: ${summary.declaredPatterns.join(", ")}`);
476
+ }
477
+ console.log("");
478
+ // Detected patterns
479
+ if (patterns.length > 0) {
480
+ console.log(c.bold("Detected Patterns:"));
481
+ for (const p of patterns) {
482
+ const conf = p.confidence === "definite" ? "" : c.dim(" (likely)");
483
+ console.log(` ${c.cyan(p.name)}${conf}`);
484
+ console.log(` ${c.dim(p.description)}`);
485
+ }
486
+ console.log("");
487
+ }
488
+ // Layers
489
+ if (layers.length > 0) {
490
+ console.log(c.bold("Layers:"));
491
+ for (const layer of layers) {
492
+ const label = layer.depth === -1 ? "?" : String(layer.depth);
493
+ console.log(` ${c.dim(label + ":")} ${layer.nodes.join(", ")}`);
494
+ }
495
+ console.log("");
496
+ }
497
+ // Suggestions
498
+ if (suggestions.length > 0) {
499
+ console.log(c.bold("Suggestions:"));
500
+ for (const s of suggestions) {
501
+ const prefix = s.level === "improvement" ? c.yellow("[improvement]") : c.dim("[info]");
502
+ const nodePart = s.node ? ` ${c.cyan(s.node)}:` : "";
503
+ console.log(` ${prefix}${nodePart} ${s.message}`);
504
+ }
505
+ console.log("");
506
+ }
507
+ }
508
+ function cmdImport(targetName, dirPath, topologyName, outputDir) {
509
+ const files = readDirRecursive(dirPath);
510
+ const name = topologyName ?? path.basename(path.resolve(dirPath)).replace(/^\./, "");
511
+ console.log(c.bold(`Importing from ${targetName} files in ${dirPath}`));
512
+ console.log("");
513
+ let atSource;
514
+ try {
515
+ atSource = importFromPlatform(files, targetName, name);
516
+ }
517
+ catch (err) {
518
+ console.error(c.red(`Import error: ${err.message}`));
519
+ process.exit(1);
520
+ }
521
+ // Determine output path
522
+ const outDir = outputDir ? path.resolve(outputDir) : process.cwd();
523
+ const outFile = path.join(outDir, `${name}.at`);
524
+ fs.mkdirSync(outDir, { recursive: true });
525
+ fs.writeFileSync(outFile, atSource, "utf-8");
526
+ console.log(c.green(` Written: ${outFile}`));
527
+ // Validate the generated file
528
+ try {
529
+ const ast = parse(atSource);
530
+ const results = validate(ast);
531
+ const errors = results.filter((r) => r.level === "error");
532
+ const warnings = results.filter((r) => r.level === "warning");
533
+ if (errors.length > 0 || warnings.length > 0) {
534
+ console.log("");
535
+ console.log(` ${c.yellow(`${errors.length} error(s), ${warnings.length} warning(s) in generated file.`)}`);
536
+ console.log(` ${c.dim("Run")} agentopology validate ${outFile} ${c.dim("for details.")}`);
537
+ }
538
+ else {
539
+ console.log(` ${c.green("Generated file passes all validation rules.")}`);
540
+ }
541
+ }
542
+ catch {
543
+ // Validation is best-effort; don't fail the import
544
+ console.log(` ${c.yellow("Note: generated file may need manual review.")}`);
545
+ }
546
+ console.log("");
547
+ }
548
+ function cmdTargets() {
549
+ console.log(c.bold("Available binding targets:"));
550
+ console.log("");
551
+ for (const [name, binding] of Object.entries(bindings)) {
552
+ console.log(` ${c.cyan(name.padEnd(16))} ${binding.description}`);
553
+ }
554
+ console.log("");
555
+ }
556
+ // ---------------------------------------------------------------------------
557
+ // Main
558
+ // ---------------------------------------------------------------------------
559
+ function main() {
560
+ const args = parseArgs(process.argv);
561
+ if (args.help || !args.command) {
562
+ usage();
563
+ process.exit(args.help ? 0 : 1);
564
+ }
565
+ switch (args.command) {
566
+ case "validate":
567
+ if (!args.file) {
568
+ console.error(c.red("Error: validate requires a file argument."));
569
+ usage();
570
+ process.exit(1);
571
+ }
572
+ cmdValidate(args.file);
573
+ break;
574
+ case "scaffold":
575
+ if (!args.file) {
576
+ console.error(c.red("Error: scaffold requires a file argument."));
577
+ usage();
578
+ process.exit(1);
579
+ }
580
+ if (!args.target) {
581
+ console.error(c.red("Error: scaffold requires --target <binding>."));
582
+ usage();
583
+ process.exit(1);
584
+ }
585
+ cmdScaffold(args.file, args.target, args.dryRun, args.output || args.dir, args.force, args.prune);
586
+ break;
587
+ case "sync":
588
+ if (!args.file) {
589
+ console.error(c.red("Error: sync requires a file argument."));
590
+ usage();
591
+ process.exit(1);
592
+ }
593
+ if (!args.target) {
594
+ console.error(c.red("Error: sync requires --target <binding>."));
595
+ usage();
596
+ process.exit(1);
597
+ }
598
+ if (!args.dir) {
599
+ console.error(c.red("Error: sync requires --dir <path>."));
600
+ usage();
601
+ process.exit(1);
602
+ }
603
+ cmdSync(args.file, args.target, args.dir);
604
+ break;
605
+ case "visualize":
606
+ if (!args.file) {
607
+ console.error(c.red("Error: visualize requires a file argument."));
608
+ usage();
609
+ process.exit(1);
610
+ }
611
+ cmdVisualize(args.file, args.output);
612
+ break;
613
+ case "export":
614
+ if (!args.file) {
615
+ console.error(c.red("Error: export requires a file argument."));
616
+ usage();
617
+ process.exit(1);
618
+ }
619
+ if (!args.format) {
620
+ console.error(c.red("Error: export requires --format <markdown|mermaid|json>."));
621
+ usage();
622
+ process.exit(1);
623
+ }
624
+ cmdExport(args.file, args.format, args.output);
625
+ break;
626
+ case "info":
627
+ if (!args.file) {
628
+ console.error(c.red("Error: info requires a file argument."));
629
+ usage();
630
+ process.exit(1);
631
+ }
632
+ cmdInfo(args.file);
633
+ break;
634
+ case "import":
635
+ if (!args.target) {
636
+ console.error(c.red("Error: import requires --target <binding>."));
637
+ usage();
638
+ process.exit(1);
639
+ }
640
+ if (!args.dir) {
641
+ console.error(c.red("Error: import requires --dir <path>."));
642
+ usage();
643
+ process.exit(1);
644
+ }
645
+ cmdImport(args.target, args.dir, args.name, args.output);
646
+ break;
647
+ case "targets":
648
+ cmdTargets();
649
+ break;
650
+ case "docs":
651
+ if (args.all) {
652
+ console.log(getAllTopics());
653
+ }
654
+ else if (args.search) {
655
+ console.log(searchTopics(args.search));
656
+ }
657
+ else if (args.file) {
658
+ // args.file is actually the topic name (second positional arg)
659
+ const content = getTopic(args.file);
660
+ if (!content) {
661
+ console.error(c.red(`Unknown topic: "${args.file}"`));
662
+ console.log("");
663
+ console.log(listTopics());
664
+ process.exit(1);
665
+ }
666
+ console.log(content);
667
+ }
668
+ else {
669
+ console.log(listTopics());
670
+ }
671
+ break;
672
+ default:
673
+ console.error(c.red(`Unknown command: "${args.command}"`));
674
+ usage();
675
+ process.exit(1);
676
+ }
677
+ }
678
+ main();
679
+ //# sourceMappingURL=index.js.map