@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/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.6";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
ARCHITECT_AGENT_ID = "agent-architect";
|
|
71
71
|
ARCHITECT_TEMPLATE_ID = "architect";
|
|
@@ -16585,10 +16585,40 @@ var init_specs2 = __esm({
|
|
|
16585
16585
|
return null;
|
|
16586
16586
|
}
|
|
16587
16587
|
}
|
|
16588
|
+
/**
|
|
16589
|
+
* Refuse a write that would land on a file already holding a DIFFERENT spec.
|
|
16590
|
+
*
|
|
16591
|
+
* In the nested layout a spec's path is derived from its parent — a component
|
|
16592
|
+
* for an interface, a contract for an implementation — and the spec's own id
|
|
16593
|
+
* is not part of it (see getInterfacePath / getImplementationPath, which take
|
|
16594
|
+
* the id but ignore it once the parent resolves). So a second id bound to the
|
|
16595
|
+
* same parent resolves to the SAME file.
|
|
16596
|
+
*
|
|
16597
|
+
* Left unguarded that is silent data loss, and doubly invisible: the caller's
|
|
16598
|
+
* `existing` lookup is by the NEW id, finds nothing, and every re-author
|
|
16599
|
+
* notice ("REMOVED …", the carry-forward seam) stays quiet. An agent renaming
|
|
16600
|
+
* an interface by defining a new one destroys the old contract, its
|
|
16601
|
+
* narratives and its lint.allows, and is told "Successfully defined".
|
|
16602
|
+
*/
|
|
16603
|
+
assertPathHoldsNoOtherSpec(p, id, kind, parentLabel) {
|
|
16604
|
+
if (!pathExists(p)) return;
|
|
16605
|
+
let occupantId;
|
|
16606
|
+
try {
|
|
16607
|
+
occupantId = readYamlFile(p)?.id;
|
|
16608
|
+
} catch {
|
|
16609
|
+
return;
|
|
16610
|
+
}
|
|
16611
|
+
if (!occupantId) return;
|
|
16612
|
+
if (occupantId === id || splitNamespace(occupantId).localId === splitNamespace(id).localId) return;
|
|
16613
|
+
throw new Error(
|
|
16614
|
+
`Cannot write ${kind} "${id}": ${parentLabel} is already ${kind === "interface" ? "served by" : "implemented by"} "${occupantId}" at ${path17.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.`
|
|
16615
|
+
);
|
|
16616
|
+
}
|
|
16588
16617
|
/** Returns non-fatal placement notices (see saveTypeSpec) — empty when there is nothing to clarify. */
|
|
16589
16618
|
saveInterfaceSpec(spec, opts) {
|
|
16590
16619
|
const notices = [];
|
|
16591
16620
|
const p = this.getInterfacePath(spec.id, spec.component);
|
|
16621
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "interface", `component "${spec.component}"`);
|
|
16592
16622
|
ensureDir(path17.dirname(p));
|
|
16593
16623
|
const specToWrite = this.prepareInterfaceForWrite(spec);
|
|
16594
16624
|
const existing = this.loadInterfaceSpec(spec.id);
|
|
@@ -16652,6 +16682,7 @@ var init_specs2 = __esm({
|
|
|
16652
16682
|
saveImplementationSpec(spec, opts) {
|
|
16653
16683
|
const notices = [];
|
|
16654
16684
|
const p = this.getImplementationPath(spec.id, spec.contract);
|
|
16685
|
+
this.assertPathHoldsNoOtherSpec(p, spec.id, "implementation", `contract "${spec.contract}"`);
|
|
16655
16686
|
ensureDir(path17.dirname(p));
|
|
16656
16687
|
const specToWrite = this.prepareImplementationForWrite(spec);
|
|
16657
16688
|
const existing = this.loadImplementationSpec(spec.id);
|
|
@@ -16946,6 +16977,35 @@ var init_specs2 = __esm({
|
|
|
16946
16977
|
}
|
|
16947
16978
|
return refs;
|
|
16948
16979
|
};
|
|
16980
|
+
const normalizeIdentity = (k) => String(k ?? "").toLowerCase().replace(/[_\-\s]/g, "");
|
|
16981
|
+
const assertDeltaMarkers = (item, label) => {
|
|
16982
|
+
if (!item || typeof item !== "object") return;
|
|
16983
|
+
if ("remove" in item && typeof item.remove !== "boolean") {
|
|
16984
|
+
throw new Error(
|
|
16985
|
+
`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.`
|
|
16986
|
+
);
|
|
16987
|
+
}
|
|
16988
|
+
if ("action" in item && item.action !== "add" && item.action !== "delete") {
|
|
16989
|
+
throw new Error(
|
|
16990
|
+
`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.`
|
|
16991
|
+
);
|
|
16992
|
+
}
|
|
16993
|
+
};
|
|
16994
|
+
const assertUnmatchedIsAnAddition = (deltaItem, key, existingKeys, label) => {
|
|
16995
|
+
if (deltaItem?.remove === true || deltaItem?.action === "delete") {
|
|
16996
|
+
throw new Error(
|
|
16997
|
+
`Refusing to delete ${label} "${String(key)}": nothing with that identity exists. ` + (existingKeys.length ? `Present: ${existingKeys.map(String).join(", ")}.` : "The collection is empty.")
|
|
16998
|
+
);
|
|
16999
|
+
}
|
|
17000
|
+
const near = existingKeys.find(
|
|
17001
|
+
(k) => String(k) !== String(key) && normalizeIdentity(k) === normalizeIdentity(key)
|
|
17002
|
+
);
|
|
17003
|
+
if (near !== void 0) {
|
|
17004
|
+
throw new Error(
|
|
17005
|
+
`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.`
|
|
17006
|
+
);
|
|
17007
|
+
}
|
|
17008
|
+
};
|
|
16949
17009
|
const mergeNarrative = (existingSteps, deltaSteps) => {
|
|
16950
17010
|
let steps = [...existingSteps];
|
|
16951
17011
|
const sortedDeltas = [...deltaSteps].sort((a, b) => a.stepNumber - b.stepNumber);
|
|
@@ -17014,6 +17074,7 @@ var init_specs2 = __esm({
|
|
|
17014
17074
|
const mergeMethods = (existingMethods, deltaMethods) => {
|
|
17015
17075
|
const merged = [...existingMethods];
|
|
17016
17076
|
for (const deltaMethod of deltaMethods) {
|
|
17077
|
+
assertDeltaMarkers(deltaMethod, `method "${deltaMethod?.name}"`);
|
|
17017
17078
|
const idx = merged.findIndex((m) => m.name === deltaMethod.name);
|
|
17018
17079
|
if (idx !== -1) {
|
|
17019
17080
|
if (deltaMethod.remove === true || deltaMethod.action === "delete") {
|
|
@@ -17033,9 +17094,8 @@ var init_specs2 = __esm({
|
|
|
17033
17094
|
};
|
|
17034
17095
|
}
|
|
17035
17096
|
} else {
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
}
|
|
17097
|
+
assertUnmatchedIsAnAddition(deltaMethod, deltaMethod.name, merged.map((m) => m.name), "method");
|
|
17098
|
+
merged.push(deltaMethod);
|
|
17039
17099
|
}
|
|
17040
17100
|
}
|
|
17041
17101
|
return merged;
|
|
@@ -17043,6 +17103,7 @@ var init_specs2 = __esm({
|
|
|
17043
17103
|
const mergeNamedArray = (existing, delta2) => {
|
|
17044
17104
|
const merged = [...existing];
|
|
17045
17105
|
for (const deltaItem of delta2) {
|
|
17106
|
+
assertDeltaMarkers(deltaItem, `entry "${deltaItem?.name}"`);
|
|
17046
17107
|
const idx = merged.findIndex((item) => item.name === deltaItem.name);
|
|
17047
17108
|
if (idx !== -1) {
|
|
17048
17109
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17055,9 +17116,8 @@ var init_specs2 = __esm({
|
|
|
17055
17116
|
};
|
|
17056
17117
|
}
|
|
17057
17118
|
} else {
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
}
|
|
17119
|
+
assertUnmatchedIsAnAddition(deltaItem, deltaItem.name, merged.map((i) => i.name), "entry");
|
|
17120
|
+
merged.push(deltaItem);
|
|
17061
17121
|
}
|
|
17062
17122
|
}
|
|
17063
17123
|
return merged;
|
|
@@ -17065,6 +17125,7 @@ var init_specs2 = __esm({
|
|
|
17065
17125
|
const mergeKeyedArray = (existing, delta2, keyOf) => {
|
|
17066
17126
|
const merged = [...existing];
|
|
17067
17127
|
for (const deltaItem of delta2) {
|
|
17128
|
+
assertDeltaMarkers(deltaItem, `entry "${keyOf(deltaItem)}"`);
|
|
17068
17129
|
const idx = merged.findIndex((item) => keyOf(item) === keyOf(deltaItem));
|
|
17069
17130
|
if (idx !== -1) {
|
|
17070
17131
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17072,7 +17133,8 @@ var init_specs2 = __esm({
|
|
|
17072
17133
|
} else {
|
|
17073
17134
|
merged[idx] = { ...merged[idx], ...deltaItem };
|
|
17074
17135
|
}
|
|
17075
|
-
} else
|
|
17136
|
+
} else {
|
|
17137
|
+
assertUnmatchedIsAnAddition(deltaItem, keyOf(deltaItem), merged.map(keyOf), "entry");
|
|
17076
17138
|
merged.push(deltaItem);
|
|
17077
17139
|
}
|
|
17078
17140
|
}
|
|
@@ -17081,6 +17143,8 @@ var init_specs2 = __esm({
|
|
|
17081
17143
|
const mergePublicInterfaces = (existing, delta2) => {
|
|
17082
17144
|
const merged = [...existing];
|
|
17083
17145
|
for (const deltaItem of delta2) {
|
|
17146
|
+
const piKey = (i) => `${i?.component}.${i?.interface}`;
|
|
17147
|
+
assertDeltaMarkers(deltaItem, `publicInterface "${piKey(deltaItem)}"`);
|
|
17084
17148
|
const idx = merged.findIndex((item) => item.component === deltaItem.component && item.interface === deltaItem.interface);
|
|
17085
17149
|
if (idx !== -1) {
|
|
17086
17150
|
if (deltaItem.remove === true || deltaItem.action === "delete") {
|
|
@@ -17092,9 +17156,8 @@ var init_specs2 = __esm({
|
|
|
17092
17156
|
};
|
|
17093
17157
|
}
|
|
17094
17158
|
} else {
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
}
|
|
17159
|
+
assertUnmatchedIsAnAddition(deltaItem, piKey(deltaItem), merged.map(piKey), "publicInterface");
|
|
17160
|
+
merged.push(deltaItem);
|
|
17098
17161
|
}
|
|
17099
17162
|
}
|
|
17100
17163
|
return merged;
|
|
@@ -17135,11 +17198,22 @@ var init_specs2 = __esm({
|
|
|
17135
17198
|
for (const deltaItem of delta2) {
|
|
17136
17199
|
const key = identityKeyOf(field, deltaItem);
|
|
17137
17200
|
const idx = key === null ? -1 : merged.findIndex((item) => identityKeyOf(field, item) === key);
|
|
17201
|
+
assertDeltaMarkers(deltaItem, `${field} entry "${String(key)}"`);
|
|
17138
17202
|
const isDelete = deltaItem?.remove === true || deltaItem?.action === "delete";
|
|
17139
17203
|
if (idx !== -1) {
|
|
17140
17204
|
if (isDelete) merged.splice(idx, 1);
|
|
17141
17205
|
else merged[idx] = { ...merged[idx], ...deltaItem };
|
|
17142
|
-
} else
|
|
17206
|
+
} else {
|
|
17207
|
+
if (key !== null) {
|
|
17208
|
+
assertUnmatchedIsAnAddition(
|
|
17209
|
+
deltaItem,
|
|
17210
|
+
key,
|
|
17211
|
+
merged.map((i) => identityKeyOf(field, i)).filter((k) => k !== null),
|
|
17212
|
+
field
|
|
17213
|
+
);
|
|
17214
|
+
} else if (isDelete) {
|
|
17215
|
+
throw new Error(`Refusing to delete a ${field} entry with no resolvable identity.`);
|
|
17216
|
+
}
|
|
17143
17217
|
merged.push(deltaItem);
|
|
17144
17218
|
}
|
|
17145
17219
|
}
|