yarramate 0.1.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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/adapter-mapping.d.ts +40 -0
  4. package/dist/adapter-mapping.js +200 -0
  5. package/dist/adapters/graphify-cli.d.ts +3 -0
  6. package/dist/adapters/graphify-cli.js +127 -0
  7. package/dist/adapters/graphify-entry.d.ts +1 -0
  8. package/dist/adapters/graphify-entry.js +1 -0
  9. package/dist/adapters/graphify.d.ts +23 -0
  10. package/dist/adapters/graphify.js +47 -0
  11. package/dist/adapters/likec4-cli.d.ts +3 -0
  12. package/dist/adapters/likec4-cli.js +662 -0
  13. package/dist/adapters/likec4-export.d.ts +25 -0
  14. package/dist/adapters/likec4-export.js +204 -0
  15. package/dist/adapters/likec4-kind-mapping.d.ts +22 -0
  16. package/dist/adapters/likec4-kind-mapping.js +60 -0
  17. package/dist/adapters/likec4-prepare.d.ts +28 -0
  18. package/dist/adapters/likec4-prepare.js +154 -0
  19. package/dist/adapters/likec4-project.d.ts +58 -0
  20. package/dist/adapters/likec4-project.js +176 -0
  21. package/dist/adapters/likec4.d.ts +4 -0
  22. package/dist/adapters/likec4.js +4 -0
  23. package/dist/architecture-state.d.ts +22 -0
  24. package/dist/architecture-state.js +63 -0
  25. package/dist/check-command.d.ts +2 -0
  26. package/dist/check-command.js +232 -0
  27. package/dist/cli-support.d.ts +24 -0
  28. package/dist/cli-support.js +81 -0
  29. package/dist/cli.d.ts +4 -0
  30. package/dist/cli.js +575 -0
  31. package/dist/compiler.d.ts +66 -0
  32. package/dist/compiler.js +940 -0
  33. package/dist/core-contract.d.ts +43 -0
  34. package/dist/core-contract.js +162 -0
  35. package/dist/evidence.d.ts +58 -0
  36. package/dist/evidence.js +161 -0
  37. package/dist/graph.d.ts +2 -0
  38. package/dist/graph.js +37 -0
  39. package/dist/index.d.ts +11 -0
  40. package/dist/index.js +9 -0
  41. package/dist/profile.d.ts +30 -0
  42. package/dist/profile.js +162 -0
  43. package/dist/projection.d.ts +42 -0
  44. package/dist/projection.js +183 -0
  45. package/dist/reconciliation.d.ts +26 -0
  46. package/dist/reconciliation.js +47 -0
  47. package/dist/source-document.d.ts +24 -0
  48. package/dist/source-document.js +80 -0
  49. package/dist/workspace.d.ts +29 -0
  50. package/dist/workspace.js +114 -0
  51. package/docs/CONSUMING-YARRAMATE.md +145 -0
  52. package/package.json +110 -0
  53. package/schema/yarramate-adapter-mapping.schema.json +59 -0
  54. package/schema/yarramate-check-result.schema.json +92 -0
  55. package/schema/yarramate-core-contract.schema.json +132 -0
  56. package/schema/yarramate-diagnostic-result.schema.json +64 -0
  57. package/schema/yarramate-document.schema.json +168 -0
  58. package/schema/yarramate-evidence-report.schema.json +73 -0
  59. package/schema/yarramate-evidence.schema.json +92 -0
  60. package/schema/yarramate-graph-v2.schema.json +170 -0
  61. package/schema/yarramate-likec4-check-result.schema.json +50 -0
  62. package/schema/yarramate-likec4-diagnostic-result.schema.json +69 -0
  63. package/schema/yarramate-likec4-generated-project-v2.schema.json +100 -0
  64. package/schema/yarramate-likec4-generated-project.schema.json +76 -0
  65. package/schema/yarramate-likec4-kind-mapping.schema.json +65 -0
  66. package/schema/yarramate-likec4-project.schema.json +160 -0
  67. package/schema/yarramate-profile.schema.json +113 -0
  68. package/schema/yarramate-projection-result.schema.json +177 -0
  69. package/schema/yarramate-projection.schema.json +126 -0
  70. package/schema/yarramate-reconciliation-report.schema.json +90 -0
  71. package/schema/yarramate-state-comparison.schema.json +61 -0
  72. package/schema/yarramate-workspace.schema.json +55 -0
  73. package/skills/yarramate-architecture/SKILL.md +135 -0
  74. package/skills/yarramate-architecture/agents/openai.yaml +4 -0
  75. package/skills/yarramate-architecture/references/journey-checklists.md +57 -0
  76. package/skills/yarramate-architecture/references/native-authoring.md +167 -0
package/dist/cli.js ADDED
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, } from 'node:fs';
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, } from './cli-support.js';
9
+ import { runCheckCommand } from './check-command.js';
10
+ import { evaluateEvidence, evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
11
+ import { reconcileEvidenceReports } from './reconciliation.js';
12
+ import { loadWorkspaceManifest } from './workspace.js';
13
+ import { evaluateProjection, loadProjection, renderProjectionMarkdown, } from './projection.js';
14
+ const runProjection = (options, cwd, output) => {
15
+ const [projectionPath, ...paths] = options;
16
+ if (projectionPath === undefined ||
17
+ paths.length === 0 ||
18
+ options.some((option) => option.startsWith('-'))) {
19
+ return { exitCode: 2, stdout: '', stderr: usage };
20
+ }
21
+ try {
22
+ const resolved = resolveCliWorkspaceSources(paths, cwd);
23
+ if (!resolved.ok) {
24
+ return {
25
+ exitCode: 1,
26
+ stdout: diagnosticJson(resolved.diagnostics),
27
+ stderr: '',
28
+ };
29
+ }
30
+ const loaded = loadProjection({
31
+ path: projectionPath,
32
+ source: readFileSync(resolve(cwd, projectionPath), 'utf8'),
33
+ });
34
+ if (!loaded.ok) {
35
+ return {
36
+ exitCode: 1,
37
+ stdout: diagnosticJson(loaded.diagnostics),
38
+ stderr: '',
39
+ };
40
+ }
41
+ const compilation = compileWorkspaceWithProfileContext(resolved.paths.map((path) => ({
42
+ path,
43
+ source: readFileSync(resolve(cwd, path), 'utf8'),
44
+ })));
45
+ if (!compilation.ok) {
46
+ return {
47
+ exitCode: 1,
48
+ stdout: diagnosticJson(compilation.diagnostics),
49
+ stderr: '',
50
+ };
51
+ }
52
+ const result = evaluateProjection(compilation.graph, loaded.projection, compilation.profileContext);
53
+ return {
54
+ exitCode: 0,
55
+ stdout: output === 'json'
56
+ ? `${JSON.stringify(result, null, 2)}\n`
57
+ : renderProjectionMarkdown(result),
58
+ stderr: '',
59
+ };
60
+ }
61
+ catch (error) {
62
+ const message = error instanceof Error ? error.message : String(error);
63
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
64
+ }
65
+ };
66
+ const runEvidence = (options, cwd) => {
67
+ const [evidencePath, ...paths] = options;
68
+ if (evidencePath === undefined ||
69
+ paths.length === 0 ||
70
+ options.some((option) => option.startsWith('-'))) {
71
+ return { exitCode: 2, stdout: '', stderr: usage };
72
+ }
73
+ try {
74
+ const resolved = resolveCliWorkspaceSources(paths, cwd);
75
+ if (!resolved.ok) {
76
+ return {
77
+ exitCode: 1,
78
+ stdout: diagnosticJson(resolved.diagnostics),
79
+ stderr: '',
80
+ };
81
+ }
82
+ const loaded = loadEvidence({
83
+ path: evidencePath,
84
+ source: readFileSync(resolve(cwd, evidencePath), 'utf8'),
85
+ });
86
+ if (!loaded.ok) {
87
+ return {
88
+ exitCode: 1,
89
+ stdout: diagnosticJson(loaded.diagnostics),
90
+ stderr: '',
91
+ };
92
+ }
93
+ const compilation = compileWorkspace(resolved.paths.map((path) => ({
94
+ path,
95
+ source: readFileSync(resolve(cwd, path), 'utf8'),
96
+ })));
97
+ if (!compilation.ok) {
98
+ return {
99
+ exitCode: 1,
100
+ stdout: diagnosticJson(compilation.diagnostics),
101
+ stderr: '',
102
+ };
103
+ }
104
+ const evaluation = evaluateEvidence(compilation.graph, loaded.evidence);
105
+ return evaluation.ok
106
+ ? {
107
+ exitCode: 0,
108
+ stdout: `${JSON.stringify(evaluation.report, null, 2)}\n`,
109
+ stderr: '',
110
+ }
111
+ : {
112
+ exitCode: 1,
113
+ stdout: diagnosticJson(evaluation.diagnostics),
114
+ stderr: '',
115
+ };
116
+ }
117
+ catch (error) {
118
+ const message = error instanceof Error ? error.message : String(error);
119
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
120
+ }
121
+ };
122
+ const runReconciliation = (options, cwd) => {
123
+ const [workspacePath] = options;
124
+ if (options.length !== 1 ||
125
+ workspacePath === undefined ||
126
+ workspacePath.startsWith('-')) {
127
+ return { exitCode: 2, stdout: '', stderr: usage };
128
+ }
129
+ try {
130
+ const loadedWorkspace = loadWorkspaceManifest({
131
+ path: workspacePath,
132
+ source: readFileSync(resolve(cwd, workspacePath), 'utf8'),
133
+ }, cwd);
134
+ if (!loadedWorkspace.ok) {
135
+ return {
136
+ exitCode: 1,
137
+ stdout: diagnosticJson(loadedWorkspace.diagnostics),
138
+ stderr: '',
139
+ };
140
+ }
141
+ const sourcePaths = [
142
+ ...loadedWorkspace.workspace.profiles,
143
+ ...loadedWorkspace.workspace.documents,
144
+ ];
145
+ const compilation = compileWorkspace(sourcePaths.map((path) => ({
146
+ path,
147
+ source: readFileSync(resolve(cwd, path), 'utf8'),
148
+ })));
149
+ if (!compilation.ok) {
150
+ return {
151
+ exitCode: 1,
152
+ stdout: diagnosticJson(compilation.diagnostics),
153
+ stderr: '',
154
+ };
155
+ }
156
+ const evidenceDocuments = [];
157
+ for (const path of loadedWorkspace.workspace.evidence) {
158
+ const loaded = loadEvidence({
159
+ path,
160
+ source: readFileSync(resolve(cwd, path), 'utf8'),
161
+ });
162
+ if (!loaded.ok) {
163
+ return {
164
+ exitCode: 1,
165
+ stdout: diagnosticJson(loaded.diagnostics),
166
+ stderr: '',
167
+ };
168
+ }
169
+ evidenceDocuments.push(loaded.evidence);
170
+ }
171
+ const evaluation = evaluateEvidenceWorkspace(compilation.graph, evidenceDocuments);
172
+ if (!evaluation.ok) {
173
+ return {
174
+ exitCode: 1,
175
+ stdout: diagnosticJson(evaluation.diagnostics),
176
+ stderr: '',
177
+ };
178
+ }
179
+ return {
180
+ exitCode: 0,
181
+ stdout: `${JSON.stringify(reconcileEvidenceReports(loadedWorkspace.workspace.id, evaluation.reports), null, 2)}\n`,
182
+ stderr: '',
183
+ };
184
+ }
185
+ catch (error) {
186
+ const message = error instanceof Error ? error.message : String(error);
187
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
188
+ }
189
+ };
190
+ const runStateComparison = (options, cwd) => {
191
+ const [from, to, ...paths] = options;
192
+ if (from === undefined ||
193
+ to === undefined ||
194
+ paths.length === 0 ||
195
+ options.some((option) => option.startsWith('-'))) {
196
+ return { exitCode: 2, stdout: '', stderr: usage };
197
+ }
198
+ try {
199
+ const resolved = resolveCliWorkspaceSources(paths, cwd);
200
+ if (!resolved.ok) {
201
+ return {
202
+ exitCode: 1,
203
+ stdout: diagnosticJson(resolved.diagnostics),
204
+ stderr: '',
205
+ };
206
+ }
207
+ const compilation = compileWorkspace(resolved.paths.map((path) => ({
208
+ path,
209
+ source: readFileSync(resolve(cwd, path), 'utf8'),
210
+ })));
211
+ if (!compilation.ok) {
212
+ return {
213
+ exitCode: 1,
214
+ stdout: diagnosticJson(compilation.diagnostics),
215
+ stderr: '',
216
+ };
217
+ }
218
+ const comparison = compareArchitectureStates(compilation.graph, from, to);
219
+ return comparison.ok
220
+ ? {
221
+ exitCode: 0,
222
+ stdout: `${JSON.stringify(comparison.comparison, null, 2)}\n`,
223
+ stderr: '',
224
+ }
225
+ : {
226
+ exitCode: 2,
227
+ stdout: '',
228
+ stderr: `${comparison.issues.map(({ message }) => message).join('\n')}\n`,
229
+ };
230
+ }
231
+ catch (error) {
232
+ const message = error instanceof Error ? error.message : String(error);
233
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
234
+ }
235
+ };
236
+ const runInit = (options, cwd) => {
237
+ const target = options[0];
238
+ if (options.length !== 1 ||
239
+ target === undefined ||
240
+ target.startsWith('-')) {
241
+ return { exitCode: 2, stdout: '', stderr: usage };
242
+ }
243
+ const workspaceRoot = resolve(cwd, target);
244
+ const documentPath = resolve(workspaceRoot, '.yarramate/architecture/main.yaml');
245
+ const manifestPath = resolve(workspaceRoot, '.yarramate/workspace.yaml');
246
+ const displayPath = relative(cwd, documentPath);
247
+ const displayManifestPath = relative(cwd, manifestPath);
248
+ const existing = [
249
+ ...(existsSync(documentPath) ? [displayPath] : []),
250
+ ...(existsSync(manifestPath) ? [displayManifestPath] : []),
251
+ ];
252
+ if (existing.length > 0) {
253
+ return {
254
+ exitCode: 2,
255
+ stdout: '',
256
+ stderr: `${existing.join(' and ')} ${existing.length === 1 ? 'already exists' : 'already exist'}; nothing was changed\n`,
257
+ };
258
+ }
259
+ mkdirSync(dirname(documentPath), { recursive: true });
260
+ writeFileSync(documentPath, 'format: yarramate/v1\n' +
261
+ 'id: main\n' +
262
+ 'profile: yarramate/core@0.1\n' +
263
+ 'concepts: []\n' +
264
+ 'relationships: []\n', 'utf8');
265
+ writeFileSync(manifestPath, 'format: yarramate/workspace/v1\n' +
266
+ 'id: main\n' +
267
+ 'documents:\n' +
268
+ ' - architecture/*.yaml\n' +
269
+ 'profiles: []\n' +
270
+ 'projections: []\n' +
271
+ 'adapterMappings: []\n' +
272
+ 'evidence: []\n', 'utf8');
273
+ return {
274
+ exitCode: 0,
275
+ stdout: `Created ${displayPath} and ${displayManifestPath}\n`,
276
+ stderr: '',
277
+ };
278
+ };
279
+ const parseFlags = (options) => {
280
+ const [path, ...rest] = options;
281
+ if (path === undefined || path.startsWith('-') || rest.length % 2 !== 0) {
282
+ return undefined;
283
+ }
284
+ const flags = new Map();
285
+ for (let index = 0; index < rest.length; index += 2) {
286
+ const flag = rest[index];
287
+ const value = rest[index + 1];
288
+ if (flag === undefined ||
289
+ value === undefined ||
290
+ !flag.startsWith('--') ||
291
+ value.startsWith('--')) {
292
+ return undefined;
293
+ }
294
+ flags.set(flag, [...(flags.get(flag) ?? []), value]);
295
+ }
296
+ return { path, flags };
297
+ };
298
+ const oneFlag = (flags, flag) => {
299
+ const values = flags.get(flag);
300
+ return values?.length === 1 ? values[0] : undefined;
301
+ };
302
+ const hasRepeatedSingletonFlag = (flags) => [...flags.entries()].some(([flag, values]) => flag !== '--source' &&
303
+ flag !== '--constraint' &&
304
+ flag !== '--present-in' &&
305
+ values.length !== 1);
306
+ const appendBlockItem = (document, collection, item) => {
307
+ document.addIn([collection], item);
308
+ const sequence = document.getIn([collection], true);
309
+ if (isSeq(sequence)) {
310
+ sequence.flow = false;
311
+ }
312
+ };
313
+ const runAdd = (options, cwd) => {
314
+ const parsed = parseFlags(options);
315
+ if (parsed === undefined) {
316
+ return { exitCode: 2, stdout: '', stderr: usage };
317
+ }
318
+ const allowed = new Set([
319
+ '--id',
320
+ '--kind',
321
+ '--name',
322
+ '--status',
323
+ '--description',
324
+ '--owner',
325
+ '--constraint',
326
+ '--present-in',
327
+ '--source',
328
+ ]);
329
+ if ([...parsed.flags.keys()].some((flag) => !allowed.has(flag)) ||
330
+ hasRepeatedSingletonFlag(parsed.flags)) {
331
+ return { exitCode: 2, stdout: '', stderr: usage };
332
+ }
333
+ const id = oneFlag(parsed.flags, '--id');
334
+ const kind = oneFlag(parsed.flags, '--kind');
335
+ const name = oneFlag(parsed.flags, '--name');
336
+ if (id === undefined || kind === undefined || name === undefined) {
337
+ return { exitCode: 2, stdout: '', stderr: usage };
338
+ }
339
+ const constraints = (parsed.flags.get('--constraint') ?? []).map((value) => {
340
+ const separator = value.indexOf('=');
341
+ return separator <= 0 || separator === value.length - 1
342
+ ? undefined
343
+ : {
344
+ id: value.slice(0, separator),
345
+ ref: value.slice(separator + 1),
346
+ };
347
+ });
348
+ if (constraints.some((constraint) => constraint === undefined)) {
349
+ return { exitCode: 2, stdout: '', stderr: usage };
350
+ }
351
+ try {
352
+ const absolutePath = resolve(cwd, parsed.path);
353
+ const document = parseDocument(readFileSync(absolutePath, 'utf8'));
354
+ appendBlockItem(document, 'concepts', {
355
+ id,
356
+ kind,
357
+ name,
358
+ ...(oneFlag(parsed.flags, '--description') === undefined
359
+ ? {}
360
+ : { description: oneFlag(parsed.flags, '--description') }),
361
+ ...(oneFlag(parsed.flags, '--status') === undefined
362
+ ? {}
363
+ : { status: oneFlag(parsed.flags, '--status') }),
364
+ ...(oneFlag(parsed.flags, '--owner') === undefined
365
+ ? {}
366
+ : { owner: oneFlag(parsed.flags, '--owner') }),
367
+ ...(constraints.length === 0 ? {} : { constraints }),
368
+ ...(parsed.flags.get('--present-in') === undefined
369
+ ? {}
370
+ : { presentIn: parsed.flags.get('--present-in') }),
371
+ });
372
+ const candidate = document.toString({ lineWidth: 0 });
373
+ const companions = resolveCliWorkspaceSources(parsed.flags.get('--source') ?? [], cwd);
374
+ if (!companions.ok) {
375
+ return {
376
+ exitCode: 1,
377
+ stdout: humanDiagnostics(companions.diagnostics),
378
+ stderr: '',
379
+ };
380
+ }
381
+ const compilation = compileWorkspace([
382
+ { path: parsed.path, source: candidate },
383
+ ...companions.paths
384
+ .filter((path) => resolve(cwd, path) !== resolve(cwd, parsed.path))
385
+ .map((path) => ({
386
+ path,
387
+ source: readFileSync(resolve(cwd, path), 'utf8'),
388
+ })),
389
+ ]);
390
+ if (!compilation.ok) {
391
+ return {
392
+ exitCode: 1,
393
+ stdout: humanDiagnostics(compilation.diagnostics),
394
+ stderr: '',
395
+ };
396
+ }
397
+ writeFileSync(absolutePath, candidate, 'utf8');
398
+ return {
399
+ exitCode: 0,
400
+ stdout: `Added concept "${id}" to ${parsed.path}\n`,
401
+ stderr: '',
402
+ };
403
+ }
404
+ catch (error) {
405
+ const message = error instanceof Error ? error.message : String(error);
406
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
407
+ }
408
+ };
409
+ const runConnect = (options, cwd) => {
410
+ const parsed = parseFlags(options);
411
+ if (parsed === undefined) {
412
+ return { exitCode: 2, stdout: '', stderr: usage };
413
+ }
414
+ const allowed = new Set([
415
+ '--id',
416
+ '--kind',
417
+ '--from',
418
+ '--to',
419
+ '--name',
420
+ '--status',
421
+ '--mode',
422
+ '--content',
423
+ '--present-in',
424
+ '--source',
425
+ ]);
426
+ if ([...parsed.flags.keys()].some((flag) => !allowed.has(flag)) ||
427
+ hasRepeatedSingletonFlag(parsed.flags)) {
428
+ return { exitCode: 2, stdout: '', stderr: usage };
429
+ }
430
+ const id = oneFlag(parsed.flags, '--id');
431
+ const kind = oneFlag(parsed.flags, '--kind');
432
+ const from = oneFlag(parsed.flags, '--from');
433
+ const to = oneFlag(parsed.flags, '--to');
434
+ if (id === undefined ||
435
+ kind === undefined ||
436
+ from === undefined ||
437
+ to === undefined) {
438
+ return { exitCode: 2, stdout: '', stderr: usage };
439
+ }
440
+ try {
441
+ const absolutePath = resolve(cwd, parsed.path);
442
+ const document = parseDocument(readFileSync(absolutePath, 'utf8'));
443
+ appendBlockItem(document, 'relationships', {
444
+ id,
445
+ kind,
446
+ from,
447
+ to,
448
+ ...(oneFlag(parsed.flags, '--name') === undefined
449
+ ? {}
450
+ : { name: oneFlag(parsed.flags, '--name') }),
451
+ ...(oneFlag(parsed.flags, '--status') === undefined
452
+ ? {}
453
+ : { status: oneFlag(parsed.flags, '--status') }),
454
+ ...(oneFlag(parsed.flags, '--mode') === undefined
455
+ ? {}
456
+ : { mode: oneFlag(parsed.flags, '--mode') }),
457
+ ...(oneFlag(parsed.flags, '--content') === undefined
458
+ ? {}
459
+ : { content: oneFlag(parsed.flags, '--content') }),
460
+ ...(parsed.flags.get('--present-in') === undefined
461
+ ? {}
462
+ : { presentIn: parsed.flags.get('--present-in') }),
463
+ });
464
+ const candidate = document.toString({ lineWidth: 0 });
465
+ const companions = resolveCliWorkspaceSources(parsed.flags.get('--source') ?? [], cwd);
466
+ if (!companions.ok) {
467
+ return {
468
+ exitCode: 1,
469
+ stdout: humanDiagnostics(companions.diagnostics),
470
+ stderr: '',
471
+ };
472
+ }
473
+ const compilation = compileWorkspace([
474
+ { path: parsed.path, source: candidate },
475
+ ...companions.paths
476
+ .filter((path) => resolve(cwd, path) !== resolve(cwd, parsed.path))
477
+ .map((path) => ({
478
+ path,
479
+ source: readFileSync(resolve(cwd, path), 'utf8'),
480
+ })),
481
+ ]);
482
+ if (!compilation.ok) {
483
+ return {
484
+ exitCode: 1,
485
+ stdout: humanDiagnostics(compilation.diagnostics),
486
+ stderr: '',
487
+ };
488
+ }
489
+ writeFileSync(absolutePath, candidate, 'utf8');
490
+ return {
491
+ exitCode: 0,
492
+ stdout: `Added relationship "${id}" to ${parsed.path}\n`,
493
+ stderr: '',
494
+ };
495
+ }
496
+ catch (error) {
497
+ const message = error instanceof Error ? error.message : String(error);
498
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
499
+ }
500
+ };
501
+ const runCompile = (options, cwd) => {
502
+ if (options.length === 0 ||
503
+ options.some((option) => option.startsWith('-'))) {
504
+ return { exitCode: 2, stdout: '', stderr: usage };
505
+ }
506
+ try {
507
+ const resolved = resolveCliWorkspaceSources(options, cwd);
508
+ if (!resolved.ok) {
509
+ return {
510
+ exitCode: 1,
511
+ stdout: humanDiagnostics(resolved.diagnostics),
512
+ stderr: '',
513
+ };
514
+ }
515
+ const result = compileWorkspace(resolved.paths.map((path) => ({
516
+ path,
517
+ source: readFileSync(resolve(cwd, path), 'utf8'),
518
+ })));
519
+ return result.ok
520
+ ? {
521
+ exitCode: 0,
522
+ stdout: serializeSemanticGraph(result.graph),
523
+ stderr: '',
524
+ }
525
+ : {
526
+ exitCode: 1,
527
+ stdout: humanDiagnostics(result.diagnostics),
528
+ stderr: '',
529
+ };
530
+ }
531
+ catch (error) {
532
+ const message = error instanceof Error ? error.message : String(error);
533
+ return { exitCode: 2, stdout: '', stderr: `${message}\n` };
534
+ }
535
+ };
536
+ export function runCli(args, cwd = process.cwd()) {
537
+ const [command, ...options] = args;
538
+ if (command === 'init') {
539
+ return runInit(options, cwd);
540
+ }
541
+ if (command === 'add') {
542
+ return runAdd(options, cwd);
543
+ }
544
+ if (command === 'connect') {
545
+ return runConnect(options, cwd);
546
+ }
547
+ if (command === 'compile') {
548
+ return runCompile(options, cwd);
549
+ }
550
+ if (command === 'context') {
551
+ return runProjection(options, cwd, 'json');
552
+ }
553
+ if (command === 'view') {
554
+ return runProjection(options, cwd, 'markdown');
555
+ }
556
+ if (command === 'compare') {
557
+ return runStateComparison(options, cwd);
558
+ }
559
+ if (command === 'evidence') {
560
+ return runEvidence(options, cwd);
561
+ }
562
+ if (command === 'reconcile') {
563
+ return runReconciliation(options, cwd);
564
+ }
565
+ if (command === 'check') {
566
+ return runCheckCommand(options, cwd);
567
+ }
568
+ return { exitCode: 2, stdout: '', stderr: usage };
569
+ }
570
+ if (isMainModule(import.meta.url, process.argv[1])) {
571
+ const result = runCli(process.argv.slice(2));
572
+ process.stdout.write(result.stdout);
573
+ process.stderr.write(result.stderr);
574
+ process.exitCode = result.exitCode;
575
+ }
@@ -0,0 +1,66 @@
1
+ export interface WorkspaceSource {
2
+ readonly path: string;
3
+ readonly source: string;
4
+ }
5
+ export interface Diagnostic {
6
+ readonly severity: 'error';
7
+ readonly code: string;
8
+ readonly message: string;
9
+ readonly path: string;
10
+ readonly pointer: string;
11
+ readonly line: number;
12
+ readonly column: number;
13
+ }
14
+ export interface GraphSource {
15
+ readonly document: string;
16
+ readonly path: string;
17
+ readonly pointer: string;
18
+ readonly line: number;
19
+ readonly column: number;
20
+ }
21
+ export interface GraphClaim {
22
+ readonly id: string;
23
+ readonly subject: string;
24
+ readonly predicate: string;
25
+ readonly object: {
26
+ readonly ref: string;
27
+ } | {
28
+ readonly value: string;
29
+ };
30
+ readonly origin: 'declared';
31
+ readonly source: GraphSource;
32
+ }
33
+ export interface SemanticGraph {
34
+ readonly format: 'yarramate/graph/v2';
35
+ readonly profiles: readonly string[];
36
+ readonly documents: ReadonlyArray<{
37
+ readonly id: string;
38
+ readonly source: string;
39
+ }>;
40
+ readonly subjects: ReadonlyArray<{
41
+ readonly id: string;
42
+ readonly type: 'concept' | 'relationship';
43
+ }>;
44
+ readonly claims: readonly GraphClaim[];
45
+ }
46
+ export interface ResolvedProfileContext {
47
+ readonly conceptKindLineages: ReadonlyMap<string, readonly string[]>;
48
+ readonly relationshipKindLineages: ReadonlyMap<string, readonly string[]>;
49
+ }
50
+ export type CompilationResult = {
51
+ readonly ok: true;
52
+ readonly graph: SemanticGraph;
53
+ } | {
54
+ readonly ok: false;
55
+ readonly diagnostics: readonly Diagnostic[];
56
+ };
57
+ export type ContextualCompilationResult = {
58
+ readonly ok: true;
59
+ readonly graph: SemanticGraph;
60
+ readonly profileContext: ResolvedProfileContext;
61
+ } | {
62
+ readonly ok: false;
63
+ readonly diagnostics: readonly Diagnostic[];
64
+ };
65
+ export declare function compileWorkspace(sources: readonly WorkspaceSource[]): CompilationResult;
66
+ export declare const compileWorkspaceWithProfileContext: (sources: readonly WorkspaceSource[]) => ContextualCompilationResult;