@encore-os/eos-spec 0.3.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.
- package/LICENSE +17 -0
- package/bin/check-dist-fresh.js +51 -0
- package/bin/eos-spec.js +7 -0
- package/dist/commands/ac-backfill.d.ts +31 -0
- package/dist/commands/ac-backfill.js +198 -0
- package/dist/commands/ac-backfill.js.map +1 -0
- package/dist/commands/archive.d.ts +2 -0
- package/dist/commands/archive.js +101 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/audit.d.ts +30 -0
- package/dist/commands/audit.js +107 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/backfill.d.ts +41 -0
- package/dist/commands/backfill.js +239 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/checkArtifacts.d.ts +2 -0
- package/dist/commands/checkArtifacts.js +48 -0
- package/dist/commands/checkArtifacts.js.map +1 -0
- package/dist/commands/checkPlacement.d.ts +2 -0
- package/dist/commands/checkPlacement.js +31 -0
- package/dist/commands/checkPlacement.js.map +1 -0
- package/dist/commands/claim.d.ts +4 -0
- package/dist/commands/claim.js +46 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/complete.d.ts +2 -0
- package/dist/commands/complete.js +138 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/conformance.d.ts +23 -0
- package/dist/commands/conformance.js +124 -0
- package/dist/commands/conformance.js.map +1 -0
- package/dist/commands/create.d.ts +6 -0
- package/dist/commands/create.js +277 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deferred.d.ts +2 -0
- package/dist/commands/deferred.js +85 -0
- package/dist/commands/deferred.js.map +1 -0
- package/dist/commands/delta.d.ts +17 -0
- package/dist/commands/delta.js +292 -0
- package/dist/commands/delta.js.map +1 -0
- package/dist/commands/freshness.d.ts +13 -0
- package/dist/commands/freshness.js +72 -0
- package/dist/commands/freshness.js.map +1 -0
- package/dist/commands/funnel.d.ts +2 -0
- package/dist/commands/funnel.js +61 -0
- package/dist/commands/funnel.js.map +1 -0
- package/dist/commands/gen-tests.d.ts +2 -0
- package/dist/commands/gen-tests.js +53 -0
- package/dist/commands/gen-tests.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +64 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +9 -0
- package/dist/commands/list.js +61 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/metrics.d.ts +2 -0
- package/dist/commands/metrics.js +66 -0
- package/dist/commands/metrics.js.map +1 -0
- package/dist/commands/next.d.ts +48 -0
- package/dist/commands/next.js +344 -0
- package/dist/commands/next.js.map +1 -0
- package/dist/commands/precedent.d.ts +66 -0
- package/dist/commands/precedent.js +343 -0
- package/dist/commands/precedent.js.map +1 -0
- package/dist/commands/ready.d.ts +28 -0
- package/dist/commands/ready.js +147 -0
- package/dist/commands/ready.js.map +1 -0
- package/dist/commands/reconcile.d.ts +44 -0
- package/dist/commands/reconcile.js +187 -0
- package/dist/commands/reconcile.js.map +1 -0
- package/dist/commands/rollup.d.ts +2 -0
- package/dist/commands/rollup.js +43 -0
- package/dist/commands/rollup.js.map +1 -0
- package/dist/commands/scorecard.d.ts +11 -0
- package/dist/commands/scorecard.js +55 -0
- package/dist/commands/scorecard.js.map +1 -0
- package/dist/commands/signoff.d.ts +2 -0
- package/dist/commands/signoff.js +114 -0
- package/dist/commands/signoff.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +100 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/tasks.d.ts +13 -0
- package/dist/commands/tasks.js +81 -0
- package/dist/commands/tasks.js.map +1 -0
- package/dist/commands/validate.d.ts +16 -0
- package/dist/commands/validate.js +89 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/verify-acs.d.ts +15 -0
- package/dist/commands/verify-acs.js +196 -0
- package/dist/commands/verify-acs.js.map +1 -0
- package/dist/commands/verifyRollout.d.ts +2 -0
- package/dist/commands/verifyRollout.js +125 -0
- package/dist/commands/verifyRollout.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/ac-draft-prompt.d.ts +18 -0
- package/dist/lib/ac-draft-prompt.js +38 -0
- package/dist/lib/ac-draft-prompt.js.map +1 -0
- package/dist/lib/ac-response-parser.d.ts +18 -0
- package/dist/lib/ac-response-parser.js +57 -0
- package/dist/lib/ac-response-parser.js.map +1 -0
- package/dist/lib/ai-call.d.ts +13 -0
- package/dist/lib/ai-call.js +17 -0
- package/dist/lib/ai-call.js.map +1 -0
- package/dist/lib/archive.d.ts +21 -0
- package/dist/lib/archive.js +136 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/artifact-integrity.d.ts +5 -0
- package/dist/lib/artifact-integrity.js +65 -0
- package/dist/lib/artifact-integrity.js.map +1 -0
- package/dist/lib/backfill-queue.d.ts +11 -0
- package/dist/lib/backfill-queue.js +14 -0
- package/dist/lib/backfill-queue.js.map +1 -0
- package/dist/lib/blockers.d.ts +66 -0
- package/dist/lib/blockers.js +103 -0
- package/dist/lib/blockers.js.map +1 -0
- package/dist/lib/claims.d.ts +39 -0
- package/dist/lib/claims.js +133 -0
- package/dist/lib/claims.js.map +1 -0
- package/dist/lib/classify-debt.d.ts +17 -0
- package/dist/lib/classify-debt.js +13 -0
- package/dist/lib/classify-debt.js.map +1 -0
- package/dist/lib/code-evidence.d.ts +35 -0
- package/dist/lib/code-evidence.js +171 -0
- package/dist/lib/code-evidence.js.map +1 -0
- package/dist/lib/coherence.d.ts +14 -0
- package/dist/lib/coherence.js +188 -0
- package/dist/lib/coherence.js.map +1 -0
- package/dist/lib/completion-gate.d.ts +17 -0
- package/dist/lib/completion-gate.js +28 -0
- package/dist/lib/completion-gate.js.map +1 -0
- package/dist/lib/compliance-signoff.d.ts +39 -0
- package/dist/lib/compliance-signoff.js +81 -0
- package/dist/lib/compliance-signoff.js.map +1 -0
- package/dist/lib/config.d.ts +63 -0
- package/dist/lib/config.js +40 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/cost.d.ts +12 -0
- package/dist/lib/cost.js +12 -0
- package/dist/lib/cost.js.map +1 -0
- package/dist/lib/debt-dashboard.d.ts +25 -0
- package/dist/lib/debt-dashboard.js +36 -0
- package/dist/lib/debt-dashboard.js.map +1 -0
- package/dist/lib/embedding-store.d.ts +103 -0
- package/dist/lib/embedding-store.js +133 -0
- package/dist/lib/embedding-store.js.map +1 -0
- package/dist/lib/embeddings-source.d.ts +36 -0
- package/dist/lib/embeddings-source.js +62 -0
- package/dist/lib/embeddings-source.js.map +1 -0
- package/dist/lib/enforcement-allowlist.d.ts +12 -0
- package/dist/lib/enforcement-allowlist.js +25 -0
- package/dist/lib/enforcement-allowlist.js.map +1 -0
- package/dist/lib/events.d.ts +47 -0
- package/dist/lib/events.js +75 -0
- package/dist/lib/events.js.map +1 -0
- package/dist/lib/exec.d.ts +8 -0
- package/dist/lib/exec.js +25 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +112 -0
- package/dist/lib/frontmatter.js +261 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/funnel.d.ts +22 -0
- package/dist/lib/funnel.js +41 -0
- package/dist/lib/funnel.js.map +1 -0
- package/dist/lib/git-mtime.d.ts +4 -0
- package/dist/lib/git-mtime.js +23 -0
- package/dist/lib/git-mtime.js.map +1 -0
- package/dist/lib/metrics.d.ts +42 -0
- package/dist/lib/metrics.js +92 -0
- package/dist/lib/metrics.js.map +1 -0
- package/dist/lib/migration-collision.d.ts +20 -0
- package/dist/lib/migration-collision.js +61 -0
- package/dist/lib/migration-collision.js.map +1 -0
- package/dist/lib/next-action.d.ts +44 -0
- package/dist/lib/next-action.js +138 -0
- package/dist/lib/next-action.js.map +1 -0
- package/dist/lib/ollama-embed.d.ts +21 -0
- package/dist/lib/ollama-embed.js +65 -0
- package/dist/lib/ollama-embed.js.map +1 -0
- package/dist/lib/output.d.ts +35 -0
- package/dist/lib/output.js +46 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/pipeline-config.d.ts +20 -0
- package/dist/lib/pipeline-config.js +208 -0
- package/dist/lib/pipeline-config.js.map +1 -0
- package/dist/lib/pipeline-map.d.ts +68 -0
- package/dist/lib/pipeline-map.js +256 -0
- package/dist/lib/pipeline-map.js.map +1 -0
- package/dist/lib/pipeline-schema.d.ts +48 -0
- package/dist/lib/pipeline-schema.js +22 -0
- package/dist/lib/pipeline-schema.js.map +1 -0
- package/dist/lib/placement.d.ts +6 -0
- package/dist/lib/placement.js +40 -0
- package/dist/lib/placement.js.map +1 -0
- package/dist/lib/precedent-search.d.ts +18 -0
- package/dist/lib/precedent-search.js +160 -0
- package/dist/lib/precedent-search.js.map +1 -0
- package/dist/lib/predicate-registry.d.ts +6 -0
- package/dist/lib/predicate-registry.js +40 -0
- package/dist/lib/predicate-registry.js.map +1 -0
- package/dist/lib/recall-cases.d.ts +15 -0
- package/dist/lib/recall-cases.js +39 -0
- package/dist/lib/recall-cases.js.map +1 -0
- package/dist/lib/repo.d.ts +20 -0
- package/dist/lib/repo.js +82 -0
- package/dist/lib/repo.js.map +1 -0
- package/dist/lib/requirements.d.ts +91 -0
- package/dist/lib/requirements.js +179 -0
- package/dist/lib/requirements.js.map +1 -0
- package/dist/lib/rollup.d.ts +24 -0
- package/dist/lib/rollup.js +34 -0
- package/dist/lib/rollup.js.map +1 -0
- package/dist/lib/scorecard-run.d.ts +6 -0
- package/dist/lib/scorecard-run.js +48 -0
- package/dist/lib/scorecard-run.js.map +1 -0
- package/dist/lib/scorecard.d.ts +69 -0
- package/dist/lib/scorecard.js +113 -0
- package/dist/lib/scorecard.js.map +1 -0
- package/dist/lib/semantic-search.d.ts +36 -0
- package/dist/lib/semantic-search.js +56 -0
- package/dist/lib/semantic-search.js.map +1 -0
- package/dist/lib/session-state.d.ts +8 -0
- package/dist/lib/session-state.js +25 -0
- package/dist/lib/session-state.js.map +1 -0
- package/dist/lib/spec-debt.d.ts +8 -0
- package/dist/lib/spec-debt.js +17 -0
- package/dist/lib/spec-debt.js.map +1 -0
- package/dist/lib/spec-pure.d.ts +59 -0
- package/dist/lib/spec-pure.js +215 -0
- package/dist/lib/spec-pure.js.map +1 -0
- package/dist/lib/specs.d.ts +148 -0
- package/dist/lib/specs.js +373 -0
- package/dist/lib/specs.js.map +1 -0
- package/dist/lib/test-generator.d.ts +5 -0
- package/dist/lib/test-generator.js +72 -0
- package/dist/lib/test-generator.js.map +1 -0
- package/dist/lib/user-stories.d.ts +6 -0
- package/dist/lib/user-stories.js +56 -0
- package/dist/lib/user-stories.js.map +1 -0
- package/dist/lib/verifies-scan.d.ts +9 -0
- package/dist/lib/verifies-scan.js +73 -0
- package/dist/lib/verifies-scan.js.map +1 -0
- package/dist/lib/verify-core.d.ts +23 -0
- package/dist/lib/verify-core.js +48 -0
- package/dist/lib/verify-core.js.map +1 -0
- package/dist/lib/with-event-log.d.ts +12 -0
- package/dist/lib/with-event-log.js +65 -0
- package/dist/lib/with-event-log.js.map +1 -0
- package/dist/lib/wizard-specs.d.ts +17 -0
- package/dist/lib/wizard-specs.js +34 -0
- package/dist/lib/wizard-specs.js.map +1 -0
- package/dist/pure.d.ts +19 -0
- package/dist/pure.js +31 -0
- package/dist/pure.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join } from 'node:path';
|
|
4
|
+
import { resolveTasksArtifact, setPipelineStatus, setTasksArtifact } from '../lib/frontmatter.js';
|
|
5
|
+
import { ErrorCode, emit, getGlobalOptions } from '../lib/output.js';
|
|
6
|
+
import { computeStage, rank } from '../lib/pipeline-map.js';
|
|
7
|
+
import { getRepoRoot } from '../lib/repo.js';
|
|
8
|
+
import { extractSpecId, listSpecs } from '../lib/specs.js';
|
|
9
|
+
import { withEventLog } from '../lib/with-event-log.js';
|
|
10
|
+
/** Classify frontmatter status vs the artifact-derived stage. */
|
|
11
|
+
export function classify(written, computed) {
|
|
12
|
+
if (written === computed)
|
|
13
|
+
return 'ok';
|
|
14
|
+
return rank(computed) > rank(written) ? 'lagging' : 'overstated';
|
|
15
|
+
}
|
|
16
|
+
/** Human-output scope label: `--spec` wins over `--core`; `--changed` → 'changed'; default `all`. */
|
|
17
|
+
export function reconcileScopeLabel(opts) {
|
|
18
|
+
if (opts.spec)
|
|
19
|
+
return String(opts.spec).toUpperCase();
|
|
20
|
+
if (opts.core)
|
|
21
|
+
return String(opts.core).toLowerCase();
|
|
22
|
+
if (opts.changed)
|
|
23
|
+
return 'changed';
|
|
24
|
+
return 'all';
|
|
25
|
+
}
|
|
26
|
+
/** Human-readable report: header + BOTH divergence directions, each with ids. */
|
|
27
|
+
export function formatReconcileText(args) {
|
|
28
|
+
const diverged = args.rows.filter((r) => r.direction !== 'ok');
|
|
29
|
+
const overstated = diverged.filter((r) => r.direction === 'overstated');
|
|
30
|
+
const lagging = diverged.filter((r) => r.direction === 'lagging');
|
|
31
|
+
const line = (r) => ` ${r.id}: written=${r.written ?? '(none)'} computed=${r.computed ?? '(none)'}`;
|
|
32
|
+
return [
|
|
33
|
+
`reconcile (${args.scope}): ${args.total} specs, ${diverged.length} diverged${args.write ? ' (written)' : ' (dry-run; pass --write to apply)'}`,
|
|
34
|
+
` overstated (status claims more than artifacts prove): ${overstated.length}`,
|
|
35
|
+
...overstated.slice(0, 50).map(line),
|
|
36
|
+
` lagging (artifacts prove more than status claims): ${lagging.length}`,
|
|
37
|
+
...lagging.slice(0, 50).map(line),
|
|
38
|
+
].join('\n');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Map a list of changed file paths to the spec IDs they belong to. Counts
|
|
42
|
+
* canonical spec markdown under `specs/<core>/specs/…` AND wizard/UX specs under
|
|
43
|
+
* `specs/<core>/ux/…` (ignores research/reviews/reports and non-spec files).
|
|
44
|
+
* Ids are extracted with the same parser `listSpecs` uses (`extractSpecId`) so
|
|
45
|
+
* the `--changed` filter matches corpus identity exactly — including qualifier
|
|
46
|
+
* ids like `HR-UX-14`. Upper-cased, de-duplicated, input order preserved.
|
|
47
|
+
* Used by `reconcile --changed` to scope the write to branch-touched specs.
|
|
48
|
+
*/
|
|
49
|
+
export function specIdsFromChangedPaths(paths) {
|
|
50
|
+
const ids = [];
|
|
51
|
+
for (const p of paths) {
|
|
52
|
+
const m = /(^|\/)specs\/([a-z]{2,3})\/(?:specs|ux)\/.+\.md$/i.exec(p);
|
|
53
|
+
if (!m)
|
|
54
|
+
continue;
|
|
55
|
+
const base = p.split('/').pop() ?? '';
|
|
56
|
+
const id = extractSpecId(base, m[2]);
|
|
57
|
+
if (id)
|
|
58
|
+
ids.push(id.toUpperCase());
|
|
59
|
+
}
|
|
60
|
+
return [...new Set(ids)];
|
|
61
|
+
}
|
|
62
|
+
/** Parse `git status --porcelain` output into pathnames (rename rows yield the new path). */
|
|
63
|
+
export function pathsFromPorcelain(out) {
|
|
64
|
+
return out
|
|
65
|
+
.split('\n')
|
|
66
|
+
.filter((line) => line.length > 3)
|
|
67
|
+
.map((line) => line.slice(3).trim())
|
|
68
|
+
.map((p) => (p.includes(' -> ') ? (p.split(' -> ').pop() ?? p) : p))
|
|
69
|
+
.map((p) => p.replace(/^"(.*)"$/, '$1'))
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Files changed on this branch vs `base` (three-dot; falling back to the diff
|
|
74
|
+
* vs HEAD when the base ref is unavailable), UNIONED with uncommitted
|
|
75
|
+
* working-tree changes from `git status --porcelain` — so post-signoff spec
|
|
76
|
+
* edits that haven't been committed yet are still in scope for `--changed`.
|
|
77
|
+
*/
|
|
78
|
+
export function gitChangedPaths(repoRoot, base) {
|
|
79
|
+
const split = (out) => out
|
|
80
|
+
.split('\n')
|
|
81
|
+
.map((l) => l.trim())
|
|
82
|
+
.filter(Boolean);
|
|
83
|
+
let committed = [];
|
|
84
|
+
try {
|
|
85
|
+
committed = split(execSync(`git diff --name-only ${base}...HEAD`, { cwd: repoRoot, encoding: 'utf8' }));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
try {
|
|
89
|
+
committed = split(execSync('git diff --name-only HEAD', { cwd: repoRoot, encoding: 'utf8' }));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
committed = [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
let workingTree = [];
|
|
96
|
+
try {
|
|
97
|
+
workingTree = pathsFromPorcelain(execSync('git status --porcelain', { cwd: repoRoot, encoding: 'utf8' }));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
workingTree = [];
|
|
101
|
+
}
|
|
102
|
+
return [...new Set([...committed, ...workingTree])];
|
|
103
|
+
}
|
|
104
|
+
export function registerReconcile(program) {
|
|
105
|
+
program
|
|
106
|
+
.command('reconcile')
|
|
107
|
+
.description('Recompute pipeline_status from artifacts; report divergence and (with --write) materialize it.')
|
|
108
|
+
.option('--core <core>', 'Limit to one core (default: all)')
|
|
109
|
+
.option('--spec <id>', 'Limit to one spec ID (safer than a whole-corpus write)')
|
|
110
|
+
.option('--changed', 'Limit to specs changed vs --base (scoped write — avoids corpus-wide conflicts)', false)
|
|
111
|
+
.option('--base <ref>', 'Base ref for --changed', 'origin/development')
|
|
112
|
+
.option('--write', 'Materialize the computed status into spec frontmatter', false)
|
|
113
|
+
.action(withEventLog('reconcile', async (options, cmd) => {
|
|
114
|
+
const global = getGlobalOptions(cmd);
|
|
115
|
+
const repoRoot = getRepoRoot(global.repoRoot);
|
|
116
|
+
// A bare `reconcile --write` rewrites pipeline_status across the WHOLE corpus,
|
|
117
|
+
// which collides with other parallel branches. Require an explicit scope when
|
|
118
|
+
// writing; dry-run stays unscoped. `--core`, `--spec`, and `--changed` all count.
|
|
119
|
+
if (options.write && !options.spec && !options.core && !options.changed) {
|
|
120
|
+
emit(global, {
|
|
121
|
+
ok: false,
|
|
122
|
+
error: {
|
|
123
|
+
code: ErrorCode.SCOPE_REQUIRED,
|
|
124
|
+
message: 'reconcile --write requires --spec/--core/--changed',
|
|
125
|
+
},
|
|
126
|
+
text: 'reconcile: --write requires a scope — pass --spec <id>, --core <core>, or --changed (bare corpus-wide writes are rejected)',
|
|
127
|
+
exitCode: 2,
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const all = listSpecs(repoRoot, options.core);
|
|
132
|
+
let specs = all;
|
|
133
|
+
if (options.changed) {
|
|
134
|
+
const ids = new Set(specIdsFromChangedPaths(gitChangedPaths(repoRoot, options.base)));
|
|
135
|
+
specs = specs.filter((s) => ids.has(s.id.toUpperCase()));
|
|
136
|
+
}
|
|
137
|
+
if (options.spec) {
|
|
138
|
+
specs = specs.filter((s) => s.id.toUpperCase() === String(options.spec).toUpperCase());
|
|
139
|
+
}
|
|
140
|
+
const rows = specs.map((spec) => {
|
|
141
|
+
const computed = computeStage(spec);
|
|
142
|
+
const written = spec.signals.pipelineStatus;
|
|
143
|
+
const direction = classify(written, computed);
|
|
144
|
+
if (options.write && computed) {
|
|
145
|
+
const original = readFileSync(spec.path, 'utf8');
|
|
146
|
+
let text = original;
|
|
147
|
+
if (direction !== 'ok')
|
|
148
|
+
text = setPipelineStatus(text, computed);
|
|
149
|
+
// Materialize artifacts.tasks alongside pipeline_status so a spec that
|
|
150
|
+
// reaches tasks_generated satisfies the pipeline-status validator
|
|
151
|
+
// (pipeline/missing-tasks-artifact) without hand-editing (#997 Bug 2).
|
|
152
|
+
// Non-destructive: a VALID existing pointer (canonical or not — many
|
|
153
|
+
// specs use `<id>-credentialing-compliance-TASKS.md` etc.) is preserved.
|
|
154
|
+
// Only an absent or stale (dangling) pointer is set to the canonical
|
|
155
|
+
// path, and only when that canonical file actually exists (#16).
|
|
156
|
+
if (spec.hasTasks) {
|
|
157
|
+
const fileExists = (rel) => existsSync(isAbsolute(rel) ? rel : join(repoRoot, rel));
|
|
158
|
+
const next = resolveTasksArtifact(text, `specs/${spec.core}/tasks/${spec.id}-TASKS.md`, fileExists);
|
|
159
|
+
if (next)
|
|
160
|
+
text = setTasksArtifact(text, next);
|
|
161
|
+
}
|
|
162
|
+
if (text !== original)
|
|
163
|
+
writeFileSync(spec.path, text, 'utf8');
|
|
164
|
+
}
|
|
165
|
+
return { id: spec.id, core: spec.core, written, computed, direction };
|
|
166
|
+
});
|
|
167
|
+
const diverged = rows.filter((r) => r.direction !== 'ok');
|
|
168
|
+
const overstated = diverged.filter((r) => r.direction === 'overstated');
|
|
169
|
+
emit(global, {
|
|
170
|
+
ok: true,
|
|
171
|
+
data: {
|
|
172
|
+
total: rows.length,
|
|
173
|
+
diverged: diverged.length,
|
|
174
|
+
overstated: overstated.length,
|
|
175
|
+
wrote: Boolean(options.write),
|
|
176
|
+
rows: diverged,
|
|
177
|
+
},
|
|
178
|
+
text: formatReconcileText({
|
|
179
|
+
scope: reconcileScopeLabel({ spec: options.spec, core: options.core, changed: options.changed }),
|
|
180
|
+
total: rows.length,
|
|
181
|
+
rows,
|
|
182
|
+
write: Boolean(options.write),
|
|
183
|
+
}),
|
|
184
|
+
});
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/commands/reconcile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { emit, getGlobalOptions } from '../lib/output.js';
|
|
4
|
+
import { coresList, getRepoRoot } from '../lib/repo.js';
|
|
5
|
+
import { computeRollup } from '../lib/rollup.js';
|
|
6
|
+
import { buildScorecardForCore } from '../lib/scorecard-run.js';
|
|
7
|
+
import { scanVerifies } from '../lib/verifies-scan.js';
|
|
8
|
+
import { withEventLog } from '../lib/with-event-log.js';
|
|
9
|
+
export function registerReadinessRollup(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('readiness-rollup')
|
|
12
|
+
.description("Aggregate every core's scorecard into a ranked platform readiness view (most-ready first).")
|
|
13
|
+
.option('--save', 'Write docs/development/PLATFORM_READINESS_ROLLUP_<date>.md', false)
|
|
14
|
+
.action(withEventLog('readiness-rollup', async (options, cmd) => {
|
|
15
|
+
const global = getGlobalOptions(cmd);
|
|
16
|
+
const repoRoot = getRepoRoot(global.repoRoot);
|
|
17
|
+
const verifiedBySpec = scanVerifies(repoRoot);
|
|
18
|
+
const perCore = coresList(repoRoot).map((core) => buildScorecardForCore(repoRoot, core, verifiedBySpec));
|
|
19
|
+
const report = computeRollup(perCore);
|
|
20
|
+
const header = '| Core | Specs | Authored % | Claims complete | Unverified-complete | Verified % |';
|
|
21
|
+
const sep = '|------|-------|-----------|-----------------|---------------------|-----------|';
|
|
22
|
+
const rowLines = report.rows.map((r) => `| ${r.core} | ${r.total} | ${r.authoredPct}% | ${r.claimsComplete} | ${r.claimedCompleteButUnverified} | ${r.verifiedPct}% |`);
|
|
23
|
+
const text = [
|
|
24
|
+
`Platform readiness rollup — ${report.cores.length} cores, ${report.totals.specs} specs`,
|
|
25
|
+
` claimed complete : ${report.totals.claimsComplete}`,
|
|
26
|
+
` claimed-complete-UNVERIFIED : ${report.totals.claimedCompleteButUnverified} ← the platform verification-debt headline`,
|
|
27
|
+
` specs w/ verified reqs : ${report.totals.withVerifiedRequirements} (${report.totals.verifiedPct}%)`,
|
|
28
|
+
'',
|
|
29
|
+
header,
|
|
30
|
+
sep,
|
|
31
|
+
...rowLines,
|
|
32
|
+
].join('\n');
|
|
33
|
+
if (options.save) {
|
|
34
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
35
|
+
const dir = join(repoRoot, 'docs/development');
|
|
36
|
+
mkdirSync(dir, { recursive: true });
|
|
37
|
+
const body = `# Platform Readiness Rollup — ${date}\n\n> Ranked most-ready first. Source: \`eos-spec scorecard\` per core (deterministic). "Verified %" = specs with >=1 \`@verifies\`-backed requirement; "Authored %" = specs at \`tasks_generated\`.\n\n\`\`\`\n${text}\n\`\`\`\n`;
|
|
38
|
+
writeFileSync(join(dir, `PLATFORM_READINESS_ROLLUP_${date}.md`), body, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
emit(global, { ok: true, data: report, text });
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=rollup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup.js","sourceRoot":"","sources":["../../src/commands/rollup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Validate `--core` against the canonical cores list. A bogus core used to fall through to
|
|
4
|
+
* listSpecs() → 0 specs → an all-zero scorecard with exit 0 — dangerously plausible output.
|
|
5
|
+
* Returns the normalized core, or an error message naming the valid cores.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveCoreFilter(core: string | undefined, repoRoot: string): {
|
|
8
|
+
core?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function registerScorecard(program: Command): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ErrorCode, emit, getGlobalOptions } from '../lib/output.js';
|
|
2
|
+
import { coresList, getRepoRoot, isKnownCore } from '../lib/repo.js';
|
|
3
|
+
import { buildScorecardForCore } from '../lib/scorecard-run.js';
|
|
4
|
+
import { scanVerifies } from '../lib/verifies-scan.js';
|
|
5
|
+
import { withEventLog } from '../lib/with-event-log.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validate `--core` against the canonical cores list. A bogus core used to fall through to
|
|
8
|
+
* listSpecs() → 0 specs → an all-zero scorecard with exit 0 — dangerously plausible output.
|
|
9
|
+
* Returns the normalized core, or an error message naming the valid cores.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveCoreFilter(core, repoRoot) {
|
|
12
|
+
if (!core)
|
|
13
|
+
return {};
|
|
14
|
+
const normalized = core.toLowerCase();
|
|
15
|
+
if (isKnownCore(normalized, repoRoot))
|
|
16
|
+
return { core: normalized };
|
|
17
|
+
return { error: `scorecard: unknown core "${core}". Valid cores: ${coresList(repoRoot).join(', ')}` };
|
|
18
|
+
}
|
|
19
|
+
export function registerScorecard(program) {
|
|
20
|
+
program
|
|
21
|
+
.command('scorecard')
|
|
22
|
+
.description('Deterministic readiness scorecard: per-spec stage, frontmatter accuracy, and @verifies-backed AC validation. Headline: claimed-complete-but-unverified.')
|
|
23
|
+
.option('--core <core>', 'Limit to one core')
|
|
24
|
+
.action(withEventLog('scorecard', async (options, cmd) => {
|
|
25
|
+
const global = getGlobalOptions(cmd);
|
|
26
|
+
const repoRoot = getRepoRoot(global.repoRoot);
|
|
27
|
+
const resolved = resolveCoreFilter(options.core, repoRoot);
|
|
28
|
+
if (resolved.error) {
|
|
29
|
+
emit(global, {
|
|
30
|
+
ok: false,
|
|
31
|
+
error: { code: ErrorCode.UNKNOWN_CORE, message: resolved.error },
|
|
32
|
+
data: { core: options.core, validCores: coresList(repoRoot) },
|
|
33
|
+
text: resolved.error,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const verifiedBySpec = scanVerifies(repoRoot);
|
|
37
|
+
const report = buildScorecardForCore(repoRoot, resolved.core, verifiedBySpec);
|
|
38
|
+
const flagged = report.rows.filter((r) => r.claimedCompleteButUnverified);
|
|
39
|
+
const text = [
|
|
40
|
+
`Readiness scorecard (${report.core}): ${report.total} specs`,
|
|
41
|
+
` claims complete : ${report.claimsComplete}`,
|
|
42
|
+
` claimed-complete-UNVERIFIED : ${report.claimedCompleteButUnverified} ← @verifies-backed AC validation = 0`,
|
|
43
|
+
` specs w/ verified reqs : ${report.withVerifiedRequirements}`,
|
|
44
|
+
'',
|
|
45
|
+
...(flagged.length
|
|
46
|
+
? [
|
|
47
|
+
' Claimed complete but unverified:',
|
|
48
|
+
...flagged.map((r) => ` ${r.id} (${r.stage}, ${r.requirementsVerified}/${r.requirementsTotal} reqs verified)`),
|
|
49
|
+
]
|
|
50
|
+
: [' None claimed-complete-but-unverified.']),
|
|
51
|
+
].join('\n');
|
|
52
|
+
emit(global, { ok: true, data: report, text });
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=scorecard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorecard.js","sourceRoot":"","sources":["../../src/commands/scorecard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { insertSignoffMarker } from '../lib/frontmatter.js';
|
|
4
|
+
import { ErrorCode, emit, getGlobalOptions } from '../lib/output.js';
|
|
5
|
+
import { getRepoRoot, specsRoot } from '../lib/repo.js';
|
|
6
|
+
import { diagnoseMissingSpec, findSpec } from '../lib/specs.js';
|
|
7
|
+
import { withEventLog } from '../lib/with-event-log.js';
|
|
8
|
+
const VALID_TYPES = [
|
|
9
|
+
'validated',
|
|
10
|
+
'spec-reviewed',
|
|
11
|
+
'ux-reviewed',
|
|
12
|
+
'heal-verified',
|
|
13
|
+
'docshots',
|
|
14
|
+
'ui-gate-waived',
|
|
15
|
+
'submit-e2e-waived',
|
|
16
|
+
'compliance',
|
|
17
|
+
];
|
|
18
|
+
/** Today's date in YYYY-MM-DD form in the operator's LOCAL timezone, suitable for
|
|
19
|
+
* sign-off markers. Local (not UTC) so an evening sign-off doesn't stamp tomorrow. */
|
|
20
|
+
function isoToday() {
|
|
21
|
+
// en-CA formats as YYYY-MM-DD; using local time avoids the UTC off-by-one.
|
|
22
|
+
return new Date().toLocaleDateString('en-CA');
|
|
23
|
+
}
|
|
24
|
+
export function registerSignoff(program) {
|
|
25
|
+
program
|
|
26
|
+
.command('signoff')
|
|
27
|
+
.description('Write a sign-off marker (validated | ux-reviewed | heal-verified | docshots | ui-gate-waived | submit-e2e-waived | compliance) to a spec; idempotent.')
|
|
28
|
+
.requiredOption('--spec <id>', 'Spec id (e.g. PM-21)')
|
|
29
|
+
.requiredOption('--type <type>', 'Marker: validated | spec-reviewed | ux-reviewed | heal-verified | docshots | ui-gate-waived | submit-e2e-waived | compliance')
|
|
30
|
+
.option('--date <YYYY-MM-DD>', 'Override the marker date (default: today)')
|
|
31
|
+
.option('--reviewer <name>', 'Reviewer name (compliance sign-off only)')
|
|
32
|
+
.action(withEventLog('signoff', async (options, cmd) => {
|
|
33
|
+
const global = getGlobalOptions(cmd);
|
|
34
|
+
const repoRoot = getRepoRoot(global.repoRoot);
|
|
35
|
+
const type = options.type;
|
|
36
|
+
if (!VALID_TYPES.includes(type)) {
|
|
37
|
+
emit(global, {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: {
|
|
40
|
+
code: ErrorCode.INVALID_ARGUMENT,
|
|
41
|
+
message: `signoff: --type must be one of ${VALID_TYPES.join(' | ')} (got "${options.type}")`,
|
|
42
|
+
},
|
|
43
|
+
data: null,
|
|
44
|
+
text: `signoff: --type must be one of ${VALID_TYPES.join(' | ')} (got "${options.type}")`,
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const spec = findSpec(repoRoot, options.spec);
|
|
49
|
+
if (!spec) {
|
|
50
|
+
const diag = diagnoseMissingSpec(repoRoot, options.spec);
|
|
51
|
+
emit(global, {
|
|
52
|
+
ok: false,
|
|
53
|
+
error: {
|
|
54
|
+
code: ErrorCode.SPEC_NOT_FOUND,
|
|
55
|
+
message: `signoff: spec ${options.spec} not found (${diag.reason})`,
|
|
56
|
+
},
|
|
57
|
+
data: null,
|
|
58
|
+
text: `signoff: spec ${options.spec} not found (${diag.reason})`,
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const date = options.date ?? isoToday();
|
|
63
|
+
if (type === 'compliance') {
|
|
64
|
+
const reviewsDir = join(specsRoot(repoRoot), spec.core, 'reviews');
|
|
65
|
+
const filePath = join(reviewsDir, `${spec.id}-COMPLIANCE-SIGNOFF.md`);
|
|
66
|
+
if (existsSync(filePath)) {
|
|
67
|
+
emit(global, {
|
|
68
|
+
ok: true,
|
|
69
|
+
data: { specId: spec.id, type, date, inserted: false, path: filePath },
|
|
70
|
+
text: `signoff: ${spec.id} already has a compliance sign-off file (no change)\n ${filePath}`,
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
75
|
+
const body = [
|
|
76
|
+
'---',
|
|
77
|
+
`spec_id: ${spec.id}`,
|
|
78
|
+
`reviewer: ${options.reviewer ?? '<name>'}`,
|
|
79
|
+
`date: ${date}`,
|
|
80
|
+
'determinations:',
|
|
81
|
+
' - regime: <regime>',
|
|
82
|
+
' status: <reviewed | not-applicable | deferred>',
|
|
83
|
+
'---',
|
|
84
|
+
'',
|
|
85
|
+
`# Compliance Sign-Off: ${spec.id}`,
|
|
86
|
+
'',
|
|
87
|
+
'| # | Item | Regulation | Confirmed | Signed Off By | Date |',
|
|
88
|
+
'|---|------|------------|-----------|---------------|------|',
|
|
89
|
+
'',
|
|
90
|
+
].join('\n');
|
|
91
|
+
writeFileSync(filePath, body, 'utf8');
|
|
92
|
+
emit(global, {
|
|
93
|
+
ok: true,
|
|
94
|
+
data: { specId: spec.id, type, date, inserted: true, path: filePath },
|
|
95
|
+
text: `signoff: wrote compliance sign-off scaffold to ${filePath}\n` +
|
|
96
|
+
' edit reviewer + determinations[] (regimes from docs/compliance/*_TRACKING.md), then commit\n' +
|
|
97
|
+
` next: run \`eos-spec reconcile --spec ${spec.id} --write\` to materialize derived pipeline_status`,
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const text = readFileSync(spec.path, 'utf8');
|
|
102
|
+
const { text: next, inserted } = insertSignoffMarker(text, type, date);
|
|
103
|
+
if (inserted)
|
|
104
|
+
writeFileSync(spec.path, next, 'utf8');
|
|
105
|
+
emit(global, {
|
|
106
|
+
ok: true,
|
|
107
|
+
data: { specId: spec.id, type, date, inserted, path: spec.path },
|
|
108
|
+
text: inserted
|
|
109
|
+
? `signoff: wrote <!-- ${type}: ${date} --> to ${spec.id}\n ${spec.path}\n next: run \`eos-spec reconcile --spec ${spec.id} --write\` to materialize derived pipeline_status`
|
|
110
|
+
: `signoff: ${spec.id} already has a ${type} marker (no change)\n ${spec.path}`,
|
|
111
|
+
});
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=signoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signoff.js","sourceRoot":"","sources":["../../src/commands/signoff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { ErrorCode, emit, getGlobalOptions } from '../lib/output.js';
|
|
4
|
+
import { computeStage } from '../lib/pipeline-map.js';
|
|
5
|
+
import { getRepoRoot } from '../lib/repo.js';
|
|
6
|
+
import { isClaimedCompleteUnverified } from '../lib/spec-debt.js';
|
|
7
|
+
import { findSpec, listSpecs } from '../lib/specs.js';
|
|
8
|
+
import { withEventLog } from '../lib/with-event-log.js';
|
|
9
|
+
export function registerStatus(program) {
|
|
10
|
+
program
|
|
11
|
+
.command('status')
|
|
12
|
+
.description('Show detail (status, files, completion) for one spec or summary for a core.')
|
|
13
|
+
.option('--core <core>', 'Show summary for one core')
|
|
14
|
+
.option('--spec <id>', 'Show detail for one spec')
|
|
15
|
+
.option('--include-archived', 'Include specs from specs/{core}/archive/', false)
|
|
16
|
+
.action(withEventLog('status', async (options, cmd) => {
|
|
17
|
+
const global = getGlobalOptions(cmd);
|
|
18
|
+
const repoRoot = getRepoRoot(global.repoRoot);
|
|
19
|
+
if (options.spec) {
|
|
20
|
+
const spec = findSpec(repoRoot, options.spec, { includeArchived: options.includeArchived });
|
|
21
|
+
if (!spec) {
|
|
22
|
+
emit(global, {
|
|
23
|
+
ok: false,
|
|
24
|
+
error: { code: ErrorCode.SPEC_NOT_FOUND, message: `status: spec ${options.spec} not found` },
|
|
25
|
+
data: null,
|
|
26
|
+
text: `status: spec ${options.spec} not found`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const tasksPath = join(repoRoot, 'specs', spec.core, 'tasks', `${spec.id}-TASKS.md`);
|
|
30
|
+
let totalTasks = 0;
|
|
31
|
+
let completedTasks = 0;
|
|
32
|
+
if (existsSync(tasksPath)) {
|
|
33
|
+
const text = readFileSync(tasksPath, 'utf8');
|
|
34
|
+
totalTasks = (text.match(/^\s*-\s*\[\s*[xX ]\s*\]\s+/gm) ?? []).length;
|
|
35
|
+
completedTasks = (text.match(/^\s*-\s*\[\s*[xX]\s*\]\s+/gm) ?? []).length;
|
|
36
|
+
}
|
|
37
|
+
const claimedCompleteUnverified = isClaimedCompleteUnverified(repoRoot, spec.id);
|
|
38
|
+
const data = {
|
|
39
|
+
id: spec.id,
|
|
40
|
+
core: spec.core,
|
|
41
|
+
title: spec.title,
|
|
42
|
+
status: spec.status,
|
|
43
|
+
pipelineStatus: spec.signals.pipelineStatus,
|
|
44
|
+
computedStage: computeStage(spec),
|
|
45
|
+
paths: { spec: spec.path, hasContext: spec.hasContext, hasPlan: spec.hasPlan, hasTasks: spec.hasTasks },
|
|
46
|
+
tasks: { total: totalTasks, completed: completedTasks },
|
|
47
|
+
claimedCompleteButUnverified: claimedCompleteUnverified,
|
|
48
|
+
};
|
|
49
|
+
emit(global, {
|
|
50
|
+
ok: true,
|
|
51
|
+
data,
|
|
52
|
+
text: [
|
|
53
|
+
`Spec : ${spec.id} — ${spec.title ?? '(no title)'}`,
|
|
54
|
+
`Status : ${spec.status ?? 'unknown'}`,
|
|
55
|
+
`Stage : computed=${computeStage(spec) ?? '(none)'} frontmatter=${spec.signals.pipelineStatus ?? '(none)'}`,
|
|
56
|
+
`Archived: ${spec.archived ? 'yes' : 'no'}`,
|
|
57
|
+
`Files : context=${spec.hasContext} plan=${spec.hasPlan} tasks=${spec.hasTasks}`,
|
|
58
|
+
`Tasks : ${completedTasks}/${totalTasks} complete`,
|
|
59
|
+
`Path : ${spec.path}`,
|
|
60
|
+
...(claimedCompleteUnverified
|
|
61
|
+
? [
|
|
62
|
+
`⚠ verification-debt: claims complete but 0 @verifies-backed ACs — run \`npm run spec:check-debt\` / \`npm run spec:verify-acs -- --enforce --spec ${spec.id}\``,
|
|
63
|
+
]
|
|
64
|
+
: []),
|
|
65
|
+
].join('\n'),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const specs = listSpecs(repoRoot, options.core, { includeArchived: options.includeArchived });
|
|
69
|
+
const summary = {
|
|
70
|
+
core: options.core ?? 'all',
|
|
71
|
+
total: specs.length,
|
|
72
|
+
archived: specs.filter((s) => s.archived).length,
|
|
73
|
+
byStatus: tally(specs.map((s) => s.status ?? 'unknown')),
|
|
74
|
+
byComputedStage: tally(specs.map((s) => computeStage(s))),
|
|
75
|
+
withPlan: specs.filter((s) => s.hasPlan).length,
|
|
76
|
+
withTasks: specs.filter((s) => s.hasTasks).length,
|
|
77
|
+
};
|
|
78
|
+
const text = [
|
|
79
|
+
`Status summary (${summary.core}):`,
|
|
80
|
+
` Total specs : ${summary.total}`,
|
|
81
|
+
` Archived : ${summary.archived}`,
|
|
82
|
+
` By status (body) : ${Object.entries(summary.byStatus)
|
|
83
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
84
|
+
.join(' ')}`,
|
|
85
|
+
` By computed stage : ${Object.entries(summary.byComputedStage)
|
|
86
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
87
|
+
.join(' ')}`,
|
|
88
|
+
` With plan : ${summary.withPlan}`,
|
|
89
|
+
` With tasks : ${summary.withTasks}`,
|
|
90
|
+
].join('\n');
|
|
91
|
+
emit(global, { ok: true, data: summary, text });
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
function tally(items) {
|
|
95
|
+
const out = {};
|
|
96
|
+
for (const item of items)
|
|
97
|
+
out[item] = (out[item] ?? 0) + 1;
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export declare function registerTasks(program: Command): void;
|
|
3
|
+
/**
|
|
4
|
+
* Rebase relative markdown-link targets written relative to `fromDir` so they
|
|
5
|
+
* resolve from `toDir` instead. In-document (`#…`), absolute (`/…`), and external
|
|
6
|
+
* (`scheme:`) links are left untouched; `#anchors` are preserved. Generic — it
|
|
7
|
+
* handles the TASKS template's sibling (`_templates/…`) and cross-core
|
|
8
|
+
* (`../pf/…`) links without hardcoding any path (#997 Bug 1).
|
|
9
|
+
*
|
|
10
|
+
* Exported for unit testing. Both dirs are POSIX, repo-relative; only their
|
|
11
|
+
* relative depth matters.
|
|
12
|
+
*/
|
|
13
|
+
export declare function rebaseRelativeLinks(body: string, fromDir: string, toDir: string): string;
|