@a-company/paradigm 3.1.5 → 3.5.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 (80) hide show
  1. package/dist/{accept-orchestration-CWZNCGZX.js → accept-orchestration-DIGPJVUR.js} +6 -5
  2. package/dist/{aggregate-W7Q6VIM2.js → aggregate-V4KPR3RW.js} +2 -2
  3. package/dist/{beacon-B47XSTL7.js → beacon-XRXL5KZB.js} +2 -2
  4. package/dist/{chunk-4LGLU2LO.js → chunk-2E2RTBSM.js} +533 -182
  5. package/dist/{chunk-YCLN7WXV.js → chunk-2QNZ6PVD.js} +219 -35
  6. package/dist/{chunk-UM54F7G5.js → chunk-4N6AYEEA.js} +1 -1
  7. package/dist/{chunk-MVXJVRFI.js → chunk-5TUAVVIG.js} +65 -1
  8. package/dist/{chunk-5C4SGQKH.js → chunk-6P4IFIK2.js} +4 -2
  9. package/dist/{chunk-WS5KM7OL.js → chunk-6RNYVBSG.js} +1 -1
  10. package/dist/{chunk-N6PJAPDE.js → chunk-AK5M6KJB.js} +18 -0
  11. package/dist/{chunk-VZ7CXFRZ.js → chunk-CRICL4FQ.js} +1004 -17
  12. package/dist/{chunk-MC7XC7XQ.js → chunk-GZDFVP2N.js} +20 -13
  13. package/dist/chunk-HPC3JAUP.js +42 -0
  14. package/dist/chunk-IRVA7NKV.js +657 -0
  15. package/dist/{chunk-ZPN7MXRA.js → chunk-KFHK6EBI.js} +184 -1
  16. package/dist/{chunk-UUZ2DMG5.js → chunk-KWDTBXP2.js} +1 -1
  17. package/dist/{chunk-DRUDZKIT.js → chunk-M2XMTJHQ.js} +693 -70
  18. package/dist/{chunk-PW2EXJQT.js → chunk-MRENOFTR.js} +24 -1
  19. package/dist/{chunk-QS36NGWV.js → chunk-QHJGB5TV.js} +1 -1
  20. package/dist/chunk-UI3XXVJ6.js +449 -0
  21. package/dist/{chunk-AD2LSCHB.js → chunk-Y4XZWCHK.js} +40 -74
  22. package/dist/{constellation-K3CIQCHI.js → constellation-GNK5DIMH.js} +2 -2
  23. package/dist/{cost-AEK6R7HK.js → cost-AGO5N7DD.js} +1 -1
  24. package/dist/{cursorrules-KI5QWHIX.js → cursorrules-LQFA7M62.js} +2 -2
  25. package/dist/{delete-W67IVTLJ.js → delete-3YXAJ5AA.js} +12 -1
  26. package/dist/{diff-AJJ5H6HV.js → diff-J6C5IHPV.js} +6 -5
  27. package/dist/{dist-2F7NO4H4-KSL6SJIO.js → dist-AG5JNIZU-XSEZ2LLK.js} +28 -3
  28. package/dist/dist-JOHRYQUA.js +7294 -0
  29. package/dist/{dist-NHJQVVUW.js → dist-Q6SAZI7X.js} +2 -2
  30. package/dist/{dist-GPQ4LAY3.js → dist-YP2CO4TG.js} +24 -6
  31. package/dist/{doctor-JBIV5PMN.js → doctor-TQYRF7KK.js} +2 -2
  32. package/dist/{edit-Y7XPYSMK.js → edit-EOMPXOG5.js} +1 -1
  33. package/dist/flow-7JUH6D4H.js +185 -0
  34. package/dist/global-AXILUM5X.js +136 -0
  35. package/dist/{habits-FA65W77Y.js → habits-CHP4EW5H.js} +234 -5
  36. package/dist/{hooks-JKWO44WH.js → hooks-DLZEYHI3.js} +1 -1
  37. package/dist/index.js +125 -100
  38. package/dist/{lint-HXKTWRNO.js → lint-N4LMMEXH.js} +141 -1
  39. package/dist/{list-R3QWW4SC.js → list-JKBJ7ESH.js} +1 -1
  40. package/dist/mcp.js +9273 -6515
  41. package/dist/{orchestrate-4ZH5GUQH.js → orchestrate-FAV64G2R.js} +6 -5
  42. package/dist/{probe-OYCP4JYG.js → probe-X3J2JX62.js} +18 -3
  43. package/dist/{promote-E6NBZ3BK.js → promote-HZH5E5CO.js} +1 -1
  44. package/dist/{providers-4PGPZEWP.js → providers-NQ67LO2Z.js} +1 -1
  45. package/dist/{record-OHQNWOUP.js → record-EECZ3E4I.js} +1 -1
  46. package/dist/{remember-6VZ74B7E.js → remember-3KJZGDUG.js} +1 -1
  47. package/dist/{review-RUHX25A5.js → review-BF26ILZB.js} +1 -1
  48. package/dist/{ripple-SBQOSTZD.js → ripple-JIUAMBLA.js} +2 -2
  49. package/dist/sentinel-ZTL224IG.js +63 -0
  50. package/dist/{server-MV4HNFVF.js → server-MZBYDXJY.js} +4193 -9
  51. package/dist/{setup-DF4F3ICN.js → setup-363IB6MO.js} +1 -1
  52. package/dist/{setup-JHBPZAG7.js → setup-UKJ3VGHI.js} +4 -4
  53. package/dist/{shift-2LQFQP4P.js → shift-KDVYB6CR.js} +16 -13
  54. package/dist/{show-WTOJXUTN.js → show-SAMTXEHG.js} +1 -1
  55. package/dist/{snapshot-GTVPRYZG.js → snapshot-KCMONZAO.js} +2 -2
  56. package/dist/{spawn-BJRQA2NR.js → spawn-EO7B2UM3.js} +2 -2
  57. package/dist/{summary-5SBFO7QK.js → summary-E2PU4UN2.js} +3 -3
  58. package/dist/{switch-6EANJ7O6.js → switch-CC2KACXO.js} +1 -1
  59. package/dist/{sync-5KSTPJ4B.js → sync-5VJPZQNX.js} +2 -2
  60. package/dist/sync-llms-7QDA3ZWC.js +166 -0
  61. package/dist/{team-NWP2KJAB.js → team-6CCNANKE.js} +7 -6
  62. package/dist/{test-MA5TWJQV.js → test-DK2RWLTK.js} +91 -8
  63. package/dist/{thread-JCJVRUQR.js → thread-RNSLADXN.js} +18 -2
  64. package/dist/{timeline-P7BARFLI.js → timeline-TJDVVVA3.js} +1 -1
  65. package/dist/{triage-TBIWJA6R.js → triage-PXMU3RWV.js} +2 -2
  66. package/dist/university-content/courses/para-101.json +2 -1
  67. package/dist/university-content/courses/para-201.json +102 -3
  68. package/dist/university-content/courses/para-301.json +14 -11
  69. package/dist/university-content/courses/para-401.json +57 -3
  70. package/dist/university-content/courses/para-501.json +204 -6
  71. package/dist/university-content/plsat/v3.0.json +808 -3
  72. package/dist/university-content/reference.json +270 -0
  73. package/dist/{upgrade-TIYFQYPO.js → upgrade-RBSE4M6I.js} +1 -1
  74. package/dist/{validate-QEEY6KFS.js → validate-2LTHHORX.js} +1 -1
  75. package/dist/{watch-4LT4O6K7.js → watch-NBPOMOEX.js} +76 -0
  76. package/dist/{watch-2XEYUH43.js → watch-PAEH6MOG.js} +1 -1
  77. package/package.json +1 -1
  78. package/dist/chunk-GWM2WRXL.js +0 -1095
  79. package/dist/sentinel-WB7GIK4V.js +0 -43
  80. /package/dist/{chunk-TAP5N3HH.js → chunk-CCG6KYBT.js} +0 -0
@@ -136,9 +136,10 @@ function parsePurposeFileDetailed(filePath) {
136
136
  detailedErrors.push({ message: error, type: "file" });
137
137
  return { data: null, errors, detailedErrors, rawContent: void 0, isYamlValid: false };
138
138
  }
139
+ const processedContent = rawContent.replace(/^([#~!$^][\w-]+):/gm, '"$1":').replace(/^(\s*-\s+)([!#][\w-]+)$/gm, '$1"$2"');
139
140
  let data = null;
140
141
  try {
141
- data = yaml.load(rawContent);
142
+ data = yaml.load(processedContent);
142
143
  } catch (e) {
143
144
  const yamlError = e;
144
145
  const line = yamlError.mark?.line ? yamlError.mark.line + 1 : void 0;
@@ -160,6 +161,28 @@ function parsePurposeFileDetailed(filePath) {
160
161
  isYamlValid: true
161
162
  };
162
163
  }
164
+ if (typeof data === "object" && data !== null) {
165
+ const obj = data;
166
+ const prefixMap = {
167
+ "#": "components",
168
+ "$": "flows",
169
+ "^": "gates",
170
+ "!": "signals",
171
+ "~": "aspects"
172
+ };
173
+ for (const key of Object.keys(obj)) {
174
+ const prefix = key[0];
175
+ const target = prefixMap[prefix];
176
+ if (!target || key.length < 2) continue;
177
+ const id = key.slice(1);
178
+ const value = obj[key];
179
+ if (typeof value !== "object" || value === null) continue;
180
+ const dict = obj[target] || {};
181
+ if (!(target in obj)) obj[target] = dict;
182
+ if (!(id in dict)) dict[id] = value;
183
+ delete obj[key];
184
+ }
185
+ }
163
186
  const parseResult = PurposeFileSchema.safeParse(data);
164
187
  if (!parseResult.success) {
165
188
  for (const issue of parseResult.error.issues) {
@@ -8,7 +8,7 @@ import {
8
8
  syncToIDE,
9
9
  writeMcpConfig,
10
10
  writeNestedContexts
11
- } from "./chunk-YCLN7WXV.js";
11
+ } from "./chunk-2QNZ6PVD.js";
12
12
  import {
13
13
  log
14
14
  } from "./chunk-4NCFWYGG.js";
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ generateScanIndex,
4
+ serializeScanIndex
5
+ } from "./chunk-AK5M6KJB.js";
6
+ import {
7
+ aggregateFromDirectory
8
+ } from "./chunk-6P4IFIK2.js";
9
+
10
+ // src/commands/scan/navigator.ts
11
+ import * as fs from "fs";
12
+ import * as path from "path";
13
+ import * as yaml from "js-yaml";
14
+ import chalk from "chalk";
15
+ import ora from "ora";
16
+ var SYMBOL_CATEGORIES = {
17
+ "@": { category: "features", prefix: "@" },
18
+ "#": { category: "components", prefix: "#" },
19
+ "^": { category: "gates", prefix: "^" },
20
+ "$": { category: "flows", prefix: "$" },
21
+ "&": { category: "integrations", prefix: "&" },
22
+ "!": { category: "signals", prefix: "!" },
23
+ "%": { category: "state", prefix: "%" }
24
+ };
25
+ var DIRECTORY_PATTERNS = {
26
+ features: ["src/features/", "features/", "app/", "src/app/", "src/modules/", "modules/"],
27
+ components: ["src/components/", "components/", "src/lib/", "lib/", "src/ui/", "ui/"],
28
+ gates: ["middleware/", "src/middleware/", "auth/", "src/auth/", "guards/", "src/guards/"],
29
+ flows: ["flows/", "src/flows/", "workflows/", "src/workflows/", "sagas/", "src/sagas/"],
30
+ integrations: ["integrations/", "src/integrations/", "external/", "src/external/", "vendors/"],
31
+ signals: ["events/", "src/events/", "handlers/", "src/handlers/"],
32
+ state: ["stores/", "src/stores/", "state/", "src/state/", "reducers/", "src/reducers/"]
33
+ };
34
+ var KEY_FILE_PATTERNS = {
35
+ config: [
36
+ ".paradigm/config.yaml",
37
+ "package.json",
38
+ "tsconfig.json",
39
+ ".env.example"
40
+ ],
41
+ entry: [
42
+ "src/index.ts",
43
+ "src/index.tsx",
44
+ "src/main.ts",
45
+ "src/main.tsx",
46
+ "index.ts",
47
+ "main.ts",
48
+ "src/app.ts",
49
+ "src/app.tsx"
50
+ ],
51
+ types: [
52
+ "src/types/",
53
+ "types/",
54
+ "src/types.ts",
55
+ "types.ts"
56
+ ]
57
+ };
58
+ var DEFAULT_SKIP_PATTERNS = {
59
+ always: [
60
+ "node_modules/",
61
+ "dist/",
62
+ "build/",
63
+ ".git/",
64
+ ".next/",
65
+ ".nuxt/",
66
+ ".cache/",
67
+ "*.lock",
68
+ "*.log"
69
+ ],
70
+ unless_testing: [
71
+ "**/*.test.ts",
72
+ "**/*.test.tsx",
73
+ "**/*.spec.ts",
74
+ "**/*.spec.tsx",
75
+ "__tests__/",
76
+ "test/",
77
+ "tests/"
78
+ ],
79
+ unless_docs: [
80
+ "docs/",
81
+ "*.md",
82
+ "README*",
83
+ "CHANGELOG*"
84
+ ]
85
+ };
86
+ async function generateNavigator(rootDir, aggregation, options = {}) {
87
+ const spinner = options.quiet ? null : ora();
88
+ spinner?.start("Generating navigator.yaml...");
89
+ const structure = buildStructure(rootDir);
90
+ const keyFiles = buildKeyFiles(rootDir);
91
+ const skipPatterns = buildSkipPatterns(rootDir);
92
+ const symbols = buildSymbolMap(aggregation.symbols, aggregation.purposeFiles, rootDir);
93
+ const navigatorConfig = {
94
+ version: "1.0",
95
+ generated: (/* @__PURE__ */ new Date()).toISOString(),
96
+ structure,
97
+ key_files: keyFiles,
98
+ skip_patterns: skipPatterns,
99
+ symbols
100
+ };
101
+ const outputDir = path.join(rootDir, ".paradigm");
102
+ const outputPath = path.join(outputDir, "navigator.yaml");
103
+ if (!fs.existsSync(outputDir)) {
104
+ fs.mkdirSync(outputDir, { recursive: true });
105
+ }
106
+ fs.writeFileSync(
107
+ outputPath,
108
+ yaml.dump(navigatorConfig, {
109
+ indent: 2,
110
+ lineWidth: 120,
111
+ noRefs: true,
112
+ sortKeys: false
113
+ }),
114
+ "utf8"
115
+ );
116
+ spinner?.succeed(chalk.green("Navigator generated"));
117
+ if (!options.quiet) {
118
+ console.log(chalk.gray(` Output: ${outputPath}`));
119
+ console.log(chalk.gray(` Structure categories: ${Object.keys(structure).length}`));
120
+ console.log(chalk.gray(` Symbol mappings: ${Object.keys(symbols).length}`));
121
+ console.log();
122
+ }
123
+ }
124
+ function buildStructure(rootDir) {
125
+ const structure = {};
126
+ for (const [category, patterns] of Object.entries(DIRECTORY_PATTERNS)) {
127
+ const existingPaths = patterns.filter((p) => {
128
+ const fullPath = path.join(rootDir, p);
129
+ return fs.existsSync(fullPath);
130
+ });
131
+ if (existingPaths.length > 0) {
132
+ const symbolInfo = Object.values(SYMBOL_CATEGORIES).find(
133
+ (s) => s.category === category
134
+ );
135
+ structure[category] = {
136
+ paths: existingPaths,
137
+ symbol: symbolInfo?.prefix || "@"
138
+ };
139
+ }
140
+ }
141
+ return structure;
142
+ }
143
+ function buildKeyFiles(rootDir) {
144
+ const keyFiles = {};
145
+ for (const [category, patterns] of Object.entries(KEY_FILE_PATTERNS)) {
146
+ const existingPaths = patterns.filter((p) => {
147
+ const fullPath = path.join(rootDir, p);
148
+ return fs.existsSync(fullPath);
149
+ });
150
+ if (existingPaths.length > 0) {
151
+ keyFiles[category] = existingPaths;
152
+ }
153
+ }
154
+ if (!keyFiles.config) keyFiles.config = [];
155
+ if (!keyFiles.entry) keyFiles.entry = [];
156
+ if (!keyFiles.types) keyFiles.types = [];
157
+ return keyFiles;
158
+ }
159
+ function buildSkipPatterns(rootDir) {
160
+ const patterns = { ...DEFAULT_SKIP_PATTERNS };
161
+ const gitignorePath = path.join(rootDir, ".gitignore");
162
+ if (fs.existsSync(gitignorePath)) {
163
+ try {
164
+ const content = fs.readFileSync(gitignorePath, "utf8");
165
+ const gitignorePatterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).filter((line) => {
166
+ return line.endsWith("/") || line.includes("*") || ["node_modules", "dist", "build", ".cache"].some(
167
+ (p) => line.includes(p)
168
+ );
169
+ }).slice(0, 20);
170
+ for (const pattern of gitignorePatterns) {
171
+ if (!patterns.always.includes(pattern)) {
172
+ patterns.always.push(pattern);
173
+ }
174
+ }
175
+ } catch {
176
+ }
177
+ }
178
+ return patterns;
179
+ }
180
+ function buildSymbolMap(symbols, purposeFiles, _rootDir) {
181
+ const symbolMap = {};
182
+ const purposeDirs = /* @__PURE__ */ new Map();
183
+ for (const pf of purposeFiles) {
184
+ const dir = path.dirname(pf);
185
+ purposeDirs.set(pf, dir);
186
+ }
187
+ for (const symbol of symbols) {
188
+ const prefix = getSymbolPrefix(symbol.type);
189
+ const symbolId = `${prefix}${symbol.id}`;
190
+ if (symbol.path) {
191
+ symbolMap[symbolId] = symbol.path;
192
+ } else if (symbol.directory) {
193
+ symbolMap[symbolId] = symbol.directory;
194
+ } else {
195
+ const matchingPurpose = purposeFiles.find((pf) => {
196
+ const dir = path.dirname(pf);
197
+ const symbolLower = symbol.id.toLowerCase();
198
+ return dir.toLowerCase().includes(symbolLower);
199
+ });
200
+ if (matchingPurpose) {
201
+ symbolMap[symbolId] = path.dirname(matchingPurpose) + "/";
202
+ }
203
+ }
204
+ }
205
+ return symbolMap;
206
+ }
207
+ function getSymbolPrefix(type) {
208
+ switch (type) {
209
+ case "feature":
210
+ return "@";
211
+ case "component":
212
+ return "#";
213
+ case "gate":
214
+ return "^";
215
+ case "flow":
216
+ return "$";
217
+ case "integration":
218
+ return "&";
219
+ case "signal":
220
+ return "!";
221
+ case "state":
222
+ return "%";
223
+ case "idea":
224
+ return "?";
225
+ case "deprecated":
226
+ return "~";
227
+ default:
228
+ return "@";
229
+ }
230
+ }
231
+
232
+ // src/commands/scan/index.ts
233
+ import * as fs2 from "fs";
234
+ import * as path2 from "path";
235
+ import chalk2 from "chalk";
236
+ import ora2 from "ora";
237
+ import * as yaml3 from "js-yaml";
238
+
239
+ // src/core/legacy-config.ts
240
+ import * as yaml2 from "js-yaml";
241
+ function parseHorizonConfig(content) {
242
+ return yaml2.load(content);
243
+ }
244
+
245
+ // src/commands/scan/index.ts
246
+ async function indexCommand(targetPath, options) {
247
+ const rootDir = targetPath ? path2.resolve(targetPath) : process.cwd();
248
+ const projectName = path2.basename(rootDir);
249
+ const spinner = ora2();
250
+ const paradigmPath = path2.join(rootDir, ".paradigm");
251
+ const paradigmIsFile = fs2.existsSync(paradigmPath) && fs2.statSync(paradigmPath).isFile();
252
+ let outputPath;
253
+ if (options.output) {
254
+ outputPath = path2.resolve(options.output);
255
+ } else if (paradigmIsFile) {
256
+ outputPath = path2.join(rootDir, ".paradigm-scan-index.json");
257
+ } else {
258
+ outputPath = path2.join(rootDir, ".paradigm", "scan-index.json");
259
+ if (!fs2.existsSync(path2.dirname(outputPath))) {
260
+ fs2.mkdirSync(path2.dirname(outputPath), { recursive: true });
261
+ }
262
+ }
263
+ if (!options.quiet) {
264
+ console.log(chalk2.blue("\n\u{1F52D} Generating Paradigm Scan Index\n"));
265
+ }
266
+ let scanConfig;
267
+ const configPaths = [
268
+ path2.join(rootDir, ".paradigm"),
269
+ path2.join(rootDir, ".paradigm", "config.yaml")
270
+ ];
271
+ for (const configPath of configPaths) {
272
+ if (fs2.existsSync(configPath) && fs2.statSync(configPath).isFile()) {
273
+ try {
274
+ const content = fs2.readFileSync(configPath, "utf8");
275
+ const config = parseHorizonConfig(content);
276
+ scanConfig = config.scan;
277
+ break;
278
+ } catch {
279
+ }
280
+ }
281
+ }
282
+ spinner.start("Aggregating symbols from purpose and portal files...");
283
+ let aggregation;
284
+ try {
285
+ aggregation = await aggregateFromDirectory(rootDir);
286
+ } catch (err) {
287
+ spinner.fail(chalk2.red("Failed to aggregate symbols"));
288
+ console.error(chalk2.gray(err.message));
289
+ process.exit(1);
290
+ }
291
+ spinner.succeed(`Found ${aggregation.symbols.length} symbols`);
292
+ if (!options.quiet) {
293
+ const breakdown = {
294
+ components: aggregation.symbols.filter((s) => s.type === "component").length,
295
+ flows: aggregation.symbols.filter((s) => s.type === "flow").length,
296
+ gates: aggregation.symbols.filter((s) => s.type === "gate").length,
297
+ signals: aggregation.symbols.filter((s) => s.type === "signal").length,
298
+ aspects: aggregation.symbols.filter((s) => s.type === "aspect").length
299
+ };
300
+ console.log(chalk2.gray(" Breakdown:"));
301
+ for (const [type, count] of Object.entries(breakdown)) {
302
+ if (count > 0) {
303
+ console.log(chalk2.gray(` ${type}: ${count}`));
304
+ }
305
+ }
306
+ console.log();
307
+ }
308
+ spinner.start("Generating scan index...");
309
+ const index = generateScanIndex(
310
+ {
311
+ symbols: aggregation.symbols,
312
+ purposeFiles: aggregation.purposeFiles,
313
+ portalFiles: aggregation.portalFiles
314
+ },
315
+ {
316
+ projectName,
317
+ visualTagMappings: scanConfig?.visualTagMappings,
318
+ screenDefinitions: scanConfig?.screens
319
+ }
320
+ );
321
+ try {
322
+ fs2.writeFileSync(outputPath, serializeScanIndex(index), "utf8");
323
+ spinner.succeed(chalk2.green("Scan index generated"));
324
+ } catch (err) {
325
+ spinner.fail(chalk2.red("Failed to write scan index"));
326
+ console.error(chalk2.gray(err.message));
327
+ process.exit(1);
328
+ }
329
+ await generateNavigator(rootDir, aggregation, { quiet: options.quiet });
330
+ const flowIndex = await generateFlowIndex(rootDir, aggregation.purposeFiles, { quiet: options.quiet });
331
+ if (flowIndex && Object.keys(flowIndex.flows).length > 0) {
332
+ const flowIndexPath = path2.join(rootDir, ".paradigm", "flow-index.json");
333
+ fs2.writeFileSync(flowIndexPath, JSON.stringify(flowIndex, null, 2), "utf8");
334
+ if (!options.quiet) {
335
+ spinner.succeed(chalk2.green(`Flow index generated (${Object.keys(flowIndex.flows).length} flows)`));
336
+ }
337
+ }
338
+ if (!options.quiet) {
339
+ console.log(chalk2.gray(`
340
+ Output: ${outputPath}`));
341
+ console.log(chalk2.gray(` Components: ${Object.keys(index.components).length}`));
342
+ console.log(chalk2.gray(` Features: ${Object.keys(index.features).length}`));
343
+ console.log(chalk2.gray(` Flows: ${Object.keys(index.flows).length}`));
344
+ console.log(chalk2.gray(` State: ${Object.keys(index.state).length}`));
345
+ console.log(chalk2.gray(` Gates: ${Object.keys(index.gates).length}`));
346
+ console.log(chalk2.gray(` Signals: ${Object.keys(index.signals).length}`));
347
+ console.log();
348
+ console.log(chalk2.blue('\u2728 Scan index ready for "paradigm probe" queries'));
349
+ console.log(chalk2.gray(' Attach an image and say "paradigm probe" to map UI to code\n'));
350
+ }
351
+ return index;
352
+ }
353
+ async function generateFlowIndex(rootDir, purposeFiles, options) {
354
+ const flows = {};
355
+ const symbolToFlows = {};
356
+ for (const filePath of purposeFiles) {
357
+ try {
358
+ const content = fs2.readFileSync(filePath, "utf8");
359
+ const data = yaml3.load(content);
360
+ if (!data?.flows) continue;
361
+ if (Array.isArray(data.flows)) {
362
+ for (const flowItem of data.flows) {
363
+ const flow = flowItem;
364
+ if (!flow.name) continue;
365
+ const flowId = `$${flow.name}`;
366
+ const steps = parseFlowSteps(flow.steps);
367
+ if (steps.length > 0) {
368
+ const testableFlow = {
369
+ id: flowId,
370
+ description: flow.description || "",
371
+ steps,
372
+ definedIn: path2.relative(rootDir, filePath)
373
+ };
374
+ flows[flowId] = testableFlow;
375
+ indexFlowSymbols(flowId, steps, symbolToFlows);
376
+ }
377
+ }
378
+ } else {
379
+ for (const [name, flowDef] of Object.entries(data.flows)) {
380
+ const flowId = name.startsWith("$") ? name : `$${name}`;
381
+ const steps = parseFlowSteps(flowDef.steps);
382
+ if (steps.length > 0) {
383
+ const testableFlow = {
384
+ id: flowId,
385
+ description: flowDef.description || "",
386
+ trigger: flowDef.trigger,
387
+ steps,
388
+ validation: flowDef.validation,
389
+ definedIn: path2.relative(rootDir, filePath)
390
+ };
391
+ flows[flowId] = testableFlow;
392
+ indexFlowSymbols(flowId, steps, symbolToFlows);
393
+ }
394
+ }
395
+ }
396
+ } catch (err) {
397
+ if (!options.quiet) {
398
+ console.warn(chalk2.yellow(` Warning: Could not parse flows from ${filePath}: ${err.message}`));
399
+ }
400
+ }
401
+ }
402
+ if (Object.keys(flows).length === 0) {
403
+ return null;
404
+ }
405
+ return {
406
+ version: "1.0",
407
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
408
+ flows,
409
+ symbolToFlows
410
+ };
411
+ }
412
+ function parseFlowSteps(steps) {
413
+ if (!steps || !Array.isArray(steps)) return [];
414
+ const result = [];
415
+ for (let index = 0; index < steps.length; index++) {
416
+ const step = steps[index];
417
+ if (typeof step === "object" && step !== null) {
418
+ const s = step;
419
+ const action = s.action || s.description || s.component || "";
420
+ if (action) {
421
+ result.push({
422
+ id: s.id || `step-${index + 1}`,
423
+ action,
424
+ symbol: s.symbol || s.component,
425
+ expect: s.expect
426
+ });
427
+ }
428
+ }
429
+ }
430
+ return result;
431
+ }
432
+ function indexFlowSymbols(flowId, steps, symbolToFlows) {
433
+ for (const step of steps) {
434
+ if (step.symbol) {
435
+ if (!symbolToFlows[step.symbol]) {
436
+ symbolToFlows[step.symbol] = [];
437
+ }
438
+ if (!symbolToFlows[step.symbol].includes(flowId)) {
439
+ symbolToFlows[step.symbol].push(flowId);
440
+ }
441
+ }
442
+ }
443
+ }
444
+
445
+ export {
446
+ generateNavigator,
447
+ indexCommand,
448
+ generateFlowIndex
449
+ };