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
@@ -0,0 +1,3 @@
1
+ import type { ResolvedProfileContext } from './compiler.js';
2
+ import type { ProjectionResult } from './projection.js';
3
+ export declare function renderBrief(result: ProjectionResult, profileContext?: ResolvedProfileContext, budgetTokens?: number): string;
package/dist/brief.js ADDED
@@ -0,0 +1,235 @@
1
+ import { conceptKinds } from './profile.js';
2
+ const coreKindNames = new Map(conceptKinds.map(({ id, name }) => [id, name]));
3
+ const motivationKindIds = new Set(conceptKinds
4
+ .filter(({ layer }) => layer === 'motivation')
5
+ .map(({ id }) => id));
6
+ const claimValue = (claims, subject, predicate) => {
7
+ const object = claims.find((claim) => claim.subject === subject && claim.predicate === predicate)?.object;
8
+ return object !== undefined && 'value' in object ? object.value : undefined;
9
+ };
10
+ const claimReferences = (claims, subject, predicate) => claims.flatMap((claim) => claim.subject === subject &&
11
+ claim.predicate === predicate &&
12
+ 'ref' in claim.object
13
+ ? [claim.object.ref]
14
+ : []);
15
+ // Resolve a qualified kind to its nearest core-profile local id through
16
+ // declared lineage, mirroring how `next` orients relationships (ADR 0048).
17
+ const coreLocalKind = (kind, lineages) => {
18
+ for (const candidate of [kind, ...(lineages?.get(kind) ?? [])]) {
19
+ const separator = candidate.indexOf('#');
20
+ if (separator !== -1 && candidate.startsWith('yarramate/core@')) {
21
+ return candidate.slice(separator + 1);
22
+ }
23
+ }
24
+ return undefined;
25
+ };
26
+ const humanizeKind = (kind) => {
27
+ const local = kind.slice(kind.indexOf('#') + 1);
28
+ return local
29
+ .replaceAll(/([a-z0-9])([A-Z])/g, '$1 $2')
30
+ .replaceAll('-', ' ')
31
+ .toLowerCase();
32
+ };
33
+ const article = (reading) => /^[aeiou]/.test(reading) ? 'an' : 'a';
34
+ const sentenceEnd = (text) => /[.!?]["')\]]*$/.test(text.trimEnd()) ? text.trimEnd() : `${text.trimEnd()}.`;
35
+ const listPhrase = (items) => items.length <= 1
36
+ ? (items[0] ?? '')
37
+ : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`;
38
+ // Phrase forms are the prose readings of each core relationship kind's
39
+ // declared intent — the same table `next` reads for ordering, spoken
40
+ // from the source's perspective.
41
+ const relationshipPhrase = (coreKind, fallbackKind, mode, content) => {
42
+ switch (coreKind) {
43
+ case 'serving':
44
+ return 'serves';
45
+ case 'access':
46
+ return mode === 'read'
47
+ ? 'reads'
48
+ : mode === 'write'
49
+ ? 'writes'
50
+ : mode === 'read-write'
51
+ ? 'reads and writes'
52
+ : 'accesses';
53
+ case 'realization':
54
+ return 'realizes';
55
+ case 'composition':
56
+ return 'comprises';
57
+ case 'aggregation':
58
+ return 'aggregates';
59
+ case 'assignment':
60
+ return 'is assigned to';
61
+ case 'triggering':
62
+ return 'triggers';
63
+ case 'flow':
64
+ return content === undefined ? 'flows to' : `sends ${content} to`;
65
+ case 'specialization':
66
+ return 'specializes';
67
+ case 'influence':
68
+ return 'influences';
69
+ case 'association':
70
+ return 'is associated with';
71
+ default:
72
+ return humanizeKind(fallbackKind);
73
+ }
74
+ };
75
+ const estimateTokens = (text) => Math.ceil(text.length / 4);
76
+ export function renderBrief(result, profileContext, budgetTokens) {
77
+ const stateIds = new Set(result.claims
78
+ .filter(({ predicate }) => predicate === 'yarramate/state/type')
79
+ .map(({ subject }) => subject));
80
+ const concepts = result.subjects.filter(({ id, type }) => type === 'concept' && !stateIds.has(id));
81
+ const nameOf = (id) => claimValue(result.claims, id, 'yarramate/concept/name') ?? id;
82
+ const kindOf = (id) => claimValue(result.claims, id, 'yarramate/concept/kind');
83
+ const statusOf = (id) => claimValue(result.claims, id, 'yarramate/lifecycle/status');
84
+ const descriptionOf = (id) => claimValue(result.claims, id, 'yarramate/concept/description');
85
+ const kindReading = (kind) => {
86
+ const core = coreLocalKind(kind, profileContext?.conceptKindLineages);
87
+ const coreName = core === undefined ? undefined : coreKindNames.get(core);
88
+ return kind.startsWith('yarramate/core@') && coreName !== undefined
89
+ ? coreName.toLowerCase()
90
+ : humanizeKind(kind);
91
+ };
92
+ const isMotivation = (id) => {
93
+ const kind = kindOf(id);
94
+ if (kind === undefined)
95
+ return false;
96
+ const core = coreLocalKind(kind, profileContext?.conceptKindLineages);
97
+ return core !== undefined && motivationKindIds.has(core);
98
+ };
99
+ const outgoing = new Map();
100
+ for (const subject of result.subjects) {
101
+ if (subject.type !== 'relationship')
102
+ continue;
103
+ const claim = result.claims.find(({ id, object }) => id === subject.id && 'ref' in object);
104
+ if (claim === undefined || !('ref' in claim.object))
105
+ continue;
106
+ const description = claimValue(result.claims, subject.id, 'yarramate/relationship/description');
107
+ const entry = {
108
+ phrase: relationshipPhrase(coreLocalKind(claim.predicate, profileContext?.relationshipKindLineages), claim.predicate, claimValue(result.claims, subject.id, 'yarramate/access/mode'), claimValue(result.claims, subject.id, 'yarramate/flow/content')),
109
+ target: claim.object.ref,
110
+ ...(description === undefined ? {} : { description }),
111
+ };
112
+ outgoing.set(claim.subject, [
113
+ ...(outgoing.get(claim.subject) ?? []),
114
+ entry,
115
+ ]);
116
+ }
117
+ const relationshipSentences = (id) => {
118
+ const entries = outgoing.get(id) ?? [];
119
+ const grouped = new Map();
120
+ const sentences = [];
121
+ for (const entry of entries) {
122
+ const target = `"${nameOf(entry.target)}"`;
123
+ if (entry.description !== undefined) {
124
+ sentences.push(`It ${entry.phrase} ${target} (${entry.description}).`);
125
+ continue;
126
+ }
127
+ grouped.set(entry.phrase, [...(grouped.get(entry.phrase) ?? []), target]);
128
+ }
129
+ return [
130
+ ...[...grouped].map(([phrase, targets]) => `It ${phrase} ${listPhrase(targets)}.`),
131
+ ...sentences,
132
+ ];
133
+ };
134
+ const supportSentences = (id) => {
135
+ const sentences = [];
136
+ const constraints = claimReferences(result.claims, id, 'yarramate/constraint/requires');
137
+ if (constraints.length > 0) {
138
+ sentences.push(`Constrained by ${listPhrase(constraints.map((constraint) => `"${nameOf(constraint)}"`))}.`);
139
+ }
140
+ const owner = claimReferences(result.claims, id, 'yarramate/ownership/owner')[0];
141
+ if (owner !== undefined) {
142
+ sentences.push(`Owned by "${nameOf(owner)}".`);
143
+ }
144
+ return sentences;
145
+ };
146
+ const motivationParagraph = (id) => {
147
+ const kind = kindOf(id) ?? '';
148
+ const reading = kindReading(kind);
149
+ const label = reading.charAt(0).toUpperCase() + reading.slice(1);
150
+ const description = descriptionOf(id);
151
+ const opening = description === undefined
152
+ ? `${label} "${nameOf(id)}".`
153
+ : `${label} "${nameOf(id)}": "${description}"`;
154
+ return [sentenceEnd(opening), ...relationshipSentences(id), ...supportSentences(id)].join(' ');
155
+ };
156
+ const workParagraph = (id) => {
157
+ const kind = kindOf(id) ?? '';
158
+ const reading = kindReading(kind);
159
+ const status = statusOf(id);
160
+ const name = `"${nameOf(id)}"`;
161
+ const opening = status === 'planned'
162
+ ? `You are building ${name}, ${article(reading)} ${reading}.`
163
+ : status === 'current'
164
+ ? `${name}, ${article(reading)} ${reading}, already exists.`
165
+ : status === 'retired'
166
+ ? `${name}, ${article(reading)} ${reading}, is retired.`
167
+ : `${name} is ${article(reading)} ${reading}.`;
168
+ const description = descriptionOf(id);
169
+ return [
170
+ opening,
171
+ ...(description === undefined ? [] : [sentenceEnd(description)]),
172
+ ...relationshipSentences(id),
173
+ ...supportSentences(id),
174
+ ].join(' ');
175
+ };
176
+ // Motivation opens the brief (the interview's waves lead with why), the
177
+ // planned work follows (it is what the reader came to do), and existing
178
+ // context comes after; the same order ranks paragraphs under a budget.
179
+ const statusRank = (id) => {
180
+ const status = statusOf(id);
181
+ return status === 'planned' ? 1 : status === 'retired' ? 3 : 2;
182
+ };
183
+ const motivation = concepts.filter(({ id }) => isMotivation(id));
184
+ const work = concepts.filter(({ id }) => !isMotivation(id));
185
+ const paragraphs = [
186
+ {
187
+ heading: '## Why this exists',
188
+ entries: motivation.map(({ id }) => ({
189
+ text: motivationParagraph(id),
190
+ rank: 0,
191
+ })),
192
+ },
193
+ {
194
+ heading: '## The pieces',
195
+ entries: work
196
+ .map(({ id }) => ({ text: workParagraph(id), rank: statusRank(id) }))
197
+ .sort((a, b) => a.rank - b.rank),
198
+ },
199
+ ];
200
+ const title = result.presentation?.title ?? result.projection;
201
+ const header = [`# ${title}`];
202
+ if (result.presentation?.description !== undefined) {
203
+ header.push('', result.presentation.description);
204
+ }
205
+ const budget = budgetTokens ?? Number.POSITIVE_INFINITY;
206
+ let spent = estimateTokens(header.join('\n'));
207
+ let omitted = 0;
208
+ let total = 0;
209
+ const sections = [];
210
+ for (const section of paragraphs) {
211
+ total += section.entries.length;
212
+ if (section.entries.length === 0)
213
+ continue;
214
+ const kept = [];
215
+ let sectionSpent = estimateTokens(section.heading);
216
+ for (const entry of section.entries) {
217
+ const cost = estimateTokens(entry.text);
218
+ if (spent + sectionSpent + cost > budget) {
219
+ omitted += 1;
220
+ continue;
221
+ }
222
+ kept.push(entry.text);
223
+ sectionSpent += cost;
224
+ }
225
+ if (kept.length > 0) {
226
+ sections.push('', section.heading, '', kept.join('\n\n'));
227
+ spent += sectionSpent;
228
+ }
229
+ }
230
+ const lines = [...header, ...sections];
231
+ if (omitted > 0) {
232
+ lines.push('', `[budget ${budgetTokens}: ${omitted} of ${total} paragraphs omitted — raise --budget or use --json for the complete slice]`);
233
+ }
234
+ return `${lines.join('\n')}\n`;
235
+ }
@@ -8,10 +8,23 @@ import { compileWorkspace } from './compiler.js';
8
8
  import { checkCoreContract, loadCoreContract, } from './core-contract.js';
9
9
  import { evaluateEvidenceWorkspace, loadEvidence, } from './evidence.js';
10
10
  import { loadProjection } from './projection.js';
11
+ import { reconcileEvidenceReports, } from './reconciliation.js';
11
12
  const Ajv2020 = Ajv2020Module.default;
13
+ const strictFindingMessage = (finding) => {
14
+ const observed = finding.evidence.message === undefined
15
+ ? finding.evidence.uri
16
+ : `${finding.evidence.uri}: ${finding.evidence.message}`;
17
+ const assertion = finding.asserted === undefined
18
+ ? `Evidence contradicts ${finding.target.type} "${finding.target.id}"`
19
+ : `Evidence contradicts claim "${finding.target.id}": the model asserts ` +
20
+ `${finding.asserted.from} -> ${finding.asserted.to} (${finding.asserted.kind})`;
21
+ return (`${assertion}, but provider "${finding.provider}" observed otherwise ` +
22
+ `(${observed}); align the model or the evidence to pass --strict`);
23
+ };
12
24
  export function runCheckCommand(options, cwd) {
13
25
  const json = options.includes('--json');
14
- const paths = options.filter((option) => option !== '--json');
26
+ const strict = options.includes('--strict');
27
+ const paths = options.filter((option) => option !== '--json' && option !== '--strict');
15
28
  const unknownOption = paths.find((path) => path.startsWith('-'));
16
29
  if (unknownOption !== undefined || paths.length === 0) {
17
30
  return { exitCode: 2, stdout: '', stderr: usage };
@@ -171,6 +184,46 @@ export function runCheckCommand(options, cwd) {
171
184
  const diagnostics = result.ok
172
185
  ? optionalDiagnostics
173
186
  : result.diagnostics;
187
+ // Strict only tightens a passing check: base diagnostics already fail
188
+ // the gate, so contradictions are folded in only once everything else
189
+ // holds, and each one is anchored at the claim the model declares.
190
+ const strictEvaluation = strict && result.ok && ok
191
+ ? (() => {
192
+ const reports = evidenceEvaluation !== undefined && evidenceEvaluation.ok
193
+ ? evidenceEvaluation.reports
194
+ : [];
195
+ const graph = result.graph;
196
+ const contradicted = reconcileEvidenceReports('strict', reports, graph).findings.filter(({ result: outcome }) => outcome === 'contradicted');
197
+ return {
198
+ observations: reports.reduce((total, report) => total + report.observations.length, 0),
199
+ diagnostics: sortDiagnostics(contradicted.map((finding) => {
200
+ const anchor = graph.claims.find(({ id }) => id === finding.target.id) ??
201
+ graph.claims.find(({ subject, predicate }) => subject === finding.target.id &&
202
+ predicate === 'yarramate/concept/kind') ??
203
+ graph.claims.find(({ subject }) => subject === finding.target.id);
204
+ return {
205
+ severity: 'error',
206
+ code: 'YM901',
207
+ message: strictFindingMessage(finding),
208
+ path: anchor?.source.path ?? finding.evidenceDocument,
209
+ pointer: anchor?.source.pointer ?? '/',
210
+ line: anchor?.source.line ?? 1,
211
+ column: anchor?.source.column ?? 1,
212
+ };
213
+ })),
214
+ };
215
+ })()
216
+ : undefined;
217
+ const strictSummary = strictEvaluation === undefined
218
+ ? undefined
219
+ : {
220
+ observations: strictEvaluation.observations,
221
+ contradicted: strictEvaluation.diagnostics.length,
222
+ };
223
+ const strictOk = strictEvaluation === undefined
224
+ ? true
225
+ : strictEvaluation.diagnostics.length === 0;
226
+ const finalOk = ok && strictOk;
174
227
  const counted = result.ok
175
228
  ? (() => {
176
229
  const states = new Set(result.graph.claims
@@ -186,8 +239,15 @@ export function runCheckCommand(options, cwd) {
186
239
  : undefined;
187
240
  if (json) {
188
241
  return {
189
- exitCode: ok ? 0 : 1,
190
- stdout: checkResultJson(ok, ok ? [] : diagnostics, ok ? counted : undefined),
242
+ exitCode: finalOk ? 0 : 1,
243
+ stdout: checkResultJson(finalOk, finalOk ? [] : ok ? strictEvaluation.diagnostics : diagnostics, finalOk ? counted : undefined, strictSummary),
244
+ stderr: '',
245
+ };
246
+ }
247
+ if (ok && !strictOk) {
248
+ return {
249
+ exitCode: 1,
250
+ stdout: humanDiagnostics(strictEvaluation.diagnostics),
191
251
  stderr: '',
192
252
  };
193
253
  }
@@ -227,13 +287,19 @@ export function runCheckCommand(options, cwd) {
227
287
  ]
228
288
  : []),
229
289
  ].join(' and ');
290
+ const strictLine = strictSummary === undefined
291
+ ? ''
292
+ : strictSummary.observations === 0
293
+ ? 'Strict: no evidence observations to evaluate\n'
294
+ : `Strict: ${strictSummary.observations} ${strictSummary.observations === 1 ? 'observation' : 'observations'}, 0 contradicted\n`;
230
295
  return {
231
296
  exitCode: 0,
232
297
  stdout: `Checked ${checked} (` +
233
298
  `${successfulCounts.concepts} ${successfulCounts.concepts === 1 ? 'concept' : 'concepts'}, ` +
234
299
  `${successfulCounts.relationships} ${successfulCounts.relationships === 1 ? 'relationship' : 'relationships'}, ` +
235
300
  `${successfulCounts.states} ${successfulCounts.states === 1 ? 'state' : 'states'}` +
236
- '): no errors\n',
301
+ '): no errors\n' +
302
+ strictLine,
237
303
  stderr: '',
238
304
  };
239
305
  }
@@ -7,13 +7,16 @@ export interface CliResult {
7
7
  export declare const isMainModule: (moduleUrl: string, entrypoint: string | undefined) => boolean;
8
8
  export declare const packageVersion: string;
9
9
  export declare const versionResult: (binary: string) => CliResult;
10
- export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate new projection <projection.yaml> --id <id> [--version <v>] [--title <text>] [--description <text>] [--document <id> ...] [--subject <ref> ...] [--kind <qualified-kind> ...] [--relationships <mode>]\n yarramate status <workspace.yaml> [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate context --subject <document-id>#<local-id> [--subject ...] <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n";
10
+ export declare const usage = "Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> \"<free text>\" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise \"<topic>\" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml>\n";
11
11
  export declare const diagnosticJson: (diagnostics: unknown) => string;
12
12
  export declare const checkResultJson: (ok: boolean, diagnostics: unknown, counted?: {
13
13
  readonly documents: number;
14
14
  readonly concepts: number;
15
15
  readonly relationships: number;
16
16
  readonly states: number;
17
+ }, strict?: {
18
+ readonly observations: number;
19
+ readonly contradicted: number;
17
20
  }) => string;
18
21
  export declare const humanDiagnostics: (diagnostics: readonly Pick<Diagnostic, "path" | "line" | "column" | "code" | "message">[]) => string;
19
22
  export declare const sortDiagnostics: <T extends Diagnostic>(diagnostics: readonly T[]) => T[];
@@ -23,16 +23,17 @@ export const versionResult = (binary) => ({
23
23
  stdout: `${binary} ${packageVersion}\n`,
24
24
  stderr: '',
25
25
  });
26
- export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate add <document.yaml> --id <id> --kind <kind> --name <name> [--status <status>] [--description <text>] [--owner <ref>] [--constraint <id>=<ref> ...] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate connect <document.yaml> --id <id> --kind <kind> --from <ref> --to <ref> [--name <name>] [--description <text>] [--status <status>] [--mode <mode>] [--content <text>] [--reference <id>=<ref> ...] [--present-in <state-ref> ...] [--source <source.yaml> ...]\n yarramate check <source.yaml> [source.yaml ...] [--json]\n yarramate new projection <projection.yaml> --id <id> [--version <v>] [--title <text>] [--description <text>] [--document <id> ...] [--subject <ref> ...] [--kind <qualified-kind> ...] [--relationships <mode>]\n yarramate status <workspace.yaml> [--json]\n yarramate compile <source.yaml> [source.yaml ...]\n yarramate context <projection.yaml> <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate context --subject <document-id>#<local-id> [--subject ...] <source.yaml> [source.yaml ...] [--budget <tokens>]\n yarramate view <projection.yaml> <source.yaml> [source.yaml ...]\n yarramate compare <from-state> <to-state> <source.yaml> [source.yaml ...]\n yarramate evidence <evidence.yaml> <source.yaml> [source.yaml ...]\n yarramate reconcile <workspace.yaml>\n';
26
+ export const usage = 'Usage:\n yarramate init <directory> [--no-pointer]\n yarramate design <workspace.yaml> [--subject <document-id>#<local-id>] [--catalogue <catalogue.yaml>] [--json]\n yarramate apply <operations.yaml> <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> [--json]\n yarramate ask <workspace.yaml> "<free text>" | <document-id>#<local-id> ... | <projection.yaml> [--budget <tokens>] [--json]\n yarramate ask <workspace.yaml> --subjects [--kind <term>] [--status <status>] [--json]\n yarramate ask <workspace.yaml> --kinds [--json]\n yarramate ask <workspace.yaml> --advise "<topic>" [--budget <tokens>] [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --next [--json]\n yarramate ask <workspace.yaml> --open [--catalogue <catalogue.yaml>] [--json]\n yarramate ask <workspace.yaml> --compare <from-state> <to-state> [--json]\n yarramate check <source.yaml> [source.yaml ...] [--json] [--strict]\n yarramate reconcile <workspace.yaml>\n yarramate export graph <workspace.yaml> [--out <file>]\n yarramate export markdown <projection.yaml> <workspace.yaml> [--out <file>]\n yarramate export briefs <projection.yaml> <workspace.yaml> --out <directory> [--budget <tokens>]\n yarramate export likec4 <likec4-project.yaml> <output-dir> <workspace.yaml>\n';
27
27
  export const diagnosticJson = (diagnostics) => `${JSON.stringify({
28
28
  format: 'yarramate/diagnostic-result/v1',
29
29
  diagnostics,
30
30
  }, null, 2)}\n`;
31
- export const checkResultJson = (ok, diagnostics, counted) => `${JSON.stringify({
31
+ export const checkResultJson = (ok, diagnostics, counted, strict) => `${JSON.stringify({
32
32
  format: 'yarramate/check-result/v1',
33
33
  ok,
34
34
  diagnostics,
35
35
  ...(counted === undefined ? {} : { counted }),
36
+ ...(strict === undefined ? {} : { strict }),
36
37
  }, null, 2)}\n`;
37
38
  export const humanDiagnostics = (diagnostics) => diagnostics
38
39
  .map((diagnostic) => `${diagnostic.path}:${diagnostic.line}:${diagnostic.column} error ${diagnostic.code} ${diagnostic.message}\n`)