@wairon/cli 5.1.1-dev.5 → 5.1.1-dev.7
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/README.md +1 -0
- package/dist/cli/index.js +1118 -880
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +488 -250
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1211,7 +1211,7 @@ var init_defaults = __esm({
|
|
|
1211
1211
|
copilot: ".github/prompts",
|
|
1212
1212
|
codex: ".codex/agents"
|
|
1213
1213
|
};
|
|
1214
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
1214
|
+
WAIRON_VERSION = "5.1.1-dev.7";
|
|
1215
1215
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1216
1216
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1217
1217
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -3361,9 +3361,9 @@ var init_surfaces = __esm({
|
|
|
3361
3361
|
// src/core/rules/namespace.ts
|
|
3362
3362
|
function isExternalNamespaceRef(ctx, ref) {
|
|
3363
3363
|
if (ref.startsWith("::") || ref.startsWith("super::")) return true;
|
|
3364
|
-
const
|
|
3365
|
-
if (
|
|
3366
|
-
return !ctx.subsystemIds.has(ref.slice(0,
|
|
3364
|
+
const sep6 = ref.indexOf("::");
|
|
3365
|
+
if (sep6 === -1) return false;
|
|
3366
|
+
return !ctx.subsystemIds.has(ref.slice(0, sep6));
|
|
3367
3367
|
}
|
|
3368
3368
|
function resolveSurfaceRef(ctx, ref) {
|
|
3369
3369
|
const local = ref.split("::").filter((seg) => seg && seg !== "super").pop();
|
|
@@ -3700,10 +3700,10 @@ function stepGraph(steps) {
|
|
|
3700
3700
|
const s = byNum.get(n);
|
|
3701
3701
|
if (s.type !== "parallel" || s.endStep === void 0 || !s.branches?.length) continue;
|
|
3702
3702
|
const entries = s.branches.map((b) => b.step).sort((a, b) => a - b);
|
|
3703
|
-
const
|
|
3703
|
+
const join16 = fallNext(s.endStep);
|
|
3704
3704
|
for (let i = 0; i < entries.length; i++) {
|
|
3705
3705
|
const armEnd = i + 1 < entries.length ? prevOf(entries[i + 1]) : s.endStep;
|
|
3706
|
-
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd,
|
|
3706
|
+
if (armEnd !== void 0 && armEnd >= entries[i]) armEndJoin.set(armEnd, join16);
|
|
3707
3707
|
}
|
|
3708
3708
|
}
|
|
3709
3709
|
const successorsOf = (n) => {
|
|
@@ -3777,7 +3777,8 @@ var init_narrative_flow = __esm({
|
|
|
3777
3777
|
{ code: "REGION_OVERLAP", defaultSeverity: "error", summary: "loop/try regions interleave \u2014 regions must nest or be disjoint to map onto structured code" },
|
|
3778
3778
|
{ code: "JUMP_INTO_REGION", defaultSeverity: "warning", summary: "Jump lands in the middle of a loop/try body from outside \u2014 regions are entered through their header" },
|
|
3779
3779
|
{ code: "FALLTHROUGH_INTO_HANDLER", defaultSeverity: "warning", summary: "try body falls through into its own catch/finally region on the success path" },
|
|
3780
|
-
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" }
|
|
3780
|
+
{ code: "BACKWARD_JUMP", defaultSeverity: "warning", summary: "Backward jump that is not a continue to an enclosing loop header \u2014 model repetition with a loop step" },
|
|
3781
|
+
{ code: "DUPLICATE_STEP_LABEL", defaultSeverity: "error", summary: "Two steps in one narrative share a label \u2014 the symbolic anchor later deltas address by" }
|
|
3781
3782
|
],
|
|
3782
3783
|
check(ctx) {
|
|
3783
3784
|
for (const impl of ctx.implementations) {
|
|
@@ -3786,6 +3787,23 @@ var init_narrative_flow = __esm({
|
|
|
3786
3787
|
const steps = implMethod.narrative;
|
|
3787
3788
|
if (!steps.length) continue;
|
|
3788
3789
|
const where = `Method "${implMethod.name}" in implementation "${impl.id}": `;
|
|
3790
|
+
const labelled = /* @__PURE__ */ new Map();
|
|
3791
|
+
for (const step of steps) {
|
|
3792
|
+
const label = step.label;
|
|
3793
|
+
if (!label) continue;
|
|
3794
|
+
const first = labelled.get(label);
|
|
3795
|
+
if (first !== void 0) {
|
|
3796
|
+
ctx.addIssue(
|
|
3797
|
+
"error",
|
|
3798
|
+
"DUPLICATE_STEP_LABEL",
|
|
3799
|
+
`${where}steps ${first} and ${step.stepNumber} share the label "${label}". A label anchors one step for later deltas to address; two steps holding it makes every symbolic reference ambiguous and blocks all further edits to this implementation. Rename one.`,
|
|
3800
|
+
impl.id,
|
|
3801
|
+
isDraftCtx
|
|
3802
|
+
);
|
|
3803
|
+
} else {
|
|
3804
|
+
labelled.set(label, step.stepNumber);
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3789
3807
|
let sound = true;
|
|
3790
3808
|
const malformed = (msg) => {
|
|
3791
3809
|
sound = false;
|
|
@@ -7011,11 +7029,11 @@ var init_narrative_antipatterns = __esm({
|
|
|
7011
7029
|
const memberEdges = keys.flatMap((k) => (adjacency.get(k) ?? []).filter((e) => inScc.has(e.toKey)));
|
|
7012
7030
|
if (memberEdges.length === 0) continue;
|
|
7013
7031
|
const anchor = [...memberEdges].sort((a, b) => a.fromKey.localeCompare(b.fromKey))[0];
|
|
7014
|
-
const
|
|
7032
|
+
const path26 = [...keys].sort().join(" \u2192 ");
|
|
7015
7033
|
ctx.addIssue(
|
|
7016
7034
|
"warning",
|
|
7017
7035
|
"UNCONDITIONAL_CALL_CYCLE",
|
|
7018
|
-
`Call cycle with no guard: ${
|
|
7036
|
+
`Call cycle with no guard: ${path26} \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.`,
|
|
7019
7037
|
anchor.impl.id,
|
|
7020
7038
|
memberEdges.some((e) => ctx.isImplementationDraft(e.impl))
|
|
7021
7039
|
);
|
|
@@ -13881,6 +13899,77 @@ var init_variants = __esm({
|
|
|
13881
13899
|
}
|
|
13882
13900
|
});
|
|
13883
13901
|
|
|
13902
|
+
// src/core/baseline.ts
|
|
13903
|
+
function baselineDir() {
|
|
13904
|
+
return process.env["WAIRON_BASELINE_DIR"] || path13.join(os4.homedir(), ".wairon", "baselines");
|
|
13905
|
+
}
|
|
13906
|
+
function keyFor(root) {
|
|
13907
|
+
const normalized = path13.resolve(root).replace(/\\/g, "/").toLowerCase();
|
|
13908
|
+
return crypto3.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
13909
|
+
}
|
|
13910
|
+
function baselinePath(root) {
|
|
13911
|
+
return path13.join(baselineDir(), `${keyFor(root)}.json`);
|
|
13912
|
+
}
|
|
13913
|
+
function readBaseline(root = getProjectRoot()) {
|
|
13914
|
+
const p = baselinePath(root);
|
|
13915
|
+
if (!pathExists(p)) return null;
|
|
13916
|
+
try {
|
|
13917
|
+
return JSON.parse(fs9.readFileSync(p, "utf8"));
|
|
13918
|
+
} catch {
|
|
13919
|
+
return null;
|
|
13920
|
+
}
|
|
13921
|
+
}
|
|
13922
|
+
function currentSpecs(root) {
|
|
13923
|
+
const mountDirs = loadSubsystemSpecs().filter((s) => s.projectPath && !s.id.includes("::")).map((s) => `${path13.resolve(root, s.projectPath).split(path13.sep).join("/")}/`);
|
|
13924
|
+
const out = {};
|
|
13925
|
+
for (const [abs, content] of snapshotSpecFiles()) {
|
|
13926
|
+
const normalized = path13.resolve(abs).split(path13.sep).join("/");
|
|
13927
|
+
if (mountDirs.some((dir) => normalized.startsWith(dir))) continue;
|
|
13928
|
+
out[path13.relative(root, abs).split(path13.sep).join("/")] = content;
|
|
13929
|
+
}
|
|
13930
|
+
return out;
|
|
13931
|
+
}
|
|
13932
|
+
function diffAgainstBaseline(root = getProjectRoot()) {
|
|
13933
|
+
const baseline = readBaseline(root);
|
|
13934
|
+
if (!baseline) return null;
|
|
13935
|
+
const current2 = currentSpecs(root);
|
|
13936
|
+
const added = [];
|
|
13937
|
+
const changed = [];
|
|
13938
|
+
const removed = [];
|
|
13939
|
+
const unchangedPaths = [];
|
|
13940
|
+
for (const [rel2, content] of Object.entries(current2)) {
|
|
13941
|
+
const before = baseline.specs[rel2];
|
|
13942
|
+
if (before === void 0) added.push(rel2);
|
|
13943
|
+
else if (before !== content) changed.push(rel2);
|
|
13944
|
+
else unchangedPaths.push(rel2);
|
|
13945
|
+
}
|
|
13946
|
+
for (const rel2 of Object.keys(baseline.specs)) {
|
|
13947
|
+
if (current2[rel2] === void 0) removed.push(rel2);
|
|
13948
|
+
}
|
|
13949
|
+
return {
|
|
13950
|
+
added: added.sort(),
|
|
13951
|
+
changed: changed.sort(),
|
|
13952
|
+
removed: removed.sort(),
|
|
13953
|
+
unchangedPaths: unchangedPaths.sort()
|
|
13954
|
+
};
|
|
13955
|
+
}
|
|
13956
|
+
function settledSpecPaths(root = getProjectRoot()) {
|
|
13957
|
+
const diff = diffAgainstBaseline(root);
|
|
13958
|
+
return diff ? new Set(diff.unchangedPaths) : null;
|
|
13959
|
+
}
|
|
13960
|
+
var fs9, os4, path13, crypto3;
|
|
13961
|
+
var init_baseline = __esm({
|
|
13962
|
+
"src/core/baseline.ts"() {
|
|
13963
|
+
"use strict";
|
|
13964
|
+
fs9 = __toESM(require("fs"));
|
|
13965
|
+
os4 = __toESM(require("os"));
|
|
13966
|
+
path13 = __toESM(require("path"));
|
|
13967
|
+
crypto3 = __toESM(require("crypto"));
|
|
13968
|
+
init_fs();
|
|
13969
|
+
init_specs2();
|
|
13970
|
+
}
|
|
13971
|
+
});
|
|
13972
|
+
|
|
13884
13973
|
// src/core/validation.ts
|
|
13885
13974
|
function projectPackSelections() {
|
|
13886
13975
|
try {
|
|
@@ -14003,7 +14092,7 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14003
14092
|
const types = loadTypeSpecs();
|
|
14004
14093
|
const surfaceSnapshots = loadSurfaceSnapshots();
|
|
14005
14094
|
const codeModel = buildCodeModel(implementations, getProjectRoot());
|
|
14006
|
-
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] :
|
|
14095
|
+
const statusBearing = treatAllAsComplete ? [...subsystems, ...components, ...interfaces, ...implementations] : settledStatusBearing({ subsystems, components, interfaces, implementations });
|
|
14007
14096
|
const statusSnapshot = statusBearing.map((s) => s.status);
|
|
14008
14097
|
for (const s of statusBearing) s.status = "complete";
|
|
14009
14098
|
try {
|
|
@@ -14125,10 +14214,34 @@ function validateSddTree(rulesOrOptions, projectType = "backend") {
|
|
|
14125
14214
|
});
|
|
14126
14215
|
}
|
|
14127
14216
|
}
|
|
14217
|
+
function settledStatusBearing(loaded) {
|
|
14218
|
+
let settled;
|
|
14219
|
+
try {
|
|
14220
|
+
settled = settledSpecPaths();
|
|
14221
|
+
} catch {
|
|
14222
|
+
return [];
|
|
14223
|
+
}
|
|
14224
|
+
if (!settled || settled.size === 0) return [];
|
|
14225
|
+
const root = getProjectRoot();
|
|
14226
|
+
const index = scanAllSpecs();
|
|
14227
|
+
const rel2 = (abs) => path14.relative(root, abs).split(path14.sep).join("/");
|
|
14228
|
+
const out = [];
|
|
14229
|
+
const take = (specs, paths) => {
|
|
14230
|
+
for (const spec of specs) {
|
|
14231
|
+
const p = paths[spec.id];
|
|
14232
|
+
if (p && settled.has(rel2(p))) out.push(spec);
|
|
14233
|
+
}
|
|
14234
|
+
};
|
|
14235
|
+
take(loaded.subsystems, index.paths.subsystem);
|
|
14236
|
+
take(loaded.components, index.paths.component);
|
|
14237
|
+
take(loaded.interfaces, index.paths.interface);
|
|
14238
|
+
take(loaded.implementations, index.paths.implementation);
|
|
14239
|
+
return out;
|
|
14240
|
+
}
|
|
14128
14241
|
function validateAsComplete(options) {
|
|
14129
14242
|
return validateSddTree({ ...options ?? {}, treatAllAsComplete: true });
|
|
14130
14243
|
}
|
|
14131
|
-
var SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
|
|
14244
|
+
var path14, SUBPROJECT_REFERENCE_CODES, SUBPROJECT_CONFORMANCE_CODES;
|
|
14132
14245
|
var init_validation = __esm({
|
|
14133
14246
|
"src/core/validation.ts"() {
|
|
14134
14247
|
"use strict";
|
|
@@ -14142,6 +14255,8 @@ var init_validation = __esm({
|
|
|
14142
14255
|
init_source_analysis();
|
|
14143
14256
|
init_specs2();
|
|
14144
14257
|
init_fs();
|
|
14258
|
+
path14 = __toESM(require("path"));
|
|
14259
|
+
init_baseline();
|
|
14145
14260
|
SUBPROJECT_REFERENCE_CODES = /* @__PURE__ */ new Set([
|
|
14146
14261
|
"UNDEFINED_TYPE_REFERENCE",
|
|
14147
14262
|
"INVALID_DEPENDENCY_REFERENCE",
|
|
@@ -14471,8 +14586,8 @@ function generateSequenceDiagram(componentId, methodName, options) {
|
|
|
14471
14586
|
const steps = [...methodImpl.narrative].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
14472
14587
|
for (const step of steps) {
|
|
14473
14588
|
for (const par of parallelArms) {
|
|
14474
|
-
const
|
|
14475
|
-
if (
|
|
14589
|
+
const sep6 = par.sepByStep.get(step.stepNumber);
|
|
14590
|
+
if (sep6 !== void 0) lines.push(` and ${escapeLabel(sep6)}`);
|
|
14476
14591
|
}
|
|
14477
14592
|
switch (step.type) {
|
|
14478
14593
|
case "local":
|
|
@@ -14766,16 +14881,16 @@ function relativizeId(id, prefix) {
|
|
|
14766
14881
|
return `${"super::".repeat(prefixParts.length - common)}${idParts.slice(common).join("::")}`;
|
|
14767
14882
|
}
|
|
14768
14883
|
function isWithin(dir, file) {
|
|
14769
|
-
const d =
|
|
14770
|
-
const f =
|
|
14771
|
-
return f === d || f.startsWith(d +
|
|
14884
|
+
const d = path15.resolve(dir);
|
|
14885
|
+
const f = path15.resolve(file);
|
|
14886
|
+
return f === d || f.startsWith(d + path15.sep);
|
|
14772
14887
|
}
|
|
14773
14888
|
function projectPathEscapesRoot(projectRoot, projectPath, resolvedChildDir) {
|
|
14774
|
-
return
|
|
14889
|
+
return path15.isAbsolute(projectPath) || !isWithin(projectRoot, resolvedChildDir);
|
|
14775
14890
|
}
|
|
14776
14891
|
function assertContainedProjectPath(projectRoot, projectPath) {
|
|
14777
|
-
const root =
|
|
14778
|
-
const resolved =
|
|
14892
|
+
const root = path15.resolve(projectRoot);
|
|
14893
|
+
const resolved = path15.resolve(root, projectPath);
|
|
14779
14894
|
if (projectPathEscapesRoot(root, projectPath, resolved)) {
|
|
14780
14895
|
throw new Error(
|
|
14781
14896
|
`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.`
|
|
@@ -14786,11 +14901,11 @@ function assertContainedProjectPath(projectRoot, projectPath) {
|
|
|
14786
14901
|
function findChainingParent(childRoot) {
|
|
14787
14902
|
let childResolved;
|
|
14788
14903
|
try {
|
|
14789
|
-
childResolved =
|
|
14904
|
+
childResolved = path15.resolve(childRoot);
|
|
14790
14905
|
} catch {
|
|
14791
14906
|
return null;
|
|
14792
14907
|
}
|
|
14793
|
-
let dir =
|
|
14908
|
+
let dir = path15.dirname(childResolved);
|
|
14794
14909
|
for (let hops = 0; hops < 32; hops++) {
|
|
14795
14910
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
14796
14911
|
if (pathExists(specsDir)) {
|
|
@@ -14805,7 +14920,7 @@ function findChainingParent(childRoot) {
|
|
|
14805
14920
|
const projectPath = raw.projectPath;
|
|
14806
14921
|
if (typeof projectPath === "string" && projectPath.trim() !== "") {
|
|
14807
14922
|
try {
|
|
14808
|
-
if (
|
|
14923
|
+
if (path15.resolve(dir, projectPath) === childResolved) {
|
|
14809
14924
|
const id = raw.id;
|
|
14810
14925
|
return { parentRoot: dir, subsystemId: typeof id === "string" ? id : "?" };
|
|
14811
14926
|
}
|
|
@@ -14815,7 +14930,7 @@ function findChainingParent(childRoot) {
|
|
|
14815
14930
|
}
|
|
14816
14931
|
}
|
|
14817
14932
|
}
|
|
14818
|
-
const up =
|
|
14933
|
+
const up = path15.dirname(dir);
|
|
14819
14934
|
if (up === dir) break;
|
|
14820
14935
|
dir = up;
|
|
14821
14936
|
}
|
|
@@ -14920,18 +15035,18 @@ function findOwner(id, components) {
|
|
|
14920
15035
|
}) ?? null;
|
|
14921
15036
|
}
|
|
14922
15037
|
function moveComponentFolder(fromDir, toDir) {
|
|
14923
|
-
if (
|
|
14924
|
-
if (!
|
|
14925
|
-
ensureDir(
|
|
14926
|
-
|
|
15038
|
+
if (path15.normalize(fromDir) === path15.normalize(toDir)) return false;
|
|
15039
|
+
if (!fs10.existsSync(fromDir) || fs10.existsSync(toDir)) return false;
|
|
15040
|
+
ensureDir(path15.dirname(toDir));
|
|
15041
|
+
fs10.renameSync(fromDir, toDir);
|
|
14927
15042
|
return true;
|
|
14928
15043
|
}
|
|
14929
15044
|
function cleanEmptyDirs(filePath, specsRoot) {
|
|
14930
|
-
let dir =
|
|
15045
|
+
let dir = path15.dirname(filePath);
|
|
14931
15046
|
while (dir !== specsRoot && dir.startsWith(specsRoot)) {
|
|
14932
|
-
if (
|
|
14933
|
-
|
|
14934
|
-
dir =
|
|
15047
|
+
if (fs10.existsSync(dir) && fs10.readdirSync(dir).length === 0) {
|
|
15048
|
+
fs10.rmdirSync(dir);
|
|
15049
|
+
dir = path15.dirname(dir);
|
|
14935
15050
|
} else {
|
|
14936
15051
|
break;
|
|
14937
15052
|
}
|
|
@@ -14950,13 +15065,13 @@ function parseOrThrow(schema, value, kind, id) {
|
|
|
14950
15065
|
function computeSpecTreeSignature(dirs) {
|
|
14951
15066
|
const parts = [];
|
|
14952
15067
|
for (const dir of dirs) {
|
|
14953
|
-
if (!
|
|
15068
|
+
if (!fs10.existsSync(dir)) {
|
|
14954
15069
|
parts.push(`${dir}:missing`);
|
|
14955
15070
|
continue;
|
|
14956
15071
|
}
|
|
14957
15072
|
for (const f of listFilesRecursive(dir, ".yaml")) {
|
|
14958
15073
|
try {
|
|
14959
|
-
const st =
|
|
15074
|
+
const st = fs10.statSync(f);
|
|
14960
15075
|
parts.push(`${f}:${st.mtimeMs}:${st.size}`);
|
|
14961
15076
|
} catch {
|
|
14962
15077
|
parts.push(`${f}:gone`);
|
|
@@ -14966,7 +15081,7 @@ function computeSpecTreeSignature(dirs) {
|
|
|
14966
15081
|
return parts.join("|");
|
|
14967
15082
|
}
|
|
14968
15083
|
function workspaceFor(rootDir) {
|
|
14969
|
-
const key =
|
|
15084
|
+
const key = path15.resolve(rootDir);
|
|
14970
15085
|
let ws = workspaces.get(key);
|
|
14971
15086
|
if (!ws) {
|
|
14972
15087
|
ws = new SpecWorkspace(key);
|
|
@@ -15105,7 +15220,7 @@ function readLockState() {
|
|
|
15105
15220
|
return { state: stateIdEquals(record.stateId, current2) ? "locked" : "stale", record, current: current2 };
|
|
15106
15221
|
}
|
|
15107
15222
|
function computeStateIdAt(root) {
|
|
15108
|
-
const resolved =
|
|
15223
|
+
const resolved = path15.resolve(root);
|
|
15109
15224
|
return runWithProjectRoot(resolved, () => {
|
|
15110
15225
|
workspaceFor(resolved).invalidate();
|
|
15111
15226
|
const system = loadSystemSpec();
|
|
@@ -15135,12 +15250,15 @@ function collectPromotableSpecs(scopeSubsystem) {
|
|
|
15135
15250
|
function applySpecStatus(kind, id, status) {
|
|
15136
15251
|
current().applySpecStatus(kind, id, status);
|
|
15137
15252
|
}
|
|
15253
|
+
function specPathsInScope(scopeSubsystem) {
|
|
15254
|
+
return current().specPathsInScope(scopeSubsystem);
|
|
15255
|
+
}
|
|
15138
15256
|
function snapshotSpecFiles() {
|
|
15139
15257
|
return current().snapshotSpecFiles();
|
|
15140
15258
|
}
|
|
15141
15259
|
function restoreSpecFiles(snapshot) {
|
|
15142
15260
|
for (const [file, content] of snapshot) {
|
|
15143
|
-
|
|
15261
|
+
fs10.writeFileSync(file, content);
|
|
15144
15262
|
}
|
|
15145
15263
|
}
|
|
15146
15264
|
function findLegacySpecFiles() {
|
|
@@ -15149,12 +15267,12 @@ function findLegacySpecFiles() {
|
|
|
15149
15267
|
function updateSpec(kind, id, delta, hooks) {
|
|
15150
15268
|
return current().updateSpec(kind, id, delta, hooks);
|
|
15151
15269
|
}
|
|
15152
|
-
var
|
|
15270
|
+
var fs10, path15, SIGNATURE_TTL_MS, SpecWorkspace, workspaces;
|
|
15153
15271
|
var init_specs2 = __esm({
|
|
15154
15272
|
"src/core/specs.ts"() {
|
|
15155
15273
|
"use strict";
|
|
15156
|
-
|
|
15157
|
-
|
|
15274
|
+
fs10 = __toESM(require("fs"));
|
|
15275
|
+
path15 = __toESM(require("path"));
|
|
15158
15276
|
init_loader();
|
|
15159
15277
|
init_fs();
|
|
15160
15278
|
init_statehash();
|
|
@@ -15175,7 +15293,7 @@ var init_specs2 = __esm({
|
|
|
15175
15293
|
this.rootSubsystems = /* @__PURE__ */ new Set();
|
|
15176
15294
|
this.scanVisitedSpecDirs = [];
|
|
15177
15295
|
this.loaderIssues = [];
|
|
15178
|
-
this.rootDir =
|
|
15296
|
+
this.rootDir = path15.resolve(rootDir);
|
|
15179
15297
|
this.paths = aiPathsAt(this.rootDir);
|
|
15180
15298
|
}
|
|
15181
15299
|
invalidate() {
|
|
@@ -15202,7 +15320,7 @@ var init_specs2 = __esm({
|
|
|
15202
15320
|
this.rootSubsystems.clear();
|
|
15203
15321
|
this.cachedRecursive = recursive;
|
|
15204
15322
|
this.scanVisitedSpecDirs = [];
|
|
15205
|
-
const visited = /* @__PURE__ */ new Set([
|
|
15323
|
+
const visited = /* @__PURE__ */ new Set([path15.resolve(this.rootDir)]);
|
|
15206
15324
|
const maxDepth = typeof recursive === "number" ? recursive : recursive ? Infinity : 0;
|
|
15207
15325
|
this.cachedIndex = this.scanSpecsForProject(this.rootDir, "", visited, maxDepth, 0);
|
|
15208
15326
|
this.cachedSpecDirs = this.scanVisitedSpecDirs;
|
|
@@ -15217,10 +15335,10 @@ var init_specs2 = __esm({
|
|
|
15217
15335
|
this.scanVisitedSpecDirs.push(specsDir);
|
|
15218
15336
|
if (!pathExists(specsDir)) return index;
|
|
15219
15337
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
15220
|
-
const systemYaml =
|
|
15338
|
+
const systemYaml = path15.normalize(projectPaths.specsSystem());
|
|
15221
15339
|
const localSubprojects = [];
|
|
15222
15340
|
for (const file of files) {
|
|
15223
|
-
const normFile =
|
|
15341
|
+
const normFile = path15.normalize(file);
|
|
15224
15342
|
if (normFile === systemYaml) continue;
|
|
15225
15343
|
let detectedType = "spec";
|
|
15226
15344
|
try {
|
|
@@ -15230,7 +15348,7 @@ var init_specs2 = __esm({
|
|
|
15230
15348
|
severity: "error",
|
|
15231
15349
|
code: "INVALID_YAML",
|
|
15232
15350
|
message: `Spec file "${file}" is not a valid YAML object or is empty.`,
|
|
15233
|
-
specId:
|
|
15351
|
+
specId: path15.basename(file, ".yaml")
|
|
15234
15352
|
});
|
|
15235
15353
|
continue;
|
|
15236
15354
|
}
|
|
@@ -15262,8 +15380,8 @@ var init_specs2 = __esm({
|
|
|
15262
15380
|
detectedType = "implementation";
|
|
15263
15381
|
const parsed = ImplementationSpecSchema.parse(raw);
|
|
15264
15382
|
if (parsed.sourcePath) {
|
|
15265
|
-
const absSourcePath =
|
|
15266
|
-
parsed.sourcePath =
|
|
15383
|
+
const absSourcePath = path15.resolve(projectDir, parsed.sourcePath);
|
|
15384
|
+
parsed.sourcePath = path15.relative(projectDir, absSourcePath).replace(/\\/g, "/");
|
|
15267
15385
|
}
|
|
15268
15386
|
index.implementations.push(parsed);
|
|
15269
15387
|
index.paths.implementation[parsed.id] = file;
|
|
@@ -15285,11 +15403,11 @@ var init_specs2 = __esm({
|
|
|
15285
15403
|
severity: "error",
|
|
15286
15404
|
code: "UNKNOWN_SPEC_TYPE",
|
|
15287
15405
|
message: `Spec file "${file}" does not match any recognized L1-L4 schema structure.`,
|
|
15288
|
-
specId:
|
|
15406
|
+
specId: path15.basename(file, ".yaml")
|
|
15289
15407
|
});
|
|
15290
15408
|
}
|
|
15291
15409
|
} catch (e) {
|
|
15292
|
-
const filename =
|
|
15410
|
+
const filename = path15.basename(file, ".yaml");
|
|
15293
15411
|
this.loaderIssues.push({
|
|
15294
15412
|
severity: "error",
|
|
15295
15413
|
code: "SCHEMA_VALIDATION_ERROR",
|
|
@@ -15387,7 +15505,7 @@ var init_specs2 = __esm({
|
|
|
15387
15505
|
}
|
|
15388
15506
|
if (currentDepth < maxDepth) {
|
|
15389
15507
|
for (const subproj of localSubprojects) {
|
|
15390
|
-
const childDir =
|
|
15508
|
+
const childDir = path15.resolve(projectDir, subproj.projectPath);
|
|
15391
15509
|
if (projectPathEscapesRoot(this.rootDir, subproj.projectPath, childDir)) {
|
|
15392
15510
|
this.loaderIssues.push({
|
|
15393
15511
|
severity: "error",
|
|
@@ -15406,7 +15524,7 @@ var init_specs2 = __esm({
|
|
|
15406
15524
|
});
|
|
15407
15525
|
continue;
|
|
15408
15526
|
}
|
|
15409
|
-
if (!
|
|
15527
|
+
if (!fs10.existsSync(childDir)) {
|
|
15410
15528
|
this.loaderIssues.push({
|
|
15411
15529
|
severity: "error",
|
|
15412
15530
|
code: "SUBPROJECT_NOT_FOUND",
|
|
@@ -15449,7 +15567,7 @@ var init_specs2 = __esm({
|
|
|
15449
15567
|
const index = this.scanAll();
|
|
15450
15568
|
const sub = index.subsystems.find((s) => s.id === currentPrefix);
|
|
15451
15569
|
if (sub && sub.projectPath) {
|
|
15452
|
-
const nextDir =
|
|
15570
|
+
const nextDir = path15.resolve(currentDir, sub.projectPath);
|
|
15453
15571
|
if (projectPathEscapesRoot(this.rootDir, sub.projectPath, nextDir)) {
|
|
15454
15572
|
this.loaderIssues.push({
|
|
15455
15573
|
severity: "error",
|
|
@@ -15501,9 +15619,9 @@ var init_specs2 = __esm({
|
|
|
15501
15619
|
return index.paths.subsystem[matches2[0]];
|
|
15502
15620
|
}
|
|
15503
15621
|
if (pathExists(this.paths.specsSubsystemsDir()) && listFiles(this.paths.specsSubsystemsDir(), ".yaml").length > 0) {
|
|
15504
|
-
return
|
|
15622
|
+
return path15.join(this.paths.specsSubsystemsDir(), `${id}.yaml`);
|
|
15505
15623
|
}
|
|
15506
|
-
return
|
|
15624
|
+
return path15.join(this.paths.specsDir(), id, ".index.yaml");
|
|
15507
15625
|
}
|
|
15508
15626
|
getComponentPath(id, subsystemId) {
|
|
15509
15627
|
const index = this.scanAll();
|
|
@@ -15523,21 +15641,21 @@ var init_specs2 = __esm({
|
|
|
15523
15641
|
if (owner) {
|
|
15524
15642
|
const ownerPath = index.paths.component[owner.id];
|
|
15525
15643
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
15526
|
-
return
|
|
15644
|
+
return path15.join(path15.dirname(ownerPath), id, ".index.yaml");
|
|
15527
15645
|
}
|
|
15528
15646
|
}
|
|
15529
15647
|
if (subsystemId) {
|
|
15530
15648
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15531
|
-
const subDir =
|
|
15649
|
+
const subDir = path15.dirname(subPath);
|
|
15532
15650
|
if (subPath.endsWith(".index.yaml")) {
|
|
15533
|
-
return
|
|
15651
|
+
return path15.join(subDir, id, ".index.yaml");
|
|
15534
15652
|
}
|
|
15535
15653
|
}
|
|
15536
15654
|
if (pathExists(this.paths.specsComponentsDir()) && listFiles(this.paths.specsComponentsDir(), ".yaml").length > 0) {
|
|
15537
|
-
return
|
|
15655
|
+
return path15.join(this.paths.specsComponentsDir(), `${id}.yaml`);
|
|
15538
15656
|
}
|
|
15539
15657
|
const targetSubsystem = subsystemId || "default";
|
|
15540
|
-
return
|
|
15658
|
+
return path15.join(this.paths.specsDir(), targetSubsystem, id, ".index.yaml");
|
|
15541
15659
|
}
|
|
15542
15660
|
getInterfacePath(id, componentId) {
|
|
15543
15661
|
const index = this.scanAll();
|
|
@@ -15563,16 +15681,16 @@ var init_specs2 = __esm({
|
|
|
15563
15681
|
}
|
|
15564
15682
|
if (componentId) {
|
|
15565
15683
|
const compPath = this.getComponentPath(componentId);
|
|
15566
|
-
const compDir =
|
|
15684
|
+
const compDir = path15.dirname(compPath);
|
|
15567
15685
|
if (compPath.endsWith(".index.yaml")) {
|
|
15568
|
-
return
|
|
15686
|
+
return path15.join(compDir, ".interface.yaml");
|
|
15569
15687
|
}
|
|
15570
15688
|
}
|
|
15571
15689
|
if (pathExists(this.paths.specsInterfacesDir()) && listFiles(this.paths.specsInterfacesDir(), ".yaml").length > 0) {
|
|
15572
|
-
return
|
|
15690
|
+
return path15.join(this.paths.specsInterfacesDir(), `${id}.yaml`);
|
|
15573
15691
|
}
|
|
15574
15692
|
const targetComponent = componentId || "default";
|
|
15575
|
-
return
|
|
15693
|
+
return path15.join(this.paths.specsDir(), "default", targetComponent, ".interface.yaml");
|
|
15576
15694
|
}
|
|
15577
15695
|
getImplementationPath(id, contractId) {
|
|
15578
15696
|
const index = this.scanAll();
|
|
@@ -15598,16 +15716,16 @@ var init_specs2 = __esm({
|
|
|
15598
15716
|
}
|
|
15599
15717
|
if (contractId) {
|
|
15600
15718
|
const intfPath = this.getInterfacePath(contractId);
|
|
15601
|
-
const intfDir =
|
|
15719
|
+
const intfDir = path15.dirname(intfPath);
|
|
15602
15720
|
if (intfPath.endsWith(".interface.yaml")) {
|
|
15603
|
-
return
|
|
15721
|
+
return path15.join(intfDir, ".implementation.yaml");
|
|
15604
15722
|
}
|
|
15605
15723
|
}
|
|
15606
15724
|
if (pathExists(this.paths.specsImplementationsDir()) && listFiles(this.paths.specsImplementationsDir(), ".yaml").length > 0) {
|
|
15607
|
-
return
|
|
15725
|
+
return path15.join(this.paths.specsImplementationsDir(), `${id}.yaml`);
|
|
15608
15726
|
}
|
|
15609
15727
|
const targetContract = contractId ? contractId.replace(/^i/, "") : "default";
|
|
15610
|
-
return
|
|
15728
|
+
return path15.join(this.paths.specsDir(), "default", targetContract, ".implementation.yaml");
|
|
15611
15729
|
}
|
|
15612
15730
|
getTypePath(id, subsystemId, group) {
|
|
15613
15731
|
const index = this.scanAll();
|
|
@@ -15638,7 +15756,7 @@ var init_specs2 = __esm({
|
|
|
15638
15756
|
const groupPath = index.paths.group[targetGroup] || index.paths.group[plainGroup];
|
|
15639
15757
|
if (groupPath) {
|
|
15640
15758
|
const localId2 = id.split("::").pop();
|
|
15641
|
-
return
|
|
15759
|
+
return path15.join(path15.dirname(groupPath), `${localId2}.yaml`);
|
|
15642
15760
|
}
|
|
15643
15761
|
}
|
|
15644
15762
|
let localId = id;
|
|
@@ -15651,12 +15769,12 @@ var init_specs2 = __esm({
|
|
|
15651
15769
|
}
|
|
15652
15770
|
if (subsystemId) {
|
|
15653
15771
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15654
|
-
const subDir =
|
|
15772
|
+
const subDir = path15.dirname(subPath);
|
|
15655
15773
|
if (subPath.endsWith(".index.yaml")) {
|
|
15656
|
-
return
|
|
15774
|
+
return path15.join(subDir, "types", `${localId}.yaml`);
|
|
15657
15775
|
}
|
|
15658
15776
|
}
|
|
15659
|
-
return
|
|
15777
|
+
return path15.join(this.paths.specsTypesDir(), `${localId}.yaml`);
|
|
15660
15778
|
}
|
|
15661
15779
|
getGroupPath(id, subsystemId) {
|
|
15662
15780
|
const index = this.scanAll();
|
|
@@ -15690,12 +15808,12 @@ var init_specs2 = __esm({
|
|
|
15690
15808
|
}
|
|
15691
15809
|
if (subsystemId) {
|
|
15692
15810
|
const subPath = this.getSubsystemPath(subsystemId);
|
|
15693
|
-
const subDir =
|
|
15811
|
+
const subDir = path15.dirname(subPath);
|
|
15694
15812
|
if (subPath.endsWith(".index.yaml")) {
|
|
15695
|
-
return
|
|
15813
|
+
return path15.join(subDir, "types", localId, ".index.yaml");
|
|
15696
15814
|
}
|
|
15697
15815
|
}
|
|
15698
|
-
return
|
|
15816
|
+
return path15.join(this.paths.specsTypesDir(), localId, ".index.yaml");
|
|
15699
15817
|
}
|
|
15700
15818
|
// -------------------------------------------------------------------------
|
|
15701
15819
|
// Level 0: System
|
|
@@ -15718,7 +15836,7 @@ var init_specs2 = __esm({
|
|
|
15718
15836
|
}
|
|
15719
15837
|
saveSystemSpec(spec) {
|
|
15720
15838
|
const p = this.paths.specsSystem();
|
|
15721
|
-
ensureDir(
|
|
15839
|
+
ensureDir(path15.dirname(p));
|
|
15722
15840
|
writeYamlFile(p, parseOrThrow(SystemSpecSchema, spec, "system", spec.name));
|
|
15723
15841
|
invalidateSpecCache();
|
|
15724
15842
|
}
|
|
@@ -15836,7 +15954,7 @@ var init_specs2 = __esm({
|
|
|
15836
15954
|
}
|
|
15837
15955
|
saveSubsystemSpec(spec) {
|
|
15838
15956
|
const p = this.getSubsystemPath(spec.id);
|
|
15839
|
-
ensureDir(
|
|
15957
|
+
ensureDir(path15.dirname(p));
|
|
15840
15958
|
const { prefix } = splitNamespace(spec.id);
|
|
15841
15959
|
if (!prefix && spec.projectPath && spec.projectPath.trim() !== "") {
|
|
15842
15960
|
assertContainedProjectPath(this.rootDir, spec.projectPath);
|
|
@@ -15867,9 +15985,9 @@ var init_specs2 = __esm({
|
|
|
15867
15985
|
}
|
|
15868
15986
|
deleteSubsystemSpec(id) {
|
|
15869
15987
|
const p = this.getSubsystemPath(id);
|
|
15870
|
-
if (!
|
|
15871
|
-
|
|
15872
|
-
cleanEmptyDirs(p,
|
|
15988
|
+
if (!fs10.existsSync(p)) return false;
|
|
15989
|
+
fs10.unlinkSync(p);
|
|
15990
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
15873
15991
|
invalidateSpecCache();
|
|
15874
15992
|
return true;
|
|
15875
15993
|
}
|
|
@@ -15902,7 +16020,7 @@ var init_specs2 = __esm({
|
|
|
15902
16020
|
saveComponentSpec(spec, opts) {
|
|
15903
16021
|
const notices = [];
|
|
15904
16022
|
const p = this.getComponentPath(spec.id, spec.subsystem);
|
|
15905
|
-
ensureDir(
|
|
16023
|
+
ensureDir(path15.dirname(p));
|
|
15906
16024
|
const specToWrite = this.prepareComponentForWrite(spec);
|
|
15907
16025
|
const existing = this.loadComponentSpec(spec.id);
|
|
15908
16026
|
if (existing) {
|
|
@@ -15919,7 +16037,7 @@ var init_specs2 = __esm({
|
|
|
15919
16037
|
const subsystemChanged = existing && existing.subsystem !== spec.subsystem && splitNamespace(existing.subsystem).localId !== splitNamespace(spec.subsystem).localId;
|
|
15920
16038
|
if (subsystemChanged && !p.endsWith(".index.yaml")) {
|
|
15921
16039
|
notices.push(
|
|
15922
|
-
`component "${spec.id}" already exists at ${
|
|
16040
|
+
`component "${spec.id}" already exists at ${path15.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}").`
|
|
15923
16041
|
);
|
|
15924
16042
|
}
|
|
15925
16043
|
specToWrite.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15930,9 +16048,9 @@ var init_specs2 = __esm({
|
|
|
15930
16048
|
}
|
|
15931
16049
|
deleteComponentSpec(id) {
|
|
15932
16050
|
const p = this.getComponentPath(id);
|
|
15933
|
-
if (!
|
|
15934
|
-
|
|
15935
|
-
cleanEmptyDirs(p,
|
|
16051
|
+
if (!fs10.existsSync(p)) return false;
|
|
16052
|
+
fs10.unlinkSync(p);
|
|
16053
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
15936
16054
|
invalidateSpecCache();
|
|
15937
16055
|
return true;
|
|
15938
16056
|
}
|
|
@@ -15945,12 +16063,12 @@ var init_specs2 = __esm({
|
|
|
15945
16063
|
if (owner) {
|
|
15946
16064
|
const ownerPath = index.paths.component[owner.id];
|
|
15947
16065
|
if (ownerPath && ownerPath.endsWith(".index.yaml")) {
|
|
15948
|
-
const ownerSubDir =
|
|
15949
|
-
return
|
|
16066
|
+
const ownerSubDir = path15.dirname(this.getSubsystemPath(owner.subsystem));
|
|
16067
|
+
return path15.join(ownerSubDir, owner.id, comp.id);
|
|
15950
16068
|
}
|
|
15951
16069
|
}
|
|
15952
|
-
const subDir =
|
|
15953
|
-
return
|
|
16070
|
+
const subDir = path15.dirname(this.getSubsystemPath(comp.subsystem));
|
|
16071
|
+
return path15.join(subDir, comp.id);
|
|
15954
16072
|
}
|
|
15955
16073
|
/**
|
|
15956
16074
|
* Move component folders so the physical tree mirrors ownership: each owned
|
|
@@ -15966,7 +16084,7 @@ var init_specs2 = __esm({
|
|
|
15966
16084
|
if (!currentPath) continue;
|
|
15967
16085
|
const desiredDir = this.desiredComponentDir(comp, index);
|
|
15968
16086
|
if (!desiredDir) continue;
|
|
15969
|
-
if (moveComponentFolder(
|
|
16087
|
+
if (moveComponentFolder(path15.dirname(currentPath), desiredDir)) moved.push(comp.id);
|
|
15970
16088
|
}
|
|
15971
16089
|
if (moved.length) invalidateSpecCache();
|
|
15972
16090
|
return moved;
|
|
@@ -15996,16 +16114,46 @@ var init_specs2 = __esm({
|
|
|
15996
16114
|
return null;
|
|
15997
16115
|
}
|
|
15998
16116
|
}
|
|
16117
|
+
/**
|
|
16118
|
+
* Refuse a write that would land on a file already holding a DIFFERENT spec.
|
|
16119
|
+
*
|
|
16120
|
+
* In the nested layout a spec's path is derived from its parent — a component
|
|
16121
|
+
* for an interface, a contract for an implementation — and the spec's own id
|
|
16122
|
+
* is not part of it (see getInterfacePath / getImplementationPath, which take
|
|
16123
|
+
* the id but ignore it once the parent resolves). So a second id bound to the
|
|
16124
|
+
* same parent resolves to the SAME file.
|
|
16125
|
+
*
|
|
16126
|
+
* Left unguarded that is silent data loss, and doubly invisible: the caller's
|
|
16127
|
+
* `existing` lookup is by the NEW id, finds nothing, and every re-author
|
|
16128
|
+
* notice ("REMOVED …", the carry-forward seam) stays quiet. An agent renaming
|
|
16129
|
+
* an interface by defining a new one destroys the old contract, its
|
|
16130
|
+
* narratives and its lint.allows, and is told "Successfully defined".
|
|
16131
|
+
*/
|
|
16132
|
+
assertPathHoldsNoOtherSpec(p, id, kind, parentLabel) {
|
|
16133
|
+
if (!pathExists(p)) return;
|
|
16134
|
+
let occupantId;
|
|
16135
|
+
try {
|
|
16136
|
+
occupantId = readYamlFile(p)?.id;
|
|
16137
|
+
} catch {
|
|
16138
|
+
return;
|
|
16139
|
+
}
|
|
16140
|
+
if (!occupantId) return;
|
|
16141
|
+
if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
|
|
16142
|
+
throw new Error(
|
|
16143
|
+
`Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path15.relative(this.rootDir, p)}, and both ids resolve to that one file. Re-author "${occupantId}" instead, or delete it first if you meant to replace it.`
|
|
16144
|
+
);
|
|
16145
|
+
}
|
|
15999
16146
|
/** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
|
|
16000
16147
|
saveInterfaceSpec(spec, opts) {
|
|
16001
16148
|
const notices = [];
|
|
16002
16149
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
16003
|
-
|
|
16150
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
|
|
16151
|
+
ensureDir(path15.dirname(p));
|
|
16004
16152
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
16005
16153
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
16006
16154
|
if (existing && existing.component !== spec.component && splitNamespace(existing.component).localId !== splitNamespace(spec.component).localId) {
|
|
16007
16155
|
notices.push(
|
|
16008
|
-
`interface "${spec.id}" already exists at ${
|
|
16156
|
+
`interface "${spec.id}" already exists at ${path15.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.`
|
|
16009
16157
|
);
|
|
16010
16158
|
}
|
|
16011
16159
|
if (existing) {
|
|
@@ -16028,9 +16176,9 @@ var init_specs2 = __esm({
|
|
|
16028
16176
|
}
|
|
16029
16177
|
deleteInterfaceSpec(id) {
|
|
16030
16178
|
const p = this.getInterfacePath(id);
|
|
16031
|
-
if (!
|
|
16032
|
-
|
|
16033
|
-
cleanEmptyDirs(p,
|
|
16179
|
+
if (!fs10.existsSync(p)) return false;
|
|
16180
|
+
fs10.unlinkSync(p);
|
|
16181
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
16034
16182
|
invalidateSpecCache();
|
|
16035
16183
|
return true;
|
|
16036
16184
|
}
|
|
@@ -16063,12 +16211,13 @@ var init_specs2 = __esm({
|
|
|
16063
16211
|
saveImplementationSpec(spec, opts) {
|
|
16064
16212
|
const notices = [];
|
|
16065
16213
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
16066
|
-
|
|
16214
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
|
|
16215
|
+
ensureDir(path15.dirname(p));
|
|
16067
16216
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
16068
16217
|
const existing = this.loadImplementationSpec(spec.id);
|
|
16069
16218
|
if (existing && existing.contract !== spec.contract && splitNamespace(existing.contract).localId !== splitNamespace(spec.contract).localId) {
|
|
16070
16219
|
notices.push(
|
|
16071
|
-
`implementation "${spec.id}" already exists at ${
|
|
16220
|
+
`implementation "${spec.id}" already exists at ${path15.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.`
|
|
16072
16221
|
);
|
|
16073
16222
|
}
|
|
16074
16223
|
if (existing) {
|
|
@@ -16084,9 +16233,9 @@ var init_specs2 = __esm({
|
|
|
16084
16233
|
}
|
|
16085
16234
|
deleteImplementationSpec(id) {
|
|
16086
16235
|
const p = this.getImplementationPath(id);
|
|
16087
|
-
if (!
|
|
16088
|
-
|
|
16089
|
-
cleanEmptyDirs(p,
|
|
16236
|
+
if (!fs10.existsSync(p)) return false;
|
|
16237
|
+
fs10.unlinkSync(p);
|
|
16238
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
16090
16239
|
invalidateSpecCache();
|
|
16091
16240
|
return true;
|
|
16092
16241
|
}
|
|
@@ -16111,11 +16260,11 @@ var init_specs2 = __esm({
|
|
|
16111
16260
|
const existing = this.loadTypeSpec(spec.id);
|
|
16112
16261
|
const group = spec.group || (existing ? existing.group : void 0);
|
|
16113
16262
|
const p = this.getTypePath(spec.id, spec.subsystem, group);
|
|
16114
|
-
ensureDir(
|
|
16263
|
+
ensureDir(path15.dirname(p));
|
|
16115
16264
|
const subsystemChanged = existing && (existing.subsystem ?? "") !== (spec.subsystem ?? "") && splitNamespace(existing.subsystem ?? "").localId !== splitNamespace(spec.subsystem ?? "").localId;
|
|
16116
16265
|
if (subsystemChanged) {
|
|
16117
16266
|
notices.push(
|
|
16118
|
-
`type "${spec.id}" already exists at ${
|
|
16267
|
+
`type "${spec.id}" already exists at ${path15.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)"}").`
|
|
16119
16268
|
);
|
|
16120
16269
|
}
|
|
16121
16270
|
if (spec.subsystem && (!existing || subsystemChanged) && !this.getSubsystemPath(spec.subsystem).endsWith(".index.yaml")) {
|
|
@@ -16138,9 +16287,9 @@ var init_specs2 = __esm({
|
|
|
16138
16287
|
deleteTypeSpec(id) {
|
|
16139
16288
|
const spec = this.loadTypeSpec(id);
|
|
16140
16289
|
const p = this.getTypePath(id, spec?.subsystem, spec?.group);
|
|
16141
|
-
if (!
|
|
16142
|
-
|
|
16143
|
-
cleanEmptyDirs(p,
|
|
16290
|
+
if (!fs10.existsSync(p)) return false;
|
|
16291
|
+
fs10.unlinkSync(p);
|
|
16292
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
16144
16293
|
invalidateSpecCache();
|
|
16145
16294
|
return true;
|
|
16146
16295
|
}
|
|
@@ -16155,7 +16304,7 @@ var init_specs2 = __esm({
|
|
|
16155
16304
|
}
|
|
16156
16305
|
saveGroupSpec(spec) {
|
|
16157
16306
|
const p = this.getGroupPath(spec.id);
|
|
16158
|
-
ensureDir(
|
|
16307
|
+
ensureDir(path15.dirname(p));
|
|
16159
16308
|
const specToWrite = this.prepareGroupForWrite(spec);
|
|
16160
16309
|
const existing = this.loadGroupSpec(spec.id);
|
|
16161
16310
|
if (existing) {
|
|
@@ -16167,9 +16316,9 @@ var init_specs2 = __esm({
|
|
|
16167
16316
|
}
|
|
16168
16317
|
deleteGroupSpec(id) {
|
|
16169
16318
|
const p = this.getGroupPath(id);
|
|
16170
|
-
if (!
|
|
16171
|
-
|
|
16172
|
-
cleanEmptyDirs(p,
|
|
16319
|
+
if (!fs10.existsSync(p)) return false;
|
|
16320
|
+
fs10.unlinkSync(p);
|
|
16321
|
+
cleanEmptyDirs(p, path15.resolve(this.paths.specsDir()));
|
|
16173
16322
|
invalidateSpecCache();
|
|
16174
16323
|
return true;
|
|
16175
16324
|
}
|
|
@@ -16217,6 +16366,50 @@ var init_specs2 = __esm({
|
|
|
16217
16366
|
}
|
|
16218
16367
|
return out;
|
|
16219
16368
|
}
|
|
16369
|
+
/**
|
|
16370
|
+
* Every spec FILE inside a subsystem scope (absolute paths), regardless of
|
|
16371
|
+
* status. `collectPromotableSpecs` answers a different question — which specs
|
|
16372
|
+
* are not yet complete — so it cannot stand in for this: a scoped approval
|
|
16373
|
+
* must cover the specs it approves whether or not they were already settled.
|
|
16374
|
+
*/
|
|
16375
|
+
specPathsInScope(scopeSubsystem) {
|
|
16376
|
+
const index = this.scanAll();
|
|
16377
|
+
const components = this.loadComponentSpecs();
|
|
16378
|
+
const interfaces = this.loadInterfaceSpecs();
|
|
16379
|
+
const implementations = this.loadImplementationSpecs();
|
|
16380
|
+
const inScope = (specSubsystem) => {
|
|
16381
|
+
if (!scopeSubsystem) return true;
|
|
16382
|
+
if (!specSubsystem) return false;
|
|
16383
|
+
return specSubsystem === scopeSubsystem || specSubsystem.startsWith(`${scopeSubsystem}::`);
|
|
16384
|
+
};
|
|
16385
|
+
const out = /* @__PURE__ */ new Set();
|
|
16386
|
+
const add = (p) => {
|
|
16387
|
+
if (p) out.add(path15.resolve(p));
|
|
16388
|
+
};
|
|
16389
|
+
for (const s of this.loadSubsystemSpecs()) {
|
|
16390
|
+
if (!scopeSubsystem || s.id === scopeSubsystem || s.id.startsWith(`${scopeSubsystem}::`)) {
|
|
16391
|
+
add(index.paths.subsystem[s.id]);
|
|
16392
|
+
}
|
|
16393
|
+
}
|
|
16394
|
+
for (const c of components) {
|
|
16395
|
+
if (inScope(c.subsystem)) add(index.paths.component[c.id]);
|
|
16396
|
+
}
|
|
16397
|
+
for (const i of interfaces) {
|
|
16398
|
+
const comp = components.find((c) => c.id === i.component);
|
|
16399
|
+
if (comp && inScope(comp.subsystem)) add(index.paths.interface[i.id]);
|
|
16400
|
+
}
|
|
16401
|
+
for (const m of implementations) {
|
|
16402
|
+
const intf = interfaces.find((i) => i.id === m.contract);
|
|
16403
|
+
const comp = intf ? components.find((c) => c.id === intf.component) : null;
|
|
16404
|
+
if (comp && inScope(comp.subsystem)) add(index.paths.implementation[m.id]);
|
|
16405
|
+
}
|
|
16406
|
+
if (!scopeSubsystem) {
|
|
16407
|
+
add(this.paths.specsSystem());
|
|
16408
|
+
for (const p of Object.values(index.paths.type)) add(p);
|
|
16409
|
+
for (const p of Object.values(index.paths.group)) add(p);
|
|
16410
|
+
}
|
|
16411
|
+
return [...out];
|
|
16412
|
+
}
|
|
16220
16413
|
/** Set a single spec's status (bumps updatedAt). Caller invalidates the cache. */
|
|
16221
16414
|
applySpecStatus(kind, id, status) {
|
|
16222
16415
|
switch (kind) {
|
|
@@ -16254,16 +16447,16 @@ var init_specs2 = __esm({
|
|
|
16254
16447
|
const files = /* @__PURE__ */ new Set();
|
|
16255
16448
|
const sysPath = this.paths.specsSystem();
|
|
16256
16449
|
if (pathExists(sysPath)) {
|
|
16257
|
-
files.add(
|
|
16450
|
+
files.add(path15.resolve(sysPath));
|
|
16258
16451
|
}
|
|
16259
16452
|
for (const group of Object.values(index.paths)) {
|
|
16260
16453
|
for (const file of Object.values(group)) {
|
|
16261
|
-
files.add(
|
|
16454
|
+
files.add(path15.resolve(file));
|
|
16262
16455
|
}
|
|
16263
16456
|
}
|
|
16264
16457
|
for (const file of files) {
|
|
16265
|
-
if (
|
|
16266
|
-
snapshot.set(file,
|
|
16458
|
+
if (fs10.existsSync(file)) {
|
|
16459
|
+
snapshot.set(file, fs10.readFileSync(file, "utf8"));
|
|
16267
16460
|
}
|
|
16268
16461
|
}
|
|
16269
16462
|
return snapshot;
|
|
@@ -16277,20 +16470,20 @@ var init_specs2 = __esm({
|
|
|
16277
16470
|
const files = listFilesRecursive(specsDir, ".yaml");
|
|
16278
16471
|
const legacy = [];
|
|
16279
16472
|
for (const f of files) {
|
|
16280
|
-
const base =
|
|
16281
|
-
const dir =
|
|
16473
|
+
const base = path15.basename(f);
|
|
16474
|
+
const dir = path15.dirname(f);
|
|
16282
16475
|
if (base === "system.yaml") {
|
|
16283
|
-
legacy.push({ path: f, expected:
|
|
16476
|
+
legacy.push({ path: f, expected: path15.join(dir, ".index.yaml") });
|
|
16284
16477
|
} else if (base === "subsystem.yaml") {
|
|
16285
|
-
legacy.push({ path: f, expected:
|
|
16478
|
+
legacy.push({ path: f, expected: path15.join(dir, ".index.yaml") });
|
|
16286
16479
|
} else if (base === "component.yaml") {
|
|
16287
|
-
legacy.push({ path: f, expected:
|
|
16480
|
+
legacy.push({ path: f, expected: path15.join(dir, ".index.yaml") });
|
|
16288
16481
|
} else if (base === "group.yaml") {
|
|
16289
|
-
legacy.push({ path: f, expected:
|
|
16482
|
+
legacy.push({ path: f, expected: path15.join(dir, ".index.yaml") });
|
|
16290
16483
|
} else if (base === "interface.yaml") {
|
|
16291
|
-
legacy.push({ path: f, expected:
|
|
16484
|
+
legacy.push({ path: f, expected: path15.join(dir, ".interface.yaml") });
|
|
16292
16485
|
} else if (base === "implementation.yaml") {
|
|
16293
|
-
legacy.push({ path: f, expected:
|
|
16486
|
+
legacy.push({ path: f, expected: path15.join(dir, ".implementation.yaml") });
|
|
16294
16487
|
}
|
|
16295
16488
|
}
|
|
16296
16489
|
return legacy;
|
|
@@ -16357,6 +16550,35 @@ var init_specs2 = __esm({
|
|
|
16357
16550
|
}
|
|
16358
16551
|
return refs;
|
|
16359
16552
|
};
|
|
16553
|
+
const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
|
|
16554
|
+
const assertDeltaMarkers = (item, label) => {
|
|
16555
|
+
if (!item || typeof item !== "object") return;
|
|
16556
|
+
if ("remove" in item && typeof item.remove !== "boolean") {
|
|
16557
|
+
throw new Error(
|
|
16558
|
+
`Refusing to update ${label}: "remove" must be the boolean true, got ${JSON.stringify(item.remove)}. A non-boolean is ignored, which would leave the element in place while reporting success.`
|
|
16559
|
+
);
|
|
16560
|
+
}
|
|
16561
|
+
if ("action" in item && item.action !== "add" && item.action !== "delete") {
|
|
16562
|
+
throw new Error(
|
|
16563
|
+
`Refusing to update ${label}: unknown action ${JSON.stringify(item.action)} \u2014 expected "add" or "delete". An unknown verb is ignored, which would leave the element in place while reporting success.`
|
|
16564
|
+
);
|
|
16565
|
+
}
|
|
16566
|
+
};
|
|
16567
|
+
const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label) => {
|
|
16568
|
+
if (deltaItem?.remove === true || deltaItem?.action === "delete") {
|
|
16569
|
+
throw new Error(
|
|
16570
|
+
`Refusing to delete ${label} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
|
|
16571
|
+
);
|
|
16572
|
+
}
|
|
16573
|
+
const near = existingKeys.find(
|
|
16574
|
+
(k) => String(k) !== String(key) && normalizeIdentity(k) === normalizeIdentity(key)
|
|
16575
|
+
);
|
|
16576
|
+
if (near !== void 0) {
|
|
16577
|
+
throw new Error(
|
|
16578
|
+
`Refusing to add ${label} "${String(key)}": "${String(near)}" already exists and differs only in case or separators. Use the existing identity to edit it, or pick a name that is not a near-duplicate.`
|
|
16579
|
+
);
|
|
16580
|
+
}
|
|
16581
|
+
};
|
|
16360
16582
|
const mergeNarrative = (existingSteps, deltaSteps) => {
|
|
16361
16583
|
let steps = [...existingSteps];
|
|
16362
16584
|
const sortedDeltas = [...deltaSteps].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
@@ -16425,6 +16647,7 @@ var init_specs2 = __esm({
|
|
|
16425
16647
|
const mergeMethods = (existingMethods, deltaMethods) => {
|
|
16426
16648
|
const merged = [...existingMethods];
|
|
16427
16649
|
for (const deltaMethod of deltaMethods) {
|
|
16650
|
+
assertDeltaMarkers(deltaMethod, `method "${deltaMethod?.name}"`);
|
|
16428
16651
|
const idx = merged.findIndex((m) => m.name === deltaMethod.name);
|
|
16429
16652
|
if (idx !== -1) {
|
|
16430
16653
|
if (deltaMethod.remove === true || deltaMethod.action === "delete") {
|
|
@@ -16444,9 +16667,8 @@ var init_specs2 = __esm({
|
|
|
16444
16667
|
};
|
|
16445
16668
|
}
|
|
16446
16669
|
} else {
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
}
|
|
16670
|
+
assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
|
|
16671
|
+
merged.push(deltaMethod);
|
|
16450
16672
|
}
|
|
16451
16673
|
}
|
|
16452
16674
|
return merged;
|
|
@@ -16454,6 +16676,7 @@ var init_specs2 = __esm({
|
|
|
16454
16676
|
const mergeNamedArray = (existing, delta2) => {
|
|
16455
16677
|
const merged = [...existing];
|
|
16456
16678
|
for (const deltaItem of delta2) {
|
|
16679
|
+
assertDeltaMarkers(deltaItem, `entry "${deltaItem?.name}"`);
|
|
16457
16680
|
const idx = merged.findIndex((item) => item.name === deltaItem.name);
|
|
16458
16681
|
if (idx !== -1) {
|
|
16459
16682
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16466,9 +16689,8 @@ var init_specs2 = __esm({
|
|
|
16466
16689
|
};
|
|
16467
16690
|
}
|
|
16468
16691
|
} else {
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
}
|
|
16692
|
+
assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
|
|
16693
|
+
merged.push(deltaItem);
|
|
16472
16694
|
}
|
|
16473
16695
|
}
|
|
16474
16696
|
return merged;
|
|
@@ -16476,6 +16698,7 @@ var init_specs2 = __esm({
|
|
|
16476
16698
|
const mergeKeyedArray = (existing, delta2, keyOf) => {
|
|
16477
16699
|
const merged = [...existing];
|
|
16478
16700
|
for (const deltaItem of delta2) {
|
|
16701
|
+
assertDeltaMarkers(deltaItem, `entry "${keyOf(deltaItem)}"`);
|
|
16479
16702
|
const idx = merged.findIndex((item) => keyOf(item) === keyOf(deltaItem));
|
|
16480
16703
|
if (idx !== -1) {
|
|
16481
16704
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16483,7 +16706,8 @@ var init_specs2 = __esm({
|
|
|
16483
16706
|
} else {
|
|
16484
16707
|
merged[idx] = { ...merged[idx], ...deltaItem };
|
|
16485
16708
|
}
|
|
16486
|
-
} else
|
|
16709
|
+
} else {
|
|
16710
|
+
assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
|
|
16487
16711
|
merged.push(deltaItem);
|
|
16488
16712
|
}
|
|
16489
16713
|
}
|
|
@@ -16492,6 +16716,8 @@ var init_specs2 = __esm({
|
|
|
16492
16716
|
const mergePublicInterfaces = (existing, delta2) => {
|
|
16493
16717
|
const merged = [...existing];
|
|
16494
16718
|
for (const deltaItem of delta2) {
|
|
16719
|
+
const piKey = (i) => `${i?.component}.${i?.interface}`;
|
|
16720
|
+
assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
|
|
16495
16721
|
const idx = merged.findIndex((item) => item.component === deltaItem.component && item.interface === deltaItem.interface);
|
|
16496
16722
|
if (idx !== -1) {
|
|
16497
16723
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16503,9 +16729,8 @@ var init_specs2 = __esm({
|
|
|
16503
16729
|
};
|
|
16504
16730
|
}
|
|
16505
16731
|
} else {
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
}
|
|
16732
|
+
assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
|
|
16733
|
+
merged.push(deltaItem);
|
|
16509
16734
|
}
|
|
16510
16735
|
}
|
|
16511
16736
|
return merged;
|
|
@@ -16546,11 +16771,22 @@ var init_specs2 = __esm({
|
|
|
16546
16771
|
for (const deltaItem of delta2) {
|
|
16547
16772
|
const key = identityKeyOf(field, deltaItem);
|
|
16548
16773
|
const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf(field, item) === key);
|
|
16774
|
+
assertDeltaMarkers(deltaItem, `${field} entry "${String(key)}"`);
|
|
16549
16775
|
const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
|
|
16550
16776
|
if (idx !== -1) {
|
|
16551
16777
|
if (isDelete) merged.splice(idx, 1);
|
|
16552
16778
|
else merged[idx] = { ...merged[idx], ...deltaItem };
|
|
16553
|
-
} else
|
|
16779
|
+
} else {
|
|
16780
|
+
if (key !== null) {
|
|
16781
|
+
assertUnmatchedIsAnAddition(
|
|
16782
|
+
deltaItem,
|
|
16783
|
+
key,
|
|
16784
|
+
merged.map((i) => identityKeyOf(field, i)).filter((k) => k !== null),
|
|
16785
|
+
field
|
|
16786
|
+
);
|
|
16787
|
+
} else if (isDelete) {
|
|
16788
|
+
throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
|
|
16789
|
+
}
|
|
16554
16790
|
merged.push(deltaItem);
|
|
16555
16791
|
}
|
|
16556
16792
|
}
|
|
@@ -16694,8 +16930,8 @@ __export(agent_resolver_exports, {
|
|
|
16694
16930
|
resolveAgentTopology: () => resolveAgentTopology
|
|
16695
16931
|
});
|
|
16696
16932
|
function listFilesRecursiveSafe(dirPath, ext) {
|
|
16697
|
-
if (!
|
|
16698
|
-
const nameLower =
|
|
16933
|
+
if (!fs11.existsSync(dirPath)) return [];
|
|
16934
|
+
const nameLower = path16.basename(dirPath).toLowerCase();
|
|
16699
16935
|
const IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
16700
16936
|
"node_modules",
|
|
16701
16937
|
"target",
|
|
@@ -16710,10 +16946,10 @@ function listFilesRecursiveSafe(dirPath, ext) {
|
|
|
16710
16946
|
".vscode"
|
|
16711
16947
|
]);
|
|
16712
16948
|
if (IGNORED_DIRS.has(nameLower)) return [];
|
|
16713
|
-
const entries =
|
|
16949
|
+
const entries = fs11.readdirSync(dirPath, { withFileTypes: true });
|
|
16714
16950
|
const files = [];
|
|
16715
16951
|
for (const entry of entries) {
|
|
16716
|
-
const fullPath =
|
|
16952
|
+
const fullPath = path16.join(dirPath, entry.name);
|
|
16717
16953
|
if (entry.isDirectory()) {
|
|
16718
16954
|
files.push(...listFilesRecursiveSafe(fullPath, ext));
|
|
16719
16955
|
} else if (entry.isFile() && entry.name.endsWith(ext)) {
|
|
@@ -16726,8 +16962,8 @@ function getProjectFiles(projectDir) {
|
|
|
16726
16962
|
let files = projectFilesCache.get(projectDir);
|
|
16727
16963
|
if (!files) {
|
|
16728
16964
|
files = [];
|
|
16729
|
-
const srcDir =
|
|
16730
|
-
const legacySrcDir =
|
|
16965
|
+
const srcDir = path16.join(projectDir, "src");
|
|
16966
|
+
const legacySrcDir = path16.join(projectDir, "legacy-src");
|
|
16731
16967
|
let searchDir = projectDir;
|
|
16732
16968
|
if (pathExists(srcDir)) {
|
|
16733
16969
|
searchDir = srcDir;
|
|
@@ -16742,7 +16978,7 @@ function getProjectFiles(projectDir) {
|
|
|
16742
16978
|
files.push(...listFilesRecursiveSafe(searchDir, ext));
|
|
16743
16979
|
}
|
|
16744
16980
|
const rootDir = getProjectRoot();
|
|
16745
|
-
files = files.map((f) =>
|
|
16981
|
+
files = files.map((f) => path16.relative(rootDir, f).replace(/\\/g, "/"));
|
|
16746
16982
|
projectFilesCache.set(projectDir, files);
|
|
16747
16983
|
}
|
|
16748
16984
|
return files;
|
|
@@ -16767,8 +17003,8 @@ function inferSourcePathForComponent(comp, subsystems) {
|
|
|
16767
17003
|
let bestFile = null;
|
|
16768
17004
|
let bestScore = -1;
|
|
16769
17005
|
for (const f of files) {
|
|
16770
|
-
const ext =
|
|
16771
|
-
const base =
|
|
17006
|
+
const ext = path16.extname(f);
|
|
17007
|
+
const base = path16.basename(f, ext).toLowerCase();
|
|
16772
17008
|
if (candidates.has(base)) {
|
|
16773
17009
|
let score = 0;
|
|
16774
17010
|
const normalizedPath = f.toLowerCase();
|
|
@@ -16864,7 +17100,7 @@ function resolveAgentTopology() {
|
|
|
16864
17100
|
});
|
|
16865
17101
|
for (const sub of subsystems) {
|
|
16866
17102
|
if (sub.projectPath) {
|
|
16867
|
-
const mountSpecPath =
|
|
17103
|
+
const mountSpecPath = path16.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/");
|
|
16868
17104
|
agents.push({
|
|
16869
17105
|
id: `${sub.id}-owner`,
|
|
16870
17106
|
name: `${sub.name} (chained subproject)`,
|
|
@@ -16886,9 +17122,9 @@ function resolveAgentTopology() {
|
|
|
16886
17122
|
}
|
|
16887
17123
|
const subComponents = components.filter((c) => c.subsystem === sub.id);
|
|
16888
17124
|
const ownedPaths = [];
|
|
16889
|
-
ownedPaths.push(
|
|
17125
|
+
ownedPaths.push(path16.relative(getProjectRoot(), getSubsystemPath(sub.id)).replace(/\\/g, "/"));
|
|
16890
17126
|
for (const c of subComponents) {
|
|
16891
|
-
ownedPaths.push(
|
|
17127
|
+
ownedPaths.push(path16.relative(getProjectRoot(), getComponentPath(c.id, sub.id)).replace(/\\/g, "/"));
|
|
16892
17128
|
}
|
|
16893
17129
|
if (!config.rules.generateComponentImplementers) {
|
|
16894
17130
|
for (const comp of subComponents) {
|
|
@@ -16961,10 +17197,10 @@ function resolveAgentTopology() {
|
|
|
16961
17197
|
}
|
|
16962
17198
|
const dependencies = comp.dependsOn.map((depId) => `${depId}-implementer`);
|
|
16963
17199
|
const readPaths = [
|
|
16964
|
-
|
|
16965
|
-
|
|
16966
|
-
...compInterfaces.map((i) =>
|
|
16967
|
-
...compImpls.map((impl) =>
|
|
17200
|
+
path16.relative(getProjectRoot(), AI_PATHS.specsSystem()).replace(/\\/g, "/"),
|
|
17201
|
+
path16.relative(getProjectRoot(), getComponentPath(comp.id, comp.subsystem)).replace(/\\/g, "/"),
|
|
17202
|
+
...compInterfaces.map((i) => path16.relative(getProjectRoot(), getInterfacePath(i.id, comp.id)).replace(/\\/g, "/")),
|
|
17203
|
+
...compImpls.map((impl) => path16.relative(getProjectRoot(), getImplementationPath(impl.id, impl.contract)).replace(/\\/g, "/"))
|
|
16968
17204
|
];
|
|
16969
17205
|
agents.push({
|
|
16970
17206
|
id: `${comp.id}-implementer`,
|
|
@@ -17047,12 +17283,12 @@ ${guidance.trim()}
|
|
|
17047
17283
|
variantGuidance: record.variantGuidance || void 0
|
|
17048
17284
|
};
|
|
17049
17285
|
}
|
|
17050
|
-
var
|
|
17286
|
+
var path16, fs11, projectFilesCache, UnknownAgentError;
|
|
17051
17287
|
var init_agent_resolver = __esm({
|
|
17052
17288
|
"src/core/agent_resolver.ts"() {
|
|
17053
17289
|
"use strict";
|
|
17054
|
-
|
|
17055
|
-
|
|
17290
|
+
path16 = __toESM(require("path"));
|
|
17291
|
+
fs11 = __toESM(require("fs"));
|
|
17056
17292
|
init_loader();
|
|
17057
17293
|
init_fs();
|
|
17058
17294
|
init_errors();
|
|
@@ -17083,12 +17319,12 @@ __export(loader_exports, {
|
|
|
17083
17319
|
saveTopologyConfig: () => saveTopologyConfig
|
|
17084
17320
|
});
|
|
17085
17321
|
function aiPathsAt(rootDir) {
|
|
17086
|
-
const resolvedRoot =
|
|
17322
|
+
const resolvedRoot = path17.resolve(rootDir);
|
|
17087
17323
|
const aiDirAt = (...segments) => {
|
|
17088
|
-
const waiPath =
|
|
17089
|
-
const waironPath =
|
|
17090
|
-
const base = !
|
|
17091
|
-
return
|
|
17324
|
+
const waiPath = path17.join(resolvedRoot, ".wai");
|
|
17325
|
+
const waironPath = path17.join(resolvedRoot, ".wairon");
|
|
17326
|
+
const base = !fs12.existsSync(waiPath) && fs12.existsSync(waironPath) ? waironPath : waiPath;
|
|
17327
|
+
return path17.join(base, ...segments);
|
|
17092
17328
|
};
|
|
17093
17329
|
const specsDir = () => {
|
|
17094
17330
|
try {
|
|
@@ -17096,7 +17332,7 @@ function aiPathsAt(rootDir) {
|
|
|
17096
17332
|
if (pathExists(projConfig)) {
|
|
17097
17333
|
const raw = readYamlFile(projConfig);
|
|
17098
17334
|
if (raw && raw.paths && raw.paths.specsDir) {
|
|
17099
|
-
return
|
|
17335
|
+
return path17.resolve(resolvedRoot, raw.paths.specsDir);
|
|
17100
17336
|
}
|
|
17101
17337
|
}
|
|
17102
17338
|
} catch {
|
|
@@ -17117,12 +17353,12 @@ function aiPathsAt(rootDir) {
|
|
|
17117
17353
|
contextDomainsMd: () => aiDirAt("context", "domains.md"),
|
|
17118
17354
|
contextWaironGuideMd: () => aiDirAt("context", "wairon-guide.md"),
|
|
17119
17355
|
specsDir,
|
|
17120
|
-
specsSystem: () =>
|
|
17121
|
-
specsSubsystemsDir: () =>
|
|
17122
|
-
specsComponentsDir: () =>
|
|
17123
|
-
specsInterfacesDir: () =>
|
|
17124
|
-
specsImplementationsDir: () =>
|
|
17125
|
-
specsTypesDir: () =>
|
|
17356
|
+
specsSystem: () => path17.join(specsDir(), ".index.yaml"),
|
|
17357
|
+
specsSubsystemsDir: () => path17.join(specsDir(), "subsystems"),
|
|
17358
|
+
specsComponentsDir: () => path17.join(specsDir(), "components"),
|
|
17359
|
+
specsInterfacesDir: () => path17.join(specsDir(), "interfaces"),
|
|
17360
|
+
specsImplementationsDir: () => path17.join(specsDir(), "implementations"),
|
|
17361
|
+
specsTypesDir: () => path17.join(specsDir(), "types")
|
|
17126
17362
|
};
|
|
17127
17363
|
}
|
|
17128
17364
|
function isProjectInitialized() {
|
|
@@ -17165,12 +17401,12 @@ function loadTopologyConfig() {
|
|
|
17165
17401
|
function saveTopologyConfig(config) {
|
|
17166
17402
|
writeYamlFile(AI_PATHS.topologyConfig(), config);
|
|
17167
17403
|
}
|
|
17168
|
-
var
|
|
17404
|
+
var fs12, path17, AI_PATHS;
|
|
17169
17405
|
var init_loader = __esm({
|
|
17170
17406
|
"src/config/loader.ts"() {
|
|
17171
17407
|
"use strict";
|
|
17172
|
-
|
|
17173
|
-
|
|
17408
|
+
fs12 = __toESM(require("fs"));
|
|
17409
|
+
path17 = __toESM(require("path"));
|
|
17174
17410
|
init_fs();
|
|
17175
17411
|
init_yaml();
|
|
17176
17412
|
init_errors();
|
|
@@ -17210,7 +17446,7 @@ __export(domains_exports, {
|
|
|
17210
17446
|
resolveDomains: () => resolveDomains
|
|
17211
17447
|
});
|
|
17212
17448
|
function rel(p) {
|
|
17213
|
-
return
|
|
17449
|
+
return path19.relative(process.cwd(), p).replace(/\\/g, "/");
|
|
17214
17450
|
}
|
|
17215
17451
|
function deriveSubsystemDomains() {
|
|
17216
17452
|
const subsystems = loadSubsystemSpecs();
|
|
@@ -17260,11 +17496,11 @@ function removeFreeStandingDomain(id) {
|
|
|
17260
17496
|
config.domains.splice(idx, 1);
|
|
17261
17497
|
saveTopologyConfig(config);
|
|
17262
17498
|
}
|
|
17263
|
-
var
|
|
17499
|
+
var path19;
|
|
17264
17500
|
var init_domains = __esm({
|
|
17265
17501
|
"src/core/domains.ts"() {
|
|
17266
17502
|
"use strict";
|
|
17267
|
-
|
|
17503
|
+
path19 = __toESM(require("path"));
|
|
17268
17504
|
init_loader();
|
|
17269
17505
|
init_specs2();
|
|
17270
17506
|
init_errors();
|
|
@@ -17595,6 +17831,7 @@ __export(src_exports, {
|
|
|
17595
17831
|
skillExtendErrors: () => skillExtendErrors,
|
|
17596
17832
|
skillsDirForTarget: () => skillsDirForTarget,
|
|
17597
17833
|
snapshotSpecFiles: () => snapshotSpecFiles,
|
|
17834
|
+
specPathsInScope: () => specPathsInScope,
|
|
17598
17835
|
splitNamespace: () => splitNamespace,
|
|
17599
17836
|
stateIdEquals: () => stateIdEquals,
|
|
17600
17837
|
stateIdString: () => stateIdString,
|
|
@@ -17625,8 +17862,8 @@ init_defaults();
|
|
|
17625
17862
|
init_loader();
|
|
17626
17863
|
|
|
17627
17864
|
// src/core/detection.ts
|
|
17628
|
-
var
|
|
17629
|
-
var
|
|
17865
|
+
var fs13 = __toESM(require("fs"));
|
|
17866
|
+
var path18 = __toESM(require("path"));
|
|
17630
17867
|
init_defaults();
|
|
17631
17868
|
var PACKAGE_MARKERS = [
|
|
17632
17869
|
"package.json",
|
|
@@ -17678,7 +17915,7 @@ function deduplicateIds(candidates, existingIds = /* @__PURE__ */ new Set()) {
|
|
|
17678
17915
|
});
|
|
17679
17916
|
}
|
|
17680
17917
|
function parseGitmodules(filePath) {
|
|
17681
|
-
const content =
|
|
17918
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
17682
17919
|
const entries = [];
|
|
17683
17920
|
let current2 = {};
|
|
17684
17921
|
for (const line of content.split("\n")) {
|
|
@@ -17700,8 +17937,8 @@ function parseGitmodules(filePath) {
|
|
|
17700
17937
|
return entries;
|
|
17701
17938
|
}
|
|
17702
17939
|
function detectGitSubmodules(projectRoot) {
|
|
17703
|
-
const gitmodulesPath =
|
|
17704
|
-
if (!
|
|
17940
|
+
const gitmodulesPath = path18.join(projectRoot, ".gitmodules");
|
|
17941
|
+
if (!fs13.existsSync(gitmodulesPath)) return [];
|
|
17705
17942
|
return parseGitmodules(gitmodulesPath).map((entry) => ({
|
|
17706
17943
|
suggestedId: pathToId(entry.path),
|
|
17707
17944
|
suggestedName: pathToName(entry.path),
|
|
@@ -17719,18 +17956,18 @@ function walkForGit(projectRoot, currentDir, depth, results) {
|
|
|
17719
17956
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
17720
17957
|
let entries;
|
|
17721
17958
|
try {
|
|
17722
|
-
entries =
|
|
17959
|
+
entries = fs13.readdirSync(currentDir, { withFileTypes: true });
|
|
17723
17960
|
} catch {
|
|
17724
17961
|
return;
|
|
17725
17962
|
}
|
|
17726
17963
|
for (const entry of entries) {
|
|
17727
17964
|
if (!entry.isDirectory()) continue;
|
|
17728
17965
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
17729
|
-
const fullPath =
|
|
17730
|
-
const relPath = normalizePath3(
|
|
17966
|
+
const fullPath = path18.join(currentDir, entry.name);
|
|
17967
|
+
const relPath = normalizePath3(path18.relative(projectRoot, fullPath));
|
|
17731
17968
|
if (relPath === "" || relPath === ".") continue;
|
|
17732
|
-
const gitPath =
|
|
17733
|
-
if (
|
|
17969
|
+
const gitPath = path18.join(fullPath, ".git");
|
|
17970
|
+
if (fs13.existsSync(gitPath)) {
|
|
17734
17971
|
results.push({
|
|
17735
17972
|
suggestedId: pathToId(relPath),
|
|
17736
17973
|
suggestedName: pathToName(relPath),
|
|
@@ -17752,17 +17989,17 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
17752
17989
|
if (depth > MAX_SCAN_DEPTH) return;
|
|
17753
17990
|
let entries;
|
|
17754
17991
|
try {
|
|
17755
|
-
entries =
|
|
17992
|
+
entries = fs13.readdirSync(currentDir, { withFileTypes: true });
|
|
17756
17993
|
} catch {
|
|
17757
17994
|
return;
|
|
17758
17995
|
}
|
|
17759
17996
|
for (const entry of entries) {
|
|
17760
17997
|
if (!entry.isDirectory()) continue;
|
|
17761
17998
|
if (SCAN_EXCLUDE_DIRS.has(entry.name)) continue;
|
|
17762
|
-
const fullPath =
|
|
17763
|
-
const relPath = normalizePath3(
|
|
17999
|
+
const fullPath = path18.join(currentDir, entry.name);
|
|
18000
|
+
const relPath = normalizePath3(path18.relative(projectRoot, fullPath));
|
|
17764
18001
|
if (relPath === "" || relPath === ".") continue;
|
|
17765
|
-
const hasMarker = PACKAGE_MARKERS.some((m) =>
|
|
18002
|
+
const hasMarker = PACKAGE_MARKERS.some((m) => fs13.existsSync(path18.join(fullPath, m)));
|
|
17766
18003
|
if (hasMarker) {
|
|
17767
18004
|
results.push({
|
|
17768
18005
|
suggestedId: pathToId(relPath),
|
|
@@ -17776,8 +18013,8 @@ function walkForPackages(projectRoot, currentDir, depth, results) {
|
|
|
17776
18013
|
}
|
|
17777
18014
|
}
|
|
17778
18015
|
function pathToId(relPath) {
|
|
17779
|
-
const
|
|
17780
|
-
return
|
|
18016
|
+
const basename10 = path18.basename(relPath);
|
|
18017
|
+
return basename10.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
17781
18018
|
}
|
|
17782
18019
|
function pathToName(relPath) {
|
|
17783
18020
|
const id = pathToId(relPath);
|
|
@@ -17797,8 +18034,8 @@ init_rules();
|
|
|
17797
18034
|
init_specs2();
|
|
17798
18035
|
|
|
17799
18036
|
// src/core/provision.ts
|
|
17800
|
-
var
|
|
17801
|
-
var
|
|
18037
|
+
var fs14 = __toESM(require("fs"));
|
|
18038
|
+
var path20 = __toESM(require("path"));
|
|
17802
18039
|
init_specs2();
|
|
17803
18040
|
init_loader();
|
|
17804
18041
|
init_fs();
|
|
@@ -17846,7 +18083,7 @@ function provisionProject(name) {
|
|
|
17846
18083
|
function ensureProjectInitialized(fallbackName) {
|
|
17847
18084
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17848
18085
|
const paths = aiPathsAt(getProjectRoot());
|
|
17849
|
-
const hasSystem =
|
|
18086
|
+
const hasSystem = fs14.existsSync(paths.specsSystem());
|
|
17850
18087
|
let name = fallbackName;
|
|
17851
18088
|
if (hasSystem) {
|
|
17852
18089
|
const existing = loadSystemSpec();
|
|
@@ -17854,7 +18091,7 @@ function ensureProjectInitialized(fallbackName) {
|
|
|
17854
18091
|
}
|
|
17855
18092
|
let wroteConfig = false;
|
|
17856
18093
|
let wroteSystem = false;
|
|
17857
|
-
if (!
|
|
18094
|
+
if (!fs14.existsSync(paths.projectConfig())) {
|
|
17858
18095
|
saveProjectConfig(defaultProjectConfig(name, now));
|
|
17859
18096
|
wroteConfig = true;
|
|
17860
18097
|
}
|
|
@@ -17883,11 +18120,11 @@ function promoteAllComplete() {
|
|
|
17883
18120
|
function walkChainedSubprojects(projectRoot, onChild) {
|
|
17884
18121
|
const visited = /* @__PURE__ */ new Set();
|
|
17885
18122
|
const walk = (dir) => {
|
|
17886
|
-
const resolved =
|
|
18123
|
+
const resolved = path20.resolve(dir);
|
|
17887
18124
|
if (visited.has(resolved)) return;
|
|
17888
18125
|
visited.add(resolved);
|
|
17889
18126
|
const specsDir = aiPathsAt(dir).specsDir();
|
|
17890
|
-
if (!
|
|
18127
|
+
if (!fs14.existsSync(specsDir)) return;
|
|
17891
18128
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17892
18129
|
let raw;
|
|
17893
18130
|
try {
|
|
@@ -17905,7 +18142,7 @@ function walkChainedSubprojects(projectRoot, onChild) {
|
|
|
17905
18142
|
continue;
|
|
17906
18143
|
}
|
|
17907
18144
|
const id = raw.id;
|
|
17908
|
-
onChild(childDir, typeof id === "string" ? id :
|
|
18145
|
+
onChild(childDir, typeof id === "string" ? id : path20.basename(childDir));
|
|
17909
18146
|
walk(childDir);
|
|
17910
18147
|
}
|
|
17911
18148
|
};
|
|
@@ -17914,7 +18151,7 @@ function walkChainedSubprojects(projectRoot, onChild) {
|
|
|
17914
18151
|
function listDirectChainedSubprojects(projectRoot) {
|
|
17915
18152
|
const out = [];
|
|
17916
18153
|
const specsDir = aiPathsAt(projectRoot).specsDir();
|
|
17917
|
-
if (!
|
|
18154
|
+
if (!fs14.existsSync(specsDir)) return out;
|
|
17918
18155
|
for (const file of listFilesRecursive(specsDir, ".yaml")) {
|
|
17919
18156
|
let raw;
|
|
17920
18157
|
try {
|
|
@@ -17932,12 +18169,12 @@ function listDirectChainedSubprojects(projectRoot) {
|
|
|
17932
18169
|
continue;
|
|
17933
18170
|
}
|
|
17934
18171
|
const id = raw.id;
|
|
17935
|
-
out.push({ dir, subsystemId: typeof id === "string" ? id :
|
|
18172
|
+
out.push({ dir, subsystemId: typeof id === "string" ? id : path20.basename(dir) });
|
|
17936
18173
|
}
|
|
17937
18174
|
return out;
|
|
17938
18175
|
}
|
|
17939
18176
|
function childHasSpecsButNoConfig(childDir) {
|
|
17940
|
-
return
|
|
18177
|
+
return fs14.existsSync(aiPathsAt(childDir).specsDir()) && !fs14.existsSync(aiPathsAt(childDir).projectConfig());
|
|
17941
18178
|
}
|
|
17942
18179
|
function findChainingSubprojectsMissingConfig(projectRoot) {
|
|
17943
18180
|
const missing = [];
|
|
@@ -17986,14 +18223,14 @@ function moveSubsystemProject(subsystemId, newProjectPath) {
|
|
|
17986
18223
|
const oldDir = assertContainedProjectPath(root, sub.projectPath);
|
|
17987
18224
|
const newDir = assertContainedProjectPath(root, nextPath);
|
|
17988
18225
|
if (oldDir !== newDir) {
|
|
17989
|
-
if (!
|
|
18226
|
+
if (!fs14.existsSync(oldDir)) {
|
|
17990
18227
|
throw new WaironError(`Subproject directory not found at its current path: ${oldDir}`);
|
|
17991
18228
|
}
|
|
17992
|
-
if (
|
|
18229
|
+
if (fs14.existsSync(newDir)) {
|
|
17993
18230
|
throw new WaironError(`Target directory already exists: ${newDir}`);
|
|
17994
18231
|
}
|
|
17995
|
-
ensureDir(
|
|
17996
|
-
|
|
18232
|
+
ensureDir(path20.dirname(newDir));
|
|
18233
|
+
fs14.renameSync(oldDir, newDir);
|
|
17997
18234
|
}
|
|
17998
18235
|
saveSubsystemSpec({ ...sub, projectPath: nextPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
17999
18236
|
invalidateSpecCache();
|
|
@@ -18002,9 +18239,9 @@ function toPosixPath(p) {
|
|
|
18002
18239
|
return p.replace(/\\/g, "/");
|
|
18003
18240
|
}
|
|
18004
18241
|
function isWithinDir(dir, file) {
|
|
18005
|
-
const d =
|
|
18006
|
-
const f =
|
|
18007
|
-
return f === d || f.startsWith(d +
|
|
18242
|
+
const d = path20.resolve(dir);
|
|
18243
|
+
const f = path20.resolve(file);
|
|
18244
|
+
return f === d || f.startsWith(d + path20.sep);
|
|
18008
18245
|
}
|
|
18009
18246
|
function externalizeSubsystem(subsystemId, projectPath) {
|
|
18010
18247
|
if (subsystemId.includes("::")) {
|
|
@@ -18016,14 +18253,14 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
18016
18253
|
}
|
|
18017
18254
|
const parentRoot = getProjectRoot();
|
|
18018
18255
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
18019
|
-
const fooDir =
|
|
18020
|
-
if (!
|
|
18256
|
+
const fooDir = path20.join(parentSpecsDir, subsystemId);
|
|
18257
|
+
if (!fs14.existsSync(fooDir)) {
|
|
18021
18258
|
throw new WaironError(`subsystem specs directory not found: ${fooDir}`);
|
|
18022
18259
|
}
|
|
18023
18260
|
const relPath = toPosixPath(projectPath);
|
|
18024
18261
|
const childDir = assertContainedProjectPath(parentRoot, relPath);
|
|
18025
|
-
const childFooDir =
|
|
18026
|
-
if (
|
|
18262
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
18263
|
+
if (fs14.existsSync(childFooDir)) {
|
|
18027
18264
|
throw new WaironError(`target already contains a "${subsystemId}" subsystem: ${childFooDir}`);
|
|
18028
18265
|
}
|
|
18029
18266
|
const renameMap = buildRenameMap(
|
|
@@ -18033,17 +18270,17 @@ function externalizeSubsystem(subsystemId, projectPath) {
|
|
|
18033
18270
|
);
|
|
18034
18271
|
const childSystemName = foo.name || subsystemId;
|
|
18035
18272
|
runWithProjectRoot(childDir, () => {
|
|
18036
|
-
ensureDir(
|
|
18273
|
+
ensureDir(path20.join(childDir, ".wai", "specs"));
|
|
18037
18274
|
provisionProject(childSystemName);
|
|
18038
18275
|
});
|
|
18039
|
-
ensureDir(
|
|
18040
|
-
|
|
18041
|
-
patchSubsystemIndex(
|
|
18276
|
+
ensureDir(path20.dirname(childFooDir));
|
|
18277
|
+
fs14.renameSync(fooDir, childFooDir);
|
|
18278
|
+
patchSubsystemIndex(path20.join(childFooDir, ".index.yaml"), (s) => {
|
|
18042
18279
|
s.parentSystem = childSystemName;
|
|
18043
18280
|
delete s.projectPath;
|
|
18044
18281
|
});
|
|
18045
18282
|
ensureDir(fooDir);
|
|
18046
|
-
writeYamlFile(
|
|
18283
|
+
writeYamlFile(path20.join(fooDir, ".index.yaml"), {
|
|
18047
18284
|
id: subsystemId,
|
|
18048
18285
|
name: foo.name,
|
|
18049
18286
|
description: foo.description,
|
|
@@ -18069,9 +18306,9 @@ function internalizeSubsystem(subsystemId) {
|
|
|
18069
18306
|
const parentRoot = getProjectRoot();
|
|
18070
18307
|
const parentSpecsDir = aiPathsAt(parentRoot).specsDir();
|
|
18071
18308
|
const childDir = assertContainedProjectPath(parentRoot, foo.projectPath);
|
|
18072
|
-
const childWai =
|
|
18073
|
-
const childFooDir =
|
|
18074
|
-
if (!
|
|
18309
|
+
const childWai = path20.join(childDir, ".wai");
|
|
18310
|
+
const childFooDir = path20.join(childDir, ".wai", "specs", subsystemId);
|
|
18311
|
+
if (!fs14.existsSync(childFooDir)) {
|
|
18075
18312
|
throw new WaironError(`external subproject missing subsystem "${subsystemId}": ${childFooDir}`);
|
|
18076
18313
|
}
|
|
18077
18314
|
const childOwnSubs = runWithProjectRoot(childDir, () => loadSubsystemSpecs()).filter((s) => !s.id.includes("::"));
|
|
@@ -18084,15 +18321,15 @@ function internalizeSubsystem(subsystemId) {
|
|
|
18084
18321
|
false
|
|
18085
18322
|
);
|
|
18086
18323
|
const parentSystemName = loadSystemSpec()?.name ?? foo.parentSystem;
|
|
18087
|
-
const fooDir =
|
|
18088
|
-
|
|
18089
|
-
ensureDir(
|
|
18090
|
-
|
|
18091
|
-
patchSubsystemIndex(
|
|
18324
|
+
const fooDir = path20.join(parentSpecsDir, subsystemId);
|
|
18325
|
+
fs14.rmSync(fooDir, { recursive: true, force: true });
|
|
18326
|
+
ensureDir(path20.dirname(fooDir));
|
|
18327
|
+
fs14.renameSync(childFooDir, fooDir);
|
|
18328
|
+
patchSubsystemIndex(path20.join(fooDir, ".index.yaml"), (s) => {
|
|
18092
18329
|
s.parentSystem = parentSystemName;
|
|
18093
18330
|
delete s.projectPath;
|
|
18094
18331
|
});
|
|
18095
|
-
|
|
18332
|
+
fs14.rmSync(childWai, { recursive: true, force: true });
|
|
18096
18333
|
rewriteRefsInDir(parentSpecsDir, renameMap, fooDir);
|
|
18097
18334
|
invalidateSpecCache();
|
|
18098
18335
|
}
|
|
@@ -18189,7 +18426,7 @@ function rewriteRefsInDir(specsDir, renameMap, excludeDir) {
|
|
|
18189
18426
|
}
|
|
18190
18427
|
}
|
|
18191
18428
|
function patchSubsystemIndex(indexPath, mutate) {
|
|
18192
|
-
if (!
|
|
18429
|
+
if (!fs14.existsSync(indexPath)) return;
|
|
18193
18430
|
const raw = readYamlFile(indexPath);
|
|
18194
18431
|
mutate(raw);
|
|
18195
18432
|
raw.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -18203,8 +18440,8 @@ init_statehash();
|
|
|
18203
18440
|
init_agent_resolver();
|
|
18204
18441
|
|
|
18205
18442
|
// src/core/skills.ts
|
|
18206
|
-
var
|
|
18207
|
-
var
|
|
18443
|
+
var path21 = __toESM(require("path"));
|
|
18444
|
+
var fs15 = __toESM(require("fs"));
|
|
18208
18445
|
init_fs();
|
|
18209
18446
|
init_defaults();
|
|
18210
18447
|
init_extensions();
|
|
@@ -18338,16 +18575,16 @@ function packNamespace(pack) {
|
|
|
18338
18575
|
return pack.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
18339
18576
|
}
|
|
18340
18577
|
function extensionsFor(builtin, packSkills) {
|
|
18341
|
-
return packSkills.filter((s) => s.extends === builtin &&
|
|
18578
|
+
return packSkills.filter((s) => s.extends === builtin && fs15.existsSync(s.sourcePath));
|
|
18342
18579
|
}
|
|
18343
18580
|
function composeBuiltinSkill(name, packSkills) {
|
|
18344
18581
|
const srcPath = skillTemplatePath(name);
|
|
18345
|
-
const base =
|
|
18582
|
+
const base = fs15.existsSync(srcPath) ? fs15.readFileSync(srcPath, "utf-8") : "";
|
|
18346
18583
|
const sections = extensionsFor(name, packSkills);
|
|
18347
18584
|
if (sections.length === 0) return base;
|
|
18348
18585
|
const parts = [base.trimEnd()];
|
|
18349
18586
|
for (const section of sections) {
|
|
18350
|
-
const body =
|
|
18587
|
+
const body = fs15.readFileSync(section.sourcePath, "utf-8").replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "");
|
|
18351
18588
|
parts.push(`## Platform: ${section.pack}`, body.trim());
|
|
18352
18589
|
}
|
|
18353
18590
|
return `${parts.join("\n\n")}
|
|
@@ -18363,16 +18600,16 @@ function readFrontmatter(raw, fallbackName) {
|
|
|
18363
18600
|
return { name: field("name") || fallbackName, description: field("description") };
|
|
18364
18601
|
}
|
|
18365
18602
|
function builtinSkillsDir() {
|
|
18366
|
-
return
|
|
18603
|
+
return path21.resolve(__dirname, "..", "templates", "skills");
|
|
18367
18604
|
}
|
|
18368
18605
|
function skillTemplatePath(name) {
|
|
18369
|
-
return
|
|
18606
|
+
return path21.join(builtinSkillsDir(), `${name}.md`);
|
|
18370
18607
|
}
|
|
18371
18608
|
function skillDestPath(type, destDir, name) {
|
|
18372
18609
|
if (type === "claude" || type === "codex" || type === "gemini" || type === "agy") {
|
|
18373
|
-
return
|
|
18610
|
+
return path21.join(destDir, name, "SKILL.md");
|
|
18374
18611
|
}
|
|
18375
|
-
return
|
|
18612
|
+
return path21.join(destDir, `${name}.md`);
|
|
18376
18613
|
}
|
|
18377
18614
|
function skillsDirForTarget(type) {
|
|
18378
18615
|
switch (type) {
|
|
@@ -18407,22 +18644,22 @@ function exportSddSkills(targetTypes) {
|
|
|
18407
18644
|
ensureDir(destDir);
|
|
18408
18645
|
destinations.push(destDir);
|
|
18409
18646
|
for (const name of SKILL_NAMES) {
|
|
18410
|
-
if (!
|
|
18647
|
+
if (!fs15.existsSync(skillTemplatePath(name))) continue;
|
|
18411
18648
|
const content = composeBuiltinSkill(name, packSkills.filter((s) => s.targets.includes(type)));
|
|
18412
18649
|
const destPath = skillDestPath(type, destDir, name);
|
|
18413
|
-
ensureDir(
|
|
18414
|
-
|
|
18650
|
+
ensureDir(path21.dirname(destPath));
|
|
18651
|
+
fs15.writeFileSync(destPath, content, "utf-8");
|
|
18415
18652
|
fileCount++;
|
|
18416
18653
|
}
|
|
18417
18654
|
for (const skill of packSkills) {
|
|
18418
18655
|
if (skill.extends !== void 0) continue;
|
|
18419
18656
|
if (!skill.targets.includes(type)) continue;
|
|
18420
|
-
if (!
|
|
18657
|
+
if (!fs15.existsSync(skill.sourcePath)) continue;
|
|
18421
18658
|
const id = packSkillId(skill);
|
|
18422
|
-
const content =
|
|
18659
|
+
const content = fs15.readFileSync(skill.sourcePath, "utf-8");
|
|
18423
18660
|
const destPath = skillDestPath(type, destDir, id);
|
|
18424
|
-
ensureDir(
|
|
18425
|
-
|
|
18661
|
+
ensureDir(path21.dirname(destPath));
|
|
18662
|
+
fs15.writeFileSync(destPath, content, "utf-8");
|
|
18426
18663
|
fileCount++;
|
|
18427
18664
|
}
|
|
18428
18665
|
}
|
|
@@ -18435,12 +18672,12 @@ function checkSkillFreshness(type) {
|
|
|
18435
18672
|
const packSkills = loadProjectExtensions2().skills.filter((s) => s.targets.includes(type));
|
|
18436
18673
|
for (const name of SKILL_NAMES) {
|
|
18437
18674
|
const destPath = skillDestPath(type, dir, name);
|
|
18438
|
-
if (!
|
|
18675
|
+
if (!fs15.existsSync(destPath)) {
|
|
18439
18676
|
result.missing.push(name);
|
|
18440
18677
|
continue;
|
|
18441
18678
|
}
|
|
18442
18679
|
const want = composeBuiltinSkill(name, packSkills);
|
|
18443
|
-
const have =
|
|
18680
|
+
const have = fs15.readFileSync(destPath, "utf-8");
|
|
18444
18681
|
if (have === want) result.ok.push(name);
|
|
18445
18682
|
else result.stale.push(name);
|
|
18446
18683
|
}
|
|
@@ -18460,7 +18697,7 @@ var SkillResourceNotFoundError = class extends Error {
|
|
|
18460
18697
|
}
|
|
18461
18698
|
};
|
|
18462
18699
|
function readSkillFrontmatter(name) {
|
|
18463
|
-
return readFrontmatter(
|
|
18700
|
+
return readFrontmatter(fs15.readFileSync(skillTemplatePath(name), "utf-8"), name);
|
|
18464
18701
|
}
|
|
18465
18702
|
function listSkillResources() {
|
|
18466
18703
|
const builtin = RESOURCE_SKILL_IDS.map((id) => {
|
|
@@ -18474,9 +18711,9 @@ function listSkillResources() {
|
|
|
18474
18711
|
defaultForHostedMcp: true
|
|
18475
18712
|
};
|
|
18476
18713
|
});
|
|
18477
|
-
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 &&
|
|
18714
|
+
const pack = loadProjectExtensions2().skills.filter((s) => s.extends === void 0 && fs15.existsSync(s.sourcePath)).map((skill) => {
|
|
18478
18715
|
const id = packSkillId(skill);
|
|
18479
|
-
const fm = readFrontmatter(
|
|
18716
|
+
const fm = readFrontmatter(fs15.readFileSync(skill.sourcePath, "utf-8"), id);
|
|
18480
18717
|
return {
|
|
18481
18718
|
id,
|
|
18482
18719
|
name: fm.name,
|
|
@@ -18492,8 +18729,8 @@ function readSkillResource(resourceId) {
|
|
|
18492
18729
|
const packSkills = loadProjectExtensions2().skills;
|
|
18493
18730
|
if (SKILL_NAMES.includes(resourceId)) return composeBuiltinSkill(resourceId, packSkills);
|
|
18494
18731
|
const packSkill = packSkills.find((s) => s.extends === void 0 && packSkillId(s) === resourceId);
|
|
18495
|
-
if (packSkill) return
|
|
18496
|
-
return
|
|
18732
|
+
if (packSkill) return fs15.readFileSync(packSkill.sourcePath, "utf-8");
|
|
18733
|
+
return fs15.readFileSync(skillTemplatePath(resourceId), "utf-8");
|
|
18497
18734
|
}
|
|
18498
18735
|
function listResources() {
|
|
18499
18736
|
return listSkillResources();
|
|
@@ -18682,7 +18919,7 @@ var WAIRON_MANAGED_MARKER = "wairon:managed";
|
|
|
18682
18919
|
var WAIRON_MANAGED_BANNER = `<!-- ${WAIRON_MANAGED_MARKER} \u2014 generated by \`wairon generate\`; do not edit, changes are overwritten -->`;
|
|
18683
18920
|
|
|
18684
18921
|
// src/exporters/claude.ts
|
|
18685
|
-
var
|
|
18922
|
+
var path22 = __toESM(require("path"));
|
|
18686
18923
|
init_fs();
|
|
18687
18924
|
var ClaudeExporter = class {
|
|
18688
18925
|
constructor() {
|
|
@@ -18691,7 +18928,7 @@ var ClaudeExporter = class {
|
|
|
18691
18928
|
outputPath(ctx) {
|
|
18692
18929
|
const { agent, target, projectRoot } = ctx;
|
|
18693
18930
|
const outputDir = "outputDir" in target ? target.outputDir : ".claude/agents";
|
|
18694
|
-
return
|
|
18931
|
+
return path22.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
18695
18932
|
}
|
|
18696
18933
|
export(ctx) {
|
|
18697
18934
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -18712,7 +18949,7 @@ var ClaudeExporter = class {
|
|
|
18712
18949
|
};
|
|
18713
18950
|
|
|
18714
18951
|
// src/exporters/custom.ts
|
|
18715
|
-
var
|
|
18952
|
+
var path23 = __toESM(require("path"));
|
|
18716
18953
|
init_fs();
|
|
18717
18954
|
var CustomExporter = class {
|
|
18718
18955
|
constructor() {
|
|
@@ -18723,7 +18960,7 @@ var CustomExporter = class {
|
|
|
18723
18960
|
if (!("outputDir" in target)) {
|
|
18724
18961
|
throw new Error("CustomExporter requires target.outputDir");
|
|
18725
18962
|
}
|
|
18726
|
-
return
|
|
18963
|
+
return path23.resolve(projectRoot, target.outputDir, `${agent.id.replace(/::/g, "--")}.md`);
|
|
18727
18964
|
}
|
|
18728
18965
|
export(ctx) {
|
|
18729
18966
|
const { agent, target, renderedInstructions } = ctx;
|
|
@@ -18746,7 +18983,7 @@ var CustomExporter = class {
|
|
|
18746
18983
|
};
|
|
18747
18984
|
|
|
18748
18985
|
// src/exporters/gemini.ts
|
|
18749
|
-
var
|
|
18986
|
+
var path24 = __toESM(require("path"));
|
|
18750
18987
|
init_fs();
|
|
18751
18988
|
var GeminiExporter = class {
|
|
18752
18989
|
constructor() {
|
|
@@ -18755,7 +18992,7 @@ var GeminiExporter = class {
|
|
|
18755
18992
|
outputPath(ctx) {
|
|
18756
18993
|
const { agent, target, projectRoot } = ctx;
|
|
18757
18994
|
const outputDir = "outputDir" in target ? target.outputDir : ".gemini/agents";
|
|
18758
|
-
return
|
|
18995
|
+
return path24.resolve(projectRoot, outputDir, `${agent.id.replace(/::/g, "--")}.yaml`);
|
|
18759
18996
|
}
|
|
18760
18997
|
export(ctx) {
|
|
18761
18998
|
const { agent, renderedInstructions } = ctx;
|
|
@@ -18780,7 +19017,7 @@ function yamlString(value) {
|
|
|
18780
19017
|
}
|
|
18781
19018
|
|
|
18782
19019
|
// src/exporters/generate.ts
|
|
18783
|
-
var
|
|
19020
|
+
var path25 = __toESM(require("path"));
|
|
18784
19021
|
init_fs();
|
|
18785
19022
|
|
|
18786
19023
|
// src/exporters/registry.ts
|
|
@@ -18847,7 +19084,7 @@ function resolveExpectedOutputPaths(agents, projectConfig, projectRoot = getProj
|
|
|
18847
19084
|
const targetConfig = resolveTargetConfig(agentTarget, projectConfig);
|
|
18848
19085
|
if (!targetConfig) continue;
|
|
18849
19086
|
const ctx = { agent, projectRoot, target: targetConfig };
|
|
18850
|
-
expected.add(
|
|
19087
|
+
expected.add(path25.resolve(getExporter(targetConfig).outputPath(ctx)));
|
|
18851
19088
|
}
|
|
18852
19089
|
}
|
|
18853
19090
|
return expected;
|
|
@@ -19242,6 +19479,7 @@ init_yaml();
|
|
|
19242
19479
|
skillExtendErrors,
|
|
19243
19480
|
skillsDirForTarget,
|
|
19244
19481
|
snapshotSpecFiles,
|
|
19482
|
+
specPathsInScope,
|
|
19245
19483
|
splitNamespace,
|
|
19246
19484
|
stateIdEquals,
|
|
19247
19485
|
stateIdString,
|