@spec-engine/spec-engine 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/agent-reference/index.html +1 -1
- package/dist/docs/architecture/index.html +10 -2
- package/dist/docs/commands/index.html +8 -5
- package/dist/docs/pagefind/fragment/en_34cad94.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_94271d5.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_aa3ebfe.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_b2867b9.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_9af49f2.pf_index +0 -0
- package/dist/docs/pagefind/pagefind-entry.json +1 -1
- package/dist/docs/pagefind/pagefind.en_beb6d752c1.pf_meta +0 -0
- package/dist/docs/platform-mapping/index.html +9 -5
- package/dist/impl.js +382 -183
- package/package.json +1 -1
- package/dist/docs/pagefind/fragment/en_4bd5a86.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_73bb90d.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_ace7862.pf_fragment +0 -0
- package/dist/docs/pagefind/fragment/en_ef547ec.pf_fragment +0 -0
- package/dist/docs/pagefind/index/en_a2165b2.pf_index +0 -0
- package/dist/docs/pagefind/pagefind.en_b7799d257e.pf_meta +0 -0
package/dist/impl.js
CHANGED
|
@@ -20,7 +20,7 @@ var package_default;
|
|
|
20
20
|
var init_package = __esm(() => {
|
|
21
21
|
package_default = {
|
|
22
22
|
name: "@spec-engine/spec-engine",
|
|
23
|
-
version: "0.
|
|
23
|
+
version: "0.6.0",
|
|
24
24
|
description: "Cross-repo spec engine \u2014 durable requirements with permanent IDs, bound to code via @spec tags, checked in CI. Ships the spec CLI, the local coverage webapp (spec serve), and offline docs (spec docs).",
|
|
25
25
|
license: "MIT",
|
|
26
26
|
type: "module",
|
|
@@ -432,19 +432,20 @@ function buildCoverageReport(rows) {
|
|
|
432
432
|
}
|
|
433
433
|
|
|
434
434
|
// ../shared/src/schema.ts
|
|
435
|
-
var SCHEMA_VERSION =
|
|
435
|
+
var SCHEMA_VERSION = 11, FILES_MAX = 1000, DEFAULT_QUERY_LIMIT = 10, LIMIT_MAX = 1000, TABLES_DDL = `
|
|
436
436
|
-- _schema_version: single-row table holding the integer schema_version on disk.
|
|
437
437
|
-- Engine reads this on open; if it doesn't match SCHEMA_VERSION, the DB is wiped
|
|
438
|
-
-- and rebuilt from scratch
|
|
438
|
+
-- and rebuilt from scratch.
|
|
439
439
|
CREATE TABLE IF NOT EXISTS _schema_version (
|
|
440
440
|
version INTEGER NOT NULL
|
|
441
441
|
);
|
|
442
442
|
|
|
443
|
-
--
|
|
443
|
+
-- No CHECK, FK, or UNIQUE constraint on any domain field. PRIMARY KEY only.
|
|
444
444
|
CREATE TABLE IF NOT EXISTS repos (
|
|
445
445
|
name TEXT PRIMARY KEY,
|
|
446
446
|
path TEXT NOT NULL,
|
|
447
|
-
pinned_spec_version INTEGER NOT NULL
|
|
447
|
+
pinned_spec_version INTEGER NOT NULL,
|
|
448
|
+
dependency_depth INTEGER NOT NULL
|
|
448
449
|
);
|
|
449
450
|
|
|
450
451
|
CREATE TABLE IF NOT EXISTS domains (
|
|
@@ -483,12 +484,10 @@ CREATE INDEX IF NOT EXISTS idx_tags_by_req ON tags(req_id);
|
|
|
483
484
|
CREATE INDEX IF NOT EXISTS idx_tags_by_repo ON tags(repo);
|
|
484
485
|
CREATE INDEX IF NOT EXISTS idx_tags_by_file ON tags(file);
|
|
485
486
|
|
|
486
|
-
-- relations
|
|
487
|
-
--
|
|
488
|
-
-- Invariant #4 there is NO FK \u2014 a to_id pointing at a missing requirement
|
|
487
|
+
-- relations: one row per relates link. from_id carries the field; to_id is
|
|
488
|
+
-- the linked requirement. No FK: a to_id pointing at a missing requirement
|
|
489
489
|
-- lands verbatim so spec check can surface BROKEN_RELATES; a to_id whose
|
|
490
|
-
-- requirement was superseded surfaces RELATES_SUPERSEDED.
|
|
491
|
-
-- line locate the authored Relates field line.
|
|
490
|
+
-- requirement was superseded surfaces RELATES_SUPERSEDED.
|
|
492
491
|
CREATE TABLE IF NOT EXISTS relations (
|
|
493
492
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
494
493
|
from_id TEXT NOT NULL,
|
|
@@ -499,11 +498,8 @@ CREATE TABLE IF NOT EXISTS relations (
|
|
|
499
498
|
CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_id);
|
|
500
499
|
CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_id);
|
|
501
500
|
|
|
502
|
-
-- term_aliases
|
|
503
|
-
-- from the
|
|
504
|
-
-- FK/CHECK/UNIQUE (SCHM-07 / Invariant #4, or the arch-fence trips). term_id is
|
|
505
|
-
-- the owning TERM's id; name the synonym string. Present-and-empty until Wave C
|
|
506
|
-
-- flattens the requirement aliases field into rows.
|
|
501
|
+
-- term_aliases: one row per glossary-term alias. id AUTOINCREMENT is excluded
|
|
502
|
+
-- from build_id. term_id is the owning TERM's id; name the synonym string.
|
|
507
503
|
CREATE TABLE IF NOT EXISTS term_aliases (
|
|
508
504
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
509
505
|
term_id TEXT NOT NULL,
|
|
@@ -512,15 +508,12 @@ CREATE TABLE IF NOT EXISTS term_aliases (
|
|
|
512
508
|
CREATE INDEX IF NOT EXISTS idx_term_aliases_term ON term_aliases(term_id);
|
|
513
509
|
CREATE INDEX IF NOT EXISTS idx_term_aliases_name ON term_aliases(name);
|
|
514
510
|
|
|
515
|
-
-- term_citations
|
|
516
|
-
--
|
|
517
|
-
--
|
|
518
|
-
--
|
|
519
|
-
--
|
|
520
|
-
--
|
|
521
|
-
-- still lands); cited_as the authored surface form; pinned_version the term
|
|
522
|
-
-- spec_version the citation was pinned to; source_file + line locate the cite.
|
|
523
|
-
-- Present-and-empty until Wave C wires the flatten.
|
|
511
|
+
-- term_citations: one row per pinned cites reference from a requirement to a
|
|
512
|
+
-- glossary TERM. id AUTOINCREMENT is excluded from build_id. No FK: a term_id
|
|
513
|
+
-- pointing at a missing or superseded TERM lands verbatim so spec check can
|
|
514
|
+
-- surface UNDEFINED_TERM / TERM_DRIFT. term_id is nullable (an unresolved cite
|
|
515
|
+
-- still lands); cited_as is the authored surface form; pinned_version the term
|
|
516
|
+
-- spec_version the citation was pinned to.
|
|
524
517
|
CREATE TABLE IF NOT EXISTS term_citations (
|
|
525
518
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
526
519
|
req_id TEXT NOT NULL,
|
|
@@ -533,30 +526,25 @@ CREATE TABLE IF NOT EXISTS term_citations (
|
|
|
533
526
|
CREATE INDEX IF NOT EXISTS idx_term_citations_req ON term_citations(req_id);
|
|
534
527
|
CREATE INDEX IF NOT EXISTS idx_term_citations_term ON term_citations(term_id);
|
|
535
528
|
|
|
536
|
-
-- provenance
|
|
537
|
-
--
|
|
538
|
-
--
|
|
539
|
-
--
|
|
540
|
-
--
|
|
541
|
-
-- Exactly ONE index, on req_id (SC3) \u2014 never on issue_id, which would read as
|
|
542
|
-
-- treating the opaque payload as a lookup key (PROV-02, grep-fenced in Plan 04).
|
|
529
|
+
-- provenance: one row per issues role:ID link. issue_id is the opaque
|
|
530
|
+
-- external tracker payload; role is a closed-allow-list label (created |
|
|
531
|
+
-- supersedes-via | amends-via), validated at parse time, never as a DB
|
|
532
|
+
-- constraint. Exactly one index, on req_id; never on issue_id, which would
|
|
533
|
+
-- treat the opaque payload as a lookup key.
|
|
543
534
|
-- @spec PROV-004
|
|
544
535
|
CREATE TABLE IF NOT EXISTS provenance (
|
|
545
536
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
546
537
|
req_id TEXT NOT NULL,
|
|
547
|
-
issue_id TEXT NOT NULL, --
|
|
538
|
+
issue_id TEXT NOT NULL, -- opaque payload string, never a PK/FK/UNIQUE/JOIN key
|
|
548
539
|
role TEXT NOT NULL, -- created | supersedes-via | amends-via
|
|
549
540
|
source_file TEXT NOT NULL,
|
|
550
541
|
line INTEGER NOT NULL
|
|
551
542
|
);
|
|
552
|
-
CREATE INDEX IF NOT EXISTS idx_provenance_by_req ON provenance(req_id); --
|
|
553
|
-
|
|
554
|
-
-- parse_diagnostics: structural diagnostics
|
|
555
|
-
--
|
|
556
|
-
--
|
|
557
|
-
-- req_id is nullable per WR-02 (03-REVIEW): the parser knows the implicated
|
|
558
|
-
-- id for BROKEN_SUPERSEDE and BAD_STATUS, and for the SECOND DUP_ID
|
|
559
|
-
-- occurrence. NULL when no specific id is implicated.
|
|
543
|
+
CREATE INDEX IF NOT EXISTS idx_provenance_by_req ON provenance(req_id); -- the only index
|
|
544
|
+
|
|
545
|
+
-- parse_diagnostics: structural diagnostics from the validate-on-parse pass
|
|
546
|
+
-- (DUP_ID, BROKEN_SUPERSEDE, BAD_STATUS), stored so spec check can render them
|
|
547
|
+
-- with file+line. req_id is NULL when no specific id is implicated.
|
|
560
548
|
CREATE TABLE IF NOT EXISTS parse_diagnostics (
|
|
561
549
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
562
550
|
code TEXT NOT NULL,
|
|
@@ -591,13 +579,9 @@ FROM requirements r
|
|
|
591
579
|
CROSS JOIN repos
|
|
592
580
|
LEFT JOIN tags t
|
|
593
581
|
ON t.req_id = r.id AND t.repo = repos.name
|
|
594
|
-
--
|
|
595
|
-
--
|
|
596
|
-
--
|
|
597
|
-
-- coverage) in ONE edit, so a migrated term never renders an empty coverage
|
|
598
|
-
-- cell. The Q4 ORPHAN_REQ / Q5 UNVERIFIED_REQ queries (sqlite.ts) carry the
|
|
599
|
-
-- SAME literal exclusion so a term never fires a coverage ERROR. The match is
|
|
600
|
-
-- the exact literal 'TERM' \u2014 never a real domain key.
|
|
582
|
+
-- A glossary TERM is a requirement row but never a code-coverage obligation.
|
|
583
|
+
-- The ORPHAN_REQ / UNVERIFIED_REQ queries in sqlite.ts carry the same literal
|
|
584
|
+
-- exclusion. The match is the exact literal 'TERM', never a real domain key.
|
|
601
585
|
-- @spec SCHM-017
|
|
602
586
|
WHERE r.key != 'TERM'
|
|
603
587
|
GROUP BY r.id, repos.name;
|
|
@@ -616,15 +600,9 @@ JOIN requirements r ON r.id = t.req_id
|
|
|
616
600
|
JOIN repos ON repos.name = t.repo
|
|
617
601
|
WHERE r.changed_at_version > repos.pinned_spec_version;
|
|
618
602
|
|
|
619
|
-
-- term_drift
|
|
620
|
-
--
|
|
621
|
-
--
|
|
622
|
-
-- changed_at_version \u2014 i.e. the term's definition moved on since the citation
|
|
623
|
-
-- was pinned. INNER JOIN (not LEFT): an unresolved term_id (no matching
|
|
624
|
-
-- requirement) is an UNDEFINED_TERM concern, NOT drift \u2014 scoped distinctly by
|
|
625
|
-
-- the Wave-D diagnostics. term.key='TERM' keeps the VIEW scoped to the reserved
|
|
626
|
-
-- glossary domain. Present-and-empty until Wave C populates term_citations; the
|
|
627
|
-
-- diagnostic (TERM_DRIFT) reads it in Wave E.
|
|
603
|
+
-- term_drift: one row per cites reference whose pinned_version is behind the
|
|
604
|
+
-- cited TERM's changed_at_version. INNER JOIN: an unresolved term_id is an
|
|
605
|
+
-- UNDEFINED_TERM concern, not drift.
|
|
628
606
|
-- @spec CHCK-018
|
|
629
607
|
CREATE VIEW IF NOT EXISTS term_drift AS
|
|
630
608
|
SELECT
|
|
@@ -638,38 +616,21 @@ FROM term_citations tc
|
|
|
638
616
|
JOIN requirements term ON term.id = tc.term_id
|
|
639
617
|
WHERE term.key = 'TERM' AND term.changed_at_version > tc.pinned_version;
|
|
640
618
|
|
|
641
|
-
-- provenance_matrix
|
|
642
|
-
--
|
|
643
|
-
--
|
|
644
|
-
--
|
|
645
|
-
--
|
|
646
|
-
--
|
|
647
|
-
--
|
|
648
|
-
--
|
|
649
|
-
--
|
|
650
|
-
-- WR-01 (13-REVIEW review-fix): test_levels is re-aggregated at the
|
|
651
|
-
-- INDIVIDUAL-level granularity via a correlated subquery over tags, NOT by
|
|
652
|
-
-- GROUP_CONCAT(DISTINCT c.test_levels). The coverage VIEW already concatenates
|
|
653
|
-
-- levels per (req,repo) into a single string (api -> "unit,integration",
|
|
654
|
-
-- mobile -> "unit"); GROUP_CONCAT(DISTINCT ...) over those whole strings
|
|
655
|
-
-- deduplicates STRINGS, not levels, so a level present in two repos emitted
|
|
656
|
-
-- twice ("unit,integration,unit"). The subquery selects DISTINCT t2.level for
|
|
657
|
-
-- this req across ALL repos (ORDER BY level for determinism), so each level
|
|
658
|
-
-- appears exactly once. The correlation is ON req_id ONLY (t2.req_id =
|
|
659
|
-
-- p.req_id) \u2014 the issue_id-opacity fence stays green (no issue_id key usage).
|
|
660
|
-
-- CRITICAL FENCE CONSTRAINT
|
|
661
|
-
-- (PROV-02/SC3): the CI issue_id-opacity fence forbids grouping by issue_id,
|
|
662
|
-
-- so we GROUP BY the provenance PK p.id (which uniquely determines the link)
|
|
663
|
-
-- and let issue_id ride as a bare projected column under SQLite's
|
|
664
|
-
-- bare-column rule. issue_id is therefore PURELY projected \u2014 never a
|
|
665
|
-
-- JOIN/GROUP-BY/PK/FK/UNIQUE/INDEX key.
|
|
619
|
+
-- provenance_matrix: provenance \xD7 requirements \xD7 coverage, never
|
|
620
|
+
-- materialized. Every join is on req_id, never on issue_id. coverage is a
|
|
621
|
+
-- per-(req, repo) matrix, so the join fans out per repo and is aggregated to
|
|
622
|
+
-- one row per provenance link, grouped by the provenance PK p.id; issue_id
|
|
623
|
+
-- rides as a bare projected column and is never a JOIN, GROUP BY, PK, FK,
|
|
624
|
+
-- UNIQUE, or INDEX key. test_levels is aggregated by a correlated subquery
|
|
625
|
+
-- over tags (DISTINCT level, ORDER BY level) so a level present in two repos
|
|
626
|
+
-- appears once.
|
|
666
627
|
CREATE VIEW IF NOT EXISTS provenance_matrix AS
|
|
667
628
|
SELECT
|
|
668
629
|
p.req_id AS req_id,
|
|
669
630
|
p.role AS role,
|
|
670
|
-
p.issue_id AS issue_id, --
|
|
631
|
+
p.issue_id AS issue_id, -- opaque payload, projected only
|
|
671
632
|
p.source_file AS source_file,
|
|
672
|
-
p.line AS line,
|
|
633
|
+
p.line AS line,
|
|
673
634
|
r.status AS req_status,
|
|
674
635
|
MAX(c.implemented) AS implemented,
|
|
675
636
|
MAX(c.verified) AS verified,
|
|
@@ -1204,19 +1165,86 @@ function packageColumnName(root, dir) {
|
|
|
1204
1165
|
function byName(a, b) {
|
|
1205
1166
|
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
1206
1167
|
}
|
|
1207
|
-
function column(name, path, pin, ignore) {
|
|
1168
|
+
function column(name, path, pin, node, ignore) {
|
|
1208
1169
|
return {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1170
|
+
repo: {
|
|
1171
|
+
name,
|
|
1172
|
+
path,
|
|
1173
|
+
pinned_spec_version: pin,
|
|
1174
|
+
...ignore && ignore.length > 0 ? { ignore } : {}
|
|
1175
|
+
},
|
|
1176
|
+
node
|
|
1213
1177
|
};
|
|
1214
1178
|
}
|
|
1179
|
+
function packageKey(ecosystem, packageName) {
|
|
1180
|
+
return `${ecosystem ?? ""}\x00${packageName}`;
|
|
1181
|
+
}
|
|
1182
|
+
function providersOf(columns) {
|
|
1183
|
+
const providers = new Map;
|
|
1184
|
+
for (const c of columns) {
|
|
1185
|
+
if (c.node.packageName === undefined)
|
|
1186
|
+
continue;
|
|
1187
|
+
const key = packageKey(c.node.ecosystem, c.node.packageName);
|
|
1188
|
+
if (!providers.has(key))
|
|
1189
|
+
providers.set(key, c.repo.name);
|
|
1190
|
+
}
|
|
1191
|
+
return providers;
|
|
1192
|
+
}
|
|
1193
|
+
function dependenciesOf(c, providers) {
|
|
1194
|
+
const deps = new Set;
|
|
1195
|
+
for (const name of c.node.dependsOn) {
|
|
1196
|
+
const provider = providers.get(packageKey(c.node.ecosystem, name));
|
|
1197
|
+
if (provider !== undefined && provider !== c.repo.name)
|
|
1198
|
+
deps.add(provider);
|
|
1199
|
+
}
|
|
1200
|
+
return deps;
|
|
1201
|
+
}
|
|
1202
|
+
function dependencyGraph(columns) {
|
|
1203
|
+
const providers = providersOf(columns);
|
|
1204
|
+
const dependents = new Map;
|
|
1205
|
+
const waitingOn = new Map;
|
|
1206
|
+
for (const c of columns) {
|
|
1207
|
+
const deps = dependenciesOf(c, providers);
|
|
1208
|
+
waitingOn.set(c.repo.name, deps.size);
|
|
1209
|
+
for (const provider of deps) {
|
|
1210
|
+
dependents.set(provider, [...dependents.get(provider) ?? [], c.repo.name]);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return { names: columns.map((c) => c.repo.name).sort(), dependents, waitingOn };
|
|
1214
|
+
}
|
|
1215
|
+
function dependencyDepths(columns) {
|
|
1216
|
+
const { names, dependents, waitingOn } = dependencyGraph(columns);
|
|
1217
|
+
const depth = new Map;
|
|
1218
|
+
const ready = names.filter((n) => waitingOn.get(n) === 0);
|
|
1219
|
+
for (const n of ready)
|
|
1220
|
+
depth.set(n, 0);
|
|
1221
|
+
for (let i = 0;i < ready.length; i++) {
|
|
1222
|
+
const provider = ready[i];
|
|
1223
|
+
const providerDepth = depth.get(provider) ?? 0;
|
|
1224
|
+
for (const consumer of dependents.get(provider) ?? []) {
|
|
1225
|
+
depth.set(consumer, Math.max(depth.get(consumer) ?? 0, providerDepth + 1));
|
|
1226
|
+
const left = (waitingOn.get(consumer) ?? 0) - 1;
|
|
1227
|
+
waitingOn.set(consumer, left);
|
|
1228
|
+
if (left === 0)
|
|
1229
|
+
ready.push(consumer);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const cyclic = Math.max(-1, ...depth.values()) + 1;
|
|
1233
|
+
for (const n of names)
|
|
1234
|
+
if (!depth.has(n))
|
|
1235
|
+
depth.set(n, cyclic);
|
|
1236
|
+
return depth;
|
|
1237
|
+
}
|
|
1238
|
+
function toRepos(columns) {
|
|
1239
|
+
const depth = dependencyDepths(columns);
|
|
1240
|
+
return columns.map((c) => ({ ...c.repo, dependency_depth: depth.get(c.repo.name) ?? 0 })).sort(byName);
|
|
1241
|
+
}
|
|
1215
1242
|
function selfMember(absPlatform, platformVersion) {
|
|
1216
1243
|
return {
|
|
1217
1244
|
name: basename(absPlatform),
|
|
1218
1245
|
path: absPlatform,
|
|
1219
1246
|
pinned_spec_version: platformVersion,
|
|
1247
|
+
dependency_depth: 0,
|
|
1220
1248
|
selfMember: true
|
|
1221
1249
|
};
|
|
1222
1250
|
}
|
|
@@ -1238,14 +1266,45 @@ async function packageColumns(repoPath, prefix, repo, parentPin) {
|
|
|
1238
1266
|
const path = join3(repoPath, pkg.path);
|
|
1239
1267
|
const configPath = join3(path, MEMBER_CONFIG_FILENAME);
|
|
1240
1268
|
if (!existsSync(configPath)) {
|
|
1241
|
-
out.push(column(name, path, parentPin));
|
|
1269
|
+
out.push(column(name, path, parentPin, pkg));
|
|
1242
1270
|
continue;
|
|
1243
1271
|
}
|
|
1244
1272
|
const cfg = await readRepoConfig(configPath);
|
|
1245
|
-
out.push(column(name, path, extractPin(cfg.specs), cfg.ignore));
|
|
1273
|
+
out.push(column(name, path, extractPin(cfg.specs), pkg, cfg.ignore));
|
|
1246
1274
|
}
|
|
1247
1275
|
return out;
|
|
1248
1276
|
}
|
|
1277
|
+
function packageDirs(repo) {
|
|
1278
|
+
const dirs = repo.packages.map((p) => p.path);
|
|
1279
|
+
return dirs.some((d) => d === "" || d === ".") ? null : dirs;
|
|
1280
|
+
}
|
|
1281
|
+
function loneRootColumn(absPlatform, repo, platformVersion) {
|
|
1282
|
+
const dirs = packageDirs(repo);
|
|
1283
|
+
const { dependency_depth: _depth, ...self } = selfMember(absPlatform, platformVersion);
|
|
1284
|
+
if (dirs === null || self.name === CANONICAL_SPECS_DIR)
|
|
1285
|
+
return [];
|
|
1286
|
+
return [{ repo: { ...self, ignore: dirs }, node: repo }];
|
|
1287
|
+
}
|
|
1288
|
+
async function loneMembers(repo, absPlatform, platformVersion) {
|
|
1289
|
+
if (repo === undefined || repo.mode !== "monorepo" || repo.packages.length === 0) {
|
|
1290
|
+
return [selfMember(absPlatform, platformVersion)];
|
|
1291
|
+
}
|
|
1292
|
+
return toRepos([
|
|
1293
|
+
...await packageColumns(absPlatform, "", repo, platformVersion),
|
|
1294
|
+
...loneRootColumn(absPlatform, repo, platformVersion)
|
|
1295
|
+
]);
|
|
1296
|
+
}
|
|
1297
|
+
async function memberColumns(repo, path, cfg) {
|
|
1298
|
+
const pin = extractPin(cfg.specs);
|
|
1299
|
+
if (repo.mode !== "monorepo" || repo.packages.length === 0) {
|
|
1300
|
+
return [column(repo.name, path, pin, repo, cfg.ignore)];
|
|
1301
|
+
}
|
|
1302
|
+
const packages = await packageColumns(path, `${repo.name}/`, repo, pin);
|
|
1303
|
+
const dirs = packageDirs(repo);
|
|
1304
|
+
if (dirs === null)
|
|
1305
|
+
return packages;
|
|
1306
|
+
return [...packages, column(repo.name, path, pin, repo, [...cfg.ignore ?? [], ...dirs])];
|
|
1307
|
+
}
|
|
1249
1308
|
function describe(d, mapped) {
|
|
1250
1309
|
const s = d.subject;
|
|
1251
1310
|
switch (d.code) {
|
|
@@ -1278,15 +1337,14 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1278
1337
|
const { mapped, located } = reading;
|
|
1279
1338
|
const diagnostics2 = surfaced(mapped);
|
|
1280
1339
|
if (mapped.mode !== "multi-repo") {
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
return { mode: mapped.mode, members: members2, unpinned: [], undeclared: [], diagnostics: diagnostics2 };
|
|
1340
|
+
const members = await loneMembers(mapped.repos[0], absPlatform, platformVersion);
|
|
1341
|
+
return { mode: mapped.mode, members, unpinned: [], undeclared: [], diagnostics: diagnostics2 };
|
|
1284
1342
|
}
|
|
1285
1343
|
if (!mapped.declared) {
|
|
1286
1344
|
const undeclared2 = mapped.repos.map((r) => ({ name: r.name, path: join3(absPlatform, r.name) }));
|
|
1287
1345
|
return { mode: "multi-repo", members: [], unpinned: [], undeclared: undeclared2, diagnostics: diagnostics2 };
|
|
1288
1346
|
}
|
|
1289
|
-
const
|
|
1347
|
+
const columns = [];
|
|
1290
1348
|
const unpinned = [];
|
|
1291
1349
|
for (const repo of mapped.repos) {
|
|
1292
1350
|
const path = located.repos[repo.name];
|
|
@@ -1297,17 +1355,10 @@ async function membersOf(reading, absPlatform, platformVersion) {
|
|
|
1297
1355
|
unpinned.push({ name: repo.name, path });
|
|
1298
1356
|
continue;
|
|
1299
1357
|
}
|
|
1300
|
-
|
|
1301
|
-
const pin = extractPin(cfg.specs);
|
|
1302
|
-
if (repo.mode === "monorepo" && repo.packages.length > 0) {
|
|
1303
|
-
members.push(...await packageColumns(path, `${repo.name}/`, repo, pin));
|
|
1304
|
-
} else {
|
|
1305
|
-
members.push(column(repo.name, path, pin, cfg.ignore));
|
|
1306
|
-
}
|
|
1358
|
+
columns.push(...await memberColumns(repo, path, await readRepoConfig(configPath)));
|
|
1307
1359
|
}
|
|
1308
|
-
members.sort(byName);
|
|
1309
1360
|
const undeclared = mapped.diagnostics.filter((d) => d.code === "UNLISTED_REPO").map((d) => ({ name: d.subject, path: join3(absPlatform, d.subject) }));
|
|
1310
|
-
return { mode: "multi-repo", members, unpinned, undeclared, diagnostics: diagnostics2 };
|
|
1361
|
+
return { mode: "multi-repo", members: toRepos(columns), unpinned, undeclared, diagnostics: diagnostics2 };
|
|
1311
1362
|
}
|
|
1312
1363
|
async function discoverRepos(platformDir) {
|
|
1313
1364
|
const absPlatform = resolve2(platformDir);
|
|
@@ -1320,7 +1371,8 @@ async function discoverRepos(platformDir) {
|
|
|
1320
1371
|
const canonical = {
|
|
1321
1372
|
name: CANONICAL_SPECS_DIR,
|
|
1322
1373
|
path: canonicalPath,
|
|
1323
|
-
pinned_spec_version: platformVersion
|
|
1374
|
+
pinned_spec_version: platformVersion,
|
|
1375
|
+
dependency_depth: 0
|
|
1324
1376
|
};
|
|
1325
1377
|
const reading = readPlatformMap(absPlatform);
|
|
1326
1378
|
const shape = reading === null ? loneSingleRepo(absPlatform, platformVersion) : await membersOf(reading, absPlatform, platformVersion);
|
|
@@ -1706,11 +1758,11 @@ SELECT
|
|
|
1706
1758
|
) AS via_other
|
|
1707
1759
|
FROM repos
|
|
1708
1760
|
WHERE repos.name != 'spec-engine'
|
|
1709
|
-
ORDER BY repos.name
|
|
1761
|
+
ORDER BY repos.dependency_depth, repos.name
|
|
1710
1762
|
`;
|
|
1711
1763
|
|
|
1712
1764
|
// src/storage/sql/reads.ts
|
|
1713
|
-
var LIST_RELATIONS_SQL, LIST_TERM_ALIASES_SQL = "SELECT term_id, name FROM term_aliases ORDER BY term_id, name", LIST_TERM_CITATIONS_SQL, LIST_PROVENANCE_SQL, LIST_PROVENANCE_MATRIX_SQL, PROVENANCE_BY_ISSUE_SQL, LIST_REPOS_SQL = "SELECT name, path, pinned_spec_version FROM repos ORDER BY name", GET_REPO_SQL = "SELECT name, path, pinned_spec_version FROM repos WHERE name = $name", LIST_DOMAINS_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains ORDER BY key", GET_DOMAIN_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains WHERE key = $key", LIST_TAGS_SQL = `SELECT id, req_id, repo, file, line, kind, level FROM tags
|
|
1765
|
+
var LIST_RELATIONS_SQL, LIST_TERM_ALIASES_SQL = "SELECT term_id, name FROM term_aliases ORDER BY term_id, name", LIST_TERM_CITATIONS_SQL, LIST_PROVENANCE_SQL, LIST_PROVENANCE_MATRIX_SQL, PROVENANCE_BY_ISSUE_SQL, LIST_REPOS_SQL = "SELECT name, path, pinned_spec_version, dependency_depth FROM repos ORDER BY name", GET_REPO_SQL = "SELECT name, path, pinned_spec_version, dependency_depth FROM repos WHERE name = $name", LIST_DOMAINS_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains ORDER BY key", GET_DOMAIN_SQL = "SELECT key, owner, schema, spec_version, source_repo FROM domains WHERE key = $key", LIST_TAGS_SQL = `SELECT id, req_id, repo, file, line, kind, level FROM tags
|
|
1714
1766
|
WHERE ($repo IS NULL OR repo = $repo)
|
|
1715
1767
|
AND ($req_id IS NULL OR req_id = $req_id)
|
|
1716
1768
|
AND ($file IS NULL OR file = $file)
|
|
@@ -1983,7 +2035,7 @@ class SqliteStorage {
|
|
|
1983
2035
|
}
|
|
1984
2036
|
#makeWriteHandle() {
|
|
1985
2037
|
const db = this.#db;
|
|
1986
|
-
const insRepo = db.prepare("INSERT OR REPLACE INTO repos (name, path, pinned_spec_version) VALUES ($name, $path, $pin)");
|
|
2038
|
+
const insRepo = db.prepare("INSERT OR REPLACE INTO repos (name, path, pinned_spec_version, dependency_depth) VALUES ($name, $path, $pin, $depth)");
|
|
1987
2039
|
const insDomain = db.prepare("INSERT OR REPLACE INTO domains (key, owner, schema, spec_version, source_repo) " + "VALUES ($key, $owner, $schema, $ver, $source)");
|
|
1988
2040
|
const insReq = db.prepare("INSERT OR REPLACE INTO requirements " + "(id, key, seq, status, superseded_by, text, why, source_file, line, spec_version, changed_at_version, superseded_at_version) " + "VALUES ($id, $key, $seq, $status, $sup, $text, $why, $file, $line, $sver, $cver, $saver)");
|
|
1989
2041
|
const insTag = db.prepare("INSERT INTO tags (req_id, repo, file, line, kind, level) " + "VALUES ($req, $repo, $file, $line, $kind, $level)");
|
|
@@ -2013,7 +2065,8 @@ class SqliteStorage {
|
|
|
2013
2065
|
insRepo.run({
|
|
2014
2066
|
name: r.name,
|
|
2015
2067
|
path: r.path,
|
|
2016
|
-
pin: r.pinned_spec_version
|
|
2068
|
+
pin: r.pinned_spec_version,
|
|
2069
|
+
depth: r.dependency_depth
|
|
2017
2070
|
});
|
|
2018
2071
|
},
|
|
2019
2072
|
upsertDomain(d) {
|
|
@@ -2136,11 +2189,7 @@ function poisonSchemaVersion(path, badVersion) {
|
|
|
2136
2189
|
function poisonRepoRow(path, name) {
|
|
2137
2190
|
const db = new Database(path);
|
|
2138
2191
|
try {
|
|
2139
|
-
db.run("INSERT INTO repos (name, path, pinned_spec_version) VALUES (?, ?, ?)", [
|
|
2140
|
-
name,
|
|
2141
|
-
"/dev/null",
|
|
2142
|
-
999
|
|
2143
|
-
]);
|
|
2192
|
+
db.run("INSERT INTO repos (name, path, pinned_spec_version, dependency_depth) VALUES (?, ?, ?, ?)", [name, "/dev/null", 999, 0]);
|
|
2144
2193
|
} finally {
|
|
2145
2194
|
db.close();
|
|
2146
2195
|
}
|
|
@@ -2165,7 +2214,7 @@ function computeBuildId(storage2) {
|
|
|
2165
2214
|
},
|
|
2166
2215
|
{
|
|
2167
2216
|
label: "repos",
|
|
2168
|
-
sql: "SELECT name, path, pinned_spec_version FROM repos ORDER BY name"
|
|
2217
|
+
sql: "SELECT name, path, pinned_spec_version, dependency_depth FROM repos ORDER BY name"
|
|
2169
2218
|
},
|
|
2170
2219
|
{
|
|
2171
2220
|
label: "domains",
|
|
@@ -2511,7 +2560,8 @@ function buildRows(input) {
|
|
|
2511
2560
|
{
|
|
2512
2561
|
name: canonical.name,
|
|
2513
2562
|
path: canonical.path,
|
|
2514
|
-
pinned_spec_version: platformVersion
|
|
2563
|
+
pinned_spec_version: platformVersion,
|
|
2564
|
+
dependency_depth: 0
|
|
2515
2565
|
},
|
|
2516
2566
|
...members
|
|
2517
2567
|
], (r) => r.name);
|
|
@@ -3288,7 +3338,24 @@ import { resolve as resolve6 } from "path";
|
|
|
3288
3338
|
function resolvePlatformDir(args) {
|
|
3289
3339
|
return resolve6(args.platformDir ?? process.cwd());
|
|
3290
3340
|
}
|
|
3291
|
-
|
|
3341
|
+
function listFlag(rawArgs, name, parsed) {
|
|
3342
|
+
const flag = `--${name}`;
|
|
3343
|
+
const raw = [];
|
|
3344
|
+
for (let i = 0;i < rawArgs.length; i++) {
|
|
3345
|
+
const arg = rawArgs[i];
|
|
3346
|
+
if (arg === flag && i + 1 < rawArgs.length)
|
|
3347
|
+
raw.push(rawArgs[++i]);
|
|
3348
|
+
else if (arg.startsWith(`${flag}=`))
|
|
3349
|
+
raw.push(arg.slice(flag.length + 1));
|
|
3350
|
+
}
|
|
3351
|
+
if (raw.length === 0) {
|
|
3352
|
+
if (parsed === undefined)
|
|
3353
|
+
return;
|
|
3354
|
+
raw.push(parsed);
|
|
3355
|
+
}
|
|
3356
|
+
return raw.flatMap((v) => v.split(",")).map((v) => v.trim()).filter((v) => v !== "");
|
|
3357
|
+
}
|
|
3358
|
+
var platformDirArg, outArg, jsonArg, freshArg, noPromptArg, livesArg;
|
|
3292
3359
|
var init__args = __esm(() => {
|
|
3293
3360
|
init_constants();
|
|
3294
3361
|
platformDirArg = {
|
|
@@ -3309,6 +3376,10 @@ var init__args = __esm(() => {
|
|
|
3309
3376
|
type: "boolean",
|
|
3310
3377
|
description: "Suppress the interactive onboarding prompt for siblings missing spec-engine.member.json (they surface as NO_SPEC_CONFIG warnings)"
|
|
3311
3378
|
};
|
|
3379
|
+
livesArg = {
|
|
3380
|
+
type: "string",
|
|
3381
|
+
description: "Lives in (livesIn) paths: repeat the flag or comma-separate; an empty value clears the list"
|
|
3382
|
+
};
|
|
3312
3383
|
});
|
|
3313
3384
|
|
|
3314
3385
|
// src/commands/index.ts
|
|
@@ -5654,19 +5725,15 @@ var init_list = __esm(() => {
|
|
|
5654
5725
|
});
|
|
5655
5726
|
|
|
5656
5727
|
// src/propagation/format.ts
|
|
5657
|
-
function sortPropagation(rows) {
|
|
5658
|
-
return [...rows].sort((a, b) => a.repo.localeCompare(b.repo));
|
|
5659
|
-
}
|
|
5660
5728
|
function renderPropagation(rows, mode) {
|
|
5661
|
-
const sorted = sortPropagation(rows);
|
|
5662
5729
|
if (mode === "json") {
|
|
5663
|
-
return JSON.stringify(
|
|
5730
|
+
return JSON.stringify(rows);
|
|
5664
5731
|
}
|
|
5665
|
-
if (
|
|
5732
|
+
if (rows.length === 0)
|
|
5666
5733
|
return "";
|
|
5667
5734
|
const header = ["REPO", "STATE", "VIA", "DRIFT?"];
|
|
5668
5735
|
const lines = [header];
|
|
5669
|
-
for (const row of
|
|
5736
|
+
for (const row of rows) {
|
|
5670
5737
|
lines.push([row.repo, row.state, row.via_req_id ?? EMPTY_CELL2, row.drifted ? "yes" : "no"]);
|
|
5671
5738
|
}
|
|
5672
5739
|
const widths = header.map((_, col) => Math.max(...lines.map((row) => row[col]?.length ?? 0)));
|
|
@@ -6426,24 +6493,27 @@ __export(exports_req, {
|
|
|
6426
6493
|
});
|
|
6427
6494
|
import { createInterface as createInterface2 } from "readline";
|
|
6428
6495
|
import { defineCommand as defineCommand12 } from "citty";
|
|
6429
|
-
async function mintFromFlags(platformDir, key, textFlag, args) {
|
|
6496
|
+
async function mintFromFlags(platformDir, key, textFlag, args, rawArgs) {
|
|
6430
6497
|
const statement = textFlag.trim();
|
|
6431
6498
|
if (statement === "") {
|
|
6432
6499
|
console.error("spec req: --text must be a non-empty Requirement");
|
|
6433
6500
|
process.exit(EXIT.USAGE);
|
|
6434
6501
|
}
|
|
6435
6502
|
await printResolvedCharter(platformDir, key);
|
|
6436
|
-
const lives = (args.lives ?? "").trim();
|
|
6437
6503
|
await mintAndReport(platformDir, key, {
|
|
6438
6504
|
statement,
|
|
6439
6505
|
why: (args.why ?? "").trim(),
|
|
6440
|
-
livesIn: lives
|
|
6506
|
+
livesIn: listFlag(rawArgs, "lives", args.lives) ?? [],
|
|
6441
6507
|
issue: (args.issue ?? "").trim() || undefined,
|
|
6508
|
+
status: statusOf(args),
|
|
6442
6509
|
json: Boolean(args.json)
|
|
6443
6510
|
});
|
|
6444
6511
|
}
|
|
6445
|
-
|
|
6446
|
-
|
|
6512
|
+
function statusOf(args) {
|
|
6513
|
+
return args.draft ? "draft" : "active";
|
|
6514
|
+
}
|
|
6515
|
+
async function mintInteractively(platformDir, key, id, status) {
|
|
6516
|
+
console.error(`Authoring ${id} \u2014 ${status === "draft" ? "Draft" : "Active"}`);
|
|
6447
6517
|
await printResolvedCharter(platformDir, key);
|
|
6448
6518
|
const requirement = (await askLine("Requirement: ")).trim();
|
|
6449
6519
|
if (requirement === "") {
|
|
@@ -6452,12 +6522,13 @@ async function mintInteractively(platformDir, key, id) {
|
|
|
6452
6522
|
return;
|
|
6453
6523
|
}
|
|
6454
6524
|
const why = (await askLine("Why it matters: ")).trim();
|
|
6455
|
-
const
|
|
6525
|
+
const livesIn = listFlag([], "lives", await askLine("Lives in: ")) ?? [];
|
|
6456
6526
|
await mintAndReport(platformDir, key, {
|
|
6457
6527
|
statement: requirement,
|
|
6458
6528
|
why,
|
|
6459
|
-
livesIn
|
|
6529
|
+
livesIn,
|
|
6460
6530
|
issue: undefined,
|
|
6531
|
+
status,
|
|
6461
6532
|
json: false
|
|
6462
6533
|
});
|
|
6463
6534
|
}
|
|
@@ -6510,10 +6581,14 @@ var init_req = __esm(() => {
|
|
|
6510
6581
|
description: "Non-interactive authoring: the Requirement field. When set, the entry appends with zero prompts."
|
|
6511
6582
|
},
|
|
6512
6583
|
why: { type: "string", description: "Why it matters field (only with --text)" },
|
|
6513
|
-
lives:
|
|
6584
|
+
lives: livesArg,
|
|
6514
6585
|
issue: {
|
|
6515
6586
|
type: "string",
|
|
6516
6587
|
description: "Originating ticket id, recorded as created-provenance on the entry (only with --text; opaque \u2014 never a requirement id)"
|
|
6588
|
+
},
|
|
6589
|
+
draft: {
|
|
6590
|
+
type: "boolean",
|
|
6591
|
+
description: "Author the entry as Draft instead of Active (promote it later with spec accept)"
|
|
6517
6592
|
}
|
|
6518
6593
|
};
|
|
6519
6594
|
reqCommand = defineCommand12({
|
|
@@ -6522,7 +6597,7 @@ var init_req = __esm(() => {
|
|
|
6522
6597
|
description: "Author a new requirement interactively (TTY) or print the next unused id (non-TTY)"
|
|
6523
6598
|
},
|
|
6524
6599
|
args: reqArgs,
|
|
6525
|
-
async run({ args }) {
|
|
6600
|
+
async run({ args, rawArgs }) {
|
|
6526
6601
|
const input = args.domainPrefix;
|
|
6527
6602
|
const platformDir = resolvePlatformDir(args);
|
|
6528
6603
|
try {
|
|
@@ -6536,7 +6611,7 @@ var init_req = __esm(() => {
|
|
|
6536
6611
|
const { key, nextId: id } = allocated;
|
|
6537
6612
|
const textFlag = args.text;
|
|
6538
6613
|
if (textFlag !== undefined) {
|
|
6539
|
-
await mintFromFlags(platformDir, key, textFlag, args);
|
|
6614
|
+
await mintFromFlags(platformDir, key, textFlag, args, rawArgs);
|
|
6540
6615
|
return;
|
|
6541
6616
|
}
|
|
6542
6617
|
const hasFieldFlags = typeof args.why === "string" || typeof args.lives === "string" || typeof args.issue === "string";
|
|
@@ -6545,6 +6620,11 @@ var init_req = __esm(() => {
|
|
|
6545
6620
|
process.exit(EXIT.USAGE);
|
|
6546
6621
|
return;
|
|
6547
6622
|
}
|
|
6623
|
+
if (args.draft && (args.json || !process.stdin.isTTY)) {
|
|
6624
|
+
console.error("spec req: --draft authors an entry \u2014 pass --text, or run on a TTY");
|
|
6625
|
+
process.exit(EXIT.USAGE);
|
|
6626
|
+
return;
|
|
6627
|
+
}
|
|
6548
6628
|
if (args.json) {
|
|
6549
6629
|
console.log(JSON.stringify({ domain: key, next_id: id }));
|
|
6550
6630
|
return;
|
|
@@ -6553,7 +6633,7 @@ var init_req = __esm(() => {
|
|
|
6553
6633
|
console.log(id);
|
|
6554
6634
|
return;
|
|
6555
6635
|
}
|
|
6556
|
-
await mintInteractively(platformDir, key, id);
|
|
6636
|
+
await mintInteractively(platformDir, key, id, statusOf(args));
|
|
6557
6637
|
}
|
|
6558
6638
|
});
|
|
6559
6639
|
});
|
|
@@ -7100,14 +7180,13 @@ async function resolveSuccessorText(args, platformDir, id) {
|
|
|
7100
7180
|
function splitAliases(raw) {
|
|
7101
7181
|
return raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
7102
7182
|
}
|
|
7103
|
-
function inputFromArgs(args, platformDir, id, statement) {
|
|
7183
|
+
function inputFromArgs(args, rawArgs, platformDir, id, statement) {
|
|
7104
7184
|
const input = { platformDir, id, statement, noBump: Boolean(args.noBump) };
|
|
7105
7185
|
if (typeof args.why === "string")
|
|
7106
7186
|
input.why = args.why;
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
input.livesIn =
|
|
7110
|
-
}
|
|
7187
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7188
|
+
if (livesIn !== undefined)
|
|
7189
|
+
input.livesIn = livesIn;
|
|
7111
7190
|
if (typeof args.term === "string")
|
|
7112
7191
|
input.term = args.term;
|
|
7113
7192
|
if (typeof args.aliases === "string")
|
|
@@ -7144,7 +7223,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7144
7223
|
},
|
|
7145
7224
|
lives: {
|
|
7146
7225
|
type: "string",
|
|
7147
|
-
description: "Successor's Lives in (default: copied from the old entry)"
|
|
7226
|
+
description: "Successor's Lives in: repeat or comma-separate; empty clears (default: copied from the old entry)"
|
|
7148
7227
|
},
|
|
7149
7228
|
term: {
|
|
7150
7229
|
type: "string",
|
|
@@ -7173,7 +7252,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7173
7252
|
description: "Supersede a shipped requirement: flip it to superseded, mint the successor, bump specVersion, and emit the retag worklist."
|
|
7174
7253
|
},
|
|
7175
7254
|
args: supersedeArgs,
|
|
7176
|
-
async run({ args }) {
|
|
7255
|
+
async run({ args, rawArgs }) {
|
|
7177
7256
|
const id = args.id;
|
|
7178
7257
|
if (!ID_RE.test(id)) {
|
|
7179
7258
|
console.error(`spec supersede: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
@@ -7186,7 +7265,7 @@ var init_supersede2 = __esm(() => {
|
|
|
7186
7265
|
handleNotAPlatform(e);
|
|
7187
7266
|
}
|
|
7188
7267
|
const statement = await resolveSuccessorText(args, platformDir, id);
|
|
7189
|
-
const result = await supersede(inputFromArgs(args, platformDir, id, statement), coldFreshTags(platformDir));
|
|
7268
|
+
const result = await supersede(inputFromArgs(args, rawArgs, platformDir, id, statement), coldFreshTags(platformDir));
|
|
7190
7269
|
if (!result.ok)
|
|
7191
7270
|
exitOnFailure("spec supersede", result);
|
|
7192
7271
|
printWarnings("spec supersede", result.warnings);
|
|
@@ -7432,16 +7511,15 @@ __export(exports_move, {
|
|
|
7432
7511
|
moveCommand: () => moveCommand
|
|
7433
7512
|
});
|
|
7434
7513
|
import { defineCommand as defineCommand17 } from "citty";
|
|
7435
|
-
function inputFromArgs2(args, platformDir, id, targetKey) {
|
|
7514
|
+
function inputFromArgs2(args, rawArgs, platformDir, id, targetKey) {
|
|
7436
7515
|
const input = { platformDir, id, targetKey, noBump: Boolean(args.noBump) };
|
|
7437
7516
|
if (typeof args.text === "string")
|
|
7438
7517
|
input.statement = args.text;
|
|
7439
7518
|
if (typeof args.why === "string")
|
|
7440
7519
|
input.why = args.why;
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
input.livesIn =
|
|
7444
|
-
}
|
|
7520
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7521
|
+
if (livesIn !== undefined)
|
|
7522
|
+
input.livesIn = livesIn;
|
|
7445
7523
|
return input;
|
|
7446
7524
|
}
|
|
7447
7525
|
var moveArgs, moveCommand;
|
|
@@ -7476,7 +7554,7 @@ var init_move2 = __esm(() => {
|
|
|
7476
7554
|
},
|
|
7477
7555
|
lives: {
|
|
7478
7556
|
type: "string",
|
|
7479
|
-
description: "Rewrite the successor's Lives in (default: copied from the source)"
|
|
7557
|
+
description: "Rewrite the successor's Lives in: repeat or comma-separate; empty clears (default: copied from the source)"
|
|
7480
7558
|
},
|
|
7481
7559
|
noBump: {
|
|
7482
7560
|
type: "boolean",
|
|
@@ -7493,7 +7571,7 @@ var init_move2 = __esm(() => {
|
|
|
7493
7571
|
description: "Move a requirement to another domain: mint the successor in <NEW-DOMAIN> carrying the source's fields, mark the source superseded, bump both specVersions, and emit the retag worklist."
|
|
7494
7572
|
},
|
|
7495
7573
|
args: moveArgs,
|
|
7496
|
-
async run({ args }) {
|
|
7574
|
+
async run({ args, rawArgs }) {
|
|
7497
7575
|
const id = args.id;
|
|
7498
7576
|
const rawTargetKey = args.newDomain;
|
|
7499
7577
|
const platformDir = resolvePlatformDir(args);
|
|
@@ -7511,7 +7589,7 @@ var init_move2 = __esm(() => {
|
|
|
7511
7589
|
} catch (e) {
|
|
7512
7590
|
handleNotAPlatform(e);
|
|
7513
7591
|
}
|
|
7514
|
-
const result = await move(inputFromArgs2(args, platformDir, id, targetKey), coldFreshTags(platformDir));
|
|
7592
|
+
const result = await move(inputFromArgs2(args, rawArgs, platformDir, id, targetKey), coldFreshTags(platformDir));
|
|
7515
7593
|
if (!result.ok)
|
|
7516
7594
|
exitOnFailure("spec move", result);
|
|
7517
7595
|
printWarnings("spec move", result.warnings);
|
|
@@ -7657,7 +7735,7 @@ function assertAmendFlags(args) {
|
|
|
7657
7735
|
function splitAliases2(raw) {
|
|
7658
7736
|
return raw === "" ? [] : raw.split(",").map((s) => s.trim()).filter((s) => s !== "");
|
|
7659
7737
|
}
|
|
7660
|
-
function fieldsFromArgs(args) {
|
|
7738
|
+
function fieldsFromArgs(args, rawArgs) {
|
|
7661
7739
|
assertAmendFlags(args);
|
|
7662
7740
|
const fields = {};
|
|
7663
7741
|
if (args.text !== undefined)
|
|
@@ -7666,10 +7744,9 @@ function fieldsFromArgs(args) {
|
|
|
7666
7744
|
const v = args.why.trim();
|
|
7667
7745
|
fields.why = v === "" ? null : v;
|
|
7668
7746
|
}
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
fields.livesIn =
|
|
7672
|
-
}
|
|
7747
|
+
const livesIn = listFlag(rawArgs, "lives", args.lives);
|
|
7748
|
+
if (livesIn !== undefined)
|
|
7749
|
+
fields.livesIn = livesIn;
|
|
7673
7750
|
if (args.term !== undefined)
|
|
7674
7751
|
fields.term = args.term.trim();
|
|
7675
7752
|
if (args.issue !== undefined && args.issue.trim() !== "")
|
|
@@ -7696,7 +7773,7 @@ var init_amend2 = __esm(() => {
|
|
|
7696
7773
|
platformDir: platformDirArg,
|
|
7697
7774
|
text: { type: "string", description: "New Requirement (statement) field value" },
|
|
7698
7775
|
why: { type: "string", description: "New Why it matters field value" },
|
|
7699
|
-
lives:
|
|
7776
|
+
lives: livesArg,
|
|
7700
7777
|
issue: {
|
|
7701
7778
|
type: "string",
|
|
7702
7779
|
description: "Ticket that motivated this amendment \u2014 recorded as amends-via provenance (opaque, never a requirement id)"
|
|
@@ -7714,7 +7791,7 @@ var init_amend2 = __esm(() => {
|
|
|
7714
7791
|
description: "Revise an unshipped requirement's fields in place (same id, no specVersion bump). Supersede shipped truth instead."
|
|
7715
7792
|
},
|
|
7716
7793
|
args: amendArgs,
|
|
7717
|
-
async run({ args }) {
|
|
7794
|
+
async run({ args, rawArgs }) {
|
|
7718
7795
|
const id = args.id;
|
|
7719
7796
|
if (!ID_RE.test(id)) {
|
|
7720
7797
|
console.error(`spec amend: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
@@ -7727,7 +7804,7 @@ var init_amend2 = __esm(() => {
|
|
|
7727
7804
|
} catch (e) {
|
|
7728
7805
|
handleNotAPlatform(e);
|
|
7729
7806
|
}
|
|
7730
|
-
const fields = fieldsFromArgs(args);
|
|
7807
|
+
const fields = fieldsFromArgs(args, rawArgs);
|
|
7731
7808
|
const result = await amend({ platformDir, id, fields }, coldFreshTags(platformDir));
|
|
7732
7809
|
if (!result.ok)
|
|
7733
7810
|
exitOnFailure("spec amend", result);
|
|
@@ -7741,6 +7818,86 @@ var init_amend2 = __esm(() => {
|
|
|
7741
7818
|
});
|
|
7742
7819
|
});
|
|
7743
7820
|
|
|
7821
|
+
// src/operations/accept.ts
|
|
7822
|
+
async function accept(input) {
|
|
7823
|
+
const { platformDir, id } = input;
|
|
7824
|
+
const located = await locateEntry(platformDir, id);
|
|
7825
|
+
if (!located.ok)
|
|
7826
|
+
return located;
|
|
7827
|
+
const { specPath, relFile, domain: domain2, req } = located;
|
|
7828
|
+
if (req.status.toLowerCase() !== "draft") {
|
|
7829
|
+
return fail("conflict", `${id} is ${displayStatus(req.status)} \u2014 only a Draft entry accepts`);
|
|
7830
|
+
}
|
|
7831
|
+
req.status = "active";
|
|
7832
|
+
domain2.updated = localToday();
|
|
7833
|
+
const res = await validateAndWrite(specPath, domain2, relFile);
|
|
7834
|
+
if (!res.ok) {
|
|
7835
|
+
return fail("invalid_domain_file", res.diagnostics.map((d) => d.detail).join(`
|
|
7836
|
+
`), {
|
|
7837
|
+
diagnostics: res.diagnostics
|
|
7838
|
+
});
|
|
7839
|
+
}
|
|
7840
|
+
return { ok: true, id, file: relFile };
|
|
7841
|
+
}
|
|
7842
|
+
var init_accept = __esm(() => {
|
|
7843
|
+
init_src();
|
|
7844
|
+
init__envelope();
|
|
7845
|
+
init__result();
|
|
7846
|
+
});
|
|
7847
|
+
|
|
7848
|
+
// src/commands/accept.ts
|
|
7849
|
+
var exports_accept = {};
|
|
7850
|
+
__export(exports_accept, {
|
|
7851
|
+
acceptCommand: () => acceptCommand
|
|
7852
|
+
});
|
|
7853
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
7854
|
+
var acceptCommand;
|
|
7855
|
+
var init_accept2 = __esm(() => {
|
|
7856
|
+
init_constants();
|
|
7857
|
+
init_discover();
|
|
7858
|
+
init_accept();
|
|
7859
|
+
init_grammar3();
|
|
7860
|
+
init__args();
|
|
7861
|
+
init__shared();
|
|
7862
|
+
acceptCommand = defineCommand19({
|
|
7863
|
+
meta: {
|
|
7864
|
+
name: "accept",
|
|
7865
|
+
description: "Promote a Draft requirement to Active (same id, fields untouched)"
|
|
7866
|
+
},
|
|
7867
|
+
args: {
|
|
7868
|
+
id: {
|
|
7869
|
+
type: "positional",
|
|
7870
|
+
required: true,
|
|
7871
|
+
description: "Draft requirement id to accept (KEY-NNN)"
|
|
7872
|
+
},
|
|
7873
|
+
platformDir: platformDirArg,
|
|
7874
|
+
json: jsonArg
|
|
7875
|
+
},
|
|
7876
|
+
async run({ args }) {
|
|
7877
|
+
const id = args.id;
|
|
7878
|
+
if (!ID_RE.test(id)) {
|
|
7879
|
+
console.error(`spec accept: id must be a requirement id (KEY-NNN); got ${id}`);
|
|
7880
|
+
process.exit(EXIT.USAGE);
|
|
7881
|
+
return;
|
|
7882
|
+
}
|
|
7883
|
+
const platformDir = resolvePlatformDir(args);
|
|
7884
|
+
try {
|
|
7885
|
+
assertSpecPlatform(platformDir);
|
|
7886
|
+
} catch (e) {
|
|
7887
|
+
handleNotAPlatform(e);
|
|
7888
|
+
}
|
|
7889
|
+
const result = await accept({ platformDir, id });
|
|
7890
|
+
if (!result.ok)
|
|
7891
|
+
exitOnFailure("spec accept", result);
|
|
7892
|
+
if (args.json) {
|
|
7893
|
+
console.log(JSON.stringify({ id, file: result.file }));
|
|
7894
|
+
return;
|
|
7895
|
+
}
|
|
7896
|
+
console.log(`accepted ${id} in ${result.file} \u2014 now Active`);
|
|
7897
|
+
}
|
|
7898
|
+
});
|
|
7899
|
+
});
|
|
7900
|
+
|
|
7744
7901
|
// src/server/authorPrompt.ts
|
|
7745
7902
|
function renderAuthorPrompt(opts) {
|
|
7746
7903
|
const charterBlock = opts.charter ? `
|
|
@@ -7916,6 +8073,19 @@ function buildMcpServer(platformDir) {
|
|
|
7916
8073
|
const r = await deprecate({ platformDir, id: req_id, reason: trimmed }, coldFreshTags(platformDir));
|
|
7917
8074
|
return r.ok ? jsonResult({ id: r.id, file: r.file, reason: r.reason, sites: r.sites }) : errorResult(r.detail);
|
|
7918
8075
|
});
|
|
8076
|
+
server.registerTool("spec_accept", {
|
|
8077
|
+
title: "Accept a draft requirement",
|
|
8078
|
+
description: "Promote a Draft requirement to Active, leaving every other field untouched. The same JSON as `spec accept --json`.",
|
|
8079
|
+
inputSchema: {
|
|
8080
|
+
req_id: z3.string().describe("The Draft requirement id to accept (KEY-NNN)")
|
|
8081
|
+
}
|
|
8082
|
+
}, async ({ req_id }) => {
|
|
8083
|
+
if (!ID_RE.test(req_id)) {
|
|
8084
|
+
return errorResult(`req_id must be a requirement id (KEY-NNN); got ${req_id}`);
|
|
8085
|
+
}
|
|
8086
|
+
const r = await accept({ platformDir, id: req_id });
|
|
8087
|
+
return r.ok ? jsonResult({ id: r.id, file: r.file }) : errorResult(r.detail);
|
|
8088
|
+
});
|
|
7919
8089
|
server.registerPrompt("author_requirements", {
|
|
7920
8090
|
title: "Author requirements from a brief",
|
|
7921
8091
|
description: "Turn a vague brief/ticket into well-formed Spec Engine requirements: one requirement per testable promise, placed by domain charter, deduped via spec query, drafted to the GUARD template + cold-read rubric, minted with spec req. The engine returns a static playbook template \u2014 your model runs it.",
|
|
@@ -7950,6 +8120,7 @@ var init_mcp = __esm(() => {
|
|
|
7950
8120
|
init_src();
|
|
7951
8121
|
init_domains();
|
|
7952
8122
|
init__index();
|
|
8123
|
+
init_accept();
|
|
7953
8124
|
init_check();
|
|
7954
8125
|
init_deprecate();
|
|
7955
8126
|
init_nextId();
|
|
@@ -7965,14 +8136,14 @@ __export(exports_mcp, {
|
|
|
7965
8136
|
mcpCommand: () => mcpCommand
|
|
7966
8137
|
});
|
|
7967
8138
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7968
|
-
import { defineCommand as
|
|
8139
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
7969
8140
|
var mcpCommand;
|
|
7970
8141
|
var init_mcp2 = __esm(() => {
|
|
7971
8142
|
init_discover();
|
|
7972
8143
|
init_mcp();
|
|
7973
8144
|
init__args();
|
|
7974
8145
|
init__shared();
|
|
7975
|
-
mcpCommand =
|
|
8146
|
+
mcpCommand = defineCommand20({
|
|
7976
8147
|
meta: {
|
|
7977
8148
|
name: "mcp",
|
|
7978
8149
|
description: "Serve Spec Engine as an MCP (Model Context Protocol) server over stdio \u2014 agent-native query/resolve/check/report tools."
|
|
@@ -8001,7 +8172,7 @@ __export(exports_resolve, {
|
|
|
8001
8172
|
});
|
|
8002
8173
|
import { statSync as statSync5 } from "fs";
|
|
8003
8174
|
import { join as join12, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
|
|
8004
|
-
import { defineCommand as
|
|
8175
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
8005
8176
|
function classifyArg(a) {
|
|
8006
8177
|
if (a.startsWith("--")) {
|
|
8007
8178
|
const eq = a.indexOf("=");
|
|
@@ -8187,7 +8358,7 @@ var init_resolve2 = __esm(() => {
|
|
|
8187
8358
|
description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
|
|
8188
8359
|
}
|
|
8189
8360
|
};
|
|
8190
|
-
resolveCommand =
|
|
8361
|
+
resolveCommand = defineCommand21({
|
|
8191
8362
|
meta: {
|
|
8192
8363
|
name: "resolve",
|
|
8193
8364
|
description: "Return the requirements tagged in the given files (RSLV-01). Accepts multiple positional file paths (e.g. `spec resolve api/src/renew.ts api/src/charge.ts`) and a comma-split fallback inside a single positional (`spec resolve a.ts,b.ts`). Paths may be platform-relative or absolute under the platform tree."
|
|
@@ -8352,7 +8523,7 @@ __export(exports_gate, {
|
|
|
8352
8523
|
});
|
|
8353
8524
|
import { existsSync as existsSync12 } from "fs";
|
|
8354
8525
|
import { join as join13 } from "path";
|
|
8355
|
-
import { defineCommand as
|
|
8526
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
8356
8527
|
function isEpipe(e) {
|
|
8357
8528
|
if (e === null || typeof e !== "object" || !("code" in e))
|
|
8358
8529
|
return false;
|
|
@@ -8415,7 +8586,7 @@ var init_gate2 = __esm(() => {
|
|
|
8415
8586
|
description: "Suppress interactive onboarding prompt for siblings missing spec-engine.member.json (defaults to NO_SPEC_CONFIG warning)"
|
|
8416
8587
|
}
|
|
8417
8588
|
};
|
|
8418
|
-
gateCommand =
|
|
8589
|
+
gateCommand = defineCommand22({
|
|
8419
8590
|
meta: {
|
|
8420
8591
|
name: "gate",
|
|
8421
8592
|
description: "Rung-3 approval primitive: passes iff <reqId> is Active and <repo>'s pinned spec_version covers its changed_at_version. Exits 0 PASS / 1 any gate failure / 2 crash or bad args."
|
|
@@ -8737,14 +8908,14 @@ var exports_guard = {};
|
|
|
8737
8908
|
__export(exports_guard, {
|
|
8738
8909
|
guardCommand: () => guardCommand
|
|
8739
8910
|
});
|
|
8740
|
-
import { defineCommand as
|
|
8911
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
8741
8912
|
var guardCommand;
|
|
8742
8913
|
var init_guard2 = __esm(() => {
|
|
8743
8914
|
init_constants();
|
|
8744
8915
|
init_guard();
|
|
8745
8916
|
init__args();
|
|
8746
8917
|
init__shared();
|
|
8747
|
-
guardCommand =
|
|
8918
|
+
guardCommand = defineCommand23({
|
|
8748
8919
|
meta: {
|
|
8749
8920
|
name: "guard",
|
|
8750
8921
|
description: "Loss detection: diff the requirement derivation at a git ref (default HEAD) against the working tree and block requirements about to be steamrolled."
|
|
@@ -11708,6 +11879,15 @@ function toLivesIn(v) {
|
|
|
11708
11879
|
return [v];
|
|
11709
11880
|
return [];
|
|
11710
11881
|
}
|
|
11882
|
+
function mintFieldsFromBody(body) {
|
|
11883
|
+
return {
|
|
11884
|
+
statement: typeof body.statement === "string" ? body.statement : "",
|
|
11885
|
+
why: typeof body.why === "string" ? body.why : "",
|
|
11886
|
+
livesIn: toLivesIn(body.livesIn),
|
|
11887
|
+
issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined,
|
|
11888
|
+
status: body.status === "draft" ? "draft" : "active"
|
|
11889
|
+
};
|
|
11890
|
+
}
|
|
11711
11891
|
function amendFieldsFromBody(body) {
|
|
11712
11892
|
const fields = {};
|
|
11713
11893
|
if (typeof body.statement === "string")
|
|
@@ -11792,14 +11972,7 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11792
11972
|
return c.json({ error: "unknown domain key" }, 400);
|
|
11793
11973
|
}
|
|
11794
11974
|
return withWriteLock(async () => {
|
|
11795
|
-
const r = await mint({
|
|
11796
|
-
platformDir,
|
|
11797
|
-
key,
|
|
11798
|
-
statement: typeof body.statement === "string" ? body.statement : "",
|
|
11799
|
-
why: typeof body.why === "string" ? body.why : "",
|
|
11800
|
-
livesIn: toLivesIn(body.livesIn),
|
|
11801
|
-
issue: typeof body.issue === "string" && body.issue.trim() !== "" ? body.issue.trim() : undefined
|
|
11802
|
-
});
|
|
11975
|
+
const r = await mint({ platformDir, key, ...mintFieldsFromBody(body) });
|
|
11803
11976
|
if (!r.ok)
|
|
11804
11977
|
return failureResponse(c, r);
|
|
11805
11978
|
await runIndex({ platformDir, storage: storage2 });
|
|
@@ -11910,6 +12083,30 @@ function mountApi(app, storage2, platformDir = process.cwd()) {
|
|
|
11910
12083
|
return c.json({ ok: true, id: r.id, file: r.file, reason: r.reason, sites: r.sites }, 200);
|
|
11911
12084
|
});
|
|
11912
12085
|
}));
|
|
12086
|
+
app.post("/api/requirements/:id/accept", guarded(async function acceptRequirement(c) {
|
|
12087
|
+
if (!featureEnabled("editor")) {
|
|
12088
|
+
return c.json({ error: featureDisabledMessage("editor") }, 404);
|
|
12089
|
+
}
|
|
12090
|
+
const originReject = rejectCrossOrigin(c);
|
|
12091
|
+
if (originReject)
|
|
12092
|
+
return originReject;
|
|
12093
|
+
const headerReject = rejectBadWriteHeaders(c);
|
|
12094
|
+
if (headerReject)
|
|
12095
|
+
return headerReject;
|
|
12096
|
+
const parsed = await readJsonBody(c);
|
|
12097
|
+
if (!parsed.ok)
|
|
12098
|
+
return parsed.res;
|
|
12099
|
+
const id = c.req.param("id") ?? "";
|
|
12100
|
+
if (!ID_RE.test(id))
|
|
12101
|
+
return c.json({ error: "id must be a requirement id (KEY-NNN)" }, 400);
|
|
12102
|
+
return withWriteLock(async () => {
|
|
12103
|
+
const r = await accept({ platformDir, id });
|
|
12104
|
+
if (!r.ok)
|
|
12105
|
+
return failureResponse(c, r);
|
|
12106
|
+
await runIndex({ platformDir, storage: storage2 });
|
|
12107
|
+
return c.json({ ok: true, id: r.id, file: r.file }, 200);
|
|
12108
|
+
});
|
|
12109
|
+
}));
|
|
11913
12110
|
app.get("/api/propagation/:id", guarded((c) => c.json(propagation2(storage2, c.req.param("id") ?? "").rows)));
|
|
11914
12111
|
app.get("/api/query", guarded((c) => {
|
|
11915
12112
|
if (!featureEnabled("query")) {
|
|
@@ -11990,6 +12187,7 @@ var init_api = __esm(() => {
|
|
|
11990
12187
|
init_discover();
|
|
11991
12188
|
init_pipeline();
|
|
11992
12189
|
init__result();
|
|
12190
|
+
init_accept();
|
|
11993
12191
|
init_amend();
|
|
11994
12192
|
init_deprecate();
|
|
11995
12193
|
init_mint();
|
|
@@ -12011,7 +12209,7 @@ __export(exports_serve, {
|
|
|
12011
12209
|
});
|
|
12012
12210
|
import { existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
|
|
12013
12211
|
import { dirname as dirname8 } from "path";
|
|
12014
|
-
import { defineCommand as
|
|
12212
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
12015
12213
|
import { Hono as Hono2 } from "hono";
|
|
12016
12214
|
function composeServeApp(storage2, platformDir = process.cwd()) {
|
|
12017
12215
|
const app = new Hono2;
|
|
@@ -12060,7 +12258,7 @@ var init_serve = __esm(() => {
|
|
|
12060
12258
|
init_sqlite();
|
|
12061
12259
|
init__args();
|
|
12062
12260
|
init__shared();
|
|
12063
|
-
serveCommand =
|
|
12261
|
+
serveCommand = defineCommand24({
|
|
12064
12262
|
meta: {
|
|
12065
12263
|
name: "serve",
|
|
12066
12264
|
description: "Run the local webapp. Binds 127.0.0.1 on --port (loopback only \u2014 no --host). --probe is the SERV-04 asset-embedding smoke."
|
|
@@ -12163,7 +12361,7 @@ __export(exports_docs, {
|
|
|
12163
12361
|
import { existsSync as existsSync14, statSync as statSync6 } from "fs";
|
|
12164
12362
|
import { join as join15, normalize, sep as sep4 } from "path";
|
|
12165
12363
|
import { fileURLToPath } from "url";
|
|
12166
|
-
import { defineCommand as
|
|
12364
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
12167
12365
|
function resolveDocsRootFrom(candidates) {
|
|
12168
12366
|
for (const candidate of candidates) {
|
|
12169
12367
|
if (existsSync14(join15(candidate, "index.html")))
|
|
@@ -12241,7 +12439,7 @@ function exitNoDocsRoot() {
|
|
|
12241
12439
|
var docsCommand;
|
|
12242
12440
|
var init_docs2 = __esm(() => {
|
|
12243
12441
|
init_constants();
|
|
12244
|
-
docsCommand =
|
|
12442
|
+
docsCommand = defineCommand25({
|
|
12245
12443
|
meta: {
|
|
12246
12444
|
name: "docs",
|
|
12247
12445
|
description: "Serve the bundled documentation site offline. Binds 127.0.0.1 on --port (loopback only \u2014 no --host). --probe is the boot-and-fetch smoke."
|
|
@@ -12302,10 +12500,10 @@ __export(exports___smoke, {
|
|
|
12302
12500
|
schemaSmokeCommand: () => schemaSmokeCommand,
|
|
12303
12501
|
schemaMismatchSmokeCommand: () => schemaMismatchSmokeCommand
|
|
12304
12502
|
});
|
|
12305
|
-
import { defineCommand as
|
|
12503
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
12306
12504
|
var schemaSmokeCommand, schemaMismatchSmokeCommand;
|
|
12307
12505
|
var init___smoke = __esm(() => {
|
|
12308
|
-
schemaSmokeCommand =
|
|
12506
|
+
schemaSmokeCommand = defineCommand26({
|
|
12309
12507
|
meta: {
|
|
12310
12508
|
name: "__schema-smoke",
|
|
12311
12509
|
description: "(hidden CI) open a fresh DB, exec DDL, assert the schema shape",
|
|
@@ -12348,7 +12546,7 @@ var init___smoke = __esm(() => {
|
|
|
12348
12546
|
console.log("__schema-smoke OK");
|
|
12349
12547
|
}
|
|
12350
12548
|
});
|
|
12351
|
-
schemaMismatchSmokeCommand =
|
|
12549
|
+
schemaMismatchSmokeCommand = defineCommand26({
|
|
12352
12550
|
meta: {
|
|
12353
12551
|
name: "__schema-mismatch-smoke",
|
|
12354
12552
|
description: "(hidden CI) corrupt _schema_version, re-open, assert silent rebuild",
|
|
@@ -12374,8 +12572,8 @@ var init___smoke = __esm(() => {
|
|
|
12374
12572
|
|
|
12375
12573
|
// src/cli.ts
|
|
12376
12574
|
init_package();
|
|
12377
|
-
import { defineCommand as
|
|
12378
|
-
var main =
|
|
12575
|
+
import { defineCommand as defineCommand27, runMain, showUsage } from "citty";
|
|
12576
|
+
var main = defineCommand27({
|
|
12379
12577
|
meta: {
|
|
12380
12578
|
name: "spec",
|
|
12381
12579
|
version: package_default.version,
|
|
@@ -12400,6 +12598,7 @@ var main = defineCommand26({
|
|
|
12400
12598
|
deprecate: () => Promise.resolve().then(() => (init_deprecate2(), exports_deprecate)).then((m) => m.deprecateCommand),
|
|
12401
12599
|
move: () => Promise.resolve().then(() => (init_move2(), exports_move)).then((m) => m.moveCommand),
|
|
12402
12600
|
amend: () => Promise.resolve().then(() => (init_amend2(), exports_amend)).then((m) => m.amendCommand),
|
|
12601
|
+
accept: () => Promise.resolve().then(() => (init_accept2(), exports_accept)).then((m) => m.acceptCommand),
|
|
12403
12602
|
mcp: () => Promise.resolve().then(() => (init_mcp2(), exports_mcp)).then((m) => m.mcpCommand),
|
|
12404
12603
|
resolve: () => Promise.resolve().then(() => (init_resolve2(), exports_resolve)).then((m) => m.resolveCommand),
|
|
12405
12604
|
gate: () => Promise.resolve().then(() => (init_gate2(), exports_gate)).then((m) => m.gateCommand),
|