@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.
- package/dist/{accept-orchestration-CWZNCGZX.js → accept-orchestration-DIGPJVUR.js} +6 -5
- package/dist/{aggregate-W7Q6VIM2.js → aggregate-V4KPR3RW.js} +2 -2
- package/dist/{beacon-B47XSTL7.js → beacon-XRXL5KZB.js} +2 -2
- package/dist/{chunk-4LGLU2LO.js → chunk-2E2RTBSM.js} +533 -182
- package/dist/{chunk-YCLN7WXV.js → chunk-2QNZ6PVD.js} +219 -35
- package/dist/{chunk-UM54F7G5.js → chunk-4N6AYEEA.js} +1 -1
- package/dist/{chunk-MVXJVRFI.js → chunk-5TUAVVIG.js} +65 -1
- package/dist/{chunk-5C4SGQKH.js → chunk-6P4IFIK2.js} +4 -2
- package/dist/{chunk-WS5KM7OL.js → chunk-6RNYVBSG.js} +1 -1
- package/dist/{chunk-N6PJAPDE.js → chunk-AK5M6KJB.js} +18 -0
- package/dist/{chunk-VZ7CXFRZ.js → chunk-CRICL4FQ.js} +1004 -17
- package/dist/{chunk-MC7XC7XQ.js → chunk-GZDFVP2N.js} +20 -13
- package/dist/chunk-HPC3JAUP.js +42 -0
- package/dist/chunk-IRVA7NKV.js +657 -0
- package/dist/{chunk-ZPN7MXRA.js → chunk-KFHK6EBI.js} +184 -1
- package/dist/{chunk-UUZ2DMG5.js → chunk-KWDTBXP2.js} +1 -1
- package/dist/{chunk-DRUDZKIT.js → chunk-M2XMTJHQ.js} +693 -70
- package/dist/{chunk-PW2EXJQT.js → chunk-MRENOFTR.js} +24 -1
- package/dist/{chunk-QS36NGWV.js → chunk-QHJGB5TV.js} +1 -1
- package/dist/chunk-UI3XXVJ6.js +449 -0
- package/dist/{chunk-AD2LSCHB.js → chunk-Y4XZWCHK.js} +40 -74
- package/dist/{constellation-K3CIQCHI.js → constellation-GNK5DIMH.js} +2 -2
- package/dist/{cost-AEK6R7HK.js → cost-AGO5N7DD.js} +1 -1
- package/dist/{cursorrules-KI5QWHIX.js → cursorrules-LQFA7M62.js} +2 -2
- package/dist/{delete-W67IVTLJ.js → delete-3YXAJ5AA.js} +12 -1
- package/dist/{diff-AJJ5H6HV.js → diff-J6C5IHPV.js} +6 -5
- package/dist/{dist-2F7NO4H4-KSL6SJIO.js → dist-AG5JNIZU-XSEZ2LLK.js} +28 -3
- package/dist/dist-JOHRYQUA.js +7294 -0
- package/dist/{dist-NHJQVVUW.js → dist-Q6SAZI7X.js} +2 -2
- package/dist/{dist-GPQ4LAY3.js → dist-YP2CO4TG.js} +24 -6
- package/dist/{doctor-JBIV5PMN.js → doctor-TQYRF7KK.js} +2 -2
- package/dist/{edit-Y7XPYSMK.js → edit-EOMPXOG5.js} +1 -1
- package/dist/flow-7JUH6D4H.js +185 -0
- package/dist/global-AXILUM5X.js +136 -0
- package/dist/{habits-FA65W77Y.js → habits-CHP4EW5H.js} +234 -5
- package/dist/{hooks-JKWO44WH.js → hooks-DLZEYHI3.js} +1 -1
- package/dist/index.js +125 -100
- package/dist/{lint-HXKTWRNO.js → lint-N4LMMEXH.js} +141 -1
- package/dist/{list-R3QWW4SC.js → list-JKBJ7ESH.js} +1 -1
- package/dist/mcp.js +9273 -6515
- package/dist/{orchestrate-4ZH5GUQH.js → orchestrate-FAV64G2R.js} +6 -5
- package/dist/{probe-OYCP4JYG.js → probe-X3J2JX62.js} +18 -3
- package/dist/{promote-E6NBZ3BK.js → promote-HZH5E5CO.js} +1 -1
- package/dist/{providers-4PGPZEWP.js → providers-NQ67LO2Z.js} +1 -1
- package/dist/{record-OHQNWOUP.js → record-EECZ3E4I.js} +1 -1
- package/dist/{remember-6VZ74B7E.js → remember-3KJZGDUG.js} +1 -1
- package/dist/{review-RUHX25A5.js → review-BF26ILZB.js} +1 -1
- package/dist/{ripple-SBQOSTZD.js → ripple-JIUAMBLA.js} +2 -2
- package/dist/sentinel-ZTL224IG.js +63 -0
- package/dist/{server-MV4HNFVF.js → server-MZBYDXJY.js} +4193 -9
- package/dist/{setup-DF4F3ICN.js → setup-363IB6MO.js} +1 -1
- package/dist/{setup-JHBPZAG7.js → setup-UKJ3VGHI.js} +4 -4
- package/dist/{shift-2LQFQP4P.js → shift-KDVYB6CR.js} +16 -13
- package/dist/{show-WTOJXUTN.js → show-SAMTXEHG.js} +1 -1
- package/dist/{snapshot-GTVPRYZG.js → snapshot-KCMONZAO.js} +2 -2
- package/dist/{spawn-BJRQA2NR.js → spawn-EO7B2UM3.js} +2 -2
- package/dist/{summary-5SBFO7QK.js → summary-E2PU4UN2.js} +3 -3
- package/dist/{switch-6EANJ7O6.js → switch-CC2KACXO.js} +1 -1
- package/dist/{sync-5KSTPJ4B.js → sync-5VJPZQNX.js} +2 -2
- package/dist/sync-llms-7QDA3ZWC.js +166 -0
- package/dist/{team-NWP2KJAB.js → team-6CCNANKE.js} +7 -6
- package/dist/{test-MA5TWJQV.js → test-DK2RWLTK.js} +91 -8
- package/dist/{thread-JCJVRUQR.js → thread-RNSLADXN.js} +18 -2
- package/dist/{timeline-P7BARFLI.js → timeline-TJDVVVA3.js} +1 -1
- package/dist/{triage-TBIWJA6R.js → triage-PXMU3RWV.js} +2 -2
- package/dist/university-content/courses/para-101.json +2 -1
- package/dist/university-content/courses/para-201.json +102 -3
- package/dist/university-content/courses/para-301.json +14 -11
- package/dist/university-content/courses/para-401.json +57 -3
- package/dist/university-content/courses/para-501.json +204 -6
- package/dist/university-content/plsat/v3.0.json +808 -3
- package/dist/university-content/reference.json +270 -0
- package/dist/{upgrade-TIYFQYPO.js → upgrade-RBSE4M6I.js} +1 -1
- package/dist/{validate-QEEY6KFS.js → validate-2LTHHORX.js} +1 -1
- package/dist/{watch-4LT4O6K7.js → watch-NBPOMOEX.js} +76 -0
- package/dist/{watch-2XEYUH43.js → watch-PAEH6MOG.js} +1 -1
- package/package.json +1 -1
- package/dist/chunk-GWM2WRXL.js +0 -1095
- package/dist/sentinel-WB7GIK4V.js +0 -43
- /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(
|
|
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) {
|
|
@@ -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
|
+
};
|