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.
- package/README.md +39 -10
- package/catalogues/core-enrichment.yaml +700 -0
- package/dist/adapters/likec4-cli.js +179 -55
- package/dist/adapters/likec4-export.d.ts +1 -1
- package/dist/adapters/likec4-prepare.d.ts +6 -0
- package/dist/adapters/likec4-prepare.js +40 -2
- package/dist/adapters/mcp-cli.js +55 -49
- package/dist/apply-command.d.ts +2 -0
- package/dist/apply-command.js +199 -0
- package/dist/{new-command.d.ts → ask-command.d.ts} +1 -1
- package/dist/ask-command.js +729 -0
- package/dist/brief.d.ts +3 -0
- package/dist/brief.js +235 -0
- package/dist/check-command.js +70 -4
- package/dist/cli-support.d.ts +4 -1
- package/dist/cli-support.js +3 -2
- package/dist/cli.js +22 -586
- package/dist/compiler.js +45 -7
- package/dist/core-contract.d.ts +1 -1
- package/dist/{status-command.d.ts → design-command.d.ts} +1 -1
- package/dist/design-command.js +218 -0
- package/dist/export-command.d.ts +2 -0
- package/dist/export-command.js +228 -0
- package/dist/interrogate-command.d.ts +104 -0
- package/dist/interrogate-command.js +268 -0
- package/dist/next-command.d.ts +22 -0
- package/dist/next-command.js +167 -0
- package/dist/reconciliation.d.ts +2 -0
- package/dist/reconciliation.js +38 -0
- package/docs/CONSUMING-YARRAMATE.md +28 -21
- package/package.json +13 -10
- package/schema/yarramate-apply-result.schema.json +32 -0
- package/schema/yarramate-ask-result.schema.json +391 -0
- package/schema/yarramate-check-result.schema.json +12 -0
- package/schema/yarramate-core-contract.schema.json +5 -10
- package/schema/yarramate-design-step.schema.json +152 -0
- package/schema/yarramate-document.schema.json +76 -9
- package/schema/yarramate-interrogation-report.schema.json +89 -0
- package/schema/yarramate-likec4-generated-project-v2.schema.json +6 -0
- package/schema/yarramate-likec4-generated-project.schema.json +6 -0
- package/schema/yarramate-operations.schema.json +303 -0
- package/schema/yarramate-question-catalogue.schema.json +431 -0
- package/schema/yarramate-reconciliation-report.schema.json +8 -2
- package/skills/yarramate-architecture/SKILL.md +42 -17
- package/skills/yarramate-architecture/references/native-authoring.md +73 -17
- package/dist/new-command.js +0 -111
- package/dist/status-command.js +0 -171
- 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 {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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 {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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 === '
|
|
726
|
-
return
|
|
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 === '
|
|
735
|
-
return options
|
|
736
|
-
? runAdHocContext(options, cwd)
|
|
737
|
-
: runProjection(options, cwd, 'json');
|
|
181
|
+
if (command === 'apply') {
|
|
182
|
+
return runApplyCommand(options, cwd);
|
|
738
183
|
}
|
|
739
|
-
if (command === '
|
|
740
|
-
return
|
|
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 === '
|
|
755
|
-
return
|
|
190
|
+
if (command === 'reconcile') {
|
|
191
|
+
return runReconciliation(options, cwd);
|
|
756
192
|
}
|
|
757
|
-
if (command === '
|
|
758
|
-
return
|
|
193
|
+
if (command === 'export') {
|
|
194
|
+
return runExportCommand(options, cwd);
|
|
759
195
|
}
|
|
760
196
|
return { exitCode: 2, stdout: '', stderr: usage };
|
|
761
197
|
}
|