@wairon/cli 5.1.1-dev.4 → 5.1.1-dev.6
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 +86 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +86 -12
- 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.6";
|
|
1215
1215
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
1216
1216
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
1217
1217
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -15996,10 +15996,40 @@ var init_specs2 = __esm({
|
|
|
15996
15996
|
return null;
|
|
15997
15997
|
}
|
|
15998
15998
|
}
|
|
15999
|
+
/**
|
|
16000
|
+
* Refuse a write that would land on a file already holding a DIFFERENT spec.
|
|
16001
|
+
*
|
|
16002
|
+
* In the nested layout a spec's path is derived from its parent — a component
|
|
16003
|
+
* for an interface, a contract for an implementation — and the spec's own id
|
|
16004
|
+
* is not part of it (see getInterfacePath / getImplementationPath, which take
|
|
16005
|
+
* the id but ignore it once the parent resolves). So a second id bound to the
|
|
16006
|
+
* same parent resolves to the SAME file.
|
|
16007
|
+
*
|
|
16008
|
+
* Left unguarded that is silent data loss, and doubly invisible: the caller's
|
|
16009
|
+
* `existing` lookup is by the NEW id, finds nothing, and every re-author
|
|
16010
|
+
* notice ("REMOVED …", the carry-forward seam) stays quiet. An agent renaming
|
|
16011
|
+
* an interface by defining a new one destroys the old contract, its
|
|
16012
|
+
* narratives and its lint.allows, and is told "Successfully defined".
|
|
16013
|
+
*/
|
|
16014
|
+
assertPathHoldsNoOtherSpec(p, id, kind, parentLabel) {
|
|
16015
|
+
if (!pathExists(p)) return;
|
|
16016
|
+
let occupantId;
|
|
16017
|
+
try {
|
|
16018
|
+
occupantId = readYamlFile(p)?.id;
|
|
16019
|
+
} catch {
|
|
16020
|
+
return;
|
|
16021
|
+
}
|
|
16022
|
+
if (!occupantId) return;
|
|
16023
|
+
if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
|
|
16024
|
+
throw new Error(
|
|
16025
|
+
`Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path13.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.`
|
|
16026
|
+
);
|
|
16027
|
+
}
|
|
15999
16028
|
/** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
|
|
16000
16029
|
saveInterfaceSpec(spec, opts) {
|
|
16001
16030
|
const notices = [];
|
|
16002
16031
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
16032
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
|
|
16003
16033
|
ensureDir(path13.dirname(p));
|
|
16004
16034
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
16005
16035
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
@@ -16063,6 +16093,7 @@ var init_specs2 = __esm({
|
|
|
16063
16093
|
saveImplementationSpec(spec, opts) {
|
|
16064
16094
|
const notices = [];
|
|
16065
16095
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
16096
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
|
|
16066
16097
|
ensureDir(path13.dirname(p));
|
|
16067
16098
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
16068
16099
|
const existing = this.loadImplementationSpec(spec.id);
|
|
@@ -16357,6 +16388,35 @@ var init_specs2 = __esm({
|
|
|
16357
16388
|
}
|
|
16358
16389
|
return refs;
|
|
16359
16390
|
};
|
|
16391
|
+
const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
|
|
16392
|
+
const assertDeltaMarkers = (item, label) => {
|
|
16393
|
+
if (!item || typeof item !== "object") return;
|
|
16394
|
+
if ("remove" in item && typeof item.remove !== "boolean") {
|
|
16395
|
+
throw new Error(
|
|
16396
|
+
`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.`
|
|
16397
|
+
);
|
|
16398
|
+
}
|
|
16399
|
+
if ("action" in item && item.action !== "add" && item.action !== "delete") {
|
|
16400
|
+
throw new Error(
|
|
16401
|
+
`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.`
|
|
16402
|
+
);
|
|
16403
|
+
}
|
|
16404
|
+
};
|
|
16405
|
+
const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label) => {
|
|
16406
|
+
if (deltaItem?.remove === true || deltaItem?.action === "delete") {
|
|
16407
|
+
throw new Error(
|
|
16408
|
+
`Refusing to delete ${label} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
|
|
16409
|
+
);
|
|
16410
|
+
}
|
|
16411
|
+
const near = existingKeys.find(
|
|
16412
|
+
(k) => String(k) !== String(key) && normalizeIdentity(k) === normalizeIdentity(key)
|
|
16413
|
+
);
|
|
16414
|
+
if (near !== void 0) {
|
|
16415
|
+
throw new Error(
|
|
16416
|
+
`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.`
|
|
16417
|
+
);
|
|
16418
|
+
}
|
|
16419
|
+
};
|
|
16360
16420
|
const mergeNarrative = (existingSteps, deltaSteps) => {
|
|
16361
16421
|
let steps = [...existingSteps];
|
|
16362
16422
|
const sortedDeltas = [...deltaSteps].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
@@ -16425,6 +16485,7 @@ var init_specs2 = __esm({
|
|
|
16425
16485
|
const mergeMethods = (existingMethods, deltaMethods) => {
|
|
16426
16486
|
const merged = [...existingMethods];
|
|
16427
16487
|
for (const deltaMethod of deltaMethods) {
|
|
16488
|
+
assertDeltaMarkers(deltaMethod, `method "${deltaMethod?.name}"`);
|
|
16428
16489
|
const idx = merged.findIndex((m) => m.name === deltaMethod.name);
|
|
16429
16490
|
if (idx !== -1) {
|
|
16430
16491
|
if (deltaMethod.remove === true || deltaMethod.action === "delete") {
|
|
@@ -16444,9 +16505,8 @@ var init_specs2 = __esm({
|
|
|
16444
16505
|
};
|
|
16445
16506
|
}
|
|
16446
16507
|
} else {
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
}
|
|
16508
|
+
assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
|
|
16509
|
+
merged.push(deltaMethod);
|
|
16450
16510
|
}
|
|
16451
16511
|
}
|
|
16452
16512
|
return merged;
|
|
@@ -16454,6 +16514,7 @@ var init_specs2 = __esm({
|
|
|
16454
16514
|
const mergeNamedArray = (existing, delta2) => {
|
|
16455
16515
|
const merged = [...existing];
|
|
16456
16516
|
for (const deltaItem of delta2) {
|
|
16517
|
+
assertDeltaMarkers(deltaItem, `entry "${deltaItem?.name}"`);
|
|
16457
16518
|
const idx = merged.findIndex((item) => item.name === deltaItem.name);
|
|
16458
16519
|
if (idx !== -1) {
|
|
16459
16520
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16466,9 +16527,8 @@ var init_specs2 = __esm({
|
|
|
16466
16527
|
};
|
|
16467
16528
|
}
|
|
16468
16529
|
} else {
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
}
|
|
16530
|
+
assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
|
|
16531
|
+
merged.push(deltaItem);
|
|
16472
16532
|
}
|
|
16473
16533
|
}
|
|
16474
16534
|
return merged;
|
|
@@ -16476,6 +16536,7 @@ var init_specs2 = __esm({
|
|
|
16476
16536
|
const mergeKeyedArray = (existing, delta2, keyOf) => {
|
|
16477
16537
|
const merged = [...existing];
|
|
16478
16538
|
for (const deltaItem of delta2) {
|
|
16539
|
+
assertDeltaMarkers(deltaItem, `entry "${keyOf(deltaItem)}"`);
|
|
16479
16540
|
const idx = merged.findIndex((item) => keyOf(item) === keyOf(deltaItem));
|
|
16480
16541
|
if (idx !== -1) {
|
|
16481
16542
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16483,7 +16544,8 @@ var init_specs2 = __esm({
|
|
|
16483
16544
|
} else {
|
|
16484
16545
|
merged[idx] = { ...merged[idx], ...deltaItem };
|
|
16485
16546
|
}
|
|
16486
|
-
} else
|
|
16547
|
+
} else {
|
|
16548
|
+
assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
|
|
16487
16549
|
merged.push(deltaItem);
|
|
16488
16550
|
}
|
|
16489
16551
|
}
|
|
@@ -16492,6 +16554,8 @@ var init_specs2 = __esm({
|
|
|
16492
16554
|
const mergePublicInterfaces = (existing, delta2) => {
|
|
16493
16555
|
const merged = [...existing];
|
|
16494
16556
|
for (const deltaItem of delta2) {
|
|
16557
|
+
const piKey = (i) => `${i?.component}.${i?.interface}`;
|
|
16558
|
+
assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
|
|
16495
16559
|
const idx = merged.findIndex((item) => item.component === deltaItem.component && item.interface === deltaItem.interface);
|
|
16496
16560
|
if (idx !== -1) {
|
|
16497
16561
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -16503,9 +16567,8 @@ var init_specs2 = __esm({
|
|
|
16503
16567
|
};
|
|
16504
16568
|
}
|
|
16505
16569
|
} else {
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
}
|
|
16570
|
+
assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
|
|
16571
|
+
merged.push(deltaItem);
|
|
16509
16572
|
}
|
|
16510
16573
|
}
|
|
16511
16574
|
return merged;
|
|
@@ -16546,11 +16609,22 @@ var init_specs2 = __esm({
|
|
|
16546
16609
|
for (const deltaItem of delta2) {
|
|
16547
16610
|
const key = identityKeyOf(field, deltaItem);
|
|
16548
16611
|
const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf(field, item) === key);
|
|
16612
|
+
assertDeltaMarkers(deltaItem, `${field} entry "${String(key)}"`);
|
|
16549
16613
|
const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
|
|
16550
16614
|
if (idx !== -1) {
|
|
16551
16615
|
if (isDelete) merged.splice(idx, 1);
|
|
16552
16616
|
else merged[idx] = { ...merged[idx], ...deltaItem };
|
|
16553
|
-
} else
|
|
16617
|
+
} else {
|
|
16618
|
+
if (key !== null) {
|
|
16619
|
+
assertUnmatchedIsAnAddition(
|
|
16620
|
+
deltaItem,
|
|
16621
|
+
key,
|
|
16622
|
+
merged.map((i) => identityKeyOf(field, i)).filter((k) => k !== null),
|
|
16623
|
+
field
|
|
16624
|
+
);
|
|
16625
|
+
} else if (isDelete) {
|
|
16626
|
+
throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
|
|
16627
|
+
}
|
|
16554
16628
|
merged.push(deltaItem);
|
|
16555
16629
|
}
|
|
16556
16630
|
}
|