yarramate 0.5.0 → 0.7.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 (48) hide show
  1. package/README.md +39 -10
  2. package/catalogues/core-enrichment.yaml +700 -0
  3. package/dist/adapters/likec4-cli.js +179 -55
  4. package/dist/adapters/likec4-export.d.ts +1 -1
  5. package/dist/adapters/likec4-prepare.d.ts +6 -0
  6. package/dist/adapters/likec4-prepare.js +40 -2
  7. package/dist/adapters/mcp-cli.js +55 -49
  8. package/dist/apply-command.d.ts +2 -0
  9. package/dist/apply-command.js +199 -0
  10. package/dist/{new-command.d.ts → ask-command.d.ts} +1 -1
  11. package/dist/ask-command.js +729 -0
  12. package/dist/brief.d.ts +3 -0
  13. package/dist/brief.js +235 -0
  14. package/dist/check-command.js +70 -4
  15. package/dist/cli-support.d.ts +4 -1
  16. package/dist/cli-support.js +3 -2
  17. package/dist/cli.js +22 -586
  18. package/dist/compiler.js +45 -7
  19. package/dist/core-contract.d.ts +1 -1
  20. package/dist/{status-command.d.ts → design-command.d.ts} +1 -1
  21. package/dist/design-command.js +218 -0
  22. package/dist/export-command.d.ts +2 -0
  23. package/dist/export-command.js +228 -0
  24. package/dist/interrogate-command.d.ts +104 -0
  25. package/dist/interrogate-command.js +268 -0
  26. package/dist/next-command.d.ts +22 -0
  27. package/dist/next-command.js +167 -0
  28. package/dist/reconciliation.d.ts +2 -0
  29. package/dist/reconciliation.js +38 -0
  30. package/docs/CONSUMING-YARRAMATE.md +28 -21
  31. package/package.json +13 -10
  32. package/schema/yarramate-apply-result.schema.json +32 -0
  33. package/schema/yarramate-ask-result.schema.json +391 -0
  34. package/schema/yarramate-check-result.schema.json +12 -0
  35. package/schema/yarramate-core-contract.schema.json +5 -10
  36. package/schema/yarramate-design-step.schema.json +152 -0
  37. package/schema/yarramate-document.schema.json +76 -9
  38. package/schema/yarramate-interrogation-report.schema.json +89 -0
  39. package/schema/yarramate-likec4-generated-project-v2.schema.json +6 -0
  40. package/schema/yarramate-likec4-generated-project.schema.json +6 -0
  41. package/schema/yarramate-operations.schema.json +303 -0
  42. package/schema/yarramate-question-catalogue.schema.json +431 -0
  43. package/schema/yarramate-reconciliation-report.schema.json +8 -2
  44. package/skills/yarramate-architecture/SKILL.md +42 -17
  45. package/skills/yarramate-architecture/references/native-authoring.md +73 -17
  46. package/dist/new-command.js +0 -111
  47. package/dist/status-command.js +0 -171
  48. package/schema/yarramate-status-result.schema.json +0 -273
package/dist/cli.js CHANGED
@@ -1,243 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
3
3
  import { dirname, relative, resolve } from 'node:path';
4
- import { isSeq, parseDocument } from 'yaml';
5
- import { compileWorkspace, compileWorkspaceWithProfileContext, } from './compiler.js';
6
- import { compareArchitectureStates } from './architecture-state.js';
7
- import { serializeSemanticGraph } from './graph.js';
8
- import { diagnosticJson, humanDiagnostics, isMainModule, resolveCliWorkspaceSources, usage, versionResult, } from './cli-support.js';
4
+ import { compileWorkspace } from './compiler.js';
5
+ import { diagnosticJson, isMainModule, usage, versionResult, } from './cli-support.js';
6
+ import { runAskCommand } from './ask-command.js';
9
7
  import { runCheckCommand } from './check-command.js';
10
- import { runNewCommand } from './new-command.js';
11
- import { runStatusCommand } from './status-command.js';
12
- import { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
8
+ import { runExportCommand } from './export-command.js';
9
+ import { runApplyCommand } from './apply-command.js';
10
+ import { runDesignCommand } from './design-command.js';
11
+ import { evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
13
12
  import { reconcileEvidenceReports } from './reconciliation.js';
14
13
  import { loadWorkspaceManifest } from './workspace.js';
15
- import { evaluateProjection, loadProjection, renderBudgetedContext, renderProjectionMarkdown, } from './projection.js';
16
- const extractBudget = (options) => {
17
- const rest = [];
18
- let budget;
19
- for (let index = 0; index < options.length; index += 1) {
20
- const option = options[index];
21
- if (option === '--budget') {
22
- const value = options[index + 1];
23
- if (value === undefined ||
24
- budget !== undefined ||
25
- !/^[1-9][0-9]*$/.test(value)) {
26
- return { ok: false };
27
- }
28
- budget = Number(value);
29
- index += 1;
30
- continue;
31
- }
32
- if (option !== undefined)
33
- rest.push(option);
34
- }
35
- return budget === undefined ? { ok: true, rest } : { ok: true, budget, rest };
36
- };
37
- const runProjection = (options, cwd, output) => {
38
- const extracted = output === 'json' ? extractBudget(options) : { ok: true, rest: options };
39
- if (!extracted.ok) {
40
- return { exitCode: 2, stdout: '', stderr: usage };
41
- }
42
- const budget = 'budget' in extracted ? extracted.budget : undefined;
43
- const [projectionPath, ...paths] = extracted.rest;
44
- if (projectionPath === undefined ||
45
- paths.length === 0 ||
46
- extracted.rest.some((option) => option.startsWith('-'))) {
47
- return { exitCode: 2, stdout: '', stderr: usage };
48
- }
49
- try {
50
- const resolved = resolveCliWorkspaceSources(paths, cwd);
51
- if (!resolved.ok) {
52
- return {
53
- exitCode: 1,
54
- stdout: diagnosticJson(resolved.diagnostics),
55
- stderr: '',
56
- };
57
- }
58
- const loaded = loadProjection({
59
- path: projectionPath,
60
- source: readFileSync(resolve(cwd, projectionPath), 'utf8'),
61
- });
62
- if (!loaded.ok) {
63
- return {
64
- exitCode: 1,
65
- stdout: diagnosticJson(loaded.diagnostics),
66
- stderr: '',
67
- };
68
- }
69
- const compilation = compileWorkspaceWithProfileContext(resolved.paths.map((path) => ({
70
- path,
71
- source: readFileSync(resolve(cwd, path), 'utf8'),
72
- })));
73
- if (!compilation.ok) {
74
- return {
75
- exitCode: 1,
76
- stdout: diagnosticJson(compilation.diagnostics),
77
- stderr: '',
78
- };
79
- }
80
- const result = evaluateProjection(compilation.graph, loaded.projection, compilation.profileContext);
81
- return {
82
- exitCode: 0,
83
- stdout: output === 'json'
84
- ? budget === undefined
85
- ? `${JSON.stringify(result, null, 2)}\n`
86
- : renderBudgetedContext(result, budget)
87
- : renderProjectionMarkdown(result),
88
- stderr: '',
89
- };
90
- }
91
- catch (error) {
92
- const message = error instanceof Error ? error.message : String(error);
93
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
94
- }
95
- };
96
- const runAdHocContext = (options, cwd) => {
97
- const extracted = extractBudget(options);
98
- if (!extracted.ok) {
99
- return { exitCode: 2, stdout: '', stderr: usage };
100
- }
101
- const budget = 'budget' in extracted ? extracted.budget : undefined;
102
- const subjects = [];
103
- const paths = [];
104
- for (let index = 0; index < extracted.rest.length; index += 1) {
105
- const option = extracted.rest[index];
106
- if (option === '--subject') {
107
- const value = extracted.rest[index + 1];
108
- if (value === undefined || value.startsWith('-')) {
109
- return { exitCode: 2, stdout: '', stderr: usage };
110
- }
111
- subjects.push(value);
112
- index += 1;
113
- continue;
114
- }
115
- if (option === undefined || option.startsWith('-')) {
116
- return { exitCode: 2, stdout: '', stderr: usage };
117
- }
118
- paths.push(option);
119
- }
120
- if (subjects.length === 0 || paths.length === 0) {
121
- return { exitCode: 2, stdout: '', stderr: usage };
122
- }
123
- const unqualified = subjects.filter((subject) => !subject.includes('#'));
124
- if (unqualified.length > 0) {
125
- return {
126
- exitCode: 2,
127
- stdout: '',
128
- stderr: 'Ad-hoc context subjects must be globally qualified as ' +
129
- `"<document-id>#<local-id>": ${unqualified.join(', ')}\n`,
130
- };
131
- }
132
- try {
133
- const resolved = resolveCliWorkspaceSources(paths, cwd);
134
- if (!resolved.ok) {
135
- return {
136
- exitCode: 1,
137
- stdout: diagnosticJson(resolved.diagnostics),
138
- stderr: '',
139
- };
140
- }
141
- const compilation = compileWorkspaceWithProfileContext(resolved.paths.map((path) => ({
142
- path,
143
- source: readFileSync(resolve(cwd, path), 'utf8'),
144
- })));
145
- if (!compilation.ok) {
146
- return {
147
- exitCode: 1,
148
- stdout: diagnosticJson(compilation.diagnostics),
149
- stderr: '',
150
- };
151
- }
152
- const known = new Set(compilation.graph.subjects.map(({ id }) => id));
153
- const unknown = subjects.filter((subject) => !known.has(subject));
154
- if (unknown.length > 0) {
155
- return {
156
- exitCode: 1,
157
- stdout: '',
158
- stderr: `Unknown subject ${unknown.length === 1 ? 'identity' : 'identities'}: ` +
159
- `${unknown.join(', ')} (the compiled workspace declares ${known.size} subjects)\n`,
160
- };
161
- }
162
- const result = evaluateProjection(compilation.graph, {
163
- format: 'yarramate/projection/v1',
164
- id: 'ad-hoc-context',
165
- version: '0.0',
166
- query: { subjects, relationships: 'connected' },
167
- presentation: {
168
- title: 'Ad-hoc context',
169
- description: `Connected neighbourhood of ${subjects.join(', ')}`,
170
- },
171
- }, compilation.profileContext);
172
- return {
173
- exitCode: 0,
174
- stdout: budget === undefined
175
- ? `${JSON.stringify(result, null, 2)}\n`
176
- : renderBudgetedContext(result, budget),
177
- stderr: '',
178
- };
179
- }
180
- catch (error) {
181
- const message = error instanceof Error ? error.message : String(error);
182
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
183
- }
184
- };
185
- const runEvidence = (options, cwd) => {
186
- const [evidencePath, ...paths] = options;
187
- if (evidencePath === undefined ||
188
- paths.length === 0 ||
189
- options.some((option) => option.startsWith('-'))) {
190
- return { exitCode: 2, stdout: '', stderr: usage };
191
- }
192
- try {
193
- const resolved = resolveCliWorkspaceSources(paths, cwd);
194
- if (!resolved.ok) {
195
- return {
196
- exitCode: 1,
197
- stdout: diagnosticJson(resolved.diagnostics),
198
- stderr: '',
199
- };
200
- }
201
- const loaded = loadEvidence({
202
- path: evidencePath,
203
- source: readFileSync(resolve(cwd, evidencePath), 'utf8'),
204
- });
205
- if (!loaded.ok) {
206
- return {
207
- exitCode: 1,
208
- stdout: diagnosticJson(loaded.diagnostics),
209
- stderr: '',
210
- };
211
- }
212
- const compilation = compileWorkspace(resolved.paths.map((path) => ({
213
- path,
214
- source: readFileSync(resolve(cwd, path), 'utf8'),
215
- })));
216
- if (!compilation.ok) {
217
- return {
218
- exitCode: 1,
219
- stdout: diagnosticJson(compilation.diagnostics),
220
- stderr: '',
221
- };
222
- }
223
- const evaluation = evaluateEvidence(compilation.graph, loaded.evidence);
224
- return evaluation.ok
225
- ? {
226
- exitCode: 0,
227
- stdout: `${JSON.stringify(evaluation.report, null, 2)}\n`,
228
- stderr: '',
229
- }
230
- : {
231
- exitCode: 1,
232
- stdout: diagnosticJson(evaluation.diagnostics),
233
- stderr: '',
234
- };
235
- }
236
- catch (error) {
237
- const message = error instanceof Error ? error.message : String(error);
238
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
239
- }
240
- };
241
14
  const runReconciliation = (options, cwd) => {
242
15
  const [workspacePath] = options;
243
16
  if (options.length !== 1 ||
@@ -306,52 +79,6 @@ const runReconciliation = (options, cwd) => {
306
79
  return { exitCode: 2, stdout: '', stderr: `${message}\n` };
307
80
  }
308
81
  };
309
- const runStateComparison = (options, cwd) => {
310
- const [from, to, ...paths] = options;
311
- if (from === undefined ||
312
- to === undefined ||
313
- paths.length === 0 ||
314
- options.some((option) => option.startsWith('-'))) {
315
- return { exitCode: 2, stdout: '', stderr: usage };
316
- }
317
- try {
318
- const resolved = resolveCliWorkspaceSources(paths, cwd);
319
- if (!resolved.ok) {
320
- return {
321
- exitCode: 1,
322
- stdout: diagnosticJson(resolved.diagnostics),
323
- stderr: '',
324
- };
325
- }
326
- const compilation = compileWorkspace(resolved.paths.map((path) => ({
327
- path,
328
- source: readFileSync(resolve(cwd, path), 'utf8'),
329
- })));
330
- if (!compilation.ok) {
331
- return {
332
- exitCode: 1,
333
- stdout: diagnosticJson(compilation.diagnostics),
334
- stderr: '',
335
- };
336
- }
337
- const comparison = compareArchitectureStates(compilation.graph, from, to);
338
- return comparison.ok
339
- ? {
340
- exitCode: 0,
341
- stdout: `${JSON.stringify(comparison.comparison, null, 2)}\n`,
342
- stderr: '',
343
- }
344
- : {
345
- exitCode: 2,
346
- stdout: '',
347
- stderr: `${comparison.issues.map(({ message }) => message).join('\n')}\n`,
348
- };
349
- }
350
- catch (error) {
351
- const message = error instanceof Error ? error.message : String(error);
352
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
353
- }
354
- };
355
82
  const runInit = (options, cwd) => {
356
83
  const positional = options.filter((option) => option !== '--no-pointer');
357
84
  const writePointer = positional.length === options.length;
@@ -398,9 +125,9 @@ const runInit = (options, cwd) => {
398
125
  'YarraMate documents in `.yarramate/`. When prose documentation and the\n' +
399
126
  'model disagree, the model is authoritative.\n' +
400
127
  '\n' +
401
- '- Orient first: `yarramate status .yarramate/workspace.yaml --json`\n' +
128
+ '- Orient first: `yarramate ask .yarramate/workspace.yaml`\n' +
129
+ '- Continue the design interview: `yarramate design .yarramate/workspace.yaml`\n' +
402
130
  '- Validate changes: `yarramate check .yarramate/workspace.yaml --json`\n' +
403
- '- Bounded task context: `yarramate context <projection.yaml> .yarramate/workspace.yaml`\n' +
404
131
  '\n' +
405
132
  'Author native documents only; never edit generated output.\n';
406
133
  // Harnesses look in different files: AGENTS.md is the cross-harness
@@ -434,283 +161,9 @@ const runInit = (options, cwd) => {
434
161
  stderr: '',
435
162
  };
436
163
  };
437
- const parseFlags = (options) => {
438
- const [path, ...rest] = options;
439
- if (path === undefined || path.startsWith('-') || rest.length % 2 !== 0) {
440
- return undefined;
441
- }
442
- const flags = new Map();
443
- for (let index = 0; index < rest.length; index += 2) {
444
- const flag = rest[index];
445
- const value = rest[index + 1];
446
- if (flag === undefined ||
447
- value === undefined ||
448
- !flag.startsWith('--') ||
449
- value.startsWith('--')) {
450
- return undefined;
451
- }
452
- flags.set(flag, [...(flags.get(flag) ?? []), value]);
453
- }
454
- return { path, flags };
455
- };
456
- const oneFlag = (flags, flag) => {
457
- const values = flags.get(flag);
458
- return values?.length === 1 ? values[0] : undefined;
459
- };
460
- const hasRepeatedSingletonFlag = (flags) => [...flags.entries()].some(([flag, values]) => flag !== '--source' &&
461
- flag !== '--constraint' &&
462
- flag !== '--reference' &&
463
- flag !== '--present-in' &&
464
- values.length !== 1);
465
- const appendBlockItem = (document, collection, item) => {
466
- document.addIn([collection], item);
467
- const sequence = document.getIn([collection], true);
468
- if (isSeq(sequence)) {
469
- sequence.flow = false;
470
- }
471
- };
472
- const identifiedReferences = (values) => {
473
- const parsed = values.map((value) => {
474
- const separator = value.indexOf('=');
475
- return separator <= 0 || separator === value.length - 1
476
- ? undefined
477
- : {
478
- id: value.slice(0, separator),
479
- ref: value.slice(separator + 1),
480
- };
481
- });
482
- return parsed.some((reference) => reference === undefined)
483
- ? undefined
484
- : parsed;
485
- };
486
- const runAdd = (options, cwd) => {
487
- const parsed = parseFlags(options);
488
- if (parsed === undefined) {
489
- return { exitCode: 2, stdout: '', stderr: usage };
490
- }
491
- const allowed = new Set([
492
- '--id',
493
- '--kind',
494
- '--name',
495
- '--status',
496
- '--description',
497
- '--owner',
498
- '--constraint',
499
- '--reference',
500
- '--present-in',
501
- '--source',
502
- ]);
503
- if ([...parsed.flags.keys()].some((flag) => !allowed.has(flag)) ||
504
- hasRepeatedSingletonFlag(parsed.flags)) {
505
- return { exitCode: 2, stdout: '', stderr: usage };
506
- }
507
- const id = oneFlag(parsed.flags, '--id');
508
- const kind = oneFlag(parsed.flags, '--kind');
509
- const name = oneFlag(parsed.flags, '--name');
510
- if (id === undefined || kind === undefined || name === undefined) {
511
- return { exitCode: 2, stdout: '', stderr: usage };
512
- }
513
- const constraints = identifiedReferences(parsed.flags.get('--constraint') ?? []);
514
- const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
515
- if (constraints === undefined || references === undefined) {
516
- return { exitCode: 2, stdout: '', stderr: usage };
517
- }
518
- try {
519
- const absolutePath = resolve(cwd, parsed.path);
520
- const document = parseDocument(readFileSync(absolutePath, 'utf8'));
521
- appendBlockItem(document, 'concepts', {
522
- id,
523
- kind,
524
- name,
525
- ...(oneFlag(parsed.flags, '--description') === undefined
526
- ? {}
527
- : { description: oneFlag(parsed.flags, '--description') }),
528
- ...(oneFlag(parsed.flags, '--status') === undefined
529
- ? {}
530
- : { status: oneFlag(parsed.flags, '--status') }),
531
- ...(oneFlag(parsed.flags, '--owner') === undefined
532
- ? {}
533
- : { owner: oneFlag(parsed.flags, '--owner') }),
534
- ...(constraints.length === 0 ? {} : { constraints }),
535
- ...(references.length === 0 ? {} : { references }),
536
- ...(parsed.flags.get('--present-in') === undefined
537
- ? {}
538
- : { presentIn: parsed.flags.get('--present-in') }),
539
- });
540
- const candidate = document.toString({ lineWidth: 0 });
541
- const companions = resolveCliWorkspaceSources(parsed.flags.get('--source') ?? [], cwd);
542
- if (!companions.ok) {
543
- return {
544
- exitCode: 1,
545
- stdout: humanDiagnostics(companions.diagnostics),
546
- stderr: '',
547
- };
548
- }
549
- const compilation = compileWorkspace([
550
- { path: parsed.path, source: candidate },
551
- ...companions.paths
552
- .filter((path) => resolve(cwd, path) !== resolve(cwd, parsed.path))
553
- .map((path) => ({
554
- path,
555
- source: readFileSync(resolve(cwd, path), 'utf8'),
556
- })),
557
- ]);
558
- if (!compilation.ok) {
559
- return {
560
- exitCode: 1,
561
- stdout: humanDiagnostics(compilation.diagnostics),
562
- stderr: '',
563
- };
564
- }
565
- writeFileSync(absolutePath, candidate, 'utf8');
566
- return {
567
- exitCode: 0,
568
- stdout: `Added concept "${id}" to ${parsed.path}\n`,
569
- stderr: '',
570
- };
571
- }
572
- catch (error) {
573
- const message = error instanceof Error ? error.message : String(error);
574
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
575
- }
576
- };
577
- const runConnect = (options, cwd) => {
578
- const parsed = parseFlags(options);
579
- if (parsed === undefined) {
580
- return { exitCode: 2, stdout: '', stderr: usage };
581
- }
582
- const allowed = new Set([
583
- '--id',
584
- '--kind',
585
- '--from',
586
- '--to',
587
- '--name',
588
- '--description',
589
- '--status',
590
- '--mode',
591
- '--content',
592
- '--reference',
593
- '--present-in',
594
- '--source',
595
- ]);
596
- if ([...parsed.flags.keys()].some((flag) => !allowed.has(flag)) ||
597
- hasRepeatedSingletonFlag(parsed.flags)) {
598
- return { exitCode: 2, stdout: '', stderr: usage };
599
- }
600
- const id = oneFlag(parsed.flags, '--id');
601
- const kind = oneFlag(parsed.flags, '--kind');
602
- const from = oneFlag(parsed.flags, '--from');
603
- const to = oneFlag(parsed.flags, '--to');
604
- if (id === undefined ||
605
- kind === undefined ||
606
- from === undefined ||
607
- to === undefined) {
608
- return { exitCode: 2, stdout: '', stderr: usage };
609
- }
610
- const references = identifiedReferences(parsed.flags.get('--reference') ?? []);
611
- if (references === undefined) {
612
- return { exitCode: 2, stdout: '', stderr: usage };
613
- }
614
- try {
615
- const absolutePath = resolve(cwd, parsed.path);
616
- const document = parseDocument(readFileSync(absolutePath, 'utf8'));
617
- appendBlockItem(document, 'relationships', {
618
- id,
619
- kind,
620
- from,
621
- to,
622
- ...(oneFlag(parsed.flags, '--name') === undefined
623
- ? {}
624
- : { name: oneFlag(parsed.flags, '--name') }),
625
- ...(oneFlag(parsed.flags, '--description') === undefined
626
- ? {}
627
- : { description: oneFlag(parsed.flags, '--description') }),
628
- ...(oneFlag(parsed.flags, '--status') === undefined
629
- ? {}
630
- : { status: oneFlag(parsed.flags, '--status') }),
631
- ...(oneFlag(parsed.flags, '--mode') === undefined
632
- ? {}
633
- : { mode: oneFlag(parsed.flags, '--mode') }),
634
- ...(oneFlag(parsed.flags, '--content') === undefined
635
- ? {}
636
- : { content: oneFlag(parsed.flags, '--content') }),
637
- ...(references.length === 0 ? {} : { references }),
638
- ...(parsed.flags.get('--present-in') === undefined
639
- ? {}
640
- : { presentIn: parsed.flags.get('--present-in') }),
641
- });
642
- const candidate = document.toString({ lineWidth: 0 });
643
- const companions = resolveCliWorkspaceSources(parsed.flags.get('--source') ?? [], cwd);
644
- if (!companions.ok) {
645
- return {
646
- exitCode: 1,
647
- stdout: humanDiagnostics(companions.diagnostics),
648
- stderr: '',
649
- };
650
- }
651
- const compilation = compileWorkspace([
652
- { path: parsed.path, source: candidate },
653
- ...companions.paths
654
- .filter((path) => resolve(cwd, path) !== resolve(cwd, parsed.path))
655
- .map((path) => ({
656
- path,
657
- source: readFileSync(resolve(cwd, path), 'utf8'),
658
- })),
659
- ]);
660
- if (!compilation.ok) {
661
- return {
662
- exitCode: 1,
663
- stdout: humanDiagnostics(compilation.diagnostics),
664
- stderr: '',
665
- };
666
- }
667
- writeFileSync(absolutePath, candidate, 'utf8');
668
- return {
669
- exitCode: 0,
670
- stdout: `Added relationship "${id}" to ${parsed.path}\n`,
671
- stderr: '',
672
- };
673
- }
674
- catch (error) {
675
- const message = error instanceof Error ? error.message : String(error);
676
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
677
- }
678
- };
679
- const runCompile = (options, cwd) => {
680
- if (options.length === 0 ||
681
- options.some((option) => option.startsWith('-'))) {
682
- return { exitCode: 2, stdout: '', stderr: usage };
683
- }
684
- try {
685
- const resolved = resolveCliWorkspaceSources(options, cwd);
686
- if (!resolved.ok) {
687
- return {
688
- exitCode: 1,
689
- stdout: humanDiagnostics(resolved.diagnostics),
690
- stderr: '',
691
- };
692
- }
693
- const result = compileWorkspace(resolved.paths.map((path) => ({
694
- path,
695
- source: readFileSync(resolve(cwd, path), 'utf8'),
696
- })));
697
- return result.ok
698
- ? {
699
- exitCode: 0,
700
- stdout: serializeSemanticGraph(result.graph),
701
- stderr: '',
702
- }
703
- : {
704
- exitCode: 1,
705
- stdout: humanDiagnostics(result.diagnostics),
706
- stderr: '',
707
- };
708
- }
709
- catch (error) {
710
- const message = error instanceof Error ? error.message : String(error);
711
- return { exitCode: 2, stdout: '', stderr: `${message}\n` };
712
- }
713
- };
164
+ // The seven-verb contract (docs/AGENT-INTERFACE.md): one verb per
165
+ // lifecycle stage — init creates, design fills, apply writes, ask reads,
166
+ // check gates, reconcile reports drift, export derives artifacts.
714
167
  export function runCli(args, cwd = process.cwd()) {
715
168
  const [command, ...options] = args;
716
169
  if (command === '--help' || command === '-h' || command === 'help') {
@@ -722,40 +175,23 @@ export function runCli(args, cwd = process.cwd()) {
722
175
  if (command === 'init') {
723
176
  return runInit(options, cwd);
724
177
  }
725
- if (command === 'add') {
726
- return runAdd(options, cwd);
727
- }
728
- if (command === 'connect') {
729
- return runConnect(options, cwd);
730
- }
731
- if (command === 'compile') {
732
- return runCompile(options, cwd);
178
+ if (command === 'design') {
179
+ return runDesignCommand(options, cwd);
733
180
  }
734
- if (command === 'context') {
735
- return options.includes('--subject')
736
- ? runAdHocContext(options, cwd)
737
- : runProjection(options, cwd, 'json');
181
+ if (command === 'apply') {
182
+ return runApplyCommand(options, cwd);
738
183
  }
739
- if (command === 'view') {
740
- return runProjection(options, cwd, 'markdown');
741
- }
742
- if (command === 'compare') {
743
- return runStateComparison(options, cwd);
744
- }
745
- if (command === 'evidence') {
746
- return runEvidence(options, cwd);
747
- }
748
- if (command === 'reconcile') {
749
- return runReconciliation(options, cwd);
184
+ if (command === 'ask') {
185
+ return runAskCommand(options, cwd);
750
186
  }
751
187
  if (command === 'check') {
752
188
  return runCheckCommand(options, cwd);
753
189
  }
754
- if (command === 'status') {
755
- return runStatusCommand(options, cwd);
190
+ if (command === 'reconcile') {
191
+ return runReconciliation(options, cwd);
756
192
  }
757
- if (command === 'new') {
758
- return runNewCommand(options, cwd);
193
+ if (command === 'export') {
194
+ return runExportCommand(options, cwd);
759
195
  }
760
196
  return { exitCode: 2, stdout: '', stderr: usage };
761
197
  }