@milehimikey/em 1.8.1 → 1.9.1

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 (105) hide show
  1. package/.claude/skills/event-modeling/SKILL.md +58 -404
  2. package/.claude/skills/event-modeling-conform/SKILL.md +77 -0
  3. package/.claude/skills/{event-modeling → event-modeling-conform}/reference/conform.md +40 -14
  4. package/.claude/skills/event-modeling-design/SKILL.md +139 -0
  5. package/.claude/skills/event-modeling-discover/SKILL.md +89 -0
  6. package/.claude/skills/{event-modeling → event-modeling-discover}/reference/extract.md +3 -2
  7. package/.claude/skills/event-modeling-implement/SKILL.md +40 -0
  8. package/.claude/skills/{event-modeling → event-modeling-implement}/reference/implement.md +3 -2
  9. package/.claude/skills/event-modeling-review/SKILL.md +58 -0
  10. package/.claude/skills/{event-modeling → event-modeling-shared}/reference/em-dsl.md +115 -83
  11. package/.claude/skills/{event-modeling → event-modeling-shared}/reference/methodology.md +6 -5
  12. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +150 -0
  13. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +381 -0
  14. package/.claude/skills/{event-modeling → event-modeling-shared}/templates/model-readme.md +2 -2
  15. package/.claude/skills/{event-modeling → event-modeling-shared}/templates/slice.md +57 -14
  16. package/README.md +24 -4
  17. package/dist/catalog/build.js +9 -0
  18. package/dist/catalog/build.js.map +1 -1
  19. package/dist/catalog/docJoin.d.ts +15 -0
  20. package/dist/catalog/docJoin.js +8 -0
  21. package/dist/catalog/docJoin.js.map +1 -1
  22. package/dist/catalog/modelCollisionValidate.d.ts +22 -0
  23. package/dist/catalog/modelCollisionValidate.js +67 -0
  24. package/dist/catalog/modelCollisionValidate.js.map +1 -0
  25. package/dist/catalog/orphanedSliceDocValidate.d.ts +11 -0
  26. package/dist/catalog/orphanedSliceDocValidate.js +98 -0
  27. package/dist/catalog/orphanedSliceDocValidate.js.map +1 -0
  28. package/dist/catalog/sliceDoc.d.ts +15 -0
  29. package/dist/catalog/sliceDoc.js +18 -0
  30. package/dist/catalog/sliceDoc.js.map +1 -1
  31. package/dist/cli/changelogBuild.d.ts +12 -0
  32. package/dist/cli/changelogBuild.js +89 -0
  33. package/dist/cli/changelogBuild.js.map +1 -0
  34. package/dist/cli/ciInit.d.ts +42 -0
  35. package/dist/cli/ciInit.js +298 -0
  36. package/dist/cli/ciInit.js.map +1 -0
  37. package/dist/cli/conformSupersede.d.ts +32 -0
  38. package/dist/cli/conformSupersede.js +102 -0
  39. package/dist/cli/conformSupersede.js.map +1 -0
  40. package/dist/cli/coverage.d.ts +6 -1
  41. package/dist/cli/coverage.js +28 -3
  42. package/dist/cli/coverage.js.map +1 -1
  43. package/dist/cli/frontmatterSurgery.d.ts +28 -0
  44. package/dist/cli/frontmatterSurgery.js +66 -0
  45. package/dist/cli/frontmatterSurgery.js.map +1 -0
  46. package/dist/cli/markImplemented.js +6 -45
  47. package/dist/cli/markImplemented.js.map +1 -1
  48. package/dist/cli/ratify.d.ts +41 -0
  49. package/dist/cli/ratify.js +151 -0
  50. package/dist/cli/ratify.js.map +1 -0
  51. package/dist/cli/reratify.d.ts +33 -0
  52. package/dist/cli/reratify.js +137 -0
  53. package/dist/cli/reratify.js.map +1 -0
  54. package/dist/cli/skillCheck.d.ts +6 -0
  55. package/dist/cli/skillCheck.js +46 -0
  56. package/dist/cli/skillCheck.js.map +1 -1
  57. package/dist/cli/skillDirs.d.ts +15 -0
  58. package/dist/cli/skillDirs.js +34 -0
  59. package/dist/cli/skillDirs.js.map +1 -0
  60. package/dist/cli/skillSync.d.ts +10 -0
  61. package/dist/cli/skillSync.js +15 -0
  62. package/dist/cli/skillSync.js.map +1 -1
  63. package/dist/cli/sliceIndex.d.ts +3 -0
  64. package/dist/cli/sliceIndex.js +6 -2
  65. package/dist/cli/sliceIndex.js.map +1 -1
  66. package/dist/cli/sliceLink.d.ts +52 -0
  67. package/dist/cli/sliceLink.js +152 -0
  68. package/dist/cli/sliceLink.js.map +1 -0
  69. package/dist/cli/status.d.ts +261 -0
  70. package/dist/cli/status.js +536 -0
  71. package/dist/cli/status.js.map +1 -0
  72. package/dist/cli/usageLog.d.ts +75 -0
  73. package/dist/cli/usageLog.js +193 -0
  74. package/dist/cli/usageLog.js.map +1 -0
  75. package/dist/cli.js +503 -132
  76. package/dist/cli.js.map +1 -1
  77. package/dist/emit/freshnessJson.d.ts +6 -0
  78. package/dist/emit/freshnessJson.js +24 -0
  79. package/dist/emit/freshnessJson.js.map +1 -0
  80. package/dist/emit/json.d.ts +62 -8
  81. package/dist/emit/json.js +14 -3
  82. package/dist/emit/json.js.map +1 -1
  83. package/dist/emit/statusJson.d.ts +7 -0
  84. package/dist/emit/statusJson.js +39 -0
  85. package/dist/emit/statusJson.js.map +1 -0
  86. package/dist/emit/typespec.d.ts +26 -0
  87. package/dist/emit/typespec.js +356 -0
  88. package/dist/emit/typespec.js.map +1 -0
  89. package/dist/emit/usageReportJson.d.ts +3 -0
  90. package/dist/emit/usageReportJson.js +23 -0
  91. package/dist/emit/usageReportJson.js.map +1 -0
  92. package/dist/mcp/server.d.ts +1 -1
  93. package/dist/mcp/server.js +301 -11
  94. package/dist/mcp/server.js.map +1 -1
  95. package/dist/model/rules.d.ts +13 -0
  96. package/dist/model/rules.js +20 -0
  97. package/dist/model/rules.js.map +1 -1
  98. package/dist/templates.d.ts +1 -1
  99. package/dist/templates.js +2 -2
  100. package/dist/util/markers.d.ts +16 -4
  101. package/dist/util/markers.js +23 -10
  102. package/dist/util/markers.js.map +1 -1
  103. package/package.json +2 -1
  104. /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/conformance-report.md +0 -0
  105. /package/.claude/skills/{event-modeling → event-modeling-shared}/templates/state.md +0 -0
@@ -0,0 +1,536 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // `em status` (MIL-163): one deterministic command that answers "what state is the system in"
3
+ // over one or more models — the surface a reader currently has to assemble by hand from the
4
+ // README's Slices table, a conformance report, and `.event-modeling.md`. Pure aggregation over
5
+ // exports the rest of `em` already computes; nothing here re-derives a fact another module owns:
6
+ //
7
+ // - lifecycle status / driftSignal come from the same doc join `em export`/`em coverage` use
8
+ // (catalog/docJoin.ts's resolveSliceDocJoin, catalog/driftSignal.ts's classification).
9
+ // - open Open Questions counts come from the same slice-doc parse `em coverage`'s scope check
10
+ // already reads (catalog/sliceDoc.ts's openQuestionsTotal/openQuestionsUnchecked).
11
+ // - invariant coverage totals come from `em coverage`'s own report builder (./coverage.ts),
12
+ // summed across every input model rather than re-implemented.
13
+ // - last-conformance + commits-behind-HEAD reuses stateFile.ts's parser and the same injectable
14
+ // GitRunner convention conform-scope.ts/diff-inputs.ts use, so every git failure branch stays
15
+ // unit-testable without a real repository.
16
+ // - slice-PRs-behind-HEAD (MIL-164) reuses conform-scope.ts's own machinery outright —
17
+ // `changedPathsSince` + `buildConformScope` — rather than re-deriving the
18
+ // implementedIn-to-changed-path match rule a second time. The standalone surface for this
19
+ // freshness clause alone (no full model rollup needed) is `em freshness` (freshness.ts).
20
+ //
21
+ // Deterministic core: no LLM calls, no wall-clock timestamps in the output, byte-stable for the
22
+ // same inputs. Text/markdown/badge are formatting layers over one aggregated StatusReport; the
23
+ // JSON document (emit/statusJson.ts) is a versioned envelope around the exact same object.
24
+ import { basename, dirname, resolve } from "node:path";
25
+ import { resolveSliceDocJoin } from "../catalog/docJoin.js";
26
+ import { readSliceDoc } from "../catalog/readSliceDoc.js";
27
+ import { realGit } from "./diff-inputs.js";
28
+ import { loadStateFile, parseState } from "./stateFile.js";
29
+ import { changedPathsSince, buildConformScope } from "./conformScope.js";
30
+ /** The 4 canonical slice-doc lifecycle statuses (docs/slice-doc-schema.md) — the same enum
31
+ * `em catalog`'s header coloring and `em render`'s status legend already recognize. */
32
+ export const CANONICAL_STATUSES = ["draft", "reviewed", "ready-to-implement", "implemented"];
33
+ export function classifyStatusBucket(found, reason, status) {
34
+ if (!found)
35
+ return "no-doc";
36
+ if (reason === "frontmatter-invalid")
37
+ return "frontmatter-invalid";
38
+ if (status !== null && CANONICAL_STATUSES.includes(status))
39
+ return status;
40
+ return "unknown";
41
+ }
42
+ /**
43
+ * Resolve every slice's status-rollup facts for one already-compiled model. `baseDir` is the
44
+ * `.em` file's directory, same doc-resolution convention every other doc-aware command uses.
45
+ * Open Questions counts are read from the slice's OWN bound doc (re-deriving the key from
46
+ * `doc.path`, not assuming it's this slice's own — MIL-121 cross-binding can resolve to a
47
+ * different slice's doc, same re-derivation `em coverage`/`--slice-ready` use) rather than a
48
+ * second independent parse.
49
+ */
50
+ export function resolveSliceStatusFacts(file, model, refs, baseDir) {
51
+ const diagnostics = [];
52
+ const facts = model.slices.map((slice, i) => {
53
+ const key = refs.sliceKeys[i];
54
+ const { doc, diagnostics: docDiags } = resolveSliceDocJoin(slice, key, baseDir, (id) => refs.refById.get(id));
55
+ diagnostics.push(...docDiags);
56
+ let docPath = null;
57
+ let openQuestionsTotal = 0;
58
+ let openQuestionsUnchecked = 0;
59
+ if (doc.found) {
60
+ const boundKey = doc.path.replace(/^slices\//, "").replace(/\.md$/, "");
61
+ docPath = resolve(baseDir, doc.path);
62
+ const parsed = readSliceDoc(baseDir, boundKey);
63
+ if (parsed) {
64
+ openQuestionsTotal = parsed.openQuestionsTotal;
65
+ openQuestionsUnchecked = parsed.openQuestionsUnchecked;
66
+ }
67
+ }
68
+ return {
69
+ file,
70
+ key,
71
+ docFound: doc.found,
72
+ docReason: doc.reason,
73
+ docPath,
74
+ rawStatus: doc.status,
75
+ implementedIn: doc.implementedIn,
76
+ owner: doc.owner,
77
+ bucket: classifyStatusBucket(doc.found, doc.reason, doc.status),
78
+ driftSignal: doc.driftSignal,
79
+ openQuestionsTotal,
80
+ openQuestionsUnchecked,
81
+ };
82
+ });
83
+ return { facts, diagnostics };
84
+ }
85
+ /** Open `issue "text"` markers in a model — the same predicate `em validate --list-issues`
86
+ * counts, here reduced to a bare count for the rollup. */
87
+ export function countOpenIssues(model) {
88
+ return model.elements.filter((el) => el.issue).length;
89
+ }
90
+ /**
91
+ * `git rev-list --count <revision>..HEAD` in `repo` (any directory inside it, not necessarily
92
+ * its root — same "anchor dir, resolve toplevel" convention `conformScope.ts`'s
93
+ * `changedPathsSince` uses). The git runner is injectable so every failure branch is
94
+ * unit-testable without a real repository.
95
+ */
96
+ export function commitsBehindHead(repo, revision, runGit = realGit) {
97
+ const toplevel = runGit(["-C", repo, "rev-parse", "--show-toplevel"]);
98
+ if (toplevel.status !== 0) {
99
+ return { ok: false, message: `em status: ${repo} is not a git repository` };
100
+ }
101
+ const repoRoot = toplevel.stdout.trim();
102
+ const result = runGit(["-C", repoRoot, "rev-list", "--count", `${revision}..HEAD`]);
103
+ if (result.status !== 0) {
104
+ return {
105
+ ok: false,
106
+ message: `em status: git rev-list failed: ${(result.stderr || "").trim() || `unknown revision "${revision}"`}`,
107
+ };
108
+ }
109
+ const count = parseInt(result.stdout.trim(), 10);
110
+ if (Number.isNaN(count)) {
111
+ return { ok: false, message: `em status: unexpected git rev-list output for revision "${revision}"` };
112
+ }
113
+ return { ok: true, count };
114
+ }
115
+ /**
116
+ * `M` in "N commits and M slice-PRs behind HEAD" (MIL-164) — how many slices whose doc's
117
+ * `implementedIn:` names a path this model's target repo changed since `revision`. Reuses the
118
+ * exact `em conform-scope` machinery for one model at a time: `changedPathsSince` (the same
119
+ * `git diff --name-only <revision>..HEAD` walk, same injectable `GitRunner`) to get the changed
120
+ * paths, then `buildConformScope`'s own `implementedIn`-prefix matching (never fuzzy, never a
121
+ * second guess) to turn them into a candidate-slice count. `full` is always `false` here — this
122
+ * is only ever called once `lastConformance` is known non-null (see `resolveConformanceEntry`
123
+ * below), so there is always a revision to diff against.
124
+ */
125
+ export function resolveSlicePRsBehindHead(repo, revision, slices, runGit = realGit) {
126
+ const changed = changedPathsSince(repo, revision, runGit);
127
+ if (!changed.ok)
128
+ return { ok: false, message: changed.message };
129
+ // `date`/`report` are irrelevant to matching (buildConformScope only echoes them back in its
130
+ // own `lastConformance` field, which this function discards) — only `revision` drives the
131
+ // implementedIn-to-changed-path match below.
132
+ const scope = buildConformScope(slices, { date: "", revision, report: "" }, changed.paths, false);
133
+ return { ok: true, count: scope.candidateSlices.length };
134
+ }
135
+ /**
136
+ * Resolve one model's conformance entry: read its sibling state file (if any — not every model
137
+ * has reached the `conform` phase, so a missing state file is routine, not an error), then
138
+ * compute commits-behind-HEAD and slice-PRs-behind-HEAD (MIL-164) when `Last conformance:` is
139
+ * set. `repoOverride` is `--repo`; omitted, each model's own directory is used as the repo to
140
+ * walk (the common single-repo project case — see `repo` field doc above). `sliceDocFacts` is
141
+ * THIS model's own slice facts (`key`/`status`/`implementedIn`) — the caller's responsibility to
142
+ * scope correctly per file, since `resolveSlicePRsBehindHead` matches against exactly this
143
+ * model's slices, not some other input model's.
144
+ *
145
+ * A state file is shared by every `.em` file in its directory (`.event-modeling.md` has no
146
+ * per-model namespacing), but its `Model file:` bullet names exactly ONE of them — so a sibling
147
+ * file it does NOT describe (the common case: a `conform-scope --seed-asis` scratch copy like
148
+ * `checkout-asis.em` sitting next to `checkout.em`) must not inherit `checkout.em`'s conformance
149
+ * record just because it lives in the same directory. When `Model file:` is set and doesn't match
150
+ * `basename(file)`, the record is reported as unattributed (via `error`, the same non-fatal
151
+ * channel a git failure uses) rather than silently misattributed.
152
+ */
153
+ export function resolveConformanceEntry(file, repoOverride, sliceDocFacts, runGit = realGit) {
154
+ const modelDir = dirname(file);
155
+ const repo = repoOverride ?? modelDir;
156
+ const loaded = loadStateFile(modelDir);
157
+ if (!loaded.ok) {
158
+ return { file, modelDir, hasStateFile: false, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, error: null };
159
+ }
160
+ const parsed = parseState(loaded.text);
161
+ if (!parsed.ok) {
162
+ return {
163
+ file,
164
+ modelDir,
165
+ hasStateFile: true,
166
+ lastConformance: null,
167
+ repo,
168
+ commitsBehindHead: null,
169
+ slicePRsBehindHead: null,
170
+ error: `state file: ${parsed.message}`,
171
+ };
172
+ }
173
+ if (parsed.state.modelPath && parsed.state.modelPath !== basename(file)) {
174
+ return {
175
+ file,
176
+ modelDir,
177
+ hasStateFile: true,
178
+ lastConformance: null,
179
+ repo,
180
+ commitsBehindHead: null,
181
+ slicePRsBehindHead: null,
182
+ error: `state file describes "${parsed.state.modelPath}", not "${basename(file)}" — not attributing its conformance record`,
183
+ };
184
+ }
185
+ if (!parsed.state.lastConformance) {
186
+ return { file, modelDir, hasStateFile: true, lastConformance: null, repo, commitsBehindHead: null, slicePRsBehindHead: null, error: null };
187
+ }
188
+ const { date, revision } = parsed.state.lastConformance;
189
+ const commitsResult = commitsBehindHead(repo, revision, runGit);
190
+ if (!commitsResult.ok) {
191
+ return {
192
+ file,
193
+ modelDir,
194
+ hasStateFile: true,
195
+ lastConformance: { date, revision },
196
+ repo,
197
+ commitsBehindHead: null,
198
+ slicePRsBehindHead: null,
199
+ error: commitsResult.message,
200
+ };
201
+ }
202
+ const slicePRsResult = resolveSlicePRsBehindHead(repo, revision, sliceDocFacts, runGit);
203
+ if (!slicePRsResult.ok) {
204
+ return {
205
+ file,
206
+ modelDir,
207
+ hasStateFile: true,
208
+ lastConformance: { date, revision },
209
+ repo,
210
+ commitsBehindHead: commitsResult.count,
211
+ slicePRsBehindHead: null,
212
+ error: slicePRsResult.message,
213
+ };
214
+ }
215
+ return {
216
+ file,
217
+ modelDir,
218
+ hasStateFile: true,
219
+ lastConformance: { date, revision },
220
+ repo,
221
+ commitsBehindHead: commitsResult.count,
222
+ slicePRsBehindHead: slicePRsResult.count,
223
+ error: null,
224
+ };
225
+ }
226
+ /** Aggregate everything `em status` reports into one `StatusReport` — pure, no I/O. Callers
227
+ * (the CLI action, the MCP tool) resolve every input first (compile each model, join every
228
+ * slice's doc, scan the test tree once, resolve conformance) and hand the results here. */
229
+ export function buildStatusReport(files, sliceFacts, openIssuesCount, invariants, conformance, diagnostics) {
230
+ const byStatus = { draft: 0, reviewed: 0, readyToImplement: 0, implemented: 0, noDoc: 0, frontmatterInvalid: 0, unknown: 0 };
231
+ const drift = {
232
+ inSync: 0,
233
+ neverImplemented: 0,
234
+ unpropagatedDelta: 0,
235
+ implementedWithoutLink: 0,
236
+ notApplicable: 0,
237
+ frontmatterInvalid: 0,
238
+ };
239
+ let openQuestionsTotal = 0;
240
+ let openQuestionsUnchecked = 0;
241
+ // MIL-121 `covers:` cross-binding lets several slices resolve to the SAME doc file — a doc's
242
+ // own Open Questions belong to the doc, not to each slice covered by it, so a shared doc is
243
+ // only counted once regardless of how many slices are bound to it (keyed by resolved absolute
244
+ // path, since two different input models' `slices/` dirs could otherwise collide on the same
245
+ // relative path string without actually being the same file).
246
+ const countedDocPaths = new Set();
247
+ for (const f of sliceFacts) {
248
+ switch (f.bucket) {
249
+ case "draft":
250
+ byStatus.draft++;
251
+ break;
252
+ case "reviewed":
253
+ byStatus.reviewed++;
254
+ break;
255
+ case "ready-to-implement":
256
+ byStatus.readyToImplement++;
257
+ break;
258
+ case "implemented":
259
+ byStatus.implemented++;
260
+ break;
261
+ case "no-doc":
262
+ byStatus.noDoc++;
263
+ break;
264
+ case "frontmatter-invalid":
265
+ byStatus.frontmatterInvalid++;
266
+ break;
267
+ case "unknown":
268
+ byStatus.unknown++;
269
+ break;
270
+ }
271
+ switch (f.driftSignal) {
272
+ case "in-sync":
273
+ drift.inSync++;
274
+ break;
275
+ case "never-implemented":
276
+ drift.neverImplemented++;
277
+ break;
278
+ case "unpropagated-delta":
279
+ drift.unpropagatedDelta++;
280
+ break;
281
+ case "implemented-without-link":
282
+ drift.implementedWithoutLink++;
283
+ break;
284
+ case null:
285
+ if (f.docReason === "frontmatter-invalid")
286
+ drift.frontmatterInvalid++;
287
+ else
288
+ drift.notApplicable++;
289
+ break;
290
+ }
291
+ const alreadyCounted = f.docPath !== null && countedDocPaths.has(f.docPath);
292
+ if (f.docPath !== null)
293
+ countedDocPaths.add(f.docPath);
294
+ if (!alreadyCounted) {
295
+ openQuestionsTotal += f.openQuestionsTotal;
296
+ openQuestionsUnchecked += f.openQuestionsUnchecked;
297
+ }
298
+ }
299
+ return {
300
+ files,
301
+ slices: { total: sliceFacts.length, byStatus },
302
+ driftSignal: drift,
303
+ invariants,
304
+ issues: { openIssues: openIssuesCount, openQuestionsTotal, openQuestionsUnchecked },
305
+ conformance,
306
+ diagnostics,
307
+ owners: sliceFacts.map((f) => ({ file: f.file, key: f.key, owner: f.owner })),
308
+ };
309
+ }
310
+ /** Sum per-model `CoverageReport`s (see `./coverage.ts`) into one `StatusInvariantTotals` —
311
+ * `em status` doesn't need the per-slice/per-ID detail `em coverage --json` carries, just the
312
+ * totals, across however many models `--tests <dir>` was scanned against. */
313
+ export function aggregateInvariantTotals(testsDir, reports) {
314
+ let total = 0;
315
+ let uncovered = 0;
316
+ for (const r of reports) {
317
+ total += r.totalInvariants;
318
+ uncovered += r.uncoveredCount;
319
+ }
320
+ return { testsDir, total, cited: total - uncovered, uncovered };
321
+ }
322
+ // ---- Text / markdown / badge formatting ----
323
+ function pluralize(n, word) {
324
+ return `${n} ${word}${n === 1 ? "" : "s"}`;
325
+ }
326
+ /** "N commits and M slice-PRs behind HEAD" (MIL-164's freshness clause), handling either count
327
+ * being independently unknown — defensive against a hand-built `ConformanceEntry` (tests) even
328
+ * though on the real `resolveConformanceEntry` path the two counts are only ever null together
329
+ * (both fail whenever `error` is set, which callers check first — see that function's doc). */
330
+ function formatBehindHead(entry) {
331
+ const commits = entry.commitsBehindHead;
332
+ const slicePRs = entry.slicePRsBehindHead;
333
+ if (commits === null && slicePRs === null)
334
+ return "commits and slice-PRs behind HEAD unknown";
335
+ if (commits === null)
336
+ return `commits behind HEAD unknown, ${pluralize(slicePRs, "slice-PR")} behind HEAD`;
337
+ if (slicePRs === null)
338
+ return `${pluralize(commits, "commit")} behind HEAD, slice-PRs behind HEAD unknown`;
339
+ return `${pluralize(commits, "commit")} and ${pluralize(slicePRs, "slice-PR")} behind HEAD`;
340
+ }
341
+ /** Exported for `em freshness` (freshness.ts, MIL-164) — the standalone surface for exactly
342
+ * this one clause, without pulling in the rest of `em status`'s rollup. */
343
+ export function formatConformancePart(entry) {
344
+ if (entry.error)
345
+ return `conformance unknown (${entry.error})`;
346
+ if (!entry.hasStateFile)
347
+ return "no state file";
348
+ if (!entry.lastConformance)
349
+ return "never conformed";
350
+ return `last conformed ${entry.lastConformance.revision} — ${formatBehindHead(entry)}`;
351
+ }
352
+ /** Same facts as `formatConformancePart`, without the leading "last conformed"/"conformance
353
+ * unknown" prose — for a markdown table cell that already carries a "Last conformed" row
354
+ * label, so the value itself shouldn't repeat it. */
355
+ function formatConformanceValue(entry) {
356
+ if (entry.error)
357
+ return `unknown (${entry.error})`;
358
+ if (!entry.hasStateFile)
359
+ return "no state file";
360
+ if (!entry.lastConformance)
361
+ return "never conformed";
362
+ return `\`${entry.lastConformance.revision}\` — ${formatBehindHead(entry)}`;
363
+ }
364
+ /** The one-line rollup: `"8/8 implemented · 20/20 invariants covered · 0 open issues · last
365
+ * conformed <rev>, N commits behind HEAD"` (MIL-163's acceptance line). For multiple input
366
+ * models, the conformance clause reports the FIRST file's entry — a single-file invocation is
367
+ * the common case this line is written for; the full per-model breakdown is always in
368
+ * `formatStatusDetail`/the JSON document regardless of file count. */
369
+ export function formatStatusSummary(report) {
370
+ const { implemented } = report.slices.byStatus;
371
+ const total = report.slices.total;
372
+ const invPart = report.invariants
373
+ ? `${report.invariants.cited}/${report.invariants.total} invariants covered`
374
+ : "invariants not checked (pass --tests <dir>)";
375
+ const issuesPart = pluralize(report.issues.openIssues, "open issue");
376
+ const oqPart = report.issues.openQuestionsUnchecked > 0 ? `, ${pluralize(report.issues.openQuestionsUnchecked, "unchecked open question")}` : "";
377
+ const conformancePart = formatConformancePart(report.conformance[0]);
378
+ return `${implemented}/${total} implemented · ${invPart} · ${issuesPart}${oqPart} · ${conformancePart}`;
379
+ }
380
+ /** The detail block below the summary line: one line per rollup dimension, plus one line per
381
+ * model's conformance entry. */
382
+ export function formatStatusDetail(report) {
383
+ const { byStatus } = report.slices;
384
+ const lines = [];
385
+ lines.push(`slices: ${report.slices.total} total — ${byStatus.implemented} implemented, ${byStatus.readyToImplement} ready-to-implement, ` +
386
+ `${byStatus.reviewed} reviewed, ${byStatus.draft} draft, ${byStatus.noDoc} no doc, ${byStatus.frontmatterInvalid} frontmatter invalid, ` +
387
+ `${byStatus.unknown} unknown status`);
388
+ const d = report.driftSignal;
389
+ lines.push(`driftSignal: ${d.inSync} in-sync, ${d.neverImplemented} never-implemented, ${d.unpropagatedDelta} unpropagated-delta, ` +
390
+ `${d.implementedWithoutLink} implemented-without-link, ${d.notApplicable} n/a (no doc), ${d.frontmatterInvalid} n/a (frontmatter invalid)`);
391
+ lines.push(report.invariants
392
+ ? `invariants: ${report.invariants.cited}/${report.invariants.total} covered (${report.invariants.uncovered} uncovered) — ${report.invariants.testsDir}`
393
+ : "invariants: not checked — pass --tests <dir> to enable");
394
+ lines.push(`issues: ${pluralize(report.issues.openIssues, "open issue")}, ` +
395
+ `${report.issues.openQuestionsUnchecked}/${report.issues.openQuestionsTotal} open question(s) unchecked`);
396
+ const multi = report.conformance.length > 1;
397
+ for (const entry of report.conformance) {
398
+ const label = multi ? `conformance (${entry.file}): ` : "conformance: ";
399
+ lines.push(`${label}${formatConformancePart(entry)}`);
400
+ }
401
+ if (report.diagnostics.length > 0) {
402
+ lines.push(`doc issues: ${pluralize(report.diagnostics.length, "warning")} — see diagnostics (${report.diagnostics.map((d) => d.code).join(", ")})`);
403
+ }
404
+ return lines.join("\n");
405
+ }
406
+ /** Full text report: the summary line, a blank line, then the detail block — same
407
+ * "rollup line, then detail" shape `em diff`'s text report uses. */
408
+ export function formatStatusText(report) {
409
+ return `${formatStatusSummary(report)}\n\n${formatStatusDetail(report)}`;
410
+ }
411
+ /** Escape characters that would break a markdown table cell: `|` (the column separator) and
412
+ * newlines — plus backslashes, escaped FIRST. Escaping `|` alone would let a pre-existing `\`
413
+ * in the source text (a Windows path in `entry.file`, or free text inside a conformance
414
+ * `error` string) combine with the newly-inserted `\` (e.g. a literal `\|`) and un-escape the
415
+ * pipe again once Markdown unescapes the backslash sequence — a CodeQL
416
+ * `js/incomplete-sanitization` finding (PR #116 review). Same convention `em slice index`'s
417
+ * own `escapeCell` (sliceIndex.ts) already uses for exactly this reason. Applied to both the
418
+ * key and value columns: the multi-model `Last conformed (${entry.file})` row embeds
419
+ * `entry.file`, which is CLI-argument/glob-controlled, not just the value column. */
420
+ function escapeCell(s) {
421
+ return s.replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
422
+ }
423
+ /** A markdown block suited for pasting into a README (MIL-163's `--md`) — a small table,
424
+ * same spirit as `em slice index`'s GENERATED Slices table, but not marker-managed: `--md`
425
+ * just prints the block, leaving where/whether to embed it to the caller. */
426
+ export function formatStatusMarkdown(report) {
427
+ const { byStatus } = report.slices;
428
+ const rows = [
429
+ ["Slices", `${byStatus.implemented}/${report.slices.total} implemented (${byStatus.readyToImplement} ready-to-implement, ${byStatus.reviewed} reviewed, ${byStatus.draft} draft)`],
430
+ ["Invariants", report.invariants ? `${report.invariants.cited}/${report.invariants.total} covered` : "not checked"],
431
+ ["Open issues", `${report.issues.openIssues}`],
432
+ ["Open questions", `${report.issues.openQuestionsUnchecked} unchecked`],
433
+ ];
434
+ if (report.conformance.length <= 1) {
435
+ const entry = report.conformance[0];
436
+ rows.push(["Last conformed", formatConformanceValue(entry)]);
437
+ }
438
+ else {
439
+ for (const entry of report.conformance) {
440
+ rows.push([`Last conformed (${entry.file})`, formatConformanceValue(entry)]);
441
+ }
442
+ }
443
+ const header = "| Metric | Value |\n|---|---|";
444
+ const body = rows.map(([k, v]) => `| ${escapeCell(k)} | ${escapeCell(v)} |`).join("\n");
445
+ return `${header}\n${body}`;
446
+ }
447
+ // ---- SVG badge ----
448
+ function escapeXml(s) {
449
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
450
+ }
451
+ /** Deterministic, dependency-free text-width estimate for a shields.io-style flat badge: a
452
+ * fixed average character advance (no font metrics table, no rendering) so the same message
453
+ * always measures to the same width — good enough for a legible badge, not a claim of
454
+ * pixel-perfect parity with shields.io's own Verdana metrics. */
455
+ function textWidth(s) {
456
+ return Math.ceil(s.length * 6.5) + 10;
457
+ }
458
+ /** Render one flat-style SVG badge — two colored rects (label, message) plus centered text,
459
+ * no external fonts/images/scripts, so it renders identically anywhere an SVG does. */
460
+ export function renderBadgeSvg(label, message, color) {
461
+ const labelWidth = textWidth(label);
462
+ const messageWidth = textWidth(message);
463
+ const totalWidth = labelWidth + messageWidth;
464
+ const labelX = labelWidth / 2;
465
+ const messageX = labelWidth + messageWidth / 2;
466
+ const ariaLabel = escapeXml(`${label}: ${message}`);
467
+ return (`<svg xmlns="http://www.w3.org/2000/svg" width="${totalWidth}" height="20" role="img" aria-label="${ariaLabel}">\n` +
468
+ ` <title>${ariaLabel}</title>\n` +
469
+ ` <linearGradient id="s" x2="0" y2="100%">\n` +
470
+ ` <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>\n` +
471
+ ` <stop offset="1" stop-opacity=".1"/>\n` +
472
+ ` </linearGradient>\n` +
473
+ ` <clipPath id="r">\n` +
474
+ ` <rect width="${totalWidth}" height="20" rx="3" fill="#fff"/>\n` +
475
+ ` </clipPath>\n` +
476
+ ` <g clip-path="url(#r)">\n` +
477
+ ` <rect width="${labelWidth}" height="20" fill="#555"/>\n` +
478
+ ` <rect x="${labelWidth}" width="${messageWidth}" height="20" fill="${color}"/>\n` +
479
+ ` <rect width="${totalWidth}" height="20" fill="url(#s)"/>\n` +
480
+ ` </g>\n` +
481
+ ` <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">\n` +
482
+ ` <text x="${labelX}" y="14">${escapeXml(label)}</text>\n` +
483
+ ` <text x="${messageX}" y="14">${escapeXml(message)}</text>\n` +
484
+ ` </g>\n` +
485
+ `</svg>`);
486
+ }
487
+ /** Badge message: a compact version of the summary line's first two/three clauses — a
488
+ * badge has no room for the full conformance clause. */
489
+ function badgeMessage(report) {
490
+ const { implemented } = report.slices.byStatus;
491
+ const total = report.slices.total;
492
+ const parts = [`${implemented}/${total} implemented`];
493
+ if (report.invariants)
494
+ parts.push(`${report.invariants.cited}/${report.invariants.total} invariants`);
495
+ parts.push(pluralize(report.issues.openIssues, "issue"));
496
+ return parts.join(" · ");
497
+ }
498
+ /** Badge color contract (documented here AND in docs/cli.md — keep both in sync):
499
+ *
500
+ * - **red** `#e05d44` — a genuine problem: an open issue, an uncovered invariant, a
501
+ * `frontmatter-invalid` doc, or `implemented-without-link` drift (a doc claiming
502
+ * `implemented` with nothing linking to it).
503
+ * - **yellow** `#dfb317` — things are merely in flight, OR conformance couldn't be verified:
504
+ * not every slice implemented yet, an unchecked Open Question, any model with
505
+ * `commitsBehindHead > 0` or `slicePRsBehindHead > 0` (MIL-164), or — MIL-163 review — any
506
+ * conformance entry carrying a non-null `error` (an unresolvable revision, an unparseable
507
+ * state file, a `--repo` that isn't a git repo, or a state file describing a different
508
+ * model). An unverifiable conformance state must never be indistinguishable from a verified,
509
+ * current one — `commitsBehindHead: null`/`slicePRsBehindHead: null` from an `error` is NOT
510
+ * the same fact as "0 behind", so neither can be allowed to coalesce to 0 and read as healthy.
511
+ * - **green** `#4c1` — otherwise. Deliberately NOT keyed on "every slice implemented" alone —
512
+ * most projects spend most of their life not fully implemented, and that's not a problem a
513
+ * badge should flag. A model with NO conformance history yet (`hasStateFile: false`, or
514
+ * `Last conformance: never` — both `error: null`) is a legitimate, unremarkable state too
515
+ * ("hasn't reached the conform phase", not "broken") and stays eligible for green — only an
516
+ * actual `error` demotes a conformance entry out of green.
517
+ */
518
+ function badgeColor(report) {
519
+ const hasProblem = report.issues.openIssues > 0 ||
520
+ (report.invariants !== null && report.invariants.uncovered > 0) ||
521
+ report.driftSignal.implementedWithoutLink > 0 ||
522
+ report.driftSignal.frontmatterInvalid > 0;
523
+ if (hasProblem)
524
+ return "#e05d44"; // red
525
+ const inFlight = report.slices.byStatus.implemented < report.slices.total ||
526
+ report.issues.openQuestionsUnchecked > 0 ||
527
+ report.conformance.some((c) => (c.commitsBehindHead ?? 0) > 0 || (c.slicePRsBehindHead ?? 0) > 0 || c.error !== null);
528
+ if (inFlight)
529
+ return "#dfb317"; // yellow
530
+ return "#4c1"; // green
531
+ }
532
+ /** Build the `em status --badge` SVG document. */
533
+ export function buildStatusBadge(report) {
534
+ return renderBadgeSvg("em status", badgeMessage(report), badgeColor(report));
535
+ }
536
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/cli/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ import { PatchResult } from "./stateFile.js";
2
+ /** The Usage log's phase vocabulary (docs/usage-data.md#what-s-captured-and-where): the state
3
+ * file's own `Current phase:` enum (`stateFile.ts`'s `PHASES`) plus `watch` — a live-viewing
4
+ * activity a session can touch without ever being a `Current phase:` value itself (see
5
+ * `stateFile.ts`'s own comment on why `watch` is deliberately excluded there). Order here is
6
+ * the canonical sort order `log-usage` writes phases in, matching docs/usage-data.md's own
7
+ * listing order. */
8
+ export declare const USAGE_PHASES: readonly ["discover", "extract", "model", "slice", "implement", "conform", "review", "validate", "watch"];
9
+ export type UsagePhase = (typeof USAGE_PHASES)[number];
10
+ export declare function isUsagePhase(value: string): value is UsagePhase;
11
+ /** Dedupe and sort a `--phases` list into `USAGE_PHASES`' own canonical order (matching
12
+ * docs/usage-data.md's own listing order) rather than input order or alphabetical — so two
13
+ * sessions naming the same phases in a different order always log an identical line. */
14
+ export declare function sortUsagePhases(phases: readonly string[]): string[];
15
+ /** Format one Usage log bullet line — the ONE place this exact shape is generated, so
16
+ * `log-usage`'s writer and any future caller never drift from `usage-report`'s parser. */
17
+ export declare function formatUsageLogLine(date: string, phases: readonly string[], categories: readonly string[]): string;
18
+ /**
19
+ * Append one Usage log bullet to `text`'s "## Usage log" section — after whatever's already
20
+ * there (the guidance comment, and/or prior bullets), never editing or reordering existing
21
+ * content. `phases`/`categories` are trusted already-deduped, already-sorted, non-empty arrays
22
+ * (the CLI layer owns that — see `sortByCanonicalOrder`/dedup below); `categories` is `["none"]`
23
+ * when nothing fired. Fails (without writing anything) if the file has no "## Usage log"
24
+ * heading at all — same "refuse rather than guess where it goes" discipline
25
+ * `stateFile.ts`'s `applyBulletUpdates` uses for a missing mechanical bullet.
26
+ */
27
+ export declare function appendUsageLogEntry(text: string, date: string, phases: readonly string[], categories: readonly string[]): PatchResult;
28
+ /** One canonically-formatted Usage log entry, parsed back into its two fields. */
29
+ export interface UsageLogEntry {
30
+ date: string;
31
+ phases: string[];
32
+ categories: string[];
33
+ }
34
+ /**
35
+ * Parse every `- YYYY-MM-DD: phases: ... — validate: ...` bullet out of `text`'s "## Usage log"
36
+ * section. A line that starts with `- ` inside the section but doesn't match the canonical shape
37
+ * (hand-edited, or written before `log-usage` existed) is returned in `malformed` verbatim
38
+ * rather than silently skipped or force-fit — `usage-report` surfaces these so a stale/foreign
39
+ * format is visible, not invisibly under-counted. Lines that are just the guidance comment (or
40
+ * blank) are ignored entirely — only an actual `- ` bullet attempt counts as malformed.
41
+ */
42
+ export declare function parseUsageLogSection(text: string): {
43
+ entries: UsageLogEntry[];
44
+ malformed: string[];
45
+ };
46
+ /** Every `.event-modeling.md` under `root`, recursively, as paths relative to `root` — sorted,
47
+ * POSIX forward slashes (see `walkDir`). Prunes VCS/dependency directories a repo-wide sweep
48
+ * should never descend into. */
49
+ export declare function findStateFiles(root: string): string[];
50
+ export interface CountEntry {
51
+ key: string;
52
+ count: number;
53
+ }
54
+ export interface UsageReport {
55
+ root: string;
56
+ files: string[];
57
+ sessions: number;
58
+ phaseCounts: CountEntry[];
59
+ /** Excludes `"none"` — same as docs/usage-data.md's own aggregation recipe (`grep -v
60
+ * '^none$'`): a session with nothing to report isn't a category worth tallying. */
61
+ categoryCounts: CountEntry[];
62
+ unparseableLines: Array<{
63
+ file: string;
64
+ line: string;
65
+ }>;
66
+ }
67
+ /** Aggregate every input file's Usage log into one report — pure, no I/O; the CLI layer resolves
68
+ * `entries` first (`findStateFiles` + a plain read per file). `root` is carried through only
69
+ * for the report's own labeling, never used to re-derive `files`. */
70
+ export declare function aggregateUsageReport(root: string, entries: Array<{
71
+ file: string;
72
+ text: string;
73
+ }>): UsageReport;
74
+ /** Text report: a one-line summary, then a phase tally and a category tally, each empty-aware. */
75
+ export declare function formatUsageReportText(report: UsageReport): string;