@wairon/cli 5.1.1-dev.2 → 5.1.1-dev.3
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/cli/index.js +1567 -1079
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +482 -293
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-architect.md +2 -2
- package/dist/templates/skills/sdd-delegate.md +38 -0
- package/dist/templates/skills/sdd-narrative.md +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ function createAgentRecord(partial) {
|
|
|
47
47
|
...partial
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
|
-
var import_zod, BuiltinTargetSchema, CustomTargetSchema, OutputTargetSchema, AgentStatusSchema, AgentRecordSchema;
|
|
50
|
+
var import_zod, BuiltinTargetSchema, CustomTargetSchema, OutputTargetSchema, AgentStatusSchema, AgentRecordSchema, AgentTemplateSchema, AgentBriefSchema;
|
|
51
51
|
var init_agent = __esm({
|
|
52
52
|
"src/models/agent.ts"() {
|
|
53
53
|
"use strict";
|
|
@@ -101,6 +101,30 @@ var init_agent = __esm({
|
|
|
101
101
|
createdAt: import_zod.z.string().datetime(),
|
|
102
102
|
updatedAt: import_zod.z.string().datetime()
|
|
103
103
|
});
|
|
104
|
+
AgentTemplateSchema = import_zod.z.object({
|
|
105
|
+
/** Template identifier (architect, domain-owner, implementer, …) */
|
|
106
|
+
templateName: import_zod.z.string(),
|
|
107
|
+
/** The raw instruction body with {{variable}} placeholders, before rendering */
|
|
108
|
+
instructions: import_zod.z.string()
|
|
109
|
+
});
|
|
110
|
+
AgentBriefSchema = import_zod.z.object({
|
|
111
|
+
/** The resolved agent's stable id (e.g. sdd_core-owner, system-architect) */
|
|
112
|
+
agentId: import_zod.z.string(),
|
|
113
|
+
/** Human-readable display name of the agent */
|
|
114
|
+
name: import_zod.z.string(),
|
|
115
|
+
/** The instruction template the brief was rendered from */
|
|
116
|
+
template: import_zod.z.string(),
|
|
117
|
+
/** Domain the agent belongs to (absent = global root) */
|
|
118
|
+
domainRoot: import_zod.z.string().optional(),
|
|
119
|
+
/** Glob patterns of the files this agent owns — the write-scope fence */
|
|
120
|
+
ownedPaths: import_zod.z.array(import_zod.z.string()),
|
|
121
|
+
/** Spec paths the subagent should read first */
|
|
122
|
+
readPaths: import_zod.z.array(import_zod.z.string()).optional(),
|
|
123
|
+
/** The fully rendered instruction body — paste-ready as a subagent prompt */
|
|
124
|
+
instructions: import_zod.z.string(),
|
|
125
|
+
/** Rendered variant guidance, also folded into instructions */
|
|
126
|
+
variantGuidance: import_zod.z.string().optional()
|
|
127
|
+
});
|
|
104
128
|
}
|
|
105
129
|
});
|
|
106
130
|
|
|
@@ -254,6 +278,14 @@ var init_project = __esm({
|
|
|
254
278
|
* per-component isolation.
|
|
255
279
|
*/
|
|
256
280
|
generateComponentImplementers: import_zod3.z.boolean().default(false),
|
|
281
|
+
/**
|
|
282
|
+
* Whether `wairon generate` writes per-subsystem owner/architect agent FILES.
|
|
283
|
+
* Off by default: agents are served as LIVE briefs (sdd_get_agent_brief /
|
|
284
|
+
* wairon-agent://), and files are merely the opt-in materialized view of the
|
|
285
|
+
* same briefs. When off, generate reconciles to zero agent files — leftover
|
|
286
|
+
* wairon-managed files are removed (hand-authored files never are).
|
|
287
|
+
*/
|
|
288
|
+
materializeAgentFiles: import_zod3.z.boolean().default(false),
|
|
257
289
|
/**
|
|
258
290
|
* Severity overrides for SDD validation rules.
|
|
259
291
|
* Key: rule code (e.g. CIRCULAR_DEPENDENCY), Value: error | warning | off
|
|
@@ -773,6 +805,20 @@ var init_specs = __esm({
|
|
|
773
805
|
* writes with hydration read-backs (MISSING_HYDRATION); optional elsewhere.
|
|
774
806
|
*/
|
|
775
807
|
effect: import_zod6.z.enum(["read", "write"]).optional(),
|
|
808
|
+
/**
|
|
809
|
+
* Typed acknowledgment of a real caller OUTSIDE the modeled narrative graph
|
|
810
|
+
* (runtime timer/hook, external system, sibling subsystem). Unused-detection
|
|
811
|
+
* seeds the method as an entrypoint, so reachability PROPAGATES through its
|
|
812
|
+
* narrative — unlike a lint.allow, which only silences the finding. `caller`
|
|
813
|
+
* states WHO invokes it (placeholder-thin prose is INVOKED_BY_UNDESCRIBED;
|
|
814
|
+
* a method the internal walk already reaches is INVOKED_BY_REDUNDANT).
|
|
815
|
+
* Prefer a `register` narrative step when the wiring is internal — the
|
|
816
|
+
* registration itself is then a modeled, checkable edge.
|
|
817
|
+
*/
|
|
818
|
+
invokedBy: import_zod6.z.object({
|
|
819
|
+
kind: import_zod6.z.enum(["runtime", "external", "sibling-subsystem"]),
|
|
820
|
+
caller: import_zod6.z.string().optional()
|
|
821
|
+
}).optional(),
|
|
776
822
|
/** Opaque pack/tool extension data (see ExtDataSchema) — preserved verbatim. */
|
|
777
823
|
ext: ExtDataSchema.optional()
|
|
778
824
|
});
|
|
@@ -798,6 +844,8 @@ var init_specs = __esm({
|
|
|
798
844
|
// cross-component call (targetComponent/targetMethod)
|
|
799
845
|
"dispatch",
|
|
800
846
|
// capability routed through a generic Portal's dispatch table (targetComponent + capability)
|
|
847
|
+
"register",
|
|
848
|
+
// runtime-callback handoff (targetComponent/targetMethod): reachability edge, never an invocation
|
|
801
849
|
"branch",
|
|
802
850
|
// if/else: condition + onTrueStep (default next) / onFalseStep
|
|
803
851
|
"switch",
|
|
@@ -847,9 +895,9 @@ var init_specs = __esm({
|
|
|
847
895
|
description: import_zod6.z.string(),
|
|
848
896
|
type: NarrativeStepTypeSchema,
|
|
849
897
|
targetComponent: import_zod6.z.string().optional(),
|
|
850
|
-
// Required if type is 'call' or 'dispatch', references L2 Component id
|
|
898
|
+
// Required if type is 'call', 'register' or 'dispatch', references L2 Component id
|
|
851
899
|
targetMethod: import_zod6.z.string().optional(),
|
|
852
|
-
// Required if type is 'call', references Method name on target interface
|
|
900
|
+
// Required if type is 'call' or 'register', references Method name on target interface
|
|
853
901
|
capability: import_zod6.z.string().optional(),
|
|
854
902
|
// Required if type is 'dispatch': the capability routed through the target Portal's dispatch table
|
|
855
903
|
/**
|
|
@@ -1163,7 +1211,7 @@ var init_defaults = __esm({
|
|
|
1163
1211
|
copilot: ".github/prompts",
|
|
1164
1212
|
codex: ".codex/agents"
|
|
1165
1213
|
};
|
|
1166
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
1214
|
+
WAIRON_VERSION = "5.1.1-dev.3";
|
|
1167
1215
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1168
1216
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1169
1217
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -1388,6 +1436,66 @@ var init_errors = __esm({
|
|
|
1388
1436
|
}
|
|
1389
1437
|
});
|
|
1390
1438
|
|
|
1439
|
+
// src/core/templates.ts
|
|
1440
|
+
function builtinTemplatesDir() {
|
|
1441
|
+
return path3.resolve(__dirname, "..", "templates");
|
|
1442
|
+
}
|
|
1443
|
+
function loadTemplate(id, globalOverride) {
|
|
1444
|
+
const dirs = templateSearchDirs(globalOverride);
|
|
1445
|
+
for (const dir of dirs) {
|
|
1446
|
+
const filePath = path3.join(dir, `${id}.yaml`);
|
|
1447
|
+
if (pathExists(filePath)) {
|
|
1448
|
+
const raw = readYamlFile(filePath);
|
|
1449
|
+
return { ...TemplateSchema.parse(raw), templateName: id };
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
throw new TemplateNotFoundError(id);
|
|
1453
|
+
}
|
|
1454
|
+
function loadAgentOverride(agentId) {
|
|
1455
|
+
const filePath = path3.join(AI_PATHS.root(), "agents", `${agentId}.md`);
|
|
1456
|
+
return readFileOrNull(filePath);
|
|
1457
|
+
}
|
|
1458
|
+
function listTemplateIds(globalOverride) {
|
|
1459
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1460
|
+
for (const dir of templateSearchDirs(globalOverride)) {
|
|
1461
|
+
for (const file of listFiles(dir, ".yaml")) {
|
|
1462
|
+
seen.add(path3.basename(file, ".yaml"));
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return Array.from(seen).sort();
|
|
1466
|
+
}
|
|
1467
|
+
function templateSearchDirs(globalOverride) {
|
|
1468
|
+
return [
|
|
1469
|
+
AI_PATHS.templatesDir(),
|
|
1470
|
+
// 1. project-local
|
|
1471
|
+
globalTemplatesDir(globalOverride),
|
|
1472
|
+
// 2. global user/org
|
|
1473
|
+
builtinTemplatesDir()
|
|
1474
|
+
// 3. built-in fallback
|
|
1475
|
+
];
|
|
1476
|
+
}
|
|
1477
|
+
function renderTemplateInstructions(template, vars) {
|
|
1478
|
+
return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
1479
|
+
return key in vars ? vars[key] : `{{${key}}}`;
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
function parseTemplate(yamlContent) {
|
|
1483
|
+
return TemplateSchema.parse(parseYaml(yamlContent));
|
|
1484
|
+
}
|
|
1485
|
+
var path3;
|
|
1486
|
+
var init_templates = __esm({
|
|
1487
|
+
"src/core/templates.ts"() {
|
|
1488
|
+
"use strict";
|
|
1489
|
+
path3 = __toESM(require("path"));
|
|
1490
|
+
init_fs();
|
|
1491
|
+
init_yaml();
|
|
1492
|
+
init_errors();
|
|
1493
|
+
init_template();
|
|
1494
|
+
init_loader();
|
|
1495
|
+
init_defaults();
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
|
|
1391
1499
|
// src/utils/version.ts
|
|
1392
1500
|
function isNewerVersion(current2, candidate) {
|
|
1393
1501
|
const baseVersion = (v) => v.replace(/-.*$/, "");
|
|
@@ -1430,20 +1538,20 @@ function packFiles(dir, prefix = "") {
|
|
|
1430
1538
|
for (const e of entries) {
|
|
1431
1539
|
if (DIGEST_EXCLUDED.has(e.name)) continue;
|
|
1432
1540
|
const rel2 = prefix ? `${prefix}/${e.name}` : e.name;
|
|
1433
|
-
if (e.isDirectory()) out.push(...packFiles(
|
|
1541
|
+
if (e.isDirectory()) out.push(...packFiles(path4.join(dir, e.name), rel2));
|
|
1434
1542
|
else if (e.isFile()) out.push(rel2);
|
|
1435
1543
|
}
|
|
1436
1544
|
return out.sort();
|
|
1437
1545
|
}
|
|
1438
1546
|
function computePackDigest(packDir) {
|
|
1439
1547
|
const hash = crypto.createHash("sha256");
|
|
1440
|
-
const abs =
|
|
1548
|
+
const abs = path4.resolve(packDir);
|
|
1441
1549
|
let isFile = false;
|
|
1442
1550
|
try {
|
|
1443
1551
|
isFile = fs2.statSync(abs).isFile();
|
|
1444
1552
|
} catch {
|
|
1445
1553
|
}
|
|
1446
|
-
const entries = isFile ? [[
|
|
1554
|
+
const entries = isFile ? [[path4.basename(abs), abs]] : packFiles(abs).map((rel2) => [rel2, path4.join(abs, rel2)]);
|
|
1447
1555
|
for (const [rel2, full] of entries) {
|
|
1448
1556
|
hash.update(rel2);
|
|
1449
1557
|
hash.update("\0");
|
|
@@ -1457,7 +1565,7 @@ function computePackDigest(packDir) {
|
|
|
1457
1565
|
}
|
|
1458
1566
|
function readInstallRecord(packPath) {
|
|
1459
1567
|
try {
|
|
1460
|
-
const raw = readYamlFile(
|
|
1568
|
+
const raw = readYamlFile(path4.join(packPath, INSTALL_RECORD));
|
|
1461
1569
|
return raw && typeof raw === "object" ? raw : {};
|
|
1462
1570
|
} catch {
|
|
1463
1571
|
return {};
|
|
@@ -1494,7 +1602,7 @@ function versionedEntries(nameDir, fallbackName) {
|
|
|
1494
1602
|
const out = [];
|
|
1495
1603
|
for (const v of versions) {
|
|
1496
1604
|
if (!v.isDirectory()) continue;
|
|
1497
|
-
const versionDir =
|
|
1605
|
+
const versionDir = path4.join(nameDir, v.name);
|
|
1498
1606
|
const entryFile = packDirEntry(versionDir);
|
|
1499
1607
|
if (!entryFile) continue;
|
|
1500
1608
|
out.push(installedFrom(versionDir, declaredName(entryFile) ?? fallbackName, v.name));
|
|
@@ -1502,7 +1610,7 @@ function versionedEntries(nameDir, fallbackName) {
|
|
|
1502
1610
|
return out;
|
|
1503
1611
|
}
|
|
1504
1612
|
function storeEntryPacks(store, entry) {
|
|
1505
|
-
const full =
|
|
1613
|
+
const full = path4.join(store, entry.name);
|
|
1506
1614
|
if (entry.isFile()) {
|
|
1507
1615
|
if (!PACK_FILE_RE.test(entry.name)) return [];
|
|
1508
1616
|
const name = declaredName(full) ?? entry.name.replace(PACK_FILE_RE, "");
|
|
@@ -1534,16 +1642,16 @@ function resolveInstalledPack(name, version) {
|
|
|
1534
1642
|
return candidates[0];
|
|
1535
1643
|
}
|
|
1536
1644
|
function installPackFromDirectory(sourceDir, origin) {
|
|
1537
|
-
const abs =
|
|
1645
|
+
const abs = path4.resolve(sourceDir);
|
|
1538
1646
|
const entryFile = packDirEntry(abs);
|
|
1539
1647
|
if (!entryFile) {
|
|
1540
1648
|
throw new Error(`"${sourceDir}" is not a pack directory (no pack.yaml | pack.cjs | index.cjs | \u2026).`);
|
|
1541
1649
|
}
|
|
1542
|
-
const manifest = readManifest(entryFile,
|
|
1650
|
+
const manifest = readManifest(entryFile, path4.dirname(entryFile));
|
|
1543
1651
|
const version = manifest.version && manifest.version.length > 0 ? manifest.version : UNVERSIONED;
|
|
1544
|
-
const dest =
|
|
1652
|
+
const dest = path4.join(packStoreDir(), manifest.name, version);
|
|
1545
1653
|
fs2.rmSync(dest, { recursive: true, force: true });
|
|
1546
|
-
fs2.mkdirSync(
|
|
1654
|
+
fs2.mkdirSync(path4.dirname(dest), { recursive: true });
|
|
1547
1655
|
fs2.cpSync(abs, dest, { recursive: true });
|
|
1548
1656
|
const digest = computePackDigest(dest);
|
|
1549
1657
|
const record = {
|
|
@@ -1552,7 +1660,7 @@ function installPackFromDirectory(sourceDir, origin) {
|
|
|
1552
1660
|
waironVersion: WAIRON_VERSION,
|
|
1553
1661
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1554
1662
|
};
|
|
1555
|
-
writeYamlFile(
|
|
1663
|
+
writeYamlFile(path4.join(dest, INSTALL_RECORD), record);
|
|
1556
1664
|
return {
|
|
1557
1665
|
name: manifest.name,
|
|
1558
1666
|
version,
|
|
@@ -1568,8 +1676,8 @@ function uninstallPack(name, version) {
|
|
|
1568
1676
|
if (targets.length === 0) return false;
|
|
1569
1677
|
for (const target of targets) {
|
|
1570
1678
|
fs2.rmSync(target.path, { recursive: true, force: true });
|
|
1571
|
-
const parent =
|
|
1572
|
-
if (
|
|
1679
|
+
const parent = path4.dirname(target.path);
|
|
1680
|
+
if (path4.resolve(parent) === path4.resolve(packStoreDir())) continue;
|
|
1573
1681
|
try {
|
|
1574
1682
|
if (fs2.readdirSync(parent).length === 0) fs2.rmdirSync(parent);
|
|
1575
1683
|
} catch {
|
|
@@ -1577,13 +1685,13 @@ function uninstallPack(name, version) {
|
|
|
1577
1685
|
}
|
|
1578
1686
|
return true;
|
|
1579
1687
|
}
|
|
1580
|
-
var crypto, fs2,
|
|
1688
|
+
var crypto, fs2, path4, UNVERSIONED, INSTALL_RECORD, DIGEST_EXCLUDED, PACK_FILE_RE;
|
|
1581
1689
|
var init_packstore = __esm({
|
|
1582
1690
|
"src/core/packstore.ts"() {
|
|
1583
1691
|
"use strict";
|
|
1584
1692
|
crypto = __toESM(require("crypto"));
|
|
1585
1693
|
fs2 = __toESM(require("fs"));
|
|
1586
|
-
|
|
1694
|
+
path4 = __toESM(require("path"));
|
|
1587
1695
|
init_yaml();
|
|
1588
1696
|
init_defaults();
|
|
1589
1697
|
init_version();
|
|
@@ -1610,11 +1718,11 @@ function emptyExtensions() {
|
|
|
1610
1718
|
return { packNames: [], packs: [], rules: [], profiles: {}, languages: {}, skills: [], patterns: [], guarantees: [], assertions: [], instructions: [], errors: [], selectionFailures: [] };
|
|
1611
1719
|
}
|
|
1612
1720
|
function globalPacksDir() {
|
|
1613
|
-
return process.env.WAIRON_PACKS_DIR ??
|
|
1721
|
+
return process.env.WAIRON_PACKS_DIR ?? path5.join(os2.homedir(), ".wairon", "packs");
|
|
1614
1722
|
}
|
|
1615
1723
|
function packDirEntry(dir) {
|
|
1616
1724
|
for (const name of PACK_DIR_ENTRIES) {
|
|
1617
|
-
const p =
|
|
1725
|
+
const p = path5.join(dir, name);
|
|
1618
1726
|
if (fs3.existsSync(p)) return p;
|
|
1619
1727
|
}
|
|
1620
1728
|
return null;
|
|
@@ -1629,7 +1737,7 @@ function latestVersionDir(nameDir) {
|
|
|
1629
1737
|
let best = null;
|
|
1630
1738
|
for (const v of versions) {
|
|
1631
1739
|
if (!v.isDirectory()) continue;
|
|
1632
|
-
const dir =
|
|
1740
|
+
const dir = path5.join(nameDir, v.name);
|
|
1633
1741
|
if (!packDirEntry(dir)) continue;
|
|
1634
1742
|
if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
|
|
1635
1743
|
}
|
|
@@ -1644,7 +1752,7 @@ function discoverPacks(dir) {
|
|
|
1644
1752
|
}
|
|
1645
1753
|
const refs = [];
|
|
1646
1754
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
1647
|
-
const full =
|
|
1755
|
+
const full = path5.join(dir, e.name);
|
|
1648
1756
|
if (e.isDirectory()) {
|
|
1649
1757
|
if (packDirEntry(full)) refs.push(full);
|
|
1650
1758
|
else {
|
|
@@ -1674,16 +1782,16 @@ function mergePack(out, pack, ref, scope, packDir) {
|
|
|
1674
1782
|
foreignBuiltins: [.../* @__PURE__ */ new Set([...existing.foreignBuiltins, ...def.foreignBuiltins])]
|
|
1675
1783
|
} : def;
|
|
1676
1784
|
}
|
|
1677
|
-
for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath:
|
|
1785
|
+
for (const s of pack.skills) out.skills.push({ ...s, pack: pack.name, packVersion: pack.version, sourcePath: path5.resolve(packDir, s.source) });
|
|
1678
1786
|
for (const p of pack.patterns) out.patterns.push({ ...p, pack: pack.name });
|
|
1679
1787
|
out.guarantees = [.../* @__PURE__ */ new Set([...out.guarantees, ...pack.guarantees])];
|
|
1680
1788
|
for (const a of pack.assertions) out.assertions.push({ ...a, pack: pack.name, fullCode: assertionFullCode(pack.name, a.code) });
|
|
1681
1789
|
for (const b of pack.instructions) out.instructions.push({ ...b, pack: pack.name });
|
|
1682
1790
|
}
|
|
1683
1791
|
function resolvePackRef(ref, projectRoot) {
|
|
1684
|
-
const pathLike = ref.startsWith(".") ||
|
|
1792
|
+
const pathLike = ref.startsWith(".") || path5.isAbsolute(ref) || isYamlPath(ref);
|
|
1685
1793
|
if (!pathLike) return ref;
|
|
1686
|
-
const resolved =
|
|
1794
|
+
const resolved = path5.resolve(projectRoot, ref);
|
|
1687
1795
|
if (fs3.existsSync(resolved) && fs3.statSync(resolved).isDirectory()) {
|
|
1688
1796
|
const entry = packDirEntry(resolved);
|
|
1689
1797
|
if (!entry) throw new Error(`directory pack has no entry file (${PACK_DIR_ENTRIES.join(" | ")})`);
|
|
@@ -1697,7 +1805,7 @@ function readManifest(target, projectRoot) {
|
|
|
1697
1805
|
if (raw == null) throw new Error("file not found or empty");
|
|
1698
1806
|
return { ...DeclarativePackSchema.parse(raw), rules: [] };
|
|
1699
1807
|
}
|
|
1700
|
-
const req = (0, import_module.createRequire)(
|
|
1808
|
+
const req = (0, import_module.createRequire)(path5.join(projectRoot, "package.json"));
|
|
1701
1809
|
const modRaw = req(target);
|
|
1702
1810
|
const mod = modRaw?.default ?? modRaw;
|
|
1703
1811
|
const parsed = DeclarativePackSchema.parse({
|
|
@@ -1725,7 +1833,7 @@ function loadExtensionPacks(refs, projectRoot) {
|
|
|
1725
1833
|
try {
|
|
1726
1834
|
const target = resolvePackRef(ref, projectRoot);
|
|
1727
1835
|
const manifest = readManifest(target, projectRoot);
|
|
1728
|
-
mergePack(out, manifest, ref, scope,
|
|
1836
|
+
mergePack(out, manifest, ref, scope, path5.dirname(target));
|
|
1729
1837
|
for (const r of manifest.rules) out.rules.push(r);
|
|
1730
1838
|
} catch (e) {
|
|
1731
1839
|
out.errors.push(`Extension pack "${ref}" failed to load: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -1778,7 +1886,7 @@ function defaultPackSelections() {
|
|
|
1778
1886
|
const entry = packDirEntry(pack.path) ?? pack.path;
|
|
1779
1887
|
let manifest;
|
|
1780
1888
|
try {
|
|
1781
|
-
manifest = readManifest(entry,
|
|
1889
|
+
manifest = readManifest(entry, path5.dirname(entry));
|
|
1782
1890
|
} catch {
|
|
1783
1891
|
continue;
|
|
1784
1892
|
}
|
|
@@ -1816,7 +1924,7 @@ function diagnoseProjectPacks() {
|
|
|
1816
1924
|
const globalsApplied = [];
|
|
1817
1925
|
if (globalsOn) {
|
|
1818
1926
|
for (const ref of discoverPacks(globalPacksDir())) {
|
|
1819
|
-
const match = installed.find((p) =>
|
|
1927
|
+
const match = installed.find((p) => path5.resolve(p.path) === path5.resolve(ref));
|
|
1820
1928
|
if (match) globalsApplied.push({ name: match.name, version: match.version });
|
|
1821
1929
|
}
|
|
1822
1930
|
}
|
|
@@ -1897,7 +2005,7 @@ function verifyIntegrity(selection, packDir, where) {
|
|
|
1897
2005
|
};
|
|
1898
2006
|
}
|
|
1899
2007
|
function detectBundleStoreDrift(selection, bundleDir) {
|
|
1900
|
-
const bundledVersion =
|
|
2008
|
+
const bundledVersion = path5.basename(bundleDir);
|
|
1901
2009
|
const installed = resolveInstalledPack(selection.name, bundledVersion);
|
|
1902
2010
|
if (!installed) return void 0;
|
|
1903
2011
|
const bundleDigest = computePackDigest(bundleDir);
|
|
@@ -1911,9 +2019,9 @@ function detectBundleStoreDrift(selection, bundleDir) {
|
|
|
1911
2019
|
};
|
|
1912
2020
|
}
|
|
1913
2021
|
function findBundledPack(projectRoot, selection) {
|
|
1914
|
-
const base =
|
|
2022
|
+
const base = path5.join(projectRoot, ".wai", "packs", selection.name);
|
|
1915
2023
|
if (selection.version) {
|
|
1916
|
-
const pinned =
|
|
2024
|
+
const pinned = path5.join(base, selection.version);
|
|
1917
2025
|
return packDirEntry(pinned) ? pinned : null;
|
|
1918
2026
|
}
|
|
1919
2027
|
if (packDirEntry(base)) return base;
|
|
@@ -1926,19 +2034,19 @@ function findBundledPack(projectRoot, selection) {
|
|
|
1926
2034
|
let best = null;
|
|
1927
2035
|
for (const v of versions) {
|
|
1928
2036
|
if (!v.isDirectory()) continue;
|
|
1929
|
-
const dir =
|
|
2037
|
+
const dir = path5.join(base, v.name);
|
|
1930
2038
|
if (!packDirEntry(dir)) continue;
|
|
1931
2039
|
if (!best || isNewerVersion(best.version, v.name)) best = { version: v.name, dir };
|
|
1932
2040
|
}
|
|
1933
2041
|
return best?.dir ?? null;
|
|
1934
2042
|
}
|
|
1935
|
-
var fs3, os2,
|
|
2043
|
+
var fs3, os2, path5, import_module, import_zod7, ProfileDefSchema, LanguagePackDefSchema, PackSkillSchema, PackInstructionBlockSchema, PackInstructionsSchema, PatternDefSchema, AssertionSelectorSchema, assertionBase, PackAssertionSchema, DeclarativePackSchema, EXTENDABLE_BUILTIN_SKILLS, GLOBAL_PACKS_DEFAULT, PACK_DIR_ENTRIES, isYamlPath;
|
|
1936
2044
|
var init_extensions = __esm({
|
|
1937
2045
|
"src/core/extensions.ts"() {
|
|
1938
2046
|
"use strict";
|
|
1939
2047
|
fs3 = __toESM(require("fs"));
|
|
1940
2048
|
os2 = __toESM(require("os"));
|
|
1941
|
-
|
|
2049
|
+
path5 = __toESM(require("path"));
|
|
1942
2050
|
import_module = require("module");
|
|
1943
2051
|
import_zod7 = require("zod");
|
|
1944
2052
|
init_yaml();
|
|
@@ -2081,7 +2189,7 @@ var init_extensions = __esm({
|
|
|
2081
2189
|
*/
|
|
2082
2190
|
applyByDefault: import_zod7.z.boolean().default(false)
|
|
2083
2191
|
});
|
|
2084
|
-
EXTENDABLE_BUILTIN_SKILLS = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement"];
|
|
2192
|
+
EXTENDABLE_BUILTIN_SKILLS = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
|
|
2085
2193
|
GLOBAL_PACKS_DEFAULT = false;
|
|
2086
2194
|
PACK_DIR_ENTRIES = ["pack.yaml", "pack.yml", "pack.cjs", "pack.js", "index.cjs", "index.js"];
|
|
2087
2195
|
isYamlPath = (p) => /\.ya?ml$/i.test(p);
|
|
@@ -2884,7 +2992,7 @@ var init_openapi = __esm({
|
|
|
2884
2992
|
|
|
2885
2993
|
// src/core/surfaces.ts
|
|
2886
2994
|
function surfacesDir(rootDir) {
|
|
2887
|
-
return
|
|
2995
|
+
return path6.join(rootDir, ".wai", SURFACES_DIRNAME);
|
|
2888
2996
|
}
|
|
2889
2997
|
function audienceRank(audience) {
|
|
2890
2998
|
const idx = SURFACE_AUDIENCES.indexOf(audience ?? "instance");
|
|
@@ -3047,7 +3155,7 @@ function listSnapshots(rootDir = getProjectRoot()) {
|
|
|
3047
3155
|
for (const file of fs4.readdirSync(dir)) {
|
|
3048
3156
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
3049
3157
|
try {
|
|
3050
|
-
out.push(SurfaceSnapshotSchema.parse(readYamlFile(
|
|
3158
|
+
out.push(SurfaceSnapshotSchema.parse(readYamlFile(path6.join(dir, file))));
|
|
3051
3159
|
} catch {
|
|
3052
3160
|
}
|
|
3053
3161
|
}
|
|
@@ -3062,13 +3170,13 @@ function snapshotFilename(projectName) {
|
|
|
3062
3170
|
function saveSnapshot(snapshot, rootDir = getProjectRoot()) {
|
|
3063
3171
|
const dir = surfacesDir(rootDir);
|
|
3064
3172
|
fs4.mkdirSync(dir, { recursive: true });
|
|
3065
|
-
const p =
|
|
3173
|
+
const p = path6.join(dir, snapshotFilename(snapshot.projectName));
|
|
3066
3174
|
writeYamlFile(p, SurfaceSnapshotSchema.parse(snapshot));
|
|
3067
3175
|
return p;
|
|
3068
3176
|
}
|
|
3069
3177
|
function removeSnapshot(projectName, rootDir = getProjectRoot()) {
|
|
3070
3178
|
const dir = surfacesDir(rootDir);
|
|
3071
|
-
const direct =
|
|
3179
|
+
const direct = path6.join(dir, snapshotFilename(projectName));
|
|
3072
3180
|
if (fs4.existsSync(direct)) {
|
|
3073
3181
|
fs4.unlinkSync(direct);
|
|
3074
3182
|
return true;
|
|
@@ -3076,7 +3184,7 @@ function removeSnapshot(projectName, rootDir = getProjectRoot()) {
|
|
|
3076
3184
|
if (!fs4.existsSync(dir)) return false;
|
|
3077
3185
|
for (const file of fs4.readdirSync(dir)) {
|
|
3078
3186
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
3079
|
-
const p =
|
|
3187
|
+
const p = path6.join(dir, file);
|
|
3080
3188
|
try {
|
|
3081
3189
|
const snap = SurfaceSnapshotSchema.parse(readYamlFile(p));
|
|
3082
3190
|
if (snap.projectName === projectName) {
|
|
@@ -3100,13 +3208,13 @@ function selectPortalSpec(renderedSet, portalId) {
|
|
|
3100
3208
|
return [hit];
|
|
3101
3209
|
}
|
|
3102
3210
|
function perPortalPath(resolvedOut, portalId) {
|
|
3103
|
-
const ext =
|
|
3211
|
+
const ext = path6.extname(resolvedOut);
|
|
3104
3212
|
const stem = ext ? resolvedOut.slice(0, -ext.length) : resolvedOut;
|
|
3105
3213
|
return `${stem}.${safeFilenamePart(portalId)}${ext}`;
|
|
3106
3214
|
}
|
|
3107
3215
|
function writeSurfaceFile(outPath, snapshot, renderedSet) {
|
|
3108
|
-
const resolved =
|
|
3109
|
-
fs4.mkdirSync(
|
|
3216
|
+
const resolved = path6.resolve(outPath);
|
|
3217
|
+
fs4.mkdirSync(path6.dirname(resolved), { recursive: true });
|
|
3110
3218
|
if (!renderedSet || renderedSet.length === 0) {
|
|
3111
3219
|
writeYamlFile(resolved, snapshot);
|
|
3112
3220
|
return [resolved];
|
|
@@ -3139,14 +3247,14 @@ function exportSurface(maxAudience, format, outPath, portalId) {
|
|
|
3139
3247
|
return exportResult(snapshot, renderedSet, writtenPaths);
|
|
3140
3248
|
}
|
|
3141
3249
|
function importSurface(sourcePath, origin) {
|
|
3142
|
-
const resolved =
|
|
3250
|
+
const resolved = path6.resolve(sourcePath);
|
|
3143
3251
|
if (!fs4.existsSync(resolved)) {
|
|
3144
3252
|
throw new Error(`Surface document not found: ${resolved}`);
|
|
3145
3253
|
}
|
|
3146
3254
|
const body = fs4.readFileSync(resolved, "utf8");
|
|
3147
3255
|
let snapshot;
|
|
3148
3256
|
if (isOpenApiDocument(body)) {
|
|
3149
|
-
const projectName =
|
|
3257
|
+
const projectName = path6.basename(resolved).replace(/\.(json|ya?ml)$/i, "");
|
|
3150
3258
|
snapshot = fromOpenApi(body, projectName);
|
|
3151
3259
|
snapshot = { ...snapshot, origin };
|
|
3152
3260
|
} else {
|
|
@@ -3172,7 +3280,7 @@ function generateChildSnapshots(rootDir = getProjectRoot()) {
|
|
|
3172
3280
|
};
|
|
3173
3281
|
const written = [];
|
|
3174
3282
|
for (const child of children) {
|
|
3175
|
-
const childDir =
|
|
3283
|
+
const childDir = path6.resolve(rootDir, child.projectPath);
|
|
3176
3284
|
if (!fs4.existsSync(childDir)) continue;
|
|
3177
3285
|
written.push(saveSnapshot(familySnapshot, childDir));
|
|
3178
3286
|
for (const sibling of topLevel) {
|
|
@@ -3217,7 +3325,7 @@ function checkChildSurfaceFreshness(rootDir = getProjectRoot()) {
|
|
|
3217
3325
|
const current2 = surfaceContentKey(projectChildSurface());
|
|
3218
3326
|
const issues = [];
|
|
3219
3327
|
for (const child of children) {
|
|
3220
|
-
const childDir =
|
|
3328
|
+
const childDir = path6.resolve(rootDir, child.projectPath);
|
|
3221
3329
|
const held = getSnapshot(system.name, childDir);
|
|
3222
3330
|
if (!held || held.origin !== "generated") continue;
|
|
3223
3331
|
if (surfaceContentKey(held) !== current2) {
|
|
@@ -3231,12 +3339,12 @@ function checkChildSurfaceFreshness(rootDir = getProjectRoot()) {
|
|
|
3231
3339
|
}
|
|
3232
3340
|
return issues;
|
|
3233
3341
|
}
|
|
3234
|
-
var fs4,
|
|
3342
|
+
var fs4, path6, SURFACES_DIRNAME, CROSS_BOUNDARY_TARGETS;
|
|
3235
3343
|
var init_surfaces = __esm({
|
|
3236
3344
|
"src/core/surfaces.ts"() {
|
|
3237
3345
|
"use strict";
|
|
3238
3346
|
fs4 = __toESM(require("fs"));
|
|
3239
|
-
|
|
3347
|
+
path6 = __toESM(require("path"));
|
|
3240
3348
|
init_fs();
|
|
3241
3349
|
init_yaml();
|
|
3242
3350
|
init_filenames();
|
|
@@ -3346,13 +3454,13 @@ var init_contracts = __esm({
|
|
|
3346
3454
|
init_namespace();
|
|
3347
3455
|
contractsRule = {
|
|
3348
3456
|
name: "contract-symmetry-and-narratives",
|
|
3349
|
-
description: "Implementations mirror their contract method-for-method. Narrative call steps must name an existing component and method, the caller must declare the dependency, and asserted semantic guarantees must be backed by the target contract.",
|
|
3457
|
+
description: "Implementations mirror their contract method-for-method. Narrative call steps (and register handoffs, which share the identical target shape) must name an existing component and method, the caller must declare the dependency, and asserted semantic guarantees must be backed by the target contract.",
|
|
3350
3458
|
codes: [
|
|
3351
3459
|
{ code: "UNEXPECTED_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Implementation method not present on the contract" },
|
|
3352
3460
|
{ code: "MISSING_IMPLEMENTATION_METHOD", defaultSeverity: "error", summary: "Contract method missing from the implementation" },
|
|
3353
|
-
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call step missing targetComponent" },
|
|
3354
|
-
{ code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call step missing targetMethod" },
|
|
3355
|
-
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call step targets a non-existent component" },
|
|
3461
|
+
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
|
|
3462
|
+
{ code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
|
|
3463
|
+
{ code: "INVALID_TARGET_COMPONENT_REFERENCE", defaultSeverity: "error", summary: "Call/register step targets a non-existent component" },
|
|
3356
3464
|
{ code: "CROSS_TREE_REF_UNRESOLVED", defaultSeverity: "warning", summary: "Cross-tree reference (super::/:: form) with no surface snapshot covering it \u2014 only the parent project can verify it" },
|
|
3357
3465
|
{ code: "SURFACE_REF_NOT_EXPOSED", defaultSeverity: "error", summary: "Cross-tree reference resolves to a surface snapshot that does not expose the called method/capability" },
|
|
3358
3466
|
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
@@ -3390,7 +3498,7 @@ var init_contracts = __esm({
|
|
|
3390
3498
|
}
|
|
3391
3499
|
for (const implMethod of impl.methods) {
|
|
3392
3500
|
for (const step of implMethod.narrative) {
|
|
3393
|
-
if (step.type !== "call" && step.type !== "dispatch") continue;
|
|
3501
|
+
if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
|
|
3394
3502
|
if (!step.targetComponent) {
|
|
3395
3503
|
ctx.addIssue(
|
|
3396
3504
|
"error",
|
|
@@ -3450,11 +3558,12 @@ var init_contracts = __esm({
|
|
|
3450
3558
|
}
|
|
3451
3559
|
continue;
|
|
3452
3560
|
}
|
|
3561
|
+
const verb = step.type === "register" ? "registers callback" : "calls";
|
|
3453
3562
|
if (!step.targetMethod) {
|
|
3454
3563
|
ctx.addIssue(
|
|
3455
3564
|
"error",
|
|
3456
3565
|
"MISSING_TARGET_METHOD",
|
|
3457
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" has a
|
|
3566
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${step.type} step (${step.stepNumber}) missing "targetMethod".`,
|
|
3458
3567
|
impl.id,
|
|
3459
3568
|
isDraftCtx
|
|
3460
3569
|
);
|
|
@@ -3470,7 +3579,7 @@ var init_contracts = __esm({
|
|
|
3470
3579
|
ctx.addIssue(
|
|
3471
3580
|
"error",
|
|
3472
3581
|
"SURFACE_REF_NOT_EXPOSED",
|
|
3473
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
3582
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} "${step.targetMethod}" on cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), but the surface snapshot of "${resolved.snapshot.projectName}" does not expose that method on "${resolved.entry.id}".`,
|
|
3474
3583
|
impl.id,
|
|
3475
3584
|
isDraftCtx,
|
|
3476
3585
|
true
|
|
@@ -3495,7 +3604,7 @@ var init_contracts = __esm({
|
|
|
3495
3604
|
ctx.addIssue(
|
|
3496
3605
|
"warning",
|
|
3497
3606
|
"CROSS_TREE_REF_UNRESOLVED",
|
|
3498
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
3607
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} cross-tree component "${step.targetComponent}" (step ${step.stepNumber}), and no surface snapshot covers it \u2014 validate from the parent project, or import/generate the producing project's surface.`,
|
|
3499
3608
|
impl.id,
|
|
3500
3609
|
isDraftCtx
|
|
3501
3610
|
);
|
|
@@ -3503,7 +3612,7 @@ var init_contracts = __esm({
|
|
|
3503
3612
|
ctx.addIssue(
|
|
3504
3613
|
"error",
|
|
3505
3614
|
"INVALID_TARGET_COMPONENT_REFERENCE",
|
|
3506
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
3615
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} component "${step.targetComponent}" which does not exist (step ${step.stepNumber}).`,
|
|
3507
3616
|
impl.id,
|
|
3508
3617
|
isDraftCtx
|
|
3509
3618
|
);
|
|
@@ -3516,7 +3625,7 @@ var init_contracts = __esm({
|
|
|
3516
3625
|
ctx.addIssue(
|
|
3517
3626
|
"error",
|
|
3518
3627
|
"UNDECLARED_DEPENDENCY_CALL",
|
|
3519
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${callingComponent.id}")
|
|
3628
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${callingComponent.id}") ${verb} component "${step.targetComponent}" (step ${step.stepNumber}) but component "${callingComponent.id}" does not list "${step.targetComponent}" as a dependency.`,
|
|
3520
3629
|
impl.id,
|
|
3521
3630
|
isDraftCtx || ctx.isComponentDraft(callingComponent.id)
|
|
3522
3631
|
);
|
|
@@ -3535,7 +3644,7 @@ var init_contracts = __esm({
|
|
|
3535
3644
|
ctx.addIssue(
|
|
3536
3645
|
"error",
|
|
3537
3646
|
"INVALID_TARGET_METHOD_REFERENCE",
|
|
3538
|
-
`Method "${implMethod.name}" in implementation "${impl.id}"
|
|
3647
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} method "${step.targetMethod}" on component "${step.targetComponent}" which is not defined on any of its interfaces (step ${step.stepNumber}).`,
|
|
3539
3648
|
impl.id,
|
|
3540
3649
|
isDraftCtx || ctx.isComponentDraft(step.targetComponent)
|
|
3541
3650
|
);
|
|
@@ -3604,6 +3713,7 @@ function stepGraph(steps) {
|
|
|
3604
3713
|
switch (s.type) {
|
|
3605
3714
|
case "local":
|
|
3606
3715
|
case "call":
|
|
3716
|
+
case "register":
|
|
3607
3717
|
case "dispatch":
|
|
3608
3718
|
succ.push(fallNext(n));
|
|
3609
3719
|
break;
|
|
@@ -3699,6 +3809,7 @@ var init_narrative_flow = __esm({
|
|
|
3699
3809
|
switch (s.type) {
|
|
3700
3810
|
case "local":
|
|
3701
3811
|
case "call":
|
|
3812
|
+
case "register":
|
|
3702
3813
|
case "dispatch": {
|
|
3703
3814
|
const extra = flowConfigOn(s);
|
|
3704
3815
|
if (extra.length) malformed(`step ${s.stepNumber} (${s.type}) carries flow config (${extra.join(", ")}) \u2014 use a flow step type instead.`);
|
|
@@ -3853,7 +3964,7 @@ var init_narrative_flow = __esm({
|
|
|
3853
3964
|
if (s.finallyStep !== void 0) handlerStarts.add(s.finallyStep);
|
|
3854
3965
|
const last = byNum.get(s.endStep);
|
|
3855
3966
|
const nxt = nextOf(s.endStep);
|
|
3856
|
-
if (last && (last.type === "local" || last.type === "call" || last.type === "dispatch") && nxt !== void 0 && handlerStarts.has(nxt)) {
|
|
3967
|
+
if (last && (last.type === "local" || last.type === "call" || last.type === "register" || last.type === "dispatch") && nxt !== void 0 && handlerStarts.has(nxt)) {
|
|
3857
3968
|
ctx.addIssue(
|
|
3858
3969
|
"warning",
|
|
3859
3970
|
"FALLTHROUGH_INTO_HANDLER",
|
|
@@ -3963,7 +4074,7 @@ function resolveTypeScript(projectRoot) {
|
|
|
3963
4074
|
return cached.ts;
|
|
3964
4075
|
}
|
|
3965
4076
|
let ts = null;
|
|
3966
|
-
const bases = [
|
|
4077
|
+
const bases = [path7.join(projectRoot, "package.json"), __filename];
|
|
3967
4078
|
for (const base of bases) {
|
|
3968
4079
|
try {
|
|
3969
4080
|
const req = (0, import_module2.createRequire)(base);
|
|
@@ -4119,7 +4230,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
4119
4230
|
}
|
|
4120
4231
|
function resolveRelativeModule(fromFile, specifier) {
|
|
4121
4232
|
if (!specifier.startsWith(".")) return null;
|
|
4122
|
-
const base =
|
|
4233
|
+
const base = path7.resolve(path7.dirname(fromFile), specifier);
|
|
4123
4234
|
const candidates = [
|
|
4124
4235
|
base,
|
|
4125
4236
|
base.replace(/\.js$/, ".ts"),
|
|
@@ -4127,8 +4238,8 @@ function resolveRelativeModule(fromFile, specifier) {
|
|
|
4127
4238
|
`${base}.ts`,
|
|
4128
4239
|
`${base}.tsx`,
|
|
4129
4240
|
`${base}.js`,
|
|
4130
|
-
|
|
4131
|
-
|
|
4241
|
+
path7.join(base, "index.ts"),
|
|
4242
|
+
path7.join(base, "index.js")
|
|
4132
4243
|
];
|
|
4133
4244
|
for (const c of candidates) {
|
|
4134
4245
|
try {
|
|
@@ -4142,7 +4253,7 @@ function chaseStarExports(ts, facts, filePath, projectRoot, visited, exactCache)
|
|
|
4142
4253
|
for (const spec of facts.starExports) {
|
|
4143
4254
|
const target = resolveRelativeModule(filePath, spec);
|
|
4144
4255
|
if (!target || visited.has(target)) continue;
|
|
4145
|
-
if (
|
|
4256
|
+
if (path7.relative(projectRoot, target).startsWith("..")) continue;
|
|
4146
4257
|
visited.add(target);
|
|
4147
4258
|
let targetFacts = exactCache.get(target);
|
|
4148
4259
|
if (targetFacts === void 0) {
|
|
@@ -4187,12 +4298,12 @@ function buildCodeModel(implementations, projectRoot) {
|
|
|
4187
4298
|
if (seen.has(sourcePath)) continue;
|
|
4188
4299
|
seen.add(sourcePath);
|
|
4189
4300
|
const empty = { declaredNames: [], anchoredNames: [], exportedNames: [], imports: [], reexports: [] };
|
|
4190
|
-
if (
|
|
4301
|
+
if (path7.isAbsolute(sourcePath) || path7.normalize(sourcePath).split(path7.sep)[0] === "..") {
|
|
4191
4302
|
files.push({ path: sourcePath, status: "escaped", ...empty });
|
|
4192
4303
|
continue;
|
|
4193
4304
|
}
|
|
4194
|
-
const absolute =
|
|
4195
|
-
if (
|
|
4305
|
+
const absolute = path7.resolve(projectRoot, sourcePath);
|
|
4306
|
+
if (path7.relative(projectRoot, absolute).startsWith("..")) {
|
|
4196
4307
|
files.push({ path: sourcePath, status: "escaped", ...empty });
|
|
4197
4308
|
continue;
|
|
4198
4309
|
}
|
|
@@ -4213,7 +4324,7 @@ function buildCodeModel(implementations, projectRoot) {
|
|
|
4213
4324
|
continue;
|
|
4214
4325
|
}
|
|
4215
4326
|
const text = buffer.toString("utf8");
|
|
4216
|
-
const language = EXTENSION_LANGUAGE[
|
|
4327
|
+
const language = EXTENSION_LANGUAGE[path7.extname(sourcePath).toLowerCase()];
|
|
4217
4328
|
let analyzed;
|
|
4218
4329
|
if (language === "typescript" || language === "javascript") {
|
|
4219
4330
|
const ts = resolveTypeScript(projectRoot);
|
|
@@ -4247,12 +4358,12 @@ function buildCodeModel(implementations, projectRoot) {
|
|
|
4247
4358
|
}
|
|
4248
4359
|
return { files, projectRoot };
|
|
4249
4360
|
}
|
|
4250
|
-
var fs5,
|
|
4361
|
+
var fs5, path7, import_module2, EXTENSION_LANGUAGE, C_FAMILY_COMMENTS, JS_PATTERNS, LANGUAGE_PATTERNS, PATTERN_ANALYSIS_MAX_BYTES, IDENTIFIER_RE, STRING_RE, tsResolutionCache, TS_RESOLUTION_RETRY_MS;
|
|
4251
4362
|
var init_source_analysis = __esm({
|
|
4252
4363
|
"src/core/source-analysis.ts"() {
|
|
4253
4364
|
"use strict";
|
|
4254
4365
|
fs5 = __toESM(require("fs"));
|
|
4255
|
-
|
|
4366
|
+
path7 = __toESM(require("path"));
|
|
4256
4367
|
import_module2 = require("module");
|
|
4257
4368
|
EXTENSION_LANGUAGE = {
|
|
4258
4369
|
".ts": "typescript",
|
|
@@ -5763,6 +5874,7 @@ var init_public_surface = __esm({
|
|
|
5763
5874
|
// src/core/rules/graph.ts
|
|
5764
5875
|
function walkNarrativeGraph(ctx, seeds, opts = {}) {
|
|
5765
5876
|
const followDispatchTables = opts.followDispatchTables ?? true;
|
|
5877
|
+
const followRegisterEdges = opts.followRegisterEdges ?? true;
|
|
5766
5878
|
const reachedComponents = /* @__PURE__ */ new Set();
|
|
5767
5879
|
const reachedMethods = /* @__PURE__ */ new Set();
|
|
5768
5880
|
const queue = [];
|
|
@@ -5821,6 +5933,10 @@ function walkNarrativeGraph(ctx, seeds, opts = {}) {
|
|
|
5821
5933
|
reachComponent(step.targetComponent);
|
|
5822
5934
|
enqueueMethod(step.targetComponent, step.targetMethod);
|
|
5823
5935
|
}
|
|
5936
|
+
if (followRegisterEdges && step.type === "register" && step.targetComponent && step.targetMethod) {
|
|
5937
|
+
reachComponent(step.targetComponent);
|
|
5938
|
+
enqueueMethod(step.targetComponent, step.targetMethod);
|
|
5939
|
+
}
|
|
5824
5940
|
if (step.type === "dispatch" && step.targetComponent) {
|
|
5825
5941
|
reachComponent(step.targetComponent);
|
|
5826
5942
|
const portal = ctx.componentMap.get(step.targetComponent);
|
|
@@ -5906,11 +6022,13 @@ var init_graph = __esm({
|
|
|
5906
6022
|
methodKey = (compId, methodName) => `${compId}#${methodName}`;
|
|
5907
6023
|
reachabilityRule = {
|
|
5908
6024
|
name: "unused-detection",
|
|
5909
|
-
description: "Walks the narrative execution graph (call steps, dispatch-table routing, lifecycle flows) from every entrypoint (Portal/Observer/published component/lifecycle entrypoint) and flags components and methods no execution chain reaches, plus types no field or signature references.",
|
|
6025
|
+
description: "Walks the narrative execution graph (call steps, register handoffs, dispatch-table routing, lifecycle flows) from every entrypoint (Portal/Observer/published component/lifecycle entrypoint/invokedBy-declared method) and flags components and methods no execution chain reaches, plus types no field or signature references. An interface method declaring invokedBy (a real caller outside the modeled graph) seeds the walk, so its narrative propagates reachability; the declaration itself is audited (thin caller prose, or a method the internal walk already reaches).",
|
|
5910
6026
|
codes: [
|
|
5911
6027
|
{ code: "UNUSED_COMPONENT", defaultSeverity: "warning", summary: "Component never reached by any narrative call chain" },
|
|
5912
6028
|
{ code: "UNUSED_METHOD", defaultSeverity: "warning", summary: "Method never called by any narrative step" },
|
|
5913
|
-
{ code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields or signatures" }
|
|
6029
|
+
{ code: "UNUSED_TYPE", defaultSeverity: "warning", summary: "Type never referenced by fields or signatures" },
|
|
6030
|
+
{ code: "INVOKED_BY_UNDESCRIBED", defaultSeverity: "warning", summary: "invokedBy declaration whose caller prose is missing or placeholder-thin" },
|
|
6031
|
+
{ code: "INVOKED_BY_REDUNDANT", defaultSeverity: "warning", summary: "invokedBy declaration on a method the internal narrative walk already reaches \u2014 stale, remove it" }
|
|
5914
6032
|
],
|
|
5915
6033
|
check(ctx) {
|
|
5916
6034
|
const seeds = [];
|
|
@@ -5929,7 +6047,37 @@ var init_graph = __esm({
|
|
|
5929
6047
|
seeds.push({ compId: le.component, methodName: le.method });
|
|
5930
6048
|
}
|
|
5931
6049
|
}
|
|
5932
|
-
const
|
|
6050
|
+
const baseWalk = walkNarrativeGraph(ctx, seeds);
|
|
6051
|
+
const invokedBySeeds = [];
|
|
6052
|
+
for (const intf of ctx.interfaces) {
|
|
6053
|
+
if (!ctx.componentMap.has(intf.component)) continue;
|
|
6054
|
+
const comp = ctx.componentMap.get(intf.component);
|
|
6055
|
+
for (const m of intf.methods) {
|
|
6056
|
+
if (!m.invokedBy) continue;
|
|
6057
|
+
invokedBySeeds.push({ compId: intf.component, methodName: m.name });
|
|
6058
|
+
if (!ctx.isSpecInScope(intf.id)) continue;
|
|
6059
|
+
const isDraftCtx = comp.status === "draft" || comp.status === "design" || intf.status === "draft" || intf.status === "design";
|
|
6060
|
+
if (!passesIntentFloor(m.invokedBy.caller, m.name)) {
|
|
6061
|
+
ctx.addIssue(
|
|
6062
|
+
"warning",
|
|
6063
|
+
"INVOKED_BY_UNDESCRIBED",
|
|
6064
|
+
`Method "${m.name}" on component "${intf.component}" declares invokedBy (${m.invokedBy.kind}) but its "caller" prose is missing or placeholder-thin \u2014 state WHO invokes it and when, so the entrypoint claim stays reviewable.`,
|
|
6065
|
+
intf.id,
|
|
6066
|
+
isDraftCtx
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
if (baseWalk.reachedMethods.has(methodKey(intf.component, m.name))) {
|
|
6070
|
+
ctx.addIssue(
|
|
6071
|
+
"warning",
|
|
6072
|
+
"INVOKED_BY_REDUNDANT",
|
|
6073
|
+
`Method "${m.name}" on component "${intf.component}" declares invokedBy (${m.invokedBy.kind}), but the internal narrative walk already reaches it \u2014 the declaration is stale; remove it.`,
|
|
6074
|
+
intf.id,
|
|
6075
|
+
isDraftCtx
|
|
6076
|
+
);
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
const { reachedComponents, reachedMethods } = invokedBySeeds.length ? walkNarrativeGraph(ctx, [...seeds, ...invokedBySeeds]) : baseWalk;
|
|
5933
6081
|
for (const comp of ctx.components) {
|
|
5934
6082
|
if (!ctx.isSpecInScope(comp.id)) continue;
|
|
5935
6083
|
if (!reachedComponents.has(comp.id)) {
|
|
@@ -6264,7 +6412,7 @@ var init_semantic_edges = __esm({
|
|
|
6264
6412
|
}
|
|
6265
6413
|
}
|
|
6266
6414
|
}
|
|
6267
|
-
const initReach = initSeeds.length ? walkNarrativeGraph(ctx, initSeeds, { followDispatchTables: false }) : null;
|
|
6415
|
+
const initReach = initSeeds.length ? walkNarrativeGraph(ctx, initSeeds, { followDispatchTables: false, followRegisterEdges: false }) : null;
|
|
6268
6416
|
for (const comp of ctx.components) {
|
|
6269
6417
|
const isDraftCtx = ctx.isComponentDraft(comp.id);
|
|
6270
6418
|
if (comp.componentType !== "Store") continue;
|
|
@@ -6662,6 +6810,7 @@ function isTerminal(step, nextOf) {
|
|
|
6662
6810
|
switch (step.type) {
|
|
6663
6811
|
case "local":
|
|
6664
6812
|
case "call":
|
|
6813
|
+
case "register":
|
|
6665
6814
|
case "dispatch":
|
|
6666
6815
|
return nextOf(n) === void 0;
|
|
6667
6816
|
case "branch":
|
|
@@ -6862,11 +7011,11 @@ var init_narrative_antipatterns = __esm({
|
|
|
6862
7011
|
const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
|
|
6863
7012
|
if (memberEdges.length === 0) continue;
|
|
6864
7013
|
const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
|
|
6865
|
-
const
|
|
7014
|
+
const path24 = [...keys].sort().join(" \u2192 ");
|
|
6866
7015
|
ctx.addIssue(
|
|
6867
7016
|
"warning",
|
|
6868
7017
|
"UNCONDITIONAL_CALL_CYCLE",
|
|
6869
|
-
`Call cycle with no guard: ${
|
|
7018
|
+
`Call cycle with no guard: ${path24} \u2014 every call edge in this cycle is unavoidable on all paths of its narrative (e.g. step ${anchor.stepNumber} of "${anchor.methodName}" in "${anchor.impl.id}" always calls ${anchor.toLabel}). This recurses without a base case, by construction. Guard at least one edge with a branch/return before the call, or lint.allow with the termination argument.`,
|
|
6870
7019
|
anchor.impl.id,
|
|
6871
7020
|
memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
|
|
6872
7021
|
);
|
|
@@ -7582,7 +7731,7 @@ var init_naming = __esm({
|
|
|
7582
7731
|
// src/core/rules/integration-conformance.ts
|
|
7583
7732
|
function resolveAgainst(mapped, fromFile, specifier) {
|
|
7584
7733
|
if (!specifier.startsWith(".")) return null;
|
|
7585
|
-
const joined = normalizePath(
|
|
7734
|
+
const joined = normalizePath(path8.posix.normalize(path8.posix.join(path8.posix.dirname(fromFile), specifier)));
|
|
7586
7735
|
const candidates = [
|
|
7587
7736
|
joined,
|
|
7588
7737
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -7598,11 +7747,11 @@ function resolveAgainst(mapped, fromFile, specifier) {
|
|
|
7598
7747
|
}
|
|
7599
7748
|
return null;
|
|
7600
7749
|
}
|
|
7601
|
-
var
|
|
7750
|
+
var path8, normalizePath, integrationConformanceRule;
|
|
7602
7751
|
var init_integration_conformance = __esm({
|
|
7603
7752
|
"src/core/rules/integration-conformance.ts"() {
|
|
7604
7753
|
"use strict";
|
|
7605
|
-
|
|
7754
|
+
path8 = __toESM(require("path"));
|
|
7606
7755
|
init_source_analysis();
|
|
7607
7756
|
init_conformance();
|
|
7608
7757
|
normalizePath = normalizeSourcePath;
|
|
@@ -7806,7 +7955,7 @@ var init_hidden_state = __esm({
|
|
|
7806
7955
|
// src/core/rules/dependency-conformance.ts
|
|
7807
7956
|
function resolveAgainst2(mapped, fromFile, specifier) {
|
|
7808
7957
|
if (!specifier.startsWith(".")) return null;
|
|
7809
|
-
const joined = normalizePath2(
|
|
7958
|
+
const joined = normalizePath2(path9.posix.normalize(path9.posix.join(path9.posix.dirname(fromFile), specifier)));
|
|
7810
7959
|
const candidates = [
|
|
7811
7960
|
joined,
|
|
7812
7961
|
joined.replace(/\.js$/, ".ts"),
|
|
@@ -7822,11 +7971,11 @@ function resolveAgainst2(mapped, fromFile, specifier) {
|
|
|
7822
7971
|
}
|
|
7823
7972
|
return null;
|
|
7824
7973
|
}
|
|
7825
|
-
var
|
|
7974
|
+
var path9, normalizePath2, dependencyConformanceRule;
|
|
7826
7975
|
var init_dependency_conformance = __esm({
|
|
7827
7976
|
"src/core/rules/dependency-conformance.ts"() {
|
|
7828
7977
|
"use strict";
|
|
7829
|
-
|
|
7978
|
+
path9 = __toESM(require("path"));
|
|
7830
7979
|
init_source_analysis();
|
|
7831
7980
|
init_conformance();
|
|
7832
7981
|
normalizePath2 = normalizeSourcePath;
|
|
@@ -8700,17 +8849,17 @@ function readLockRecord() {
|
|
|
8700
8849
|
}
|
|
8701
8850
|
function writeLockRecord(record) {
|
|
8702
8851
|
const p = lockPath();
|
|
8703
|
-
fs6.mkdirSync(
|
|
8852
|
+
fs6.mkdirSync(path10.dirname(p), { recursive: true });
|
|
8704
8853
|
const tmp = `${p}.tmp`;
|
|
8705
8854
|
fs6.writeFileSync(tmp, JSON.stringify(record, null, 2) + "\n");
|
|
8706
8855
|
fs6.renameSync(tmp, p);
|
|
8707
8856
|
}
|
|
8708
|
-
var fs6,
|
|
8857
|
+
var fs6, path10;
|
|
8709
8858
|
var init_lockfile = __esm({
|
|
8710
8859
|
"src/core/lockfile.ts"() {
|
|
8711
8860
|
"use strict";
|
|
8712
8861
|
fs6 = __toESM(require("fs"));
|
|
8713
|
-
|
|
8862
|
+
path10 = __toESM(require("path"));
|
|
8714
8863
|
init_fs();
|
|
8715
8864
|
}
|
|
8716
8865
|
});
|
|
@@ -9242,6 +9391,9 @@ function buildCanvasModel(issues = []) {
|
|
|
9242
9391
|
text: s.description,
|
|
9243
9392
|
kind: s.type,
|
|
9244
9393
|
...s.type === "call" && s.targetComponent && s.targetMethod ? { call: { component: s.targetComponent, method: s.targetMethod } } : {},
|
|
9394
|
+
// Register (runtime-callback handoff) carries its target like a
|
|
9395
|
+
// call step — rendered as an edge, labeled by its own kind.
|
|
9396
|
+
...s.type === "register" && s.targetComponent && s.targetMethod ? { call: { component: s.targetComponent, method: s.targetMethod } } : {},
|
|
9245
9397
|
...s.type === "dispatch" && s.targetComponent && s.capability ? { call: { component: s.targetComponent, method: `\u27E8${s.capability}\u27E9` } } : {},
|
|
9246
9398
|
...s.condition ? { cond: s.condition } : {},
|
|
9247
9399
|
...s.onTrueStep !== void 0 ? { onTrue: s.onTrueStep } : {},
|
|
@@ -9440,9 +9592,9 @@ function embedJson(value) {
|
|
|
9440
9592
|
}
|
|
9441
9593
|
function loadCytoscapeLib() {
|
|
9442
9594
|
const candidates = [
|
|
9443
|
-
|
|
9595
|
+
path11.resolve(__dirname, "..", "templates", "canvas", "cytoscape.min.js"),
|
|
9444
9596
|
// src/core & dist/cli
|
|
9445
|
-
|
|
9597
|
+
path11.resolve(__dirname, "templates", "canvas", "cytoscape.min.js")
|
|
9446
9598
|
// dist (library entry)
|
|
9447
9599
|
];
|
|
9448
9600
|
for (const p of candidates) {
|
|
@@ -9459,12 +9611,12 @@ function renderCanvasHtml(model) {
|
|
|
9459
9611
|
function escapeHtml(s) {
|
|
9460
9612
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
9461
9613
|
}
|
|
9462
|
-
var fs7,
|
|
9614
|
+
var fs7, path11, CANVAS_TEMPLATE;
|
|
9463
9615
|
var init_canvas = __esm({
|
|
9464
9616
|
"src/core/canvas.ts"() {
|
|
9465
9617
|
"use strict";
|
|
9466
9618
|
fs7 = __toESM(require("fs"));
|
|
9467
|
-
|
|
9619
|
+
path11 = __toESM(require("path"));
|
|
9468
9620
|
init_specs2();
|
|
9469
9621
|
init_type_analysis();
|
|
9470
9622
|
init_diagram_export();
|
|
@@ -12848,6 +13000,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
12848
13000
|
case 'try': return s.n + '. \\u26E8 try \\u2014 ' + s.text;
|
|
12849
13001
|
case 'parallel': return s.n + '. \\u2225 ' + s.text;
|
|
12850
13002
|
case 'jump': return s.n + '. \\u21B7 ' + s.text;
|
|
13003
|
+
case 'register': return s.n + '. \\u27F2 register \\u2014 ' + s.text;
|
|
12851
13004
|
case 'return': return s.n + '. \\u23CE return' + (s.outcome ? ' \\u2014 ' + s.outcome : '');
|
|
12852
13005
|
case 'throw': return s.n + '. \\u26A1 throw' + (s.err ? ' ' + s.err : '');
|
|
12853
13006
|
default: return s.n + '. ' + s.text;
|
|
@@ -12871,7 +13024,9 @@ var MODEL = __MODEL_JSON__;
|
|
|
12871
13024
|
(graph.joins || []).forEach(function (j2) { joinByHeader[j2.header] = j2; });
|
|
12872
13025
|
graph.steps.forEach(function (s, i) {
|
|
12873
13026
|
var id = 'n' + s.n;
|
|
12874
|
-
|
|
13027
|
+
// Register steps render like calls (target edge + label) \u2014 the label
|
|
13028
|
+
// itself says "register", keeping the handoff visually distinct.
|
|
13029
|
+
var isCall = (s.kind === 'call' || s.kind === 'dispatch' || s.kind === 'register') && !!s.call;
|
|
12875
13030
|
// A detached call's target renders as a separate ghost node (below), so
|
|
12876
13031
|
// the step node itself stays plain and undrillable.
|
|
12877
13032
|
var isDetached = isCall && !!s.detach;
|
|
@@ -13126,7 +13281,7 @@ var MODEL = __MODEL_JSON__;
|
|
|
13126
13281
|
comps.push({ id: 'start', name: flowTitle() + '()', subsystem: 'flow', componentType: 'Start', public: false, owns: [] });
|
|
13127
13282
|
graph.steps.forEach(function (s) {
|
|
13128
13283
|
var name = flowStepLabel(s) + (s.call && !s.detach ? ' \\u2192 ' + s.call.component + '.' + s.call.method + '()' : '');
|
|
13129
|
-
comps.push({ id: 'n' + s.n, name: name, subsystem: 'flow', componentType: (s.kind === 'call' || s.kind === 'dispatch') ? 'Call' : 'Step', public: false, owns: [] });
|
|
13284
|
+
comps.push({ id: 'n' + s.n, name: name, subsystem: 'flow', componentType: (s.kind === 'call' || s.kind === 'dispatch' || s.kind === 'register') ? 'Call' : 'Step', public: false, owns: [] });
|
|
13130
13285
|
});
|
|
13131
13286
|
// Virtual flow nodes (join bars, detached-call ghosts) export as plain
|
|
13132
13287
|
// steps so the editable diagrams keep the fan-out/join and detachment.
|
|
@@ -13636,7 +13791,7 @@ var init_repository = __esm({
|
|
|
13636
13791
|
|
|
13637
13792
|
// src/core/variants.ts
|
|
13638
13793
|
function globalVariantsDir() {
|
|
13639
|
-
return process.env.WAIRON_VARIANTS_DIR ??
|
|
13794
|
+
return process.env.WAIRON_VARIANTS_DIR ?? path12.join(os3.homedir(), ".wairon", "variants");
|
|
13640
13795
|
}
|
|
13641
13796
|
function readVariantsDir(dir) {
|
|
13642
13797
|
let entries;
|
|
@@ -13648,7 +13803,7 @@ function readVariantsDir(dir) {
|
|
|
13648
13803
|
const out = [];
|
|
13649
13804
|
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
13650
13805
|
if (!e.isFile() || !/\.ya?ml$/i.test(e.name)) continue;
|
|
13651
|
-
const full =
|
|
13806
|
+
const full = path12.join(dir, e.name);
|
|
13652
13807
|
try {
|
|
13653
13808
|
const raw = readYamlFile(full);
|
|
13654
13809
|
if (raw == null) continue;
|
|
@@ -13695,19 +13850,19 @@ function loadProjectVariants() {
|
|
|
13695
13850
|
try {
|
|
13696
13851
|
const byId = /* @__PURE__ */ new Map();
|
|
13697
13852
|
for (const v of readVariantsDir(globalVariantsDir())) byId.set(v.id, v);
|
|
13698
|
-
for (const v of readVariantsDir(
|
|
13853
|
+
for (const v of readVariantsDir(path12.join(getProjectRoot(), ".wai", "variants"))) byId.set(v.id, v);
|
|
13699
13854
|
return [...byId.values()];
|
|
13700
13855
|
} catch {
|
|
13701
13856
|
return [];
|
|
13702
13857
|
}
|
|
13703
13858
|
}
|
|
13704
|
-
var fs8, os3,
|
|
13859
|
+
var fs8, os3, path12, import_zod8, VariantDefSchema;
|
|
13705
13860
|
var init_variants = __esm({
|
|
13706
13861
|
"src/core/variants.ts"() {
|
|
13707
13862
|
"use strict";
|
|
13708
13863
|
fs8 = __toESM(require("fs"));
|
|
13709
13864
|
os3 = __toESM(require("os"));
|
|
13710
|
-
|
|
13865
|
+
path12 = __toESM(require("path"));
|
|
13711
13866
|
import_zod8 = require("zod");
|
|
13712
13867
|
init_yaml();
|
|
13713
13868
|
init_fs();
|
|
@@ -14393,6 +14548,17 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
14393
14548
|
}
|
|
14394
14549
|
break;
|
|
14395
14550
|
}
|
|
14551
|
+
case "register": {
|
|
14552
|
+
if (!step.targetComponent || !step.targetMethod) break;
|
|
14553
|
+
const regTarget = componentById.get(step.targetComponent);
|
|
14554
|
+
if (!regTarget) {
|
|
14555
|
+
lines.push(` Note over ${selfId}: ${escapeLabel(`registers callback on unknown "${step.targetComponent}"`)}`);
|
|
14556
|
+
break;
|
|
14557
|
+
}
|
|
14558
|
+
const regTargetId = declare(regTarget);
|
|
14559
|
+
lines.push(` ${selfId}--)${regTargetId}: ${escapeLabel(`register ${step.targetMethod}()`)}`);
|
|
14560
|
+
break;
|
|
14561
|
+
}
|
|
14396
14562
|
case "call": {
|
|
14397
14563
|
if (!step.targetComponent || !step.targetMethod) break;
|
|
14398
14564
|
const target = componentById.get(step.targetComponent);
|
|
@@ -14600,16 +14766,16 @@ function relativizeId(id, prefix) {
|
|
|
14600
14766
|
return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
|
|
14601
14767
|
}
|
|
14602
14768
|
function isWithin(dir, file) {
|
|
14603
|
-
const d =
|
|
14604
|
-
const f =
|
|
14605
|
-
return f === d || f.startsWith(d +
|
|
14769
|
+
const d = path13.resolve(dir);
|
|
14770
|
+
const f = path13.resolve(file);
|
|
14771
|
+
return f === d || f.startsWith(d + path13.sep);
|
|
14606
14772
|
}
|
|
14607
14773
|
function projectPathEscapesRoot(projectRoot, projectPath, resolvedChildDir) {
|
|
14608
|
-
return
|
|
14774
|
+
return path13.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir);
|
|
14609
14775
|
}
|
|
14610
14776
|
function assertContainedProjectPath(projectRoot, projectPath) {
|
|
14611
|
-
const root =
|
|
14612
|
-
const resolved =
|
|
14777
|
+
const root = path13.resolve(projectRoot);
|
|
14778
|
+
const resolved = path13.resolve(root, projectPath);
|
|
14613
14779
|
if (projectPathEscapesRoot(root, projectPath, resolved)) {
|
|
14614
14780
|
throw new Error(
|
|
14615
14781
|
`projectPath "${projectPath}" must resolve within the project root "${root}", but resolves to "${resolved}"; absolute paths and ../-escaping paths are rejected so a chained subproject is always contained by its parent.`
|
|
@@ -14620,11 +14786,11 @@ function assertContainedProjectPath(projectRoot, projectPath) {
|
|
|
14620
14786
|
function findChainingParent(childRoot) {
|
|
14621
14787
|
let childResolved;
|
|
14622
14788
|
try {
|
|
14623
|
-
childResolved =
|
|
14789
|
+
childResolved = path13.resolve(childRoot);
|
|
14624
14790
|
} catch {
|
|
14625
14791
|
return null;
|
|
14626
14792
|
}
|
|
14627
|
-
let dir =
|
|
14793
|
+
let dir = path13.dirname(childResolved);
|
|
14628
14794
|
for (let hops = 0; hops < 32; hops++) {
|
|
14629
14795
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
14630
14796
|
if (pathExists(specsDir)) {
|
|
@@ -14639,7 +14805,7 @@ function findChainingParent(childRoot) {
|
|
|
14639
14805
|
const projectPath = raw.projectPath;
|
|
14640
14806
|
if (typeof projectPath === "string" && projectPath.trim() !== "") {
|
|
14641
14807
|
try {
|
|
14642
|
-
if (
|
|
14808
|
+
if (path13.resolve(dir, projectPath) === childResolved) {
|
|
14643
14809
|
const id = raw.id;
|
|
14644
14810
|
return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
|
|
14645
14811
|
}
|
|
@@ -14649,7 +14815,7 @@ function findChainingParent(childRoot) {
|
|
|
14649
14815
|
}
|
|
14650
14816
|
}
|
|
14651
14817
|
}
|
|
14652
|
-
const up =
|
|
14818
|
+
const up = path13.dirname(dir);
|
|
14653
14819
|
if (up === dir) break;
|
|
14654
14820
|
dir = up;
|
|
14655
14821
|
}
|
|
@@ -14754,18 +14920,18 @@ function findOwner(id, components) {
|
|
|
14754
14920
|
}) ?? null;
|
|
14755
14921
|
}
|
|
14756
14922
|
function moveComponentFolder(fromDir, toDir) {
|
|
14757
|
-
if (
|
|
14923
|
+
if (path13.normalize(fromDir) === path13.normalize(toDir)) return false;
|
|
14758
14924
|
if (!fs9.existsSync(fromDir) || fs9.existsSync(toDir)) return false;
|
|
14759
|
-
ensureDir(
|
|
14925
|
+
ensureDir(path13.dirname(toDir));
|
|
14760
14926
|
fs9.renameSync(fromDir, toDir);
|
|
14761
14927
|
return true;
|
|
14762
14928
|
}
|
|
14763
14929
|
function cleanEmptyDirs(filePath, specsRoot) {
|
|
14764
|
-
let dir =
|
|
14930
|
+
let dir = path13.dirname(filePath);
|
|
14765
14931
|
while (dir !== specsRoot && dir.startsWith(specsRoot)) {
|
|
14766
14932
|
if (fs9.existsSync(dir) && fs9.readdirSync(dir).length === 0) {
|
|
14767
14933
|
fs9.rmdirSync(dir);
|
|
14768
|
-
dir =
|
|
14934
|
+
dir = path13.dirname(dir);
|
|
14769
14935
|
} else {
|
|
14770
14936
|
break;
|
|
14771
14937
|
}
|
|
@@ -14800,7 +14966,7 @@ function computeSpecTreeSignature(dirs) {
|
|
|
14800
14966
|
return parts.join("|");
|
|
14801
14967
|
}
|
|
14802
14968
|
function workspaceFor(rootDir) {
|
|
14803
|
-
const key =
|
|
14969
|
+
const key = path13.resolve(rootDir);
|
|
14804
14970
|
let ws = workspaces.get(key);
|
|
14805
14971
|
if (!ws) {
|
|
14806
14972
|
ws = new SpecWorkspace(key);
|
|
@@ -14939,7 +15105,7 @@ function readLockState() {
|
|
|
14939
15105
|
return { state: stateIdEquals(record.stateId, current2) ? "locked" : "stale", record, current: current2 };
|
|
14940
15106
|
}
|
|
14941
15107
|
function computeStateIdAt(root) {
|
|
14942
|
-
const resolved =
|
|
15108
|
+
const resolved = path13.resolve(root);
|
|
14943
15109
|
return runWithProjectRoot(resolved, () => {
|
|
14944
15110
|
workspaceFor(resolved).invalidate();
|
|
14945
15111
|
const system = loadSystemSpec();
|
|
@@ -14983,12 +15149,12 @@ function findLegacySpecFiles() {
|
|
|
14983
15149
|
function updateSpec(kind, id, delta, hooks) {
|
|
14984
15150
|
return current().updateSpec(kind, id, delta, hooks);
|
|
14985
15151
|
}
|
|
14986
|
-
var fs9,
|
|
15152
|
+
var fs9, path13, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
14987
15153
|
var init_specs2 = __esm({
|
|
14988
15154
|
"src/core/specs.ts"() {
|
|
14989
15155
|
"use strict";
|
|
14990
15156
|
fs9 = __toESM(require("fs"));
|
|
14991
|
-
|
|
15157
|
+
path13 = __toESM(require("path"));
|
|
14992
15158
|
init_loader();
|
|
14993
15159
|
init_fs();
|
|
14994
15160
|
init_statehash();
|
|
@@ -15009,7 +15175,7 @@ var init_specs2 = __esm({
|
|
|
15009
15175
|
this.rootSubsystems = /* @__PURE__ */ new Set();
|
|
15010
15176
|
this.scanVisitedSpecDirs = [];
|
|
15011
15177
|
this.loaderIssues = [];
|
|
15012
|
-
this.rootDir =
|
|
15178
|
+
this.rootDir = path13.resolve(rootDir);
|
|
15013
15179
|
this.paths = aiPathsAt(this.rootDir);
|
|
15014
15180
|
}
|
|
15015
15181
|
invalidate() {
|
|
@@ -15036,7 +15202,7 @@ var init_specs2 = __esm({
|
|
|
15036
15202
|
this.rootSubsystems.clear();
|
|
15037
15203
|
this.cachedRecursive = recursive;
|
|
15038
15204
|
this.scanVisitedSpecDirs = [];
|
|
15039
|
-
const visited = /* @__PURE__ */ new Set([
|
|
15205
|
+
const visited = /* @__PURE__ */ new Set([path13.resolve(this.rootDir)]);
|
|
15040
15206
|
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
15041
15207
|
this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
|
|
15042
15208
|
this.cachedSpecDirs = this.scanVisitedSpecDirs;
|
|
@@ -15051,10 +15217,10 @@ var init_specs2 = __esm({
|
|
|
15051
15217
|
this.scanVisitedSpecDirs.push(specsDir);
|
|
15052
15218
|
if (!pathExists(specsDir)) return index;
|
|
15053
15219
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
15054
|
-
const systemYaml =
|
|
15220
|
+
const systemYaml = path13.normalize(projectPaths.specsSystem());
|
|
15055
15221
|
const localSubprojects = [];
|
|
15056
15222
|
for (const file of files) {
|
|
15057
|
-
const normFile =
|
|
15223
|
+
const normFile = path13.normalize(file);
|
|
15058
15224
|
if (normFile === systemYaml) continue;
|
|
15059
15225
|
let detectedType = "spec";
|
|
15060
15226
|
try {
|
|
@@ -15064,7 +15230,7 @@ var init_specs2 = __esm({
|
|
|
15064
15230
|
severity: "error",
|
|
15065
15231
|
code: "INVALID_YAML",
|
|
15066
15232
|
message: `Spec file "${file}" is not a valid YAML object or is empty.`,
|
|
15067
|
-
specId:
|
|
15233
|
+
specId: path13.basename(file, ".yaml")
|
|
15068
15234
|
});
|
|
15069
15235
|
continue;
|
|
15070
15236
|
}
|
|
@@ -15096,8 +15262,8 @@ var init_specs2 = __esm({
|
|
|
15096
15262
|
detectedType = "implementation";
|
|
15097
15263
|
const parsed = ImplementationSpecSchema.parse(raw);
|
|
15098
15264
|
if (parsed.sourcePath) {
|
|
15099
|
-
const absSourcePath =
|
|
15100
|
-
parsed.sourcePath =
|
|
15265
|
+
const absSourcePath = path13.resolve(projectDir, parsed.sourcePath);
|
|
15266
|
+
parsed.sourcePath = path13.relative(projectDir, absSourcePath).replace(/\\/g, "/");
|
|
15101
15267
|
}
|
|
15102
15268
|
index.implementations.push(parsed);
|
|
15103
15269
|
index.paths.implementation[parsed.id] = file;
|
|
@@ -15119,11 +15285,11 @@ var init_specs2 = __esm({
|
|
|
15119
15285
|
severity: "error",
|
|
15120
15286
|
code: "UNKNOWN_SPEC_TYPE",
|
|
15121
15287
|
message: `Spec file "${file}" does not match any recognized L1-L4 schema structure.`,
|
|
15122
|
-
specId:
|
|
15288
|
+
specId: path13.basename(file, ".yaml")
|
|
15123
15289
|
});
|
|
15124
15290
|
}
|
|
15125
15291
|
} catch (e) {
|
|
15126
|
-
const filename =
|
|
15292
|
+
const filename = path13.basename(file, ".yaml");
|
|
15127
15293
|
this.loaderIssues.push({
|
|
15128
15294
|
severity: "error",
|
|
15129
15295
|
code: "SCHEMA_VALIDATION_ERROR",
|
|
@@ -15221,7 +15387,7 @@ var init_specs2 = __esm({
|
|
|
15221
15387
|
}
|
|
15222
15388
|
if (currentDepth < maxDepth) {
|
|
15223
15389
|
for (const subproj of localSubprojects) {
|
|
15224
|
-
const childDir =
|
|
15390
|
+
const childDir = path13.resolve(projectDir, subproj.projectPath);
|
|
15225
15391
|
if (projectPathEscapesRoot(this.rootDir, subproj.projectPath, childDir)) {
|
|
15226
15392
|
this.loaderIssues.push({
|
|
15227
15393
|
severity: "error",
|
|
@@ -15283,7 +15449,7 @@ var init_specs2 = __esm({
|
|
|
15283
15449
|
const index = this.scanAll();
|
|
15284
15450
|
const sub = index.subsystems.find((s) => s.id === currentPrefix);
|
|
15285
15451
|
if (sub && sub.projectPath) {
|
|
15286
|
-
const nextDir =
|
|
15452
|
+
const nextDir = path13.resolve(currentDir, sub.projectPath);
|
|
15287
15453
|
if (projectPathEscapesRoot(this.rootDir, sub.projectPath, nextDir)) {
|
|
15288
15454
|
this.loaderIssues.push({
|
|
15289
15455
|
severity: "error",
|
|
@@ -15335,9 +15501,9 @@ var init_specs2 = __esm({
|
|
|
15335
15501
|
return index.paths.subsystem[matches2[0]];
|
|
15336
15502
|
}
|
|
15337
15503
|
if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
|
|
15338
|
-
return
|
|
15504
|
+
return path13.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
|
|
15339
15505
|
}
|
|
15340
|
-
return
|
|
15506
|
+
return path13.join(this.paths.specsDir(), id, ".index.yaml");
|
|
15341
15507
|
}
|
|
15342
15508
|
getComponentPath(id, subsystemId) {
|
|
15343
15509
|
const index = this.scanAll();
|
|
@@ -15357,21 +15523,21 @@ var init_specs2 = __esm({
|
|
|
15357
15523
|
if (owner) {
|
|
15358
15524
|
const ownerPath = index.paths.component[owner.id];
|
|
15359
15525
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
15360
|
-
return
|
|
15526
|
+
return path13.join(path13.dirname(ownerPath), id, ".index.yaml");
|
|
15361
15527
|
}
|
|
15362
15528
|
}
|
|
15363
15529
|
if (subsystemId) {
|
|
15364
15530
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15365
|
-
const subDir =
|
|
15531
|
+
const subDir = path13.dirname(subPath);
|
|
15366
15532
|
if (subPath.endsWith(".index.yaml")) {
|
|
15367
|
-
return
|
|
15533
|
+
return path13.join(subDir, id, ".index.yaml");
|
|
15368
15534
|
}
|
|
15369
15535
|
}
|
|
15370
15536
|
if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
|
|
15371
|
-
return
|
|
15537
|
+
return path13.join(this.paths.specsComponentsDir(), `${id}.yaml`);
|
|
15372
15538
|
}
|
|
15373
15539
|
const targetSubsystem = subsystemId || "default";
|
|
15374
|
-
return
|
|
15540
|
+
return path13.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
|
|
15375
15541
|
}
|
|
15376
15542
|
getInterfacePath(id, componentId) {
|
|
15377
15543
|
const index = this.scanAll();
|
|
@@ -15397,16 +15563,16 @@ var init_specs2 = __esm({
|
|
|
15397
15563
|
}
|
|
15398
15564
|
if (componentId) {
|
|
15399
15565
|
const compPath = this.getComponentPath(componentId);
|
|
15400
|
-
const compDir =
|
|
15566
|
+
const compDir = path13.dirname(compPath);
|
|
15401
15567
|
if (compPath.endsWith(".index.yaml")) {
|
|
15402
|
-
return
|
|
15568
|
+
return path13.join(compDir, ".interface.yaml");
|
|
15403
15569
|
}
|
|
15404
15570
|
}
|
|
15405
15571
|
if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
|
|
15406
|
-
return
|
|
15572
|
+
return path13.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
|
|
15407
15573
|
}
|
|
15408
15574
|
const targetComponent = componentId || "default";
|
|
15409
|
-
return
|
|
15575
|
+
return path13.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
|
|
15410
15576
|
}
|
|
15411
15577
|
getImplementationPath(id, contractId) {
|
|
15412
15578
|
const index = this.scanAll();
|
|
@@ -15432,16 +15598,16 @@ var init_specs2 = __esm({
|
|
|
15432
15598
|
}
|
|
15433
15599
|
if (contractId) {
|
|
15434
15600
|
const intfPath = this.getInterfacePath(contractId);
|
|
15435
|
-
const intfDir =
|
|
15601
|
+
const intfDir = path13.dirname(intfPath);
|
|
15436
15602
|
if (intfPath.endsWith(".interface.yaml")) {
|
|
15437
|
-
return
|
|
15603
|
+
return path13.join(intfDir, ".implementation.yaml");
|
|
15438
15604
|
}
|
|
15439
15605
|
}
|
|
15440
15606
|
if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
|
|
15441
|
-
return
|
|
15607
|
+
return path13.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
|
|
15442
15608
|
}
|
|
15443
15609
|
const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
|
|
15444
|
-
return
|
|
15610
|
+
return path13.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
|
|
15445
15611
|
}
|
|
15446
15612
|
getTypePath(id, subsystemId, group) {
|
|
15447
15613
|
const index = this.scanAll();
|
|
@@ -15472,7 +15638,7 @@ var init_specs2 = __esm({
|
|
|
15472
15638
|
const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
|
|
15473
15639
|
if (groupPath) {
|
|
15474
15640
|
const localId2 = id.split("::").pop();
|
|
15475
|
-
return
|
|
15641
|
+
return path13.join(path13.dirname(groupPath), `${localId2}.yaml`);
|
|
15476
15642
|
}
|
|
15477
15643
|
}
|
|
15478
15644
|
let localId = id;
|
|
@@ -15485,12 +15651,12 @@ var init_specs2 = __esm({
|
|
|
15485
15651
|
}
|
|
15486
15652
|
if (subsystemId) {
|
|
15487
15653
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15488
|
-
const subDir =
|
|
15654
|
+
const subDir = path13.dirname(subPath);
|
|
15489
15655
|
if (subPath.endsWith(".index.yaml")) {
|
|
15490
|
-
return
|
|
15656
|
+
return path13.join(subDir, "types", `${localId}.yaml`);
|
|
15491
15657
|
}
|
|
15492
15658
|
}
|
|
15493
|
-
return
|
|
15659
|
+
return path13.join(this.paths.specsTypesDir(), `${localId}.yaml`);
|
|
15494
15660
|
}
|
|
15495
15661
|
getGroupPath(id, subsystemId) {
|
|
15496
15662
|
const index = this.scanAll();
|
|
@@ -15524,12 +15690,12 @@ var init_specs2 = __esm({
|
|
|
15524
15690
|
}
|
|
15525
15691
|
if (subsystemId) {
|
|
15526
15692
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15527
|
-
const subDir =
|
|
15693
|
+
const subDir = path13.dirname(subPath);
|
|
15528
15694
|
if (subPath.endsWith(".index.yaml")) {
|
|
15529
|
-
return
|
|
15695
|
+
return path13.join(subDir, "types", localId, ".index.yaml");
|
|
15530
15696
|
}
|
|
15531
15697
|
}
|
|
15532
|
-
return
|
|
15698
|
+
return path13.join(this.paths.specsTypesDir(), localId, ".index.yaml");
|
|
15533
15699
|
}
|
|
15534
15700
|
// -------------------------------------------------------------------------
|
|
15535
15701
|
// Level 0: System
|
|
@@ -15552,7 +15718,7 @@ var init_specs2 = __esm({
|
|
|
15552
15718
|
}
|
|
15553
15719
|
saveSystemSpec(spec) {
|
|
15554
15720
|
const p = this.paths.specsSystem();
|
|
15555
|
-
ensureDir(
|
|
15721
|
+
ensureDir(path13.dirname(p));
|
|
15556
15722
|
writeYamlFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
|
|
15557
15723
|
invalidateSpecCache();
|
|
15558
15724
|
}
|
|
@@ -15670,7 +15836,7 @@ var init_specs2 = __esm({
|
|
|
15670
15836
|
}
|
|
15671
15837
|
saveSubsystemSpec(spec) {
|
|
15672
15838
|
const p = this.getSubsystemPath(spec.id);
|
|
15673
|
-
ensureDir(
|
|
15839
|
+
ensureDir(path13.dirname(p));
|
|
15674
15840
|
const { prefix } = splitNamespace(spec.id);
|
|
15675
15841
|
if (!prefix && spec.projectPath && spec.projectPath.trim() !== "") {
|
|
15676
15842
|
assertContainedProjectPath(this.rootDir, spec.projectPath);
|
|
@@ -15703,7 +15869,7 @@ var init_specs2 = __esm({
|
|
|
15703
15869
|
const p = this.getSubsystemPath(id);
|
|
15704
15870
|
if (!fs9.existsSync(p)) return false;
|
|
15705
15871
|
fs9.unlinkSync(p);
|
|
15706
|
-
cleanEmptyDirs(p,
|
|
15872
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
15707
15873
|
invalidateSpecCache();
|
|
15708
15874
|
return true;
|
|
15709
15875
|
}
|
|
@@ -15736,7 +15902,7 @@ var init_specs2 = __esm({
|
|
|
15736
15902
|
saveComponentSpec(spec, opts) {
|
|
15737
15903
|
const notices = [];
|
|
15738
15904
|
const p = this.getComponentPath(spec.id, spec.subsystem);
|
|
15739
|
-
ensureDir(
|
|
15905
|
+
ensureDir(path13.dirname(p));
|
|
15740
15906
|
const specToWrite = this.prepareComponentForWrite(spec);
|
|
15741
15907
|
const existing = this.loadComponentSpec(spec.id);
|
|
15742
15908
|
if (existing) {
|
|
@@ -15753,7 +15919,7 @@ var init_specs2 = __esm({
|
|
|
15753
15919
|
const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
|
|
15754
15920
|
if (subsystemChanged && !p.endsWith(".index.yaml")) {
|
|
15755
15921
|
notices.push(
|
|
15756
|
-
`component "${spec.id}" already exists at ${
|
|
15922
|
+
`component "${spec.id}" already exists at ${path13.relative(this.rootDir, p)} \u2014 the flat layout re-saves in place and never relocates the file. The subsystem field is now "${spec.subsystem}" (was "${existing.subsystem}").`
|
|
15757
15923
|
);
|
|
15758
15924
|
}
|
|
15759
15925
|
specToWrite.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15766,7 +15932,7 @@ var init_specs2 = __esm({
|
|
|
15766
15932
|
const p = this.getComponentPath(id);
|
|
15767
15933
|
if (!fs9.existsSync(p)) return false;
|
|
15768
15934
|
fs9.unlinkSync(p);
|
|
15769
|
-
cleanEmptyDirs(p,
|
|
15935
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
15770
15936
|
invalidateSpecCache();
|
|
15771
15937
|
return true;
|
|
15772
15938
|
}
|
|
@@ -15779,12 +15945,12 @@ var init_specs2 = __esm({
|
|
|
15779
15945
|
if (owner) {
|
|
15780
15946
|
const ownerPath = index.paths.component[owner.id];
|
|
15781
15947
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
15782
|
-
const ownerSubDir =
|
|
15783
|
-
return
|
|
15948
|
+
const ownerSubDir = path13.dirname(this.getSubsystemPath(owner.subsystem));
|
|
15949
|
+
return path13.join(ownerSubDir, owner.id, comp.id);
|
|
15784
15950
|
}
|
|
15785
15951
|
}
|
|
15786
|
-
const subDir =
|
|
15787
|
-
return
|
|
15952
|
+
const subDir = path13.dirname(this.getSubsystemPath(comp.subsystem));
|
|
15953
|
+
return path13.join(subDir, comp.id);
|
|
15788
15954
|
}
|
|
15789
15955
|
/**
|
|
15790
15956
|
* Move component folders so the physical tree mirrors ownership: each owned
|
|
@@ -15800,7 +15966,7 @@ var init_specs2 = __esm({
|
|
|
15800
15966
|
if (!currentPath) continue;
|
|
15801
15967
|
const desiredDir = this.desiredComponentDir(comp, index);
|
|
15802
15968
|
if (!desiredDir) continue;
|
|
15803
|
-
if (moveComponentFolder(
|
|
15969
|
+
if (moveComponentFolder(path13.dirname(currentPath), desiredDir)) moved.push(comp.id);
|
|
15804
15970
|
}
|
|
15805
15971
|
if (moved.length) invalidateSpecCache();
|
|
15806
15972
|
return moved;
|
|
@@ -15834,12 +16000,12 @@ var init_specs2 = __esm({
|
|
|
15834
16000
|
saveInterfaceSpec(spec, opts) {
|
|
15835
16001
|
const notices = [];
|
|
15836
16002
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
15837
|
-
ensureDir(
|
|
16003
|
+
ensureDir(path13.dirname(p));
|
|
15838
16004
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
15839
16005
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
15840
16006
|
if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
|
|
15841
16007
|
notices.push(
|
|
15842
|
-
`interface "${spec.id}" already exists at ${
|
|
16008
|
+
`interface "${spec.id}" already exists at ${path13.relative(this.rootDir, p)} \u2014 re-saving updates the component binding field in place (now "${spec.component}", was "${existing.component}") and never moves the file.`
|
|
15843
16009
|
);
|
|
15844
16010
|
}
|
|
15845
16011
|
if (existing) {
|
|
@@ -15864,7 +16030,7 @@ var init_specs2 = __esm({
|
|
|
15864
16030
|
const p = this.getInterfacePath(id);
|
|
15865
16031
|
if (!fs9.existsSync(p)) return false;
|
|
15866
16032
|
fs9.unlinkSync(p);
|
|
15867
|
-
cleanEmptyDirs(p,
|
|
16033
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
15868
16034
|
invalidateSpecCache();
|
|
15869
16035
|
return true;
|
|
15870
16036
|
}
|
|
@@ -15897,12 +16063,12 @@ var init_specs2 = __esm({
|
|
|
15897
16063
|
saveImplementationSpec(spec, opts) {
|
|
15898
16064
|
const notices = [];
|
|
15899
16065
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
15900
|
-
ensureDir(
|
|
16066
|
+
ensureDir(path13.dirname(p));
|
|
15901
16067
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
15902
16068
|
const existing = this.loadImplementationSpec(spec.id);
|
|
15903
16069
|
if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
|
|
15904
16070
|
notices.push(
|
|
15905
|
-
`implementation "${spec.id}" already exists at ${
|
|
16071
|
+
`implementation "${spec.id}" already exists at ${path13.relative(this.rootDir, p)} \u2014 re-saving updates the contract binding field in place (now "${spec.contract}", was "${existing.contract}") and never moves the file.`
|
|
15906
16072
|
);
|
|
15907
16073
|
}
|
|
15908
16074
|
if (existing) {
|
|
@@ -15920,7 +16086,7 @@ var init_specs2 = __esm({
|
|
|
15920
16086
|
const p = this.getImplementationPath(id);
|
|
15921
16087
|
if (!fs9.existsSync(p)) return false;
|
|
15922
16088
|
fs9.unlinkSync(p);
|
|
15923
|
-
cleanEmptyDirs(p,
|
|
16089
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
15924
16090
|
invalidateSpecCache();
|
|
15925
16091
|
return true;
|
|
15926
16092
|
}
|
|
@@ -15945,11 +16111,11 @@ var init_specs2 = __esm({
|
|
|
15945
16111
|
const existing = this.loadTypeSpec(spec.id);
|
|
15946
16112
|
const group = spec.group || (existing ? existing.group : void 0);
|
|
15947
16113
|
const p = this.getTypePath(spec.id, spec.subsystem, group);
|
|
15948
|
-
ensureDir(
|
|
16114
|
+
ensureDir(path13.dirname(p));
|
|
15949
16115
|
const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
|
|
15950
16116
|
if (subsystemChanged) {
|
|
15951
16117
|
notices.push(
|
|
15952
|
-
`type "${spec.id}" already exists at ${
|
|
16118
|
+
`type "${spec.id}" already exists at ${path13.relative(this.rootDir, p)} \u2014 re-saving updates fields in place and never relocates the file. The subsystem field is now "${spec.subsystem ?? "(none)"}" (was "${existing.subsystem ?? "(none)"}").`
|
|
15953
16119
|
);
|
|
15954
16120
|
}
|
|
15955
16121
|
if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
|
|
@@ -15974,7 +16140,7 @@ var init_specs2 = __esm({
|
|
|
15974
16140
|
const p = this.getTypePath(id, spec?.subsystem, spec?.group);
|
|
15975
16141
|
if (!fs9.existsSync(p)) return false;
|
|
15976
16142
|
fs9.unlinkSync(p);
|
|
15977
|
-
cleanEmptyDirs(p,
|
|
16143
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
15978
16144
|
invalidateSpecCache();
|
|
15979
16145
|
return true;
|
|
15980
16146
|
}
|
|
@@ -15989,7 +16155,7 @@ var init_specs2 = __esm({
|
|
|
15989
16155
|
}
|
|
15990
16156
|
saveGroupSpec(spec) {
|
|
15991
16157
|
const p = this.getGroupPath(spec.id);
|
|
15992
|
-
ensureDir(
|
|
16158
|
+
ensureDir(path13.dirname(p));
|
|
15993
16159
|
const specToWrite = this.prepareGroupForWrite(spec);
|
|
15994
16160
|
const existing = this.loadGroupSpec(spec.id);
|
|
15995
16161
|
if (existing) {
|
|
@@ -16003,7 +16169,7 @@ var init_specs2 = __esm({
|
|
|
16003
16169
|
const p = this.getGroupPath(id);
|
|
16004
16170
|
if (!fs9.existsSync(p)) return false;
|
|
16005
16171
|
fs9.unlinkSync(p);
|
|
16006
|
-
cleanEmptyDirs(p,
|
|
16172
|
+
cleanEmptyDirs(p, path13.resolve(this.paths.specsDir()));
|
|
16007
16173
|
invalidateSpecCache();
|
|
16008
16174
|
return true;
|
|
16009
16175
|
}
|
|
@@ -16088,11 +16254,11 @@ var init_specs2 = __esm({
|
|
|
16088
16254
|
const files = /* @__PURE__ */ new Set();
|
|
16089
16255
|
const sysPath = this.paths.specsSystem();
|
|
16090
16256
|
if (pathExists(sysPath)) {
|
|
16091
|
-
files.add(
|
|
16257
|
+
files.add(path13.resolve(sysPath));
|
|
16092
16258
|
}
|
|
16093
16259
|
for (const group of Object.values(index.paths)) {
|
|
16094
16260
|
for (const file of Object.values(group)) {
|
|
16095
|
-
files.add(
|
|
16261
|
+
files.add(path13.resolve(file));
|
|
16096
16262
|
}
|
|
16097
16263
|
}
|
|
16098
16264
|
for (const file of files) {
|
|
@@ -16111,20 +16277,20 @@ var init_specs2 = __esm({
|
|
|
16111
16277
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
16112
16278
|
const legacy = [];
|
|
16113
16279
|
for (const f of files) {
|
|
16114
|
-
const base =
|
|
16115
|
-
const dir =
|
|
16280
|
+
const base = path13.basename(f);
|
|
16281
|
+
const dir = path13.dirname(f);
|
|
16116
16282
|
if (base === "system.yaml") {
|
|
16117
|
-
legacy.push({ path: f, expected:
|
|
16283
|
+
legacy.push({ path: f, expected: path13.join(dir, ".index.yaml") });
|
|
16118
16284
|
} else if (base === "subsystem.yaml") {
|
|
16119
|
-
legacy.push({ path: f, expected:
|
|
16285
|
+
legacy.push({ path: f, expected: path13.join(dir, ".index.yaml") });
|
|
16120
16286
|
} else if (base === "component.yaml") {
|
|
16121
|
-
legacy.push({ path: f, expected:
|
|
16287
|
+
legacy.push({ path: f, expected: path13.join(dir, ".index.yaml") });
|
|
16122
16288
|
} else if (base === "group.yaml") {
|
|
16123
|
-
legacy.push({ path: f, expected:
|
|
16289
|
+
legacy.push({ path: f, expected: path13.join(dir, ".index.yaml") });
|
|
16124
16290
|
} else if (base === "interface.yaml") {
|
|
16125
|
-
legacy.push({ path: f, expected:
|
|
16291
|
+
legacy.push({ path: f, expected: path13.join(dir, ".interface.yaml") });
|
|
16126
16292
|
} else if (base === "implementation.yaml") {
|
|
16127
|
-
legacy.push({ path: f, expected:
|
|
16293
|
+
legacy.push({ path: f, expected: path13.join(dir, ".implementation.yaml") });
|
|
16128
16294
|
}
|
|
16129
16295
|
}
|
|
16130
16296
|
return legacy;
|
|
@@ -16523,11 +16689,13 @@ var init_specs2 = __esm({
|
|
|
16523
16689
|
// src/core/agent_resolver.ts
|
|
16524
16690
|
var agent_resolver_exports = {};
|
|
16525
16691
|
__export(agent_resolver_exports, {
|
|
16692
|
+
UnknownAgentError: () => UnknownAgentError,
|
|
16693
|
+
composeAgentBrief: () => composeAgentBrief,
|
|
16526
16694
|
resolveAgentTopology: () => resolveAgentTopology
|
|
16527
16695
|
});
|
|
16528
16696
|
function listFilesRecursiveSafe(dirPath, ext) {
|
|
16529
16697
|
if (!fs10.existsSync(dirPath)) return [];
|
|
16530
|
-
const nameLower =
|
|
16698
|
+
const nameLower = path14.basename(dirPath).toLowerCase();
|
|
16531
16699
|
const IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
16532
16700
|
"node_modules",
|
|
16533
16701
|
"target",
|
|
@@ -16545,7 +16713,7 @@ function listFilesRecursiveSafe(dirPath, ext) {
|
|
|
16545
16713
|
const entries = fs10.readdirSync(dirPath, { withFileTypes: true });
|
|
16546
16714
|
const files = [];
|
|
16547
16715
|
for (const entry of entries) {
|
|
16548
|
-
const fullPath =
|
|
16716
|
+
const fullPath = path14.join(dirPath, entry.name);
|
|
16549
16717
|
if (entry.isDirectory()) {
|
|
16550
16718
|
files.push(...listFilesRecursiveSafe(fullPath, ext));
|
|
16551
16719
|
} else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
@@ -16558,8 +16726,8 @@ function getProjectFiles(projectDir) {
|
|
|
16558
16726
|
let files = projectFilesCache.get(projectDir);
|
|
16559
16727
|
if (!files) {
|
|
16560
16728
|
files = [];
|
|
16561
|
-
const srcDir =
|
|
16562
|
-
const legacySrcDir =
|
|
16729
|
+
const srcDir = path14.join(projectDir, "src");
|
|
16730
|
+
const legacySrcDir = path14.join(projectDir, "legacy-src");
|
|
16563
16731
|
let searchDir = projectDir;
|
|
16564
16732
|
if (pathExists(srcDir)) {
|
|
16565
16733
|
searchDir = srcDir;
|
|
@@ -16574,7 +16742,7 @@ function getProjectFiles(projectDir) {
|
|
|
16574
16742
|
files.push(...listFilesRecursiveSafe(searchDir, ext));
|
|
16575
16743
|
}
|
|
16576
16744
|
const rootDir = getProjectRoot();
|
|
16577
|
-
files = files.map((f) =>
|
|
16745
|
+
files = files.map((f) => path14.relative(rootDir, f).replace(/\\/g, "/"));
|
|
16578
16746
|
projectFilesCache.set(projectDir, files);
|
|
16579
16747
|
}
|
|
16580
16748
|
return files;
|
|
@@ -16599,8 +16767,8 @@ function inferSourcePathForComponent(comp, subsystems) {
|
|
|
16599
16767
|
let bestFile = null;
|
|
16600
16768
|
let bestScore = -1;
|
|
16601
16769
|
for (const f of files) {
|
|
16602
|
-
const ext =
|
|
16603
|
-
const base =
|
|
16770
|
+
const ext = path14.extname(f);
|
|
16771
|
+
const base = path14.basename(f, ext).toLowerCase();
|
|
16604
16772
|
if (candidates.has(base)) {
|
|
16605
16773
|
let score = 0;
|
|
16606
16774
|
const normalizedPath = f.toLowerCase();
|
|
@@ -16696,7 +16864,7 @@ function resolveAgentTopology() {
|
|
|
16696
16864
|
});
|
|
16697
16865
|
for (const sub of subsystems) {
|
|
16698
16866
|
if (sub.projectPath) {
|
|
16699
|
-
const mountSpecPath =
|
|
16867
|
+
const mountSpecPath = path14.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
|
|
16700
16868
|
agents.push({
|
|
16701
16869
|
id: `${sub.id}-owner`,
|
|
16702
16870
|
name: `${sub.name} (chained subproject)`,
|
|
@@ -16718,9 +16886,9 @@ function resolveAgentTopology() {
|
|
|
16718
16886
|
}
|
|
16719
16887
|
const subComponents = components.filter((c) => c.subsystem === sub.id);
|
|
16720
16888
|
const ownedPaths = [];
|
|
16721
|
-
ownedPaths.push(
|
|
16889
|
+
ownedPaths.push(path14.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
|
|
16722
16890
|
for (const c of subComponents) {
|
|
16723
|
-
ownedPaths.push(
|
|
16891
|
+
ownedPaths.push(path14.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
|
|
16724
16892
|
}
|
|
16725
16893
|
if (!config.rules.generateComponentImplementers) {
|
|
16726
16894
|
for (const comp of subComponents) {
|
|
@@ -16793,10 +16961,10 @@ function resolveAgentTopology() {
|
|
|
16793
16961
|
}
|
|
16794
16962
|
const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
|
|
16795
16963
|
const readPaths = [
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
...compInterfaces.map((i) =>
|
|
16799
|
-
...compImpls.map((impl) =>
|
|
16964
|
+
path14.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
|
|
16965
|
+
path14.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
|
|
16966
|
+
...compInterfaces.map((i) => path14.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
|
|
16967
|
+
...compImpls.map((impl) => path14.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
|
|
16800
16968
|
];
|
|
16801
16969
|
agents.push({
|
|
16802
16970
|
id: `${comp.id}-implementer`,
|
|
@@ -16840,17 +17008,64 @@ function resolveAgentTopology() {
|
|
|
16840
17008
|
}
|
|
16841
17009
|
return agents;
|
|
16842
17010
|
}
|
|
16843
|
-
|
|
17011
|
+
function composeAgentBrief(agentId) {
|
|
17012
|
+
const records = resolveAgentTopology();
|
|
17013
|
+
const record = records.find((r) => r.id === agentId);
|
|
17014
|
+
if (!record) {
|
|
17015
|
+
throw new UnknownAgentError(agentId, records.map((r) => r.id));
|
|
17016
|
+
}
|
|
17017
|
+
const template = loadTemplate(record.template, loadProjectConfig().globalTemplatesDir);
|
|
17018
|
+
let instructions = renderTemplateInstructions(template, {
|
|
17019
|
+
agentId: record.id,
|
|
17020
|
+
agentName: record.name,
|
|
17021
|
+
agentDescription: record.description,
|
|
17022
|
+
ownedPaths: record.ownedPaths.join("\n"),
|
|
17023
|
+
tags: record.tags.join(", "),
|
|
17024
|
+
renderContext: "root",
|
|
17025
|
+
contextNote: "",
|
|
17026
|
+
domainPath: ".",
|
|
17027
|
+
domainName: "",
|
|
17028
|
+
variantGuidance: record.variantGuidance ?? ""
|
|
17029
|
+
});
|
|
17030
|
+
const guidance = loadAgentOverride(agentId);
|
|
17031
|
+
if (guidance !== null) {
|
|
17032
|
+
instructions = `${instructions.trimEnd()}
|
|
17033
|
+
|
|
17034
|
+
## Project guidance
|
|
17035
|
+
|
|
17036
|
+
${guidance.trim()}
|
|
17037
|
+
`;
|
|
17038
|
+
}
|
|
17039
|
+
return {
|
|
17040
|
+
agentId: record.id,
|
|
17041
|
+
name: record.name,
|
|
17042
|
+
template: record.template,
|
|
17043
|
+
domainRoot: record.domainRoot,
|
|
17044
|
+
ownedPaths: record.ownedPaths,
|
|
17045
|
+
readPaths: record.readPaths,
|
|
17046
|
+
instructions,
|
|
17047
|
+
variantGuidance: record.variantGuidance || void 0
|
|
17048
|
+
};
|
|
17049
|
+
}
|
|
17050
|
+
var path14, fs10, projectFilesCache, UnknownAgentError;
|
|
16844
17051
|
var init_agent_resolver = __esm({
|
|
16845
17052
|
"src/core/agent_resolver.ts"() {
|
|
16846
17053
|
"use strict";
|
|
16847
|
-
|
|
17054
|
+
path14 = __toESM(require("path"));
|
|
16848
17055
|
fs10 = __toESM(require("fs"));
|
|
16849
17056
|
init_loader();
|
|
16850
17057
|
init_fs();
|
|
17058
|
+
init_errors();
|
|
17059
|
+
init_templates();
|
|
16851
17060
|
init_specs2();
|
|
16852
17061
|
init_variants();
|
|
16853
17062
|
projectFilesCache = /* @__PURE__ */ new Map();
|
|
17063
|
+
UnknownAgentError = class extends WaironError {
|
|
17064
|
+
constructor(agentId, knownIds) {
|
|
17065
|
+
super(`Unknown agent id: "${agentId}". Known agent ids: ${knownIds.join(", ")}`);
|
|
17066
|
+
this.name = "UnknownAgentError";
|
|
17067
|
+
}
|
|
17068
|
+
};
|
|
16854
17069
|
}
|
|
16855
17070
|
});
|
|
16856
17071
|
|
|
@@ -16868,12 +17083,12 @@ __export(loader_exports, {
|
|
|
16868
17083
|
saveTopologyConfig: () => saveTopologyConfig
|
|
16869
17084
|
});
|
|
16870
17085
|
function aiPathsAt(rootDir) {
|
|
16871
|
-
const resolvedRoot =
|
|
17086
|
+
const resolvedRoot = path15.resolve(rootDir);
|
|
16872
17087
|
const aiDirAt = (...segments) => {
|
|
16873
|
-
const waiPath =
|
|
16874
|
-
const waironPath =
|
|
17088
|
+
const waiPath = path15.join(resolvedRoot, ".wai");
|
|
17089
|
+
const waironPath = path15.join(resolvedRoot, ".wairon");
|
|
16875
17090
|
const base = !fs11.existsSync(waiPath) && fs11.existsSync(waironPath) ? waironPath : waiPath;
|
|
16876
|
-
return
|
|
17091
|
+
return path15.join(base, ...segments);
|
|
16877
17092
|
};
|
|
16878
17093
|
const specsDir = () => {
|
|
16879
17094
|
try {
|
|
@@ -16881,7 +17096,7 @@ function aiPathsAt(rootDir) {
|
|
|
16881
17096
|
if (pathExists(projConfig)) {
|
|
16882
17097
|
const raw = readYamlFile(projConfig);
|
|
16883
17098
|
if (raw && raw.paths && raw.paths.specsDir) {
|
|
16884
|
-
return
|
|
17099
|
+
return path15.resolve(resolvedRoot, raw.paths.specsDir);
|
|
16885
17100
|
}
|
|
16886
17101
|
}
|
|
16887
17102
|
} catch {
|
|
@@ -16902,12 +17117,12 @@ function aiPathsAt(rootDir) {
|
|
|
16902
17117
|
contextDomainsMd: () => aiDirAt("context", "domains.md"),
|
|
16903
17118
|
contextWaironGuideMd: () => aiDirAt("context", "wairon-guide.md"),
|
|
16904
17119
|
specsDir,
|
|
16905
|
-
specsSystem: () =>
|
|
16906
|
-
specsSubsystemsDir: () =>
|
|
16907
|
-
specsComponentsDir: () =>
|
|
16908
|
-
specsInterfacesDir: () =>
|
|
16909
|
-
specsImplementationsDir: () =>
|
|
16910
|
-
specsTypesDir: () =>
|
|
17120
|
+
specsSystem: () => path15.join(specsDir(), ".index.yaml"),
|
|
17121
|
+
specsSubsystemsDir: () => path15.join(specsDir(), "subsystems"),
|
|
17122
|
+
specsComponentsDir: () => path15.join(specsDir(), "components"),
|
|
17123
|
+
specsInterfacesDir: () => path15.join(specsDir(), "interfaces"),
|
|
17124
|
+
specsImplementationsDir: () => path15.join(specsDir(), "implementations"),
|
|
17125
|
+
specsTypesDir: () => path15.join(specsDir(), "types")
|
|
16911
17126
|
};
|
|
16912
17127
|
}
|
|
16913
17128
|
function isProjectInitialized() {
|
|
@@ -16950,12 +17165,12 @@ function loadTopologyConfig() {
|
|
|
16950
17165
|
function saveTopologyConfig(config) {
|
|
16951
17166
|
writeYamlFile(AI_PATHS.topologyConfig(), config);
|
|
16952
17167
|
}
|
|
16953
|
-
var fs11,
|
|
17168
|
+
var fs11, path15, AI_PATHS;
|
|
16954
17169
|
var init_loader = __esm({
|
|
16955
17170
|
"src/config/loader.ts"() {
|
|
16956
17171
|
"use strict";
|
|
16957
17172
|
fs11 = __toESM(require("fs"));
|
|
16958
|
-
|
|
17173
|
+
path15 = __toESM(require("path"));
|
|
16959
17174
|
init_fs();
|
|
16960
17175
|
init_yaml();
|
|
16961
17176
|
init_errors();
|
|
@@ -16995,7 +17210,7 @@ __export(domains_exports, {
|
|
|
16995
17210
|
resolveDomains: () => resolveDomains
|
|
16996
17211
|
});
|
|
16997
17212
|
function rel(p) {
|
|
16998
|
-
return
|
|
17213
|
+
return path17.relative(process.cwd(), p).replace(/\\/g, "/");
|
|
16999
17214
|
}
|
|
17000
17215
|
function deriveSubsystemDomains() {
|
|
17001
17216
|
const subsystems = loadSubsystemSpecs();
|
|
@@ -17045,11 +17260,11 @@ function removeFreeStandingDomain(id) {
|
|
|
17045
17260
|
config.domains.splice(idx, 1);
|
|
17046
17261
|
saveTopologyConfig(config);
|
|
17047
17262
|
}
|
|
17048
|
-
var
|
|
17263
|
+
var path17;
|
|
17049
17264
|
var init_domains = __esm({
|
|
17050
17265
|
"src/core/domains.ts"() {
|
|
17051
17266
|
"use strict";
|
|
17052
|
-
|
|
17267
|
+
path17 = __toESM(require("path"));
|
|
17053
17268
|
init_loader();
|
|
17054
17269
|
init_specs2();
|
|
17055
17270
|
init_errors();
|
|
@@ -17062,8 +17277,10 @@ __export(src_exports, {
|
|
|
17062
17277
|
AI_PATHS: () => AI_PATHS,
|
|
17063
17278
|
ARCHITECT_AGENT_ID: () => ARCHITECT_AGENT_ID,
|
|
17064
17279
|
ARCHITECT_TEMPLATE_ID: () => ARCHITECT_TEMPLATE_ID,
|
|
17280
|
+
AgentBriefSchema: () => AgentBriefSchema,
|
|
17065
17281
|
AgentRecordSchema: () => AgentRecordSchema,
|
|
17066
17282
|
AgentStatusSchema: () => AgentStatusSchema,
|
|
17283
|
+
AgentTemplateSchema: () => AgentTemplateSchema,
|
|
17067
17284
|
AssertionSelectorSchema: () => AssertionSelectorSchema,
|
|
17068
17285
|
BACKEND_COMMANDS: () => BACKEND_COMMANDS,
|
|
17069
17286
|
BUILTIN_PROFILES: () => BUILTIN_PROFILES,
|
|
@@ -17175,6 +17392,7 @@ __export(src_exports, {
|
|
|
17175
17392
|
TypeMethodSchema: () => TypeMethodSchema,
|
|
17176
17393
|
TypeSpecSchema: () => TypeSpecSchema,
|
|
17177
17394
|
UNVERSIONED: () => UNVERSIONED,
|
|
17395
|
+
UnknownAgentError: () => UnknownAgentError,
|
|
17178
17396
|
VariantDefSchema: () => VariantDefSchema,
|
|
17179
17397
|
WAIRON_MANAGED_BANNER: () => WAIRON_MANAGED_BANNER,
|
|
17180
17398
|
WAIRON_MANAGED_MARKER: () => WAIRON_MANAGED_MARKER,
|
|
@@ -17200,6 +17418,7 @@ __export(src_exports, {
|
|
|
17200
17418
|
checkSkillFreshness: () => checkSkillFreshness,
|
|
17201
17419
|
clearLoaderIssues: () => clearLoaderIssues,
|
|
17202
17420
|
collectPromotableSpecs: () => collectPromotableSpecs,
|
|
17421
|
+
composeAgentBrief: () => composeAgentBrief,
|
|
17203
17422
|
composeRuleSequence: () => composeRuleSequence,
|
|
17204
17423
|
composeVariantGuidance: () => composeVariantGuidance,
|
|
17205
17424
|
computeGateStateId: () => computeGateStateId,
|
|
@@ -17288,6 +17507,7 @@ __export(src_exports, {
|
|
|
17288
17507
|
listSkillResources: () => listSkillResources,
|
|
17289
17508
|
listSnapshots: () => listSnapshots,
|
|
17290
17509
|
listTemplateIds: () => listTemplateIds,
|
|
17510
|
+
loadAgentOverride: () => loadAgentOverride,
|
|
17291
17511
|
loadComponentSpec: () => loadComponentSpec,
|
|
17292
17512
|
loadComponentSpecs: () => loadComponentSpecs,
|
|
17293
17513
|
loadExtensionPacks: () => loadExtensionPacks,
|
|
@@ -17351,6 +17571,7 @@ __export(src_exports, {
|
|
|
17351
17571
|
resolveAgentTopology: () => resolveAgentTopology,
|
|
17352
17572
|
resolveChainingParent: () => resolveChainingParent,
|
|
17353
17573
|
resolveDomains: () => resolveDomains,
|
|
17574
|
+
resolveExpectedOutputPaths: () => resolveExpectedOutputPaths,
|
|
17354
17575
|
resolveInstalledPack: () => resolveInstalledPack,
|
|
17355
17576
|
resolvePackRef: () => resolvePackRef,
|
|
17356
17577
|
resolveSubprojectForNamespace: () => resolveSubprojectForNamespace,
|
|
@@ -17405,7 +17626,7 @@ init_loader();
|
|
|
17405
17626
|
|
|
17406
17627
|
// src/core/detection.ts
|
|
17407
17628
|
var fs12 = __toESM(require("fs"));
|
|
17408
|
-
var
|
|
17629
|
+
var path16 = __toESM(require("path"));
|
|
17409
17630
|
init_defaults();
|
|
17410
17631
|
var PACKAGE_MARKERS = [
|
|
17411
17632
|
"package.json",
|
|
@@ -17479,7 +17700,7 @@ function parseGitmodules(filePath) {
|
|
|
17479
17700
|
return entries;
|
|
17480
17701
|
}
|
|
17481
17702
|
function detectGitSubmodules(projectRoot) {
|
|
17482
|
-
const gitmodulesPath =
|
|
17703
|
+
const gitmodulesPath = path16.join(projectRoot, ".gitmodules");
|
|
17483
17704
|
if (!fs12.existsSync(gitmodulesPath)) return [];
|
|
17484
17705
|
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
17485
17706
|
suggestedId: pathToId(entry.path),
|
|
@@ -17505,10 +17726,10 @@ function walkForGit(projectRoot, currentDir, depth, results) {
|
|
|
17505
17726
|
for (const entry of entries) {
|
|
17506
17727
|
if (!entry.isDirectory()) continue;
|
|
17507
17728
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
17508
|
-
const fullPath =
|
|
17509
|
-
const relPath = normalizePath3(
|
|
17729
|
+
const fullPath = path16.join(currentDir, entry.name);
|
|
17730
|
+
const relPath = normalizePath3(path16.relative(projectRoot, fullPath));
|
|
17510
17731
|
if (relPath === "" || relPath === ".") continue;
|
|
17511
|
-
const gitPath =
|
|
17732
|
+
const gitPath = path16.join(fullPath, ".git");
|
|
17512
17733
|
if (fs12.existsSync(gitPath)) {
|
|
17513
17734
|
results.push({
|
|
17514
17735
|
suggestedId: pathToId(relPath),
|
|
@@ -17538,10 +17759,10 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
17538
17759
|
for (const entry of entries) {
|
|
17539
17760
|
if (!entry.isDirectory()) continue;
|
|
17540
17761
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
17541
|
-
const fullPath =
|
|
17542
|
-
const relPath = normalizePath3(
|
|
17762
|
+
const fullPath = path16.join(currentDir, entry.name);
|
|
17763
|
+
const relPath = normalizePath3(path16.relative(projectRoot, fullPath));
|
|
17543
17764
|
if (relPath === "" || relPath === ".") continue;
|
|
17544
|
-
const hasMarker = PACKAGE_MARKERS.some((m) => fs12.existsSync(
|
|
17765
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs12.existsSync(path16.join(fullPath, m)));
|
|
17545
17766
|
if (hasMarker) {
|
|
17546
17767
|
results.push({
|
|
17547
17768
|
suggestedId: pathToId(relPath),
|
|
@@ -17555,7 +17776,7 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
17555
17776
|
}
|
|
17556
17777
|
}
|
|
17557
17778
|
function pathToId(relPath) {
|
|
17558
|
-
const basename9 =
|
|
17779
|
+
const basename9 = path16.basename(relPath);
|
|
17559
17780
|
return basename9.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
17560
17781
|
}
|
|
17561
17782
|
function pathToName(relPath) {
|
|
@@ -17568,58 +17789,7 @@ function normalizePath3(p) {
|
|
|
17568
17789
|
|
|
17569
17790
|
// src/core/index.ts
|
|
17570
17791
|
init_domains();
|
|
17571
|
-
|
|
17572
|
-
// src/core/templates.ts
|
|
17573
|
-
var path17 = __toESM(require("path"));
|
|
17574
|
-
init_fs();
|
|
17575
|
-
init_yaml();
|
|
17576
|
-
init_errors();
|
|
17577
|
-
init_template();
|
|
17578
|
-
init_loader();
|
|
17579
|
-
init_defaults();
|
|
17580
|
-
function builtinTemplatesDir() {
|
|
17581
|
-
return path17.resolve(__dirname, "..", "templates");
|
|
17582
|
-
}
|
|
17583
|
-
function loadTemplate(id, globalOverride) {
|
|
17584
|
-
const dirs = templateSearchDirs(globalOverride);
|
|
17585
|
-
for (const dir of dirs) {
|
|
17586
|
-
const filePath = path17.join(dir, `${id}.yaml`);
|
|
17587
|
-
if (pathExists(filePath)) {
|
|
17588
|
-
const raw = readYamlFile(filePath);
|
|
17589
|
-
return TemplateSchema.parse(raw);
|
|
17590
|
-
}
|
|
17591
|
-
}
|
|
17592
|
-
throw new TemplateNotFoundError(id);
|
|
17593
|
-
}
|
|
17594
|
-
function listTemplateIds(globalOverride) {
|
|
17595
|
-
const seen = /* @__PURE__ */ new Set();
|
|
17596
|
-
for (const dir of templateSearchDirs(globalOverride)) {
|
|
17597
|
-
for (const file of listFiles(dir, ".yaml")) {
|
|
17598
|
-
seen.add(path17.basename(file, ".yaml"));
|
|
17599
|
-
}
|
|
17600
|
-
}
|
|
17601
|
-
return Array.from(seen).sort();
|
|
17602
|
-
}
|
|
17603
|
-
function templateSearchDirs(globalOverride) {
|
|
17604
|
-
return [
|
|
17605
|
-
AI_PATHS.templatesDir(),
|
|
17606
|
-
// 1. project-local
|
|
17607
|
-
globalTemplatesDir(globalOverride),
|
|
17608
|
-
// 2. global user/org
|
|
17609
|
-
builtinTemplatesDir()
|
|
17610
|
-
// 3. built-in fallback
|
|
17611
|
-
];
|
|
17612
|
-
}
|
|
17613
|
-
function renderTemplateInstructions(template, vars) {
|
|
17614
|
-
return template.instructions.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
17615
|
-
return key in vars ? vars[key] : `{{${key}}}`;
|
|
17616
|
-
});
|
|
17617
|
-
}
|
|
17618
|
-
function parseTemplate(yamlContent) {
|
|
17619
|
-
return TemplateSchema.parse(parseYaml(yamlContent));
|
|
17620
|
-
}
|
|
17621
|
-
|
|
17622
|
-
// src/core/index.ts
|
|
17792
|
+
init_templates();
|
|
17623
17793
|
init_validation();
|
|
17624
17794
|
init_extensions();
|
|
17625
17795
|
init_variants();
|
|
@@ -17649,6 +17819,9 @@ function defaultProjectConfig(name, now) {
|
|
|
17649
17819
|
// a large tree/subproject with per-component implementers emits thousands
|
|
17650
17820
|
// of agents that every session then loads. Opt in with `true` on small trees.
|
|
17651
17821
|
generateComponentImplementers: false,
|
|
17822
|
+
// Files are the opt-in materialized view of the live briefs — off means
|
|
17823
|
+
// `generate` reconciles to zero agent files.
|
|
17824
|
+
materializeAgentFiles: false,
|
|
17652
17825
|
sddRuleSeverity: {}
|
|
17653
17826
|
},
|
|
17654
17827
|
paths: { specsDir: ".wai/specs" },
|
|
@@ -18118,7 +18291,17 @@ before you design anything**, and the others when their turn comes \u2014 they c
|
|
|
18118
18291
|
architecture standard, the narrative step reference, and the review checklist that
|
|
18119
18292
|
this briefing only points at:
|
|
18120
18293
|
|
|
18121
|
-
${skillPointers(skills)}
|
|
18294
|
+
${skillPointers(skills)}
|
|
18295
|
+
|
|
18296
|
+
## Delegating implementation
|
|
18297
|
+
|
|
18298
|
+
Delegate component work from a LIVE agent brief, never a generated file: discover
|
|
18299
|
+
agents via \`listAgents\` (or the \`wairon-agent://\` resources), fetch the target's
|
|
18300
|
+
brief with \`sdd_get_agent_brief\`, and spawn a scoped subagent from it \u2014 its
|
|
18301
|
+
\`instructions\` as prompt, its \`ownedPaths\` as write fence. Briefs are composed
|
|
18302
|
+
from the current spec tree on every call, so a re-lock never requires a session
|
|
18303
|
+
restart \u2014 fetch fresh per delegation. \`wairon-skill://sdd-delegate\` carries the
|
|
18304
|
+
full flow.`;
|
|
18122
18305
|
}
|
|
18123
18306
|
function buildServerInstructions() {
|
|
18124
18307
|
const profile = governingProfile();
|
|
@@ -18144,7 +18327,7 @@ ${block.text.trim()}`);
|
|
|
18144
18327
|
}
|
|
18145
18328
|
|
|
18146
18329
|
// src/core/skills.ts
|
|
18147
|
-
var SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement"];
|
|
18330
|
+
var SKILL_NAMES = ["sdd-architect", "sdd-narrative", "sdd-auditor", "sdd-implement", "sdd-delegate"];
|
|
18148
18331
|
function loadProjectExtensions2() {
|
|
18149
18332
|
return loadProjectExtensions();
|
|
18150
18333
|
}
|
|
@@ -18345,7 +18528,7 @@ If \`.wai/specs/\` exists, the wairon SDD workflow is active; otherwise ignore i
|
|
|
18345
18528
|
1. **Skills**: Use \`sdd-architect\` to design (and \`sdd-implement\`, \`sdd-narrative\`, \`sdd-auditor\`). Refer to project's local guide file for detailed constraints.
|
|
18346
18529
|
2. **MCP Tools Only**: Author/validate specs *only* via \`sdd_*\` tools (e.g. \`sdd_initialize_system\`, \`sdd_validate_tree\`).
|
|
18347
18530
|
3. **No CLI Exec**: Do not run the \`wairon\` CLI (human tool). Use MCP tools \`sdd_validate_tree\` and \`sdd_get_status\` instead.
|
|
18348
|
-
4. **
|
|
18531
|
+
4. **Delegation**: Delegate implementation via the \`sdd-delegate\` skill \u2014 live agent briefs (\`sdd_get_agent_brief\` MCP tool / \`wairon-agent://\` resource) are composed per call and always current; no session restart. Generated agent files are an optional materialized view of the same topology. User-owned per-agent guidance may live in \`.wai/agents/<agent-id>.md\` (folded into every brief; scaffold via \`wairon agent customize <id>\`).
|
|
18349
18532
|
5. **Design First**: Complete spec and pass \`sdd_validate_tree\` before writing code.
|
|
18350
18533
|
6. **Consistency**: Code must match L3 interfaces and L5 narratives exactly. If the spec is wrong, stop and update the spec.
|
|
18351
18534
|
7. **Subprojects & Namespacing**: If a subsystem uses \`projectPath\` delegation, target its specs using namespaced IDs (e.g. \`subsystem::component\`). Use leading \`::\` to target root (e.g. \`::shared::type\`) and \`super::\` to go up a level (e.g. \`super::sibling\`). wairon automatically resolves the path and strips the prefix on writes.`;
|
|
@@ -18492,6 +18675,7 @@ init_surfaces();
|
|
|
18492
18675
|
init_openapi();
|
|
18493
18676
|
init_packstore();
|
|
18494
18677
|
init_extensions();
|
|
18678
|
+
init_agent_resolver();
|
|
18495
18679
|
|
|
18496
18680
|
// src/exporters/base.ts
|
|
18497
18681
|
var WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
@@ -18596,6 +18780,7 @@ function yamlString(value) {
|
|
|
18596
18780
|
}
|
|
18597
18781
|
|
|
18598
18782
|
// src/exporters/generate.ts
|
|
18783
|
+
var path23 = __toESM(require("path"));
|
|
18599
18784
|
init_fs();
|
|
18600
18785
|
|
|
18601
18786
|
// src/exporters/registry.ts
|
|
@@ -18626,7 +18811,7 @@ function generateAgent(agent, projectConfig, options = {}) {
|
|
|
18626
18811
|
const projectRoot = options.projectRoot ?? getProjectRoot();
|
|
18627
18812
|
const template = loadTemplate(agent.template, projectConfig.globalTemplatesDir);
|
|
18628
18813
|
const rendered = `${WAIRON_MANAGED_BANNER}
|
|
18629
|
-
${
|
|
18814
|
+
${composeAgentBrief(agent.id).instructions}`;
|
|
18630
18815
|
const results = [];
|
|
18631
18816
|
for (const agentTarget of agent.targets) {
|
|
18632
18817
|
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
@@ -18655,19 +18840,17 @@ function generateAll(agents, projectConfig, options = {}) {
|
|
|
18655
18840
|
}
|
|
18656
18841
|
return pool.map((agent) => generateAgent(agent, projectConfig, options));
|
|
18657
18842
|
}
|
|
18658
|
-
function
|
|
18659
|
-
|
|
18660
|
-
|
|
18661
|
-
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
18669
|
-
variantGuidance: agent.variantGuidance ?? ""
|
|
18670
|
-
};
|
|
18843
|
+
function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProjectRoot()) {
|
|
18844
|
+
const expected = /* @__PURE__ */ new Set();
|
|
18845
|
+
for (const agent of agents) {
|
|
18846
|
+
for (const agentTarget of agent.targets) {
|
|
18847
|
+
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
18848
|
+
if (!targetConfig) continue;
|
|
18849
|
+
const ctx = { agent, projectRoot, target: targetConfig };
|
|
18850
|
+
expected.add(path23.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
18851
|
+
}
|
|
18852
|
+
}
|
|
18853
|
+
return expected;
|
|
18671
18854
|
}
|
|
18672
18855
|
function resolveTargetConfig(agentTarget, projectConfig) {
|
|
18673
18856
|
const targetType = typeof agentTarget === "string" ? agentTarget : agentTarget.type;
|
|
@@ -18741,8 +18924,10 @@ init_yaml();
|
|
|
18741
18924
|
AI_PATHS,
|
|
18742
18925
|
ARCHITECT_AGENT_ID,
|
|
18743
18926
|
ARCHITECT_TEMPLATE_ID,
|
|
18927
|
+
AgentBriefSchema,
|
|
18744
18928
|
AgentRecordSchema,
|
|
18745
18929
|
AgentStatusSchema,
|
|
18930
|
+
AgentTemplateSchema,
|
|
18746
18931
|
AssertionSelectorSchema,
|
|
18747
18932
|
BACKEND_COMMANDS,
|
|
18748
18933
|
BUILTIN_PROFILES,
|
|
@@ -18854,6 +19039,7 @@ init_yaml();
|
|
|
18854
19039
|
TypeMethodSchema,
|
|
18855
19040
|
TypeSpecSchema,
|
|
18856
19041
|
UNVERSIONED,
|
|
19042
|
+
UnknownAgentError,
|
|
18857
19043
|
VariantDefSchema,
|
|
18858
19044
|
WAIRON_MANAGED_BANNER,
|
|
18859
19045
|
WAIRON_MANAGED_MARKER,
|
|
@@ -18879,6 +19065,7 @@ init_yaml();
|
|
|
18879
19065
|
checkSkillFreshness,
|
|
18880
19066
|
clearLoaderIssues,
|
|
18881
19067
|
collectPromotableSpecs,
|
|
19068
|
+
composeAgentBrief,
|
|
18882
19069
|
composeRuleSequence,
|
|
18883
19070
|
composeVariantGuidance,
|
|
18884
19071
|
computeGateStateId,
|
|
@@ -18967,6 +19154,7 @@ init_yaml();
|
|
|
18967
19154
|
listSkillResources,
|
|
18968
19155
|
listSnapshots,
|
|
18969
19156
|
listTemplateIds,
|
|
19157
|
+
loadAgentOverride,
|
|
18970
19158
|
loadComponentSpec,
|
|
18971
19159
|
loadComponentSpecs,
|
|
18972
19160
|
loadExtensionPacks,
|
|
@@ -19030,6 +19218,7 @@ init_yaml();
|
|
|
19030
19218
|
resolveAgentTopology,
|
|
19031
19219
|
resolveChainingParent,
|
|
19032
19220
|
resolveDomains,
|
|
19221
|
+
resolveExpectedOutputPaths,
|
|
19033
19222
|
resolveInstalledPack,
|
|
19034
19223
|
resolvePackRef,
|
|
19035
19224
|
resolveSubprojectForNamespace,
|