@a-company/paradigm 1.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 (114) hide show
  1. package/README.md +142 -0
  2. package/dist/accept-orchestration-CWZNCGZX.js +188 -0
  3. package/dist/agents-suggest-35LIQKDH.js +83 -0
  4. package/dist/aggregate-W7Q6VIM2.js +88 -0
  5. package/dist/auto-IU7VN55K.js +470 -0
  6. package/dist/beacon-B47XSTL7.js +251 -0
  7. package/dist/chunk-2M6OSOIG.js +1302 -0
  8. package/dist/chunk-4NCFWYGG.js +110 -0
  9. package/dist/chunk-5C4SGQKH.js +705 -0
  10. package/dist/chunk-5GOA7WYD.js +1095 -0
  11. package/dist/chunk-5JGJACDU.js +37 -0
  12. package/dist/chunk-6QC3YGB6.js +114 -0
  13. package/dist/chunk-753RICFF.js +325 -0
  14. package/dist/chunk-AD2LSCHB.js +1595 -0
  15. package/dist/chunk-CHSHON3O.js +669 -0
  16. package/dist/chunk-ELLR7WP6.js +3175 -0
  17. package/dist/chunk-ILOWBJRC.js +12 -0
  18. package/dist/chunk-IRKUEJVW.js +405 -0
  19. package/dist/chunk-MC7XC7XQ.js +533 -0
  20. package/dist/chunk-MO4EEYFW.js +38 -0
  21. package/dist/chunk-MQWH7PFI.js +13366 -0
  22. package/dist/chunk-N6PJAPDE.js +364 -0
  23. package/dist/chunk-PBHIFAL4.js +259 -0
  24. package/dist/chunk-PMXRGPRQ.js +305 -0
  25. package/dist/chunk-PW2EXJQT.js +689 -0
  26. package/dist/chunk-TAP5N3HH.js +245 -0
  27. package/dist/chunk-THFVK5AE.js +148 -0
  28. package/dist/chunk-UM54F7G5.js +1533 -0
  29. package/dist/chunk-UUZ2DMG5.js +185 -0
  30. package/dist/chunk-WS5KM7OL.js +780 -0
  31. package/dist/chunk-YDNKXH4Z.js +2316 -0
  32. package/dist/chunk-YO6DVTL7.js +99 -0
  33. package/dist/claude-SUYNN72C.js +362 -0
  34. package/dist/claude-cli-OF43XAO3.js +276 -0
  35. package/dist/claude-code-PW6SKD2M.js +126 -0
  36. package/dist/claude-code-teams-JLZ5IXB6.js +199 -0
  37. package/dist/constellation-K3CIQCHI.js +225 -0
  38. package/dist/cost-AEK6R7HK.js +174 -0
  39. package/dist/cost-KYXIQ62X.js +93 -0
  40. package/dist/cursor-cli-IHJMPRCW.js +269 -0
  41. package/dist/cursorrules-KI5QWHIX.js +84 -0
  42. package/dist/diff-AJJ5H6HV.js +125 -0
  43. package/dist/dist-7MPIRMTZ-IOQOREMZ.js +10866 -0
  44. package/dist/dist-NHJQVVUW.js +68 -0
  45. package/dist/dist-ZEMSQV74.js +20 -0
  46. package/dist/doctor-6Y6L6HEB.js +11 -0
  47. package/dist/echo-VYZW3OTT.js +248 -0
  48. package/dist/export-R4FJ5NOH.js +38 -0
  49. package/dist/history-EVO3L6SC.js +277 -0
  50. package/dist/hooks-MBWE4ILT.js +12 -0
  51. package/dist/index.d.ts +2 -0
  52. package/dist/index.js +568 -0
  53. package/dist/lint-HXKTWRNO.js +316 -0
  54. package/dist/manual-Y3QOXWYA.js +204 -0
  55. package/dist/mcp.js +14745 -0
  56. package/dist/orchestrate-4ZH5GUQH.js +323 -0
  57. package/dist/probe-OYCP4JYG.js +151 -0
  58. package/dist/promote-Z52ZJTJU.js +181 -0
  59. package/dist/providers-4PGPZEWP.js +104 -0
  60. package/dist/remember-6VZ74B7E.js +77 -0
  61. package/dist/ripple-SBQOSTZD.js +215 -0
  62. package/dist/sentinel-LCFD56OJ.js +43 -0
  63. package/dist/server-F5ITNK6T.js +9846 -0
  64. package/dist/server-T6WIFYRQ.js +16076 -0
  65. package/dist/setup-DF4F3ICN.js +25 -0
  66. package/dist/setup-JHBPZAG7.js +296 -0
  67. package/dist/shift-HKIAP4ZN.js +226 -0
  68. package/dist/snapshot-GTVPRYZG.js +62 -0
  69. package/dist/spawn-BJRQA2NR.js +196 -0
  70. package/dist/summary-H6J6N6PJ.js +140 -0
  71. package/dist/switch-6EANJ7O6.js +232 -0
  72. package/dist/sync-BEOCW7TZ.js +11 -0
  73. package/dist/team-NWP2KJAB.js +32 -0
  74. package/dist/test-MA5TWJQV.js +934 -0
  75. package/dist/thread-JCJVRUQR.js +258 -0
  76. package/dist/triage-ETVXXFMV.js +1880 -0
  77. package/dist/tutorial-L5Q3ZDHK.js +666 -0
  78. package/dist/university-R2WDQLSI.js +40 -0
  79. package/dist/upgrade-5B3YGGC6.js +550 -0
  80. package/dist/validate-F3YHBCRZ.js +39 -0
  81. package/dist/validate-QEEY6KFS.js +64 -0
  82. package/dist/watch-4LT4O6K7.js +123 -0
  83. package/dist/watch-6IIWPWDN.js +111 -0
  84. package/dist/wisdom-LRM4FFCH.js +319 -0
  85. package/package.json +68 -0
  86. package/templates/paradigm/config.yaml +175 -0
  87. package/templates/paradigm/docs/commands.md +727 -0
  88. package/templates/paradigm/docs/decisions/000-template.md +47 -0
  89. package/templates/paradigm/docs/decisions/README.md +26 -0
  90. package/templates/paradigm/docs/error-patterns.md +215 -0
  91. package/templates/paradigm/docs/patterns.md +358 -0
  92. package/templates/paradigm/docs/queries.md +200 -0
  93. package/templates/paradigm/docs/troubleshooting.md +477 -0
  94. package/templates/paradigm/echoes.yaml +25 -0
  95. package/templates/paradigm/prompts/add-feature.md +152 -0
  96. package/templates/paradigm/prompts/add-gate.md +117 -0
  97. package/templates/paradigm/prompts/debug-auth.md +174 -0
  98. package/templates/paradigm/prompts/implement-ftux.md +722 -0
  99. package/templates/paradigm/prompts/implement-sandbox.md +651 -0
  100. package/templates/paradigm/prompts/read-docs.md +84 -0
  101. package/templates/paradigm/prompts/refactor.md +106 -0
  102. package/templates/paradigm/prompts/run-e2e-tests.md +340 -0
  103. package/templates/paradigm/prompts/trace-flow.md +202 -0
  104. package/templates/paradigm/prompts/validate-portals.md +279 -0
  105. package/templates/paradigm/specs/context-tracking.md +200 -0
  106. package/templates/paradigm/specs/context.md +461 -0
  107. package/templates/paradigm/specs/disciplines.md +413 -0
  108. package/templates/paradigm/specs/history.md +339 -0
  109. package/templates/paradigm/specs/logger.md +303 -0
  110. package/templates/paradigm/specs/navigator.md +236 -0
  111. package/templates/paradigm/specs/purpose.md +265 -0
  112. package/templates/paradigm/specs/scan.md +177 -0
  113. package/templates/paradigm/specs/symbols.md +451 -0
  114. package/templates/paradigm/specs/wisdom.md +294 -0
@@ -0,0 +1,689 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../purpose/core/dist/index.js
4
+ import * as fs from "fs";
5
+ import * as yaml from "js-yaml";
6
+ import { z } from "zod";
7
+ import * as path from "path";
8
+ import { glob } from "glob";
9
+ var PurposeItemSchema = z.object({
10
+ description: z.string(),
11
+ endpoints: z.array(z.string()).optional(),
12
+ tests: z.array(z.string()).optional(),
13
+ rules: z.record(z.unknown()).optional(),
14
+ aspects: z.array(z.string()).optional(),
15
+ // Symbol reference arrays
16
+ flows: z.array(z.string()).optional(),
17
+ gates: z.array(z.string()).optional(),
18
+ signals: z.array(z.string()).optional(),
19
+ states: z.array(z.string()).optional(),
20
+ components: z.array(z.string()).optional(),
21
+ // Extra fields preserved
22
+ tags: z.array(z.string()).optional(),
23
+ location: z.string().optional(),
24
+ locations: z.array(z.string()).optional(),
25
+ uses: z.array(z.string()).optional(),
26
+ "used-by": z.array(z.string()).optional(),
27
+ "used-for": z.array(z.string()).optional(),
28
+ exports: z.array(z.string()).optional(),
29
+ status: z.string().optional(),
30
+ properties: z.record(z.unknown()).optional(),
31
+ handles: z.array(z.string()).optional()
32
+ }).passthrough();
33
+ var PurposeItemArraySchema = PurposeItemSchema.extend({
34
+ id: z.string()
35
+ });
36
+ var SignalDefinitionObjectSchema = z.object({
37
+ description: z.string().optional(),
38
+ category: z.string().optional(),
39
+ severity: z.enum(["info", "warn", "error"]).optional(),
40
+ emitters: z.array(z.string()).optional(),
41
+ related: z.array(z.string()).optional(),
42
+ data: z.record(z.unknown()).optional()
43
+ });
44
+ var SignalDefinitionSchema = z.union([
45
+ SignalDefinitionObjectSchema,
46
+ z.string().transform((desc) => ({ description: desc }))
47
+ ]);
48
+ var RelationshipObjectSchema = z.object({
49
+ from: z.string(),
50
+ to: z.string(),
51
+ type: z.string(),
52
+ description: z.string().optional()
53
+ });
54
+ var RelationshipSchema = z.union([RelationshipObjectSchema, z.string()]);
55
+ var FlowStepObjectSchema = z.object({
56
+ component: z.string(),
57
+ action: z.string(),
58
+ description: z.string().optional()
59
+ });
60
+ var FlowStepSchema = z.union([FlowStepObjectSchema, z.string()]);
61
+ var FlowWithStepsSchema = z.object({
62
+ name: z.string(),
63
+ description: z.string().optional(),
64
+ steps: z.array(FlowStepSchema)
65
+ });
66
+ var FlowDefinitionSchema = z.object({
67
+ description: z.string().optional(),
68
+ gates: z.array(z.string()).optional(),
69
+ signals: z.array(z.string()).optional(),
70
+ components: z.array(z.string()).optional(),
71
+ steps: z.array(FlowStepSchema).optional()
72
+ });
73
+ var GateDefinitionSchema = z.object({
74
+ description: z.string().optional(),
75
+ requires: z.array(z.string()).optional(),
76
+ keys: z.array(z.string()).optional(),
77
+ signals: z.array(z.string()).optional()
78
+ });
79
+ var StateDefinitionSchema = z.object({
80
+ description: z.string().optional(),
81
+ default: z.unknown().optional(),
82
+ type: z.string().optional()
83
+ });
84
+ var AspectDefinitionSchema = z.object({
85
+ description: z.string().optional(),
86
+ tags: z.array(z.string()).optional(),
87
+ anchors: z.array(z.string()).optional(),
88
+ "applies-to": z.array(z.string()).optional(),
89
+ enforcement: z.string().optional()
90
+ });
91
+ var ReferenceSchema = z.object({
92
+ target: z.string(),
93
+ type: z.string(),
94
+ path: z.string()
95
+ });
96
+ var PurposeFileSchema = z.object({
97
+ version: z.string().optional(),
98
+ description: z.string().optional(),
99
+ apiSpec: z.string().optional(),
100
+ context: z.array(z.string()).optional(),
101
+ rules: z.record(z.unknown()).optional(),
102
+ // Support both array format [{ id, description }] and record format { id: { description } }
103
+ features: z.union([
104
+ z.array(PurposeItemArraySchema),
105
+ z.record(PurposeItemSchema)
106
+ ]).optional(),
107
+ components: z.union([
108
+ z.array(PurposeItemArraySchema),
109
+ z.record(PurposeItemSchema)
110
+ ]).optional(),
111
+ gates: z.record(GateDefinitionSchema).optional(),
112
+ states: z.record(StateDefinitionSchema).optional(),
113
+ signals: z.record(SignalDefinitionSchema).optional(),
114
+ aspects: z.record(AspectDefinitionSchema).optional(),
115
+ relationships: z.array(RelationshipSchema).optional(),
116
+ // Support both array format and record format for flows
117
+ flows: z.union([
118
+ z.array(FlowWithStepsSchema),
119
+ z.record(FlowDefinitionSchema)
120
+ ]).optional(),
121
+ references: z.array(ReferenceSchema).optional()
122
+ });
123
+ function parsePurposeFile(filePath) {
124
+ const result = parsePurposeFileDetailed(filePath);
125
+ return { data: result.data, errors: result.errors };
126
+ }
127
+ function parsePurposeFileDetailed(filePath) {
128
+ const errors = [];
129
+ const detailedErrors = [];
130
+ let rawContent;
131
+ try {
132
+ rawContent = fs.readFileSync(filePath, "utf8");
133
+ } catch (e) {
134
+ const error = `Cannot read file: ${e.message}`;
135
+ errors.push(error);
136
+ detailedErrors.push({ message: error, type: "file" });
137
+ return { data: null, errors, detailedErrors, rawContent: void 0, isYamlValid: false };
138
+ }
139
+ let data = null;
140
+ try {
141
+ data = yaml.load(rawContent);
142
+ } catch (e) {
143
+ const yamlError = e;
144
+ const line = yamlError.mark?.line ? yamlError.mark.line + 1 : void 0;
145
+ const message = `YAML syntax error: ${yamlError.reason || e.message}`;
146
+ errors.push(`${message}${line ? ` (line ${line})` : ""}`);
147
+ detailedErrors.push({
148
+ message,
149
+ line,
150
+ type: "yaml"
151
+ });
152
+ return { data: null, errors, detailedErrors, rawContent, isYamlValid: false };
153
+ }
154
+ if (data === null || data === void 0) {
155
+ return {
156
+ data: {},
157
+ errors: [],
158
+ detailedErrors: [],
159
+ rawContent,
160
+ isYamlValid: true
161
+ };
162
+ }
163
+ const parseResult = PurposeFileSchema.safeParse(data);
164
+ if (!parseResult.success) {
165
+ for (const issue of parseResult.error.issues) {
166
+ const path2 = issue.path.join(".");
167
+ const message = issue.message;
168
+ errors.push(`Schema error at ${path2 || "/"}: ${message}`);
169
+ detailedErrors.push({
170
+ message,
171
+ path: path2 || "/",
172
+ type: "schema"
173
+ });
174
+ }
175
+ return { data, errors, detailedErrors, rawContent, isYamlValid: true };
176
+ }
177
+ return { data: parseResult.data, errors: [], detailedErrors: [], rawContent, isYamlValid: true };
178
+ }
179
+ function serializePurposeFile(data) {
180
+ return yaml.dump(data, {
181
+ indent: 2,
182
+ lineWidth: -1,
183
+ noRefs: true,
184
+ sortKeys: false
185
+ });
186
+ }
187
+ function getDefaultPurposeContent() {
188
+ const defaultFile = {
189
+ version: "1.0.0",
190
+ description: "Project purpose and context",
191
+ context: [
192
+ "Add contextual notes for AI agents here"
193
+ ],
194
+ features: {},
195
+ components: {}
196
+ };
197
+ return serializePurposeFile(defaultFile);
198
+ }
199
+ function normalizeItemsToEntries(items) {
200
+ if (!items) return [];
201
+ if (Array.isArray(items)) {
202
+ return items.map((item) => [item.id, item]);
203
+ } else {
204
+ return Object.entries(items);
205
+ }
206
+ }
207
+ function aggregatePurposes(parsedFiles) {
208
+ const basePurpose = {
209
+ description: "",
210
+ context: [],
211
+ rules: {},
212
+ features: {},
213
+ components: {},
214
+ referencedItems: {},
215
+ ruleConflicts: []
216
+ };
217
+ if (!parsedFiles || parsedFiles.length === 0) {
218
+ return basePurpose;
219
+ }
220
+ parsedFiles.forEach(({ data }) => {
221
+ const existingContext = new Set(basePurpose.context);
222
+ for (const ctx of data.context || []) {
223
+ if (!existingContext.has(ctx)) {
224
+ basePurpose.context.push(ctx);
225
+ existingContext.add(ctx);
226
+ }
227
+ }
228
+ if (data.rules) {
229
+ for (const [key, value] of Object.entries(data.rules)) {
230
+ if (basePurpose.rules[key] !== void 0 && basePurpose.rules[key] !== value) {
231
+ basePurpose.ruleConflicts.push(
232
+ `Conflict on rule "${key}": existing value "${basePurpose.rules[key]}" overwritten with "${value}"`
233
+ );
234
+ }
235
+ basePurpose.rules[key] = value;
236
+ }
237
+ }
238
+ const featureEntries = normalizeItemsToEntries(data.features);
239
+ for (const [id, item] of featureEntries) {
240
+ basePurpose.features[id] = item;
241
+ }
242
+ const componentEntries = normalizeItemsToEntries(data.components);
243
+ for (const [id, item] of componentEntries) {
244
+ basePurpose.components[id] = item;
245
+ }
246
+ });
247
+ const lastFile = parsedFiles[parsedFiles.length - 1];
248
+ basePurpose.description = lastFile.data.description || basePurpose.description;
249
+ basePurpose.apiSpec = lastFile.data.apiSpec || basePurpose.apiSpec;
250
+ return basePurpose;
251
+ }
252
+ async function findPurposeFiles(rootDir) {
253
+ const absoluteRoot = path.resolve(rootDir);
254
+ const files = await glob("**/.purpose", {
255
+ cwd: absoluteRoot,
256
+ absolute: true,
257
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
258
+ });
259
+ return files.sort((a, b) => {
260
+ const depthA = a.split(path.sep).length;
261
+ const depthB = b.split(path.sep).length;
262
+ return depthA - depthB;
263
+ });
264
+ }
265
+ async function getAllPurposeFiles(rootDir) {
266
+ const files = await findPurposeFiles(rootDir);
267
+ const parsed = [];
268
+ for (const filePath of files) {
269
+ const { data, errors } = parsePurposeFile(filePath);
270
+ if (data) {
271
+ parsed.push({ filePath, data });
272
+ if (errors.length > 0) {
273
+ console.warn(`Warnings parsing ${filePath}:`, errors);
274
+ }
275
+ }
276
+ }
277
+ return parsed;
278
+ }
279
+ function extractFeatures(parsedFiles) {
280
+ const features = /* @__PURE__ */ new Map();
281
+ for (const { filePath, data } of parsedFiles) {
282
+ const entries = normalizeItemsToEntries(data.features);
283
+ for (const [id, item] of entries) {
284
+ features.set(id, { item, filePath });
285
+ }
286
+ }
287
+ return features;
288
+ }
289
+ function extractComponents(parsedFiles) {
290
+ const components = /* @__PURE__ */ new Map();
291
+ for (const { filePath, data } of parsedFiles) {
292
+ const entries = normalizeItemsToEntries(data.components);
293
+ for (const [id, item] of entries) {
294
+ components.set(id, { item, filePath });
295
+ }
296
+ }
297
+ return components;
298
+ }
299
+ function extractGates(parsedFiles) {
300
+ const gates = /* @__PURE__ */ new Map();
301
+ for (const { filePath, data } of parsedFiles) {
302
+ if (data.gates) {
303
+ for (const [id, item] of Object.entries(data.gates)) {
304
+ gates.set(id, { item, filePath });
305
+ }
306
+ }
307
+ }
308
+ return gates;
309
+ }
310
+ function extractStates(parsedFiles) {
311
+ const states = /* @__PURE__ */ new Map();
312
+ for (const { filePath, data } of parsedFiles) {
313
+ if (data.states) {
314
+ for (const [id, item] of Object.entries(data.states)) {
315
+ states.set(id, { item, filePath });
316
+ }
317
+ }
318
+ }
319
+ return states;
320
+ }
321
+ function extractFlows(parsedFiles) {
322
+ const flows = /* @__PURE__ */ new Map();
323
+ for (const { filePath, data } of parsedFiles) {
324
+ if (data.flows) {
325
+ if (Array.isArray(data.flows)) {
326
+ for (const flow of data.flows) {
327
+ flows.set(flow.name, {
328
+ item: {
329
+ id: flow.name,
330
+ description: flow.description,
331
+ steps: flow.steps
332
+ },
333
+ filePath
334
+ });
335
+ }
336
+ } else {
337
+ for (const [id, flowDef] of Object.entries(data.flows)) {
338
+ flows.set(id, {
339
+ item: {
340
+ id,
341
+ description: flowDef.description,
342
+ gates: flowDef.gates,
343
+ signals: flowDef.signals,
344
+ components: flowDef.components,
345
+ steps: flowDef.steps
346
+ },
347
+ filePath
348
+ });
349
+ }
350
+ }
351
+ }
352
+ }
353
+ return flows;
354
+ }
355
+ function extractSignals(parsedFiles) {
356
+ const signals = /* @__PURE__ */ new Map();
357
+ for (const { filePath, data } of parsedFiles) {
358
+ if (data.signals) {
359
+ for (const [id, item] of Object.entries(data.signals)) {
360
+ signals.set(id, { item, filePath });
361
+ }
362
+ }
363
+ }
364
+ return signals;
365
+ }
366
+ function extractAspects(parsedFiles) {
367
+ const aspects = /* @__PURE__ */ new Map();
368
+ for (const { filePath, data } of parsedFiles) {
369
+ if (data.aspects) {
370
+ for (const [id, item] of Object.entries(data.aspects)) {
371
+ aspects.set(id, { item, filePath });
372
+ }
373
+ }
374
+ }
375
+ return aspects;
376
+ }
377
+ function extractSymbolReferences(parsedFiles) {
378
+ const refs = [];
379
+ const seen = /* @__PURE__ */ new Set();
380
+ for (const { filePath, data } of parsedFiles) {
381
+ const featureEntries = normalizeItemsToEntries(data.features);
382
+ for (const [id, item] of featureEntries) {
383
+ extractRefsFromItem(`#${id}`, item, filePath, refs, seen);
384
+ }
385
+ const componentEntries = normalizeItemsToEntries(data.components);
386
+ for (const [id, item] of componentEntries) {
387
+ extractRefsFromItem(`#${id}`, item, filePath, refs, seen);
388
+ }
389
+ }
390
+ return refs;
391
+ }
392
+ function extractRefsFromItem(sourceSymbol, item, filePath, refs, seen) {
393
+ if (item.flows) {
394
+ for (const flow of item.flows) {
395
+ const symbol = flow.startsWith("$") ? flow : `$${flow}`;
396
+ if (!seen.has(symbol)) {
397
+ seen.add(symbol);
398
+ refs.push({ symbol, type: "flow", sourceSymbol, filePath });
399
+ }
400
+ }
401
+ }
402
+ if (item.gates) {
403
+ for (const gate of item.gates) {
404
+ const symbol = gate.startsWith("^") ? gate : `^${gate}`;
405
+ if (!seen.has(symbol)) {
406
+ seen.add(symbol);
407
+ refs.push({ symbol, type: "gate", sourceSymbol, filePath });
408
+ }
409
+ }
410
+ }
411
+ if (item.signals) {
412
+ for (const signal of item.signals) {
413
+ const symbol = signal.startsWith("!") ? signal : `!${signal}`;
414
+ if (!seen.has(symbol)) {
415
+ seen.add(symbol);
416
+ refs.push({ symbol, type: "signal", sourceSymbol, filePath });
417
+ }
418
+ }
419
+ }
420
+ if (item.states) {
421
+ for (const state of item.states) {
422
+ const symbol = state.startsWith("#") ? state : state.startsWith("%") ? `#${state.slice(1)}` : `#${state}`;
423
+ if (!seen.has(symbol)) {
424
+ seen.add(symbol);
425
+ refs.push({ symbol, type: "component", sourceSymbol, filePath });
426
+ }
427
+ }
428
+ }
429
+ if (item.components) {
430
+ for (const comp of item.components) {
431
+ const symbol = comp.startsWith("#") ? comp : `#${comp}`;
432
+ if (!seen.has(symbol)) {
433
+ seen.add(symbol);
434
+ refs.push({ symbol, type: "component", sourceSymbol, filePath });
435
+ }
436
+ }
437
+ }
438
+ if (item.aspects) {
439
+ for (const aspect of item.aspects) {
440
+ const symbol = aspect.startsWith("~") ? aspect : `~${aspect}`;
441
+ if (!seen.has(symbol)) {
442
+ seen.add(symbol);
443
+ refs.push({ symbol, type: "aspect", sourceSymbol, filePath });
444
+ }
445
+ }
446
+ }
447
+ if (item.description) {
448
+ const descRefs = extractSymbolsFromText(item.description);
449
+ for (const { symbol, type } of descRefs) {
450
+ if (!seen.has(symbol)) {
451
+ seen.add(symbol);
452
+ refs.push({ symbol, type, sourceSymbol, filePath });
453
+ }
454
+ }
455
+ }
456
+ }
457
+ var SYMBOL_BLOCKLIST = /* @__PURE__ */ new Set([
458
+ "$lib",
459
+ "$env",
460
+ "$app",
461
+ "$service-worker",
462
+ "$virtual",
463
+ "$schema",
464
+ "$ref",
465
+ "$id",
466
+ "$type"
467
+ ]);
468
+ function extractSymbolsFromText(text) {
469
+ const results = [];
470
+ const pattern = /([$^!#~%])([a-zA-Z][a-zA-Z0-9._-]*)/g;
471
+ let match;
472
+ while ((match = pattern.exec(text)) !== null) {
473
+ const prefix = match[1];
474
+ const id = match[2];
475
+ let symbol;
476
+ let type;
477
+ switch (prefix) {
478
+ case "#":
479
+ type = "component";
480
+ symbol = `#${id}`;
481
+ break;
482
+ case "$":
483
+ type = "flow";
484
+ symbol = `$${id}`;
485
+ break;
486
+ case "^":
487
+ type = "gate";
488
+ symbol = `^${id}`;
489
+ break;
490
+ case "!":
491
+ type = "signal";
492
+ symbol = `!${id}`;
493
+ break;
494
+ case "~":
495
+ type = "aspect";
496
+ symbol = `~${id}`;
497
+ break;
498
+ // Legacy: %state → #component
499
+ case "%":
500
+ type = "component";
501
+ symbol = `#${id}`;
502
+ break;
503
+ default:
504
+ continue;
505
+ }
506
+ if (SYMBOL_BLOCKLIST.has(symbol)) continue;
507
+ results.push({ symbol, type });
508
+ }
509
+ return results;
510
+ }
511
+ function normalizeToEntries(items) {
512
+ if (!items) return [];
513
+ if (Array.isArray(items)) {
514
+ return items.map((item) => [item.id, item]);
515
+ } else {
516
+ return Object.entries(items);
517
+ }
518
+ }
519
+ function getItemIds(items) {
520
+ if (!items) return [];
521
+ if (Array.isArray(items)) {
522
+ return items.map((item) => item.id);
523
+ } else {
524
+ return Object.keys(items);
525
+ }
526
+ }
527
+ function validatePurposeFile(data, filePath) {
528
+ const issues = [];
529
+ const prefix = filePath ? `${filePath}: ` : "";
530
+ const featureEntries = normalizeToEntries(data.features);
531
+ for (const [id, feature] of featureEntries) {
532
+ validatePurposeItem(id, feature, "feature", prefix, issues);
533
+ }
534
+ const componentEntries = normalizeToEntries(data.components);
535
+ for (const [id, component] of componentEntries) {
536
+ validatePurposeItem(id, component, "component", prefix, issues);
537
+ }
538
+ if (data.relationships) {
539
+ const allIds = /* @__PURE__ */ new Set([
540
+ ...getItemIds(data.features),
541
+ ...getItemIds(data.components)
542
+ ]);
543
+ for (const rel of data.relationships) {
544
+ if (typeof rel === "string" || !rel || !rel.from || !rel.to) {
545
+ continue;
546
+ }
547
+ const fromId = rel.from.replace(/^[@#$%~^!?]/, "");
548
+ if (!allIds.has(fromId) && !rel.from.includes(".")) {
549
+ issues.push({
550
+ type: "warning",
551
+ message: `${prefix}Relationship references unknown source: "${rel.from}"`,
552
+ path: "relationships"
553
+ });
554
+ }
555
+ const toId = rel.to.replace(/^[@#$%~^!?]/, "");
556
+ if (!allIds.has(toId) && !rel.to.includes(".")) {
557
+ issues.push({
558
+ type: "warning",
559
+ message: `${prefix}Relationship references unknown target: "${rel.to}"`,
560
+ path: "relationships"
561
+ });
562
+ }
563
+ }
564
+ }
565
+ if (data.flows) {
566
+ const componentIds = new Set(getItemIds(data.components));
567
+ if (Array.isArray(data.flows)) {
568
+ for (const flow of data.flows) {
569
+ if (!flow || typeof flow !== "object") continue;
570
+ if (!flow.name) {
571
+ issues.push({
572
+ type: "error",
573
+ message: `${prefix}Flow missing required "name" field`,
574
+ path: "flows"
575
+ });
576
+ }
577
+ if (flow.steps && Array.isArray(flow.steps)) {
578
+ for (const step of flow.steps) {
579
+ if (typeof step === "string" || !step || !step.component) continue;
580
+ const componentId = step.component.replace(/^#/, "");
581
+ if (!componentIds.has(componentId)) {
582
+ issues.push({
583
+ type: "warning",
584
+ message: `${prefix}Flow "${flow.name}" references unknown component: "${step.component}"`,
585
+ path: `flows.${flow.name}`
586
+ });
587
+ }
588
+ }
589
+ }
590
+ }
591
+ } else {
592
+ for (const [flowId, flowDef] of Object.entries(data.flows)) {
593
+ if (!flowDef || typeof flowDef !== "object") continue;
594
+ if (flowDef.steps && Array.isArray(flowDef.steps)) {
595
+ for (const step of flowDef.steps) {
596
+ if (typeof step === "string" || !step || !step.component) continue;
597
+ const componentId = step.component.replace(/^#/, "");
598
+ if (!componentIds.has(componentId)) {
599
+ issues.push({
600
+ type: "warning",
601
+ message: `${prefix}Flow "${flowId}" references unknown component: "${step.component}"`,
602
+ path: `flows.${flowId}`
603
+ });
604
+ }
605
+ }
606
+ }
607
+ }
608
+ }
609
+ }
610
+ return {
611
+ valid: issues.filter((i) => i.type === "error").length === 0,
612
+ issues
613
+ };
614
+ }
615
+ function validatePurposeItem(id, item, itemType, prefix, issues) {
616
+ const path2 = `${itemType}s.${id}`;
617
+ if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(id)) {
618
+ issues.push({
619
+ type: "warning",
620
+ message: `${prefix}${itemType} ID "${id}" should use alphanumeric characters and hyphens`,
621
+ path: path2
622
+ });
623
+ }
624
+ if (!item.description || item.description.trim() === "") {
625
+ issues.push({
626
+ type: "warning",
627
+ message: `${prefix}${itemType} "${id}" has no description`,
628
+ path: path2
629
+ });
630
+ }
631
+ if (item.endpoints) {
632
+ for (const endpoint of item.endpoints) {
633
+ if (!/^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+\//.test(endpoint)) {
634
+ issues.push({
635
+ type: "warning",
636
+ message: `${prefix}Endpoint "${endpoint}" in ${itemType} "${id}" may not be in standard format (e.g., "GET /api/users")`,
637
+ path: `${path2}.endpoints`
638
+ });
639
+ }
640
+ }
641
+ }
642
+ }
643
+ function formatValidationResult(result) {
644
+ if (result.valid && result.issues.length === 0) {
645
+ return "\u2705 Purpose file is valid";
646
+ }
647
+ const lines = [];
648
+ const errors = result.issues.filter((i) => i.type === "error");
649
+ const warnings = result.issues.filter((i) => i.type === "warning");
650
+ if (errors.length > 0) {
651
+ lines.push(`
652
+ \u274C ${errors.length} error(s):`);
653
+ for (const issue of errors) {
654
+ lines.push(` \u2022 ${issue.message}${issue.path ? ` (${issue.path})` : ""}`);
655
+ }
656
+ }
657
+ if (warnings.length > 0) {
658
+ lines.push(`
659
+ \u26A0\uFE0F ${warnings.length} warning(s):`);
660
+ for (const issue of warnings) {
661
+ lines.push(` \u2022 ${issue.message}${issue.path ? ` (${issue.path})` : ""}`);
662
+ }
663
+ }
664
+ if (result.valid) {
665
+ lines.push("\n\u2705 Purpose file is valid (with warnings)");
666
+ } else {
667
+ lines.push("\n\u274C Purpose file is invalid");
668
+ }
669
+ return lines.join("\n");
670
+ }
671
+
672
+ export {
673
+ parsePurposeFileDetailed,
674
+ serializePurposeFile,
675
+ getDefaultPurposeContent,
676
+ aggregatePurposes,
677
+ findPurposeFiles,
678
+ getAllPurposeFiles,
679
+ extractFeatures,
680
+ extractComponents,
681
+ extractGates,
682
+ extractStates,
683
+ extractFlows,
684
+ extractSignals,
685
+ extractAspects,
686
+ extractSymbolReferences,
687
+ validatePurposeFile,
688
+ formatValidationResult
689
+ };