@wairon/cli 5.1.1-dev.43 → 5.1.1-dev.45
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 +103 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +102 -29
- package/dist/index.js.map +1 -1
- package/dist/templates/skills/sdd-narrative.md +1 -0
- 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.45";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -1287,6 +1287,11 @@ function passesIntentFloor(text2, methodName) {
|
|
|
1287
1287
|
if (norm(t) === norm(methodName)) return false;
|
|
1288
1288
|
return true;
|
|
1289
1289
|
}
|
|
1290
|
+
function parseDeclaredCall(ref) {
|
|
1291
|
+
const at = ref.lastIndexOf(".");
|
|
1292
|
+
if (at <= 0 || at === ref.length - 1) return null;
|
|
1293
|
+
return { compId: ref.slice(0, at), methodName: ref.slice(at + 1) };
|
|
1294
|
+
}
|
|
1290
1295
|
function methodSourceFile(method2, implementationSourcePath) {
|
|
1291
1296
|
return method2.sourcePath || implementationSourcePath || void 0;
|
|
1292
1297
|
}
|
|
@@ -1917,6 +1922,31 @@ var init_specs = __esm({
|
|
|
1917
1922
|
intent: import_zod7.z.string().optional(),
|
|
1918
1923
|
/** Conformance tier for THIS method (overrides the spec-level default). */
|
|
1919
1924
|
conformance: ConformanceTierSchema.optional(),
|
|
1925
|
+
/**
|
|
1926
|
+
* The calls this method makes, when its narrative does not show them.
|
|
1927
|
+
*
|
|
1928
|
+
* The mirror of the contract's `invokedBy`: that declares the caller
|
|
1929
|
+
* OUTSIDE the modeled graph, this declares the callees INSIDE it. It lives
|
|
1930
|
+
* here and not on the L3 method because what a method CALLS is a property
|
|
1931
|
+
* of the realization — the detail dial that hides the steps is here, and two
|
|
1932
|
+
* implementations of one contract may reach different collaborators —
|
|
1933
|
+
* whereas an external caller is a fact about the contract's role that must
|
|
1934
|
+
* hold for every realization.
|
|
1935
|
+
*
|
|
1936
|
+
* Each entry names one target the way the debt register and a lint allow
|
|
1937
|
+
* name a unit: `<component>.<method>` (a `covers` entry is the same
|
|
1938
|
+
* spelling with the step number a declaration has no equivalent of). A
|
|
1939
|
+
* third vocabulary for "which method" would be the worse outcome.
|
|
1940
|
+
*
|
|
1941
|
+
* The narrative graph walk takes exactly these edges for a method with no
|
|
1942
|
+
* narrative, and NOTHING else: an undeclared collaborator is not reached,
|
|
1943
|
+
* so a lower detail dial no longer vouches for everything the component
|
|
1944
|
+
* declares. Each entry gets the same target validation a `call` step gets
|
|
1945
|
+
* (the component resolves, the caller declares it, the method is on its
|
|
1946
|
+
* contract). Refused beside a non-empty narrative, where the steps already
|
|
1947
|
+
* say what is called and a second spelling could only disagree.
|
|
1948
|
+
*/
|
|
1949
|
+
calls: import_zod7.z.array(import_zod7.z.string().min(1)).optional(),
|
|
1920
1950
|
/**
|
|
1921
1951
|
* The code-level name realizing this contract method in the sourcePath
|
|
1922
1952
|
* file, when it legitimately differs from the intent-language contract
|
|
@@ -1953,7 +1983,16 @@ var init_specs = __esm({
|
|
|
1953
1983
|
* stereotypes should bind tech directly (TECH_ON_LOGIC_COMPONENT).
|
|
1954
1984
|
*/
|
|
1955
1985
|
technologies: import_zod7.z.array(import_zod7.z.string()).optional(),
|
|
1956
|
-
methods: import_zod7.z.array(MethodImplementationSchema).default([]),
|
|
1986
|
+
methods: import_zod7.z.array(MethodImplementationSchema).default([]).superRefine((methods, ctx) => {
|
|
1987
|
+
methods.forEach((m, i) => {
|
|
1988
|
+
if (!m.calls?.length || m.narrative.length === 0) return;
|
|
1989
|
+
ctx.addIssue({
|
|
1990
|
+
code: import_zod7.z.ZodIssueCode.custom,
|
|
1991
|
+
path: [i, "calls"],
|
|
1992
|
+
message: `Method "${m.name}" declares calls AND a narrative \u2014 \`calls\` says what a method calls when its narrative does not show it, and this one has ${m.narrative.length} step(s) that already do. Add the call step, or drop the declaration.`
|
|
1993
|
+
});
|
|
1994
|
+
});
|
|
1995
|
+
}),
|
|
1957
1996
|
/** Spec-level narrative detail default for all methods (each may override). */
|
|
1958
1997
|
detail: NarrativeDetailSchema.optional(),
|
|
1959
1998
|
/** Spec-level conformance tier default (each method may override). */
|
|
@@ -9838,16 +9877,36 @@ var init_contract_symmetry = __esm({
|
|
|
9838
9877
|
});
|
|
9839
9878
|
|
|
9840
9879
|
// src/core/rules/narrative/narrative-target-references.ts
|
|
9841
|
-
var narrativeTargetReferencesRule;
|
|
9880
|
+
var targetsOf, narrativeTargetReferencesRule;
|
|
9842
9881
|
var init_narrative_target_references = __esm({
|
|
9843
9882
|
"src/core/rules/narrative/narrative-target-references.ts"() {
|
|
9844
9883
|
"use strict";
|
|
9884
|
+
init_models();
|
|
9885
|
+
targetsOf = (implMethod) => {
|
|
9886
|
+
const entries = [];
|
|
9887
|
+
for (const step of implMethod.narrative) {
|
|
9888
|
+
if (step.type !== "call" && step.type !== "dispatch" && step.type !== "register") continue;
|
|
9889
|
+
entries.push({
|
|
9890
|
+
kind: step.type,
|
|
9891
|
+
stepNumber: step.stepNumber,
|
|
9892
|
+
targetComponent: step.targetComponent,
|
|
9893
|
+
targetMethod: step.targetMethod,
|
|
9894
|
+
assertsGuarantees: step.assertsGuarantees
|
|
9895
|
+
});
|
|
9896
|
+
}
|
|
9897
|
+
for (const ref of implMethod.calls ?? []) {
|
|
9898
|
+
const parsed = parseDeclaredCall(ref);
|
|
9899
|
+
entries.push(parsed ? { kind: "declared", targetComponent: parsed.compId, targetMethod: parsed.methodName } : { kind: "declared", unparsed: ref });
|
|
9900
|
+
}
|
|
9901
|
+
return entries;
|
|
9902
|
+
};
|
|
9845
9903
|
narrativeTargetReferencesRule = {
|
|
9846
9904
|
name: "narrative-target-references",
|
|
9847
|
-
description:
|
|
9905
|
+
description: `A narrative call, dispatch or register step must name a target component, and a target method unless it dispatches; a method's declared calls name the same pair as one "<component>.<method>" reference, the spelling the conformance debt register and a lint allow name a unit with. A target this tree contains must be a collaborator the calling component declares, must carry the named method on one of its interfaces, and that method must declare every semantic guarantee the entry asserts.`,
|
|
9848
9906
|
codes: [
|
|
9849
9907
|
{ code: "MISSING_TARGET_COMPONENT", defaultSeverity: "error", summary: "Call/register step missing targetComponent" },
|
|
9850
9908
|
{ code: "MISSING_TARGET_METHOD", defaultSeverity: "error", summary: "Call/register step missing targetMethod" },
|
|
9909
|
+
{ code: "MALFORMED_DECLARED_CALL", defaultSeverity: "error", summary: 'Declared call that is not a "<component>.<method>" reference' },
|
|
9851
9910
|
{ code: "UNDECLARED_DEPENDENCY_CALL", defaultSeverity: "error", summary: "Call step targets a component the caller does not depend on or own" },
|
|
9852
9911
|
{ code: "INVALID_TARGET_METHOD_REFERENCE", defaultSeverity: "error", summary: "Call step targets a method not on any target interface" },
|
|
9853
9912
|
{ code: "NARRATIVE_SEMANTIC_UNBACKED", defaultSeverity: "warning", summary: "Narrative asserts a guarantee the called contract does not declare" }
|
|
@@ -9859,25 +9918,35 @@ var init_narrative_target_references = __esm({
|
|
|
9859
9918
|
const isDraftCtx = ctx.isImplementationDraft(impl);
|
|
9860
9919
|
const caller = ctx.componentMap.get(contract.component);
|
|
9861
9920
|
for (const implMethod of impl.methods) {
|
|
9862
|
-
for (const
|
|
9863
|
-
|
|
9864
|
-
if (
|
|
9921
|
+
for (const entry of targetsOf(implMethod)) {
|
|
9922
|
+
const where = entry.stepNumber === void 0 ? "" : ` (step ${entry.stepNumber})`;
|
|
9923
|
+
if (entry.unparsed !== void 0) {
|
|
9924
|
+
ctx.addIssue(
|
|
9925
|
+
"error",
|
|
9926
|
+
"MALFORMED_DECLARED_CALL",
|
|
9927
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" declares the call "${entry.unparsed}", which is not a "<component>.<method>" reference \u2014 the spelling the conformance debt register and a lint allow name a unit with.`,
|
|
9928
|
+
impl.id,
|
|
9929
|
+
isDraftCtx
|
|
9930
|
+
);
|
|
9931
|
+
continue;
|
|
9932
|
+
}
|
|
9933
|
+
if (!entry.targetComponent) {
|
|
9865
9934
|
ctx.addIssue(
|
|
9866
9935
|
"error",
|
|
9867
9936
|
"MISSING_TARGET_COMPONENT",
|
|
9868
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${
|
|
9937
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${entry.kind} step (${entry.stepNumber}) missing "targetComponent".`,
|
|
9869
9938
|
impl.id,
|
|
9870
9939
|
isDraftCtx
|
|
9871
9940
|
);
|
|
9872
9941
|
continue;
|
|
9873
9942
|
}
|
|
9874
|
-
const target =
|
|
9875
|
-
const verb =
|
|
9876
|
-
if (
|
|
9943
|
+
const target = entry.targetComponent;
|
|
9944
|
+
const verb = entry.kind === "dispatch" ? "dispatches through" : entry.kind === "register" ? "registers callback" : entry.kind === "declared" ? "declares a call to" : "calls";
|
|
9945
|
+
if (entry.kind !== "dispatch" && !entry.targetMethod) {
|
|
9877
9946
|
ctx.addIssue(
|
|
9878
9947
|
"error",
|
|
9879
9948
|
"MISSING_TARGET_METHOD",
|
|
9880
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${
|
|
9949
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" has a ${entry.kind} step (${entry.stepNumber}) missing "targetMethod".`,
|
|
9881
9950
|
impl.id,
|
|
9882
9951
|
isDraftCtx
|
|
9883
9952
|
);
|
|
@@ -9888,16 +9957,16 @@ var init_narrative_target_references = __esm({
|
|
|
9888
9957
|
ctx.addIssue(
|
|
9889
9958
|
"error",
|
|
9890
9959
|
"UNDECLARED_DEPENDENCY_CALL",
|
|
9891
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${caller.id}") ${verb} component "${target}"
|
|
9960
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" (component "${caller.id}") ${verb} component "${target}"${where} but component "${caller.id}" does not list "${target}" as a dependency.`,
|
|
9892
9961
|
impl.id,
|
|
9893
9962
|
isDraftCtx || ctx.isComponentDraft(caller.id)
|
|
9894
9963
|
);
|
|
9895
9964
|
}
|
|
9896
|
-
if (
|
|
9965
|
+
if (entry.kind === "dispatch") continue;
|
|
9897
9966
|
const targetInterfaces = ctx.interfacesByComponent.get(target) ?? [];
|
|
9898
9967
|
let targetMethodSpec;
|
|
9899
9968
|
for (const targetIntf of targetInterfaces) {
|
|
9900
|
-
const found = targetIntf.methods.find((m) => m.name ===
|
|
9969
|
+
const found = targetIntf.methods.find((m) => m.name === entry.targetMethod);
|
|
9901
9970
|
if (found) {
|
|
9902
9971
|
targetMethodSpec = found;
|
|
9903
9972
|
break;
|
|
@@ -9907,19 +9976,19 @@ var init_narrative_target_references = __esm({
|
|
|
9907
9976
|
ctx.addIssue(
|
|
9908
9977
|
"error",
|
|
9909
9978
|
"INVALID_TARGET_METHOD_REFERENCE",
|
|
9910
|
-
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} method "${
|
|
9979
|
+
`Method "${implMethod.name}" in implementation "${impl.id}" ${verb} method "${entry.targetMethod}" on component "${target}" which is not defined on any of its interfaces${where}.`,
|
|
9911
9980
|
impl.id,
|
|
9912
9981
|
isDraftCtx || ctx.isComponentDraft(target)
|
|
9913
9982
|
);
|
|
9914
9983
|
continue;
|
|
9915
9984
|
}
|
|
9916
9985
|
const declared = new Set(targetMethodSpec.guarantees ?? []);
|
|
9917
|
-
for (const g of
|
|
9986
|
+
for (const g of entry.assertsGuarantees ?? []) {
|
|
9918
9987
|
if (!declared.has(g)) {
|
|
9919
9988
|
ctx.addIssue(
|
|
9920
9989
|
"warning",
|
|
9921
9990
|
"NARRATIVE_SEMANTIC_UNBACKED",
|
|
9922
|
-
`Step ${
|
|
9991
|
+
`Step ${entry.stepNumber} of "${implMethod.name}" in implementation "${impl.id}" explicitly asserts guarantee "${g}", but the method it calls \u2014 "${entry.targetMethod}" on "${target}" \u2014 does not list "${g}" among its L3 contract guarantees. Declare it on that method (and ensure its shape can deliver it), or revise the narrative.`,
|
|
9923
9992
|
impl.id,
|
|
9924
9993
|
isDraftCtx || ctx.isComponentDraft(target)
|
|
9925
9994
|
);
|
|
@@ -12610,15 +12679,11 @@ function walk(ctx, seeds, options = {}) {
|
|
|
12610
12679
|
if (edge.methodName) enqueueMethod(edge.compId, edge.methodName);
|
|
12611
12680
|
}
|
|
12612
12681
|
}
|
|
12613
|
-
|
|
12614
|
-
const
|
|
12615
|
-
if (
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
reachComponent(depId);
|
|
12619
|
-
enqueueAllMethods(depId);
|
|
12620
|
-
}
|
|
12621
|
-
}
|
|
12682
|
+
for (const ref of methodImpl.calls ?? []) {
|
|
12683
|
+
const parsed = parseDeclaredCall(ref);
|
|
12684
|
+
if (!parsed || !ctx.componentMap.has(parsed.compId)) continue;
|
|
12685
|
+
reachComponent(parsed.compId);
|
|
12686
|
+
enqueueMethod(parsed.compId, parsed.methodName);
|
|
12622
12687
|
}
|
|
12623
12688
|
}
|
|
12624
12689
|
return {
|
|
@@ -12683,7 +12748,9 @@ var init_unused_detection = __esm({
|
|
|
12683
12748
|
"INVOKED_BY_REDUNDANT",
|
|
12684
12749
|
`Method "${m.name}" on component "${intf.component}" declares invokedBy (${m.invokedBy.kind}), but the internal narrative walk already reaches it \u2014 the declaration is stale; remove it.`,
|
|
12685
12750
|
intf.id,
|
|
12686
|
-
isDraftCtx
|
|
12751
|
+
isDraftCtx,
|
|
12752
|
+
void 0,
|
|
12753
|
+
{ at: m.name }
|
|
12687
12754
|
);
|
|
12688
12755
|
}
|
|
12689
12756
|
}
|
|
@@ -12711,7 +12778,11 @@ var init_unused_detection = __esm({
|
|
|
12711
12778
|
"UNUSED_METHOD",
|
|
12712
12779
|
`Method "${m.name}" on component "${comp.id}" is defined but never called by any narrative step.`,
|
|
12713
12780
|
intf.id,
|
|
12714
|
-
isDraftCtx
|
|
12781
|
+
isDraftCtx,
|
|
12782
|
+
void 0,
|
|
12783
|
+
// The site is the method, so an allow covers exactly the one it
|
|
12784
|
+
// names - never its neighbours on the same interface.
|
|
12785
|
+
{ at: m.name }
|
|
12715
12786
|
);
|
|
12716
12787
|
}
|
|
12717
12788
|
}
|
|
@@ -28723,7 +28794,8 @@ function createMcpServer(options = {}) {
|
|
|
28723
28794
|
conformance: conformanceEnum.optional().describe("Conformance tier for this method (overrides the spec default)"),
|
|
28724
28795
|
symbol: import_zod11.z.string().optional().describe("Code-level name realizing this contract method in the sourcePath file, when it legitimately differs from the intent-language contract name (e.g. put realized by saveSnapshot)"),
|
|
28725
28796
|
ext: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Opaque pack/tool extension data for this method (namespaced keys) \u2014 preserved verbatim"),
|
|
28726
|
-
narrative: import_zod11.z.array(narrativeStepInput).optional()
|
|
28797
|
+
narrative: import_zod11.z.array(narrativeStepInput).optional(),
|
|
28798
|
+
calls: import_zod11.z.array(import_zod11.z.string().min(1)).optional().describe('The calls this method makes, when its narrative does not show them (detail: intent, or calls-only with no steps authored) \u2014 each as "<component>.<method>", the spelling the debt register and lint allows name a unit with. The narrative walk takes exactly these edges and no others, so an undeclared collaborator is NOT reached: this is what keeps unused-detection honest at a lower detail dial. Each entry is validated like a call step (the component resolves, this component declares it, the method is on its contract). Refused beside a non-empty narrative \u2014 add the call step there instead')
|
|
28727
28799
|
};
|
|
28728
28800
|
const implMethodInputFields = Object.keys(implMethodShape);
|
|
28729
28801
|
const implInput = {
|
|
@@ -28744,7 +28816,7 @@ function createMcpServer(options = {}) {
|
|
|
28744
28816
|
server,
|
|
28745
28817
|
"sdd_write_narrative",
|
|
28746
28818
|
{
|
|
28747
|
-
description: "Write L4 Concrete Implementation spec containing L5 method narratives. Narratives are a FLAT ordered step list; flow steps (branch/switch/loop/try/parallel/jump/return/throw) jump by step number \u2014 blocks are just skipped regions. Steps may declare a `label` anchor, and every jump field has a *Label twin (toLabel, onTrueLabel, endLabel, \u2026) resolved to step numbers at write time \u2014 prefer labels over hand-counted numbers; an unresolvable label rejects the write. Detail dial per method: full (narrative required) | calls-only (call choreography suffices) | intent (prose instead of steps); omitted = stereotype default (Portal/Observer/Adapter: calls-only, Store/Index/Registry: intent, else full). Conformance dial per method or spec: declared | anchored | off \u2014 how strictly structural conformance requires contract methods to be realized in their source file (omitted = Portal: anchored, else declared). A method whose body lives in its own file names it in the method's sourcePath; the implementation's sourcePath is the default for every method that names none. Re-authoring an existing id REPLACES the method list: a method left out of the input is REMOVED together with its narrative (and reported); spec-level lint/ext are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the notices a restatement raised, each as its own entry.",
|
|
28819
|
+
description: "Write L4 Concrete Implementation spec containing L5 method narratives. Narratives are a FLAT ordered step list; flow steps (branch/switch/loop/try/parallel/jump/return/throw) jump by step number \u2014 blocks are just skipped regions. Steps may declare a `label` anchor, and every jump field has a *Label twin (toLabel, onTrueLabel, endLabel, \u2026) resolved to step numbers at write time \u2014 prefer labels over hand-counted numbers; an unresolvable label rejects the write. Detail dial per method: full (narrative required) | calls-only (call choreography suffices) | intent (prose instead of steps); omitted = stereotype default (Portal/Observer/Adapter: calls-only, Store/Index/Registry: intent, else full). A method whose narrative shows no steps declares the calls it makes in `calls` (one \"<component>.<method>\" each): the reachability walk takes exactly those edges and no others, so an intent-level method that reaches a collaborator must name it or that collaborator is reported unused. Conformance dial per method or spec: declared | anchored | off \u2014 how strictly structural conformance requires contract methods to be realized in their source file (omitted = Portal: anchored, else declared). A method whose body lives in its own file names it in the method's sourcePath; the implementation's sourcePath is the default for every method that names none. Re-authoring an existing id REPLACES the method list: a method left out of the input is REMOVED together with its narrative (and reported); spec-level lint/ext are carried forward, and the stored status is kept unless this input states a higher one. The answer carries a write receipt as structured content beside the sentence \u2014 the status written, whether a spec already held the id, and the notices a restatement raised, each as its own entry.",
|
|
28748
28820
|
inputSchema: implInput,
|
|
28749
28821
|
outputSchema: specWriteReceiptOutput
|
|
28750
28822
|
},
|