@pmelab/gtd 59.0.1 → 60.0.0
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/.claude-plugin/plugin.json +1 -1
- package/README.md +37 -33
- package/claude/hooks/doors.ts +8 -4
- package/dist/chunks/flows-DENt0uWV.mjs +707 -0
- package/dist/chunks/{steering-CCNvngSH.mjs → steering-DF_BMadH.mjs} +292 -289
- package/dist/chunks/{testkit-07Z_ENch.mjs → testkit-BvecVjQ0.mjs} +570 -390
- package/dist/chunks/{wire-BTUhRGXj.mjs → wire-DZ8R1RU9.mjs} +8 -1
- package/dist/flows.mjs +2 -676
- package/dist/gtd.bundle.mjs +751 -645
- package/dist/steering.mjs +2 -2
- package/dist/testkit/gherkin.mjs +245 -36
- package/dist/testkit.mjs +3 -2
- package/dist/web/main.js +234 -232
- package/dist/wire.mjs +2 -2
- package/dist/workflow-kit.mjs +3 -3
- package/package.json +1 -1
- package/skills/authoring/SKILL.md +66 -60
- package/src/cli/Cli.test.ts +183 -182
- package/src/cli/Cli.ts +164 -186
- package/src/cli/History.ts +9 -4
- package/src/cli/Judges.ts +1 -1
- package/src/cli/Output.test.ts +35 -0
- package/src/cli/Output.ts +31 -1
- package/src/cli/Program.test.ts +65 -79
- package/src/cli/Program.ts +157 -118
- package/src/edge/Edge.test.ts +64 -56
- package/src/edge/Edge.ts +3 -3
- package/src/flows/factory.test.ts +14 -0
- package/src/flows/factory.ts +22 -5
- package/src/flows/helpers.test.ts +35 -0
- package/src/flows/helpers.ts +19 -10
- package/src/flows/index.test.ts +66 -0
- package/src/flows/index.ts +109 -5
- package/src/flows/internal.ts +7 -0
- package/src/flows/runtime.test.ts +192 -1
- package/src/flows/runtime.ts +188 -54
- package/src/gherkin/git-control.feature +6 -6
- package/src/gherkin/index.test.ts +127 -0
- package/src/gherkin/index.ts +370 -51
- package/src/gherkin/messages.test.ts +38 -4
- package/src/gherkin/messages.ts +21 -4
- package/src/gherkin/more-steps.feature +170 -0
- package/src/gherkin/scenario-end.feature +101 -0
- package/src/gherkin/testkit.feature +83 -7
- package/src/install/Install.test.ts +7 -7
- package/src/install/Install.ts +8 -8
- package/src/replay/Replay.test.ts +13 -2
- package/src/replay/Replay.ts +19 -30
- package/src/replay/judgeDiffShape.test.ts +1 -1
- package/src/steering/Footnotes.test.ts +1 -1
- package/src/steering/SteeringFormats.test.ts +1 -1
- package/src/steering/freeform.test.ts +1 -1
- package/src/steering/index.test.ts +27 -1
- package/src/steering/index.ts +6 -104
- package/src/steering/internal.ts +11 -0
- package/src/steering/qa.test.ts +7 -2
- package/src/steering/reads.test.ts +42 -0
- package/src/steering/reads.ts +43 -0
- package/src/steering/registry.ts +93 -0
- package/src/steering/review.test.ts +2 -2
- package/src/step/planStart.test.ts +18 -16
- package/src/step/planStart.ts +9 -9
- package/src/testkit/TestRepo.test.ts +245 -7
- package/src/testkit/TestRepo.ts +271 -40
- package/src/testkit/imports.test.ts +58 -0
- package/src/testkit/imports.ts +102 -0
- package/src/testkit/index.test.ts +28 -0
- package/src/testkit/index.ts +14 -2
- package/src/twins/EmittedScriptRecognizer.ts +2 -6
- package/src/twins/EmittedScripts.test.ts +4 -2
- package/src/twins/InMemRepo.ts +1 -0
- package/src/wire/Script.ts +11 -0
- package/src/wire/WireSchema.test.ts +3 -0
- package/src/wire/golden/1/script.json +4 -0
- package/src/wire/index.ts +1 -0
- package/src/workflow/SteeringFormats.test.ts +1 -1
- package/src/workflow/compile.ts +2 -2
- package/src/workflow/discovery.test.ts +3 -3
- package/src/workflow/load.test.ts +256 -116
- package/src/workflow/load.ts +169 -146
- package/src/workflow-kit/index.test.ts +25 -0
- package/src/workflow-kit/index.ts +37 -4
- package/src/workflow-kit/review.ts +2 -2
package/dist/web/main.js
CHANGED
|
@@ -30764,6 +30764,238 @@ const blockRunInline = (content, nodes, options) => {
|
|
|
30764
30764
|
return joinInlineRuns(visibleNodes(content, nodes, options).map((node) => nodeInline(content, node, options)));
|
|
30765
30765
|
};
|
|
30766
30766
|
//#endregion
|
|
30767
|
+
//#region src/steering/findings.ts
|
|
30768
|
+
/** The `# ` heading the findings a judge called noise sit under. */
|
|
30769
|
+
const NOISE_HEADING = "Dropped as noise";
|
|
30770
|
+
const OPEN_GROUP = "Open";
|
|
30771
|
+
const MARKER = /\[\^[^\]\s]+\]/g;
|
|
30772
|
+
const isBox = (item, content) => {
|
|
30773
|
+
if (typeof item.checked !== "boolean") return false;
|
|
30774
|
+
const first = item.children[0];
|
|
30775
|
+
if (first?.type !== "paragraph" || first.position === void 0) return false;
|
|
30776
|
+
const text = content.slice(first.position.start.offset, first.position.end.offset);
|
|
30777
|
+
return new RegExp(`^fix(?![\\w-])`).test(text);
|
|
30778
|
+
};
|
|
30779
|
+
const slice = (content, node) => content.slice(node.position.start.offset, node.position.end.offset);
|
|
30780
|
+
const lineOf = (node, end = false) => toLspPosition(end ? node.position.end : node.position.start).line;
|
|
30781
|
+
/** The box item's own text past its label: a formatter may fold a guidance line into it. */
|
|
30782
|
+
const inlineGuidance = (content, item) => {
|
|
30783
|
+
const first = item.children[0];
|
|
30784
|
+
return content.slice(first.position.start.offset, item.position.end.offset).slice(3).replace(/^[\s:—–-]+/, "").trim();
|
|
30785
|
+
};
|
|
30786
|
+
const guidanceOf = (parts) => parts.map((p) => p.replace(MARKER, "").trim()).filter((p) => p !== "").join("\n\n");
|
|
30787
|
+
/** `nodes` split at `box`: the body before it, the guidance after it. */
|
|
30788
|
+
const splitAtBox = (content, nodes, box) => {
|
|
30789
|
+
const at = nodes.findIndex((n) => n.type === "list" && n.children.includes(box));
|
|
30790
|
+
const list = nodes[at];
|
|
30791
|
+
const boxAt = list.children.indexOf(box);
|
|
30792
|
+
const bodyNodes = nodes.slice(0, boxAt > 0 ? at + 1 : at);
|
|
30793
|
+
const trailing = list.children.slice(boxAt + 1);
|
|
30794
|
+
const after = nodes.slice(at + 1);
|
|
30795
|
+
return {
|
|
30796
|
+
bodyNodes,
|
|
30797
|
+
body: bodyNodes.map((n) => n === list ? content.slice(n.position.start.offset, box.position.start.offset).trimEnd() : slice(content, n)).join("\n\n"),
|
|
30798
|
+
notBody: list.children.slice(boxAt),
|
|
30799
|
+
guidance: guidanceOf([inlineGuidance(content, box), ...[...trailing, ...after].map((n) => slice(content, n))]),
|
|
30800
|
+
last: after.at(-1) ?? trailing.at(-1) ?? box
|
|
30801
|
+
};
|
|
30802
|
+
};
|
|
30803
|
+
const entryOf = (content, title, noise, headingLine, nodes, endLine) => {
|
|
30804
|
+
const notes = nodes.filter((n) => n.type !== "footnoteDefinition");
|
|
30805
|
+
const boxes = notes.flatMap((n) => n.type === "list" ? n.children.filter((i) => isBox(i, content)) : []);
|
|
30806
|
+
const box = boxes[0];
|
|
30807
|
+
const split = box === void 0 ? {
|
|
30808
|
+
bodyNodes: notes,
|
|
30809
|
+
body: notes.map((n) => slice(content, n)).join("\n\n"),
|
|
30810
|
+
notBody: [],
|
|
30811
|
+
guidance: ""
|
|
30812
|
+
} : splitAtBox(content, notes, box);
|
|
30813
|
+
return {
|
|
30814
|
+
title,
|
|
30815
|
+
body: split.body,
|
|
30816
|
+
noise,
|
|
30817
|
+
fix: box?.checked === true,
|
|
30818
|
+
guidance: split.guidance,
|
|
30819
|
+
boxes: boxes.length,
|
|
30820
|
+
headingLine,
|
|
30821
|
+
bodyNodes: split.bodyNodes,
|
|
30822
|
+
...box === void 0 ? {} : { box },
|
|
30823
|
+
notBody: new Set(split.notBody),
|
|
30824
|
+
lastLine: split.last === void 0 ? headingLine : lineOf(split.last, true),
|
|
30825
|
+
endLine
|
|
30826
|
+
};
|
|
30827
|
+
};
|
|
30828
|
+
const parse = (content) => {
|
|
30829
|
+
const tree = parseMarkdown(content);
|
|
30830
|
+
const entries = [];
|
|
30831
|
+
let noise = false;
|
|
30832
|
+
let open;
|
|
30833
|
+
const close = (endLine) => {
|
|
30834
|
+
if (open !== void 0) entries.push(entryOf(content, open.title, noise, open.line, open.nodes, endLine));
|
|
30835
|
+
open = void 0;
|
|
30836
|
+
};
|
|
30837
|
+
for (const node of tree.children) {
|
|
30838
|
+
if (node.type === "heading" && node.depth <= 2) {
|
|
30839
|
+
close(lineOf(node) - 1);
|
|
30840
|
+
const text = headingText$1(content, node, (t) => t.replace(MARKER, ""));
|
|
30841
|
+
if (node.depth === 1) noise = text === NOISE_HEADING;
|
|
30842
|
+
else open = {
|
|
30843
|
+
title: text,
|
|
30844
|
+
line: lineOf(node),
|
|
30845
|
+
nodes: []
|
|
30846
|
+
};
|
|
30847
|
+
continue;
|
|
30848
|
+
}
|
|
30849
|
+
open?.nodes.push(node);
|
|
30850
|
+
}
|
|
30851
|
+
close(content.split(/\r?\n/).length - 1);
|
|
30852
|
+
return entries;
|
|
30853
|
+
};
|
|
30854
|
+
const entryFindings = (content) => parse(content).flatMap((e) => {
|
|
30855
|
+
if (e.boxes === 0) return [{
|
|
30856
|
+
message: `"${e.title}" has no \`- [ ] fix\` box — restore it, ticked to fix the finding or unticked to drop it`,
|
|
30857
|
+
line: e.headingLine
|
|
30858
|
+
}];
|
|
30859
|
+
if (e.boxes > 1) return [{
|
|
30860
|
+
message: `"${e.title}" has ${e.boxes} \`fix\` boxes — keep exactly one`,
|
|
30861
|
+
line: e.headingLine
|
|
30862
|
+
}];
|
|
30863
|
+
if (!e.fix && e.guidance !== "") return [{
|
|
30864
|
+
message: `"${e.title}" has guidance under an unticked box — tick it to fix the finding, or delete the text to drop it`,
|
|
30865
|
+
line: lineOf(e.box)
|
|
30866
|
+
}];
|
|
30867
|
+
return [];
|
|
30868
|
+
});
|
|
30869
|
+
const findingsView = (content) => {
|
|
30870
|
+
const noteAt = noteLookup(content);
|
|
30871
|
+
return { nodes: parse(content).map((e, index) => {
|
|
30872
|
+
const note = noteAt(e.headingLine);
|
|
30873
|
+
return {
|
|
30874
|
+
title: e.title,
|
|
30875
|
+
anchor: {
|
|
30876
|
+
kind: "chunk",
|
|
30877
|
+
index
|
|
30878
|
+
},
|
|
30879
|
+
group: e.noise ? NOISE_HEADING : OPEN_GROUP,
|
|
30880
|
+
...e.boxes === 1 ? { checked: e.fix } : {},
|
|
30881
|
+
...e.guidance === "" ? {} : { note: e.guidance },
|
|
30882
|
+
...note?.thread !== void 0 ? { thread: note.thread } : {},
|
|
30883
|
+
body: blockNodesOfRun(content, e.bodyNodes, { skipListItem: (i) => e.notBody.has(i) })
|
|
30884
|
+
};
|
|
30885
|
+
}) };
|
|
30886
|
+
};
|
|
30887
|
+
const findingsOutline = (content) => {
|
|
30888
|
+
const lines = content.split(/\r?\n/);
|
|
30889
|
+
return [...parse(content).map((e) => ({
|
|
30890
|
+
name: e.title,
|
|
30891
|
+
detail: e.noise ? "noise" : e.fix ? "fix" : "drop",
|
|
30892
|
+
range: spanRange(lines, e.headingLine, Math.max(e.headingLine, e.endLine)),
|
|
30893
|
+
selectionRange: lineRange(lines, e.headingLine)
|
|
30894
|
+
})), ...threadOutlineNodes(content).map((t) => t.node)];
|
|
30895
|
+
};
|
|
30896
|
+
const findingsAnnotate = (content, anchor, text) => {
|
|
30897
|
+
const entries = parse(content);
|
|
30898
|
+
const lines = content.split(/\r?\n/);
|
|
30899
|
+
const resolved = (() => {
|
|
30900
|
+
if (anchor.kind === "chunk") {
|
|
30901
|
+
const e = entries[anchor.index];
|
|
30902
|
+
return e && {
|
|
30903
|
+
line: e.headingLine,
|
|
30904
|
+
blockEndLine: e.headingLine,
|
|
30905
|
+
key: `chunk:${anchor.index}`
|
|
30906
|
+
};
|
|
30907
|
+
}
|
|
30908
|
+
if (anchor.kind !== "paragraph") return void 0;
|
|
30909
|
+
const block = entries.flatMap((e) => e.bodyNodes).find((n) => anchor.line >= lineOf(n) && anchor.line <= lineOf(n, true));
|
|
30910
|
+
return block && {
|
|
30911
|
+
line: anchor.line,
|
|
30912
|
+
blockEndLine: lineOf(block, true),
|
|
30913
|
+
key: `paragraph:${anchor.line}`
|
|
30914
|
+
};
|
|
30915
|
+
})();
|
|
30916
|
+
if (resolved === void 0) return {
|
|
30917
|
+
ok: false,
|
|
30918
|
+
reason: "anchor-not-found"
|
|
30919
|
+
};
|
|
30920
|
+
const result = footnoteAttachEdits(content, {
|
|
30921
|
+
...resolved,
|
|
30922
|
+
endCharacter: (lines[resolved.line] ?? "").length
|
|
30923
|
+
}, text);
|
|
30924
|
+
return result.ok ? {
|
|
30925
|
+
ok: true,
|
|
30926
|
+
edits: result.edits
|
|
30927
|
+
} : result;
|
|
30928
|
+
};
|
|
30929
|
+
/** Ticks or unticks a finding's box; `text` replaces its guidance, and unticking drops it. */
|
|
30930
|
+
const findingsApply = (content, anchor, opts) => {
|
|
30931
|
+
if (anchor.kind !== "chunk") return {
|
|
30932
|
+
ok: false,
|
|
30933
|
+
reason: "anchor-not-found"
|
|
30934
|
+
};
|
|
30935
|
+
const e = parse(content)[anchor.index];
|
|
30936
|
+
if (e?.box === void 0 || e.boxes !== 1) return {
|
|
30937
|
+
ok: false,
|
|
30938
|
+
reason: "anchor-not-found"
|
|
30939
|
+
};
|
|
30940
|
+
const fix = opts.checked ?? e.fix;
|
|
30941
|
+
const guidance = fix ? (opts.text ?? e.guidance).trim() : "";
|
|
30942
|
+
if (fix === e.fix && guidance === e.guidance) return {
|
|
30943
|
+
ok: true,
|
|
30944
|
+
edits: []
|
|
30945
|
+
};
|
|
30946
|
+
const lines = content.split(/\r?\n/);
|
|
30947
|
+
const eol = eolOf(content);
|
|
30948
|
+
const start = lineOf(e.box);
|
|
30949
|
+
const box = `${lines[start].match(/^\s*[-*+]\s+/)[0]}[${fix ? "x" : " "}] fix`;
|
|
30950
|
+
const text = guidance === "" ? box : `${box}${eol}${eol}${guidance.split(/\r?\n/).join(eol)}`;
|
|
30951
|
+
return {
|
|
30952
|
+
ok: true,
|
|
30953
|
+
edits: [{
|
|
30954
|
+
range: {
|
|
30955
|
+
start: {
|
|
30956
|
+
line: start,
|
|
30957
|
+
character: 0
|
|
30958
|
+
},
|
|
30959
|
+
end: lineRange(lines, e.lastLine).end
|
|
30960
|
+
},
|
|
30961
|
+
newText: text
|
|
30962
|
+
}]
|
|
30963
|
+
};
|
|
30964
|
+
};
|
|
30965
|
+
/** The finding gate's file: one `## ` finding each, with one `fix` box and optional guidance below it. */
|
|
30966
|
+
const findingsDescriptor = {
|
|
30967
|
+
sample: `# Quality
|
|
30968
|
+
|
|
30969
|
+
## A finding a lens wrote[^nafind1]
|
|
30970
|
+
|
|
30971
|
+
[^nafind1]:
|
|
30972
|
+
Attached via the phone UI, this note shows a comment on a finding with a
|
|
30973
|
+
\`multi word code span\` that runs past eighty characters in total length.
|
|
30974
|
+
|
|
30975
|
+
What the lens found, and where.
|
|
30976
|
+
|
|
30977
|
+
- [x] fix
|
|
30978
|
+
|
|
30979
|
+
Fix it the simple way.
|
|
30980
|
+
|
|
30981
|
+
# Dropped as noise
|
|
30982
|
+
|
|
30983
|
+
## A finding the judge called noise
|
|
30984
|
+
|
|
30985
|
+
Why the judge dropped it.
|
|
30986
|
+
|
|
30987
|
+
- [ ] fix
|
|
30988
|
+
`,
|
|
30989
|
+
validate: (content) => [...entryFindings(content), ...threadFindings(content)],
|
|
30990
|
+
outline: findingsOutline,
|
|
30991
|
+
actions: (content, range) => threadReplyActions(content, range.start),
|
|
30992
|
+
view: findingsView,
|
|
30993
|
+
annotate: findingsAnnotate,
|
|
30994
|
+
apply: findingsApply,
|
|
30995
|
+
clearTicks: (content) => content,
|
|
30996
|
+
openThreads: openThreadsOf
|
|
30997
|
+
};
|
|
30998
|
+
//#endregion
|
|
30767
30999
|
//#region src/steering/qa.ts
|
|
30768
31000
|
/**
|
|
30769
31001
|
* A marker as plain text. Local to this module because `headingText`/
|
|
@@ -32566,242 +32798,12 @@ const decisionOf$1 = (chunk) => {
|
|
|
32566
32798
|
};
|
|
32567
32799
|
};
|
|
32568
32800
|
//#endregion
|
|
32569
|
-
//#region src/steering/
|
|
32570
|
-
/** The `# ` heading the findings a judge called noise sit under. */
|
|
32571
|
-
const NOISE_HEADING = "Dropped as noise";
|
|
32572
|
-
const OPEN_GROUP = "Open";
|
|
32573
|
-
const MARKER = /\[\^[^\]\s]+\]/g;
|
|
32574
|
-
const isBox = (item, content) => {
|
|
32575
|
-
if (typeof item.checked !== "boolean") return false;
|
|
32576
|
-
const first = item.children[0];
|
|
32577
|
-
if (first?.type !== "paragraph" || first.position === void 0) return false;
|
|
32578
|
-
const text = content.slice(first.position.start.offset, first.position.end.offset);
|
|
32579
|
-
return new RegExp(`^fix(?![\\w-])`).test(text);
|
|
32580
|
-
};
|
|
32581
|
-
const slice = (content, node) => content.slice(node.position.start.offset, node.position.end.offset);
|
|
32582
|
-
const lineOf = (node, end = false) => toLspPosition(end ? node.position.end : node.position.start).line;
|
|
32583
|
-
/** The box item's own text past its label: a formatter may fold a guidance line into it. */
|
|
32584
|
-
const inlineGuidance = (content, item) => {
|
|
32585
|
-
const first = item.children[0];
|
|
32586
|
-
return content.slice(first.position.start.offset, item.position.end.offset).slice(3).replace(/^[\s:—–-]+/, "").trim();
|
|
32587
|
-
};
|
|
32588
|
-
const guidanceOf = (parts) => parts.map((p) => p.replace(MARKER, "").trim()).filter((p) => p !== "").join("\n\n");
|
|
32589
|
-
/** `nodes` split at `box`: the body before it, the guidance after it. */
|
|
32590
|
-
const splitAtBox = (content, nodes, box) => {
|
|
32591
|
-
const at = nodes.findIndex((n) => n.type === "list" && n.children.includes(box));
|
|
32592
|
-
const list = nodes[at];
|
|
32593
|
-
const boxAt = list.children.indexOf(box);
|
|
32594
|
-
const bodyNodes = nodes.slice(0, boxAt > 0 ? at + 1 : at);
|
|
32595
|
-
const trailing = list.children.slice(boxAt + 1);
|
|
32596
|
-
const after = nodes.slice(at + 1);
|
|
32597
|
-
return {
|
|
32598
|
-
bodyNodes,
|
|
32599
|
-
body: bodyNodes.map((n) => n === list ? content.slice(n.position.start.offset, box.position.start.offset).trimEnd() : slice(content, n)).join("\n\n"),
|
|
32600
|
-
notBody: list.children.slice(boxAt),
|
|
32601
|
-
guidance: guidanceOf([inlineGuidance(content, box), ...[...trailing, ...after].map((n) => slice(content, n))]),
|
|
32602
|
-
last: after.at(-1) ?? trailing.at(-1) ?? box
|
|
32603
|
-
};
|
|
32604
|
-
};
|
|
32605
|
-
const entryOf = (content, title, noise, headingLine, nodes, endLine) => {
|
|
32606
|
-
const notes = nodes.filter((n) => n.type !== "footnoteDefinition");
|
|
32607
|
-
const boxes = notes.flatMap((n) => n.type === "list" ? n.children.filter((i) => isBox(i, content)) : []);
|
|
32608
|
-
const box = boxes[0];
|
|
32609
|
-
const split = box === void 0 ? {
|
|
32610
|
-
bodyNodes: notes,
|
|
32611
|
-
body: notes.map((n) => slice(content, n)).join("\n\n"),
|
|
32612
|
-
notBody: [],
|
|
32613
|
-
guidance: ""
|
|
32614
|
-
} : splitAtBox(content, notes, box);
|
|
32615
|
-
return {
|
|
32616
|
-
title,
|
|
32617
|
-
body: split.body,
|
|
32618
|
-
noise,
|
|
32619
|
-
fix: box?.checked === true,
|
|
32620
|
-
guidance: split.guidance,
|
|
32621
|
-
boxes: boxes.length,
|
|
32622
|
-
headingLine,
|
|
32623
|
-
bodyNodes: split.bodyNodes,
|
|
32624
|
-
...box === void 0 ? {} : { box },
|
|
32625
|
-
notBody: new Set(split.notBody),
|
|
32626
|
-
lastLine: split.last === void 0 ? headingLine : lineOf(split.last, true),
|
|
32627
|
-
endLine
|
|
32628
|
-
};
|
|
32629
|
-
};
|
|
32630
|
-
const parse = (content) => {
|
|
32631
|
-
const tree = parseMarkdown(content);
|
|
32632
|
-
const entries = [];
|
|
32633
|
-
let noise = false;
|
|
32634
|
-
let open;
|
|
32635
|
-
const close = (endLine) => {
|
|
32636
|
-
if (open !== void 0) entries.push(entryOf(content, open.title, noise, open.line, open.nodes, endLine));
|
|
32637
|
-
open = void 0;
|
|
32638
|
-
};
|
|
32639
|
-
for (const node of tree.children) {
|
|
32640
|
-
if (node.type === "heading" && node.depth <= 2) {
|
|
32641
|
-
close(lineOf(node) - 1);
|
|
32642
|
-
const text = headingText$1(content, node, (t) => t.replace(MARKER, ""));
|
|
32643
|
-
if (node.depth === 1) noise = text === NOISE_HEADING;
|
|
32644
|
-
else open = {
|
|
32645
|
-
title: text,
|
|
32646
|
-
line: lineOf(node),
|
|
32647
|
-
nodes: []
|
|
32648
|
-
};
|
|
32649
|
-
continue;
|
|
32650
|
-
}
|
|
32651
|
-
open?.nodes.push(node);
|
|
32652
|
-
}
|
|
32653
|
-
close(content.split(/\r?\n/).length - 1);
|
|
32654
|
-
return entries;
|
|
32655
|
-
};
|
|
32656
|
-
const entryFindings = (content) => parse(content).flatMap((e) => {
|
|
32657
|
-
if (e.boxes === 0) return [{
|
|
32658
|
-
message: `"${e.title}" has no \`- [ ] fix\` box — restore it, ticked to fix the finding or unticked to drop it`,
|
|
32659
|
-
line: e.headingLine
|
|
32660
|
-
}];
|
|
32661
|
-
if (e.boxes > 1) return [{
|
|
32662
|
-
message: `"${e.title}" has ${e.boxes} \`fix\` boxes — keep exactly one`,
|
|
32663
|
-
line: e.headingLine
|
|
32664
|
-
}];
|
|
32665
|
-
if (!e.fix && e.guidance !== "") return [{
|
|
32666
|
-
message: `"${e.title}" has guidance under an unticked box — tick it to fix the finding, or delete the text to drop it`,
|
|
32667
|
-
line: lineOf(e.box)
|
|
32668
|
-
}];
|
|
32669
|
-
return [];
|
|
32670
|
-
});
|
|
32671
|
-
const findingsView = (content) => {
|
|
32672
|
-
const noteAt = noteLookup(content);
|
|
32673
|
-
return { nodes: parse(content).map((e, index) => {
|
|
32674
|
-
const note = noteAt(e.headingLine);
|
|
32675
|
-
return {
|
|
32676
|
-
title: e.title,
|
|
32677
|
-
anchor: {
|
|
32678
|
-
kind: "chunk",
|
|
32679
|
-
index
|
|
32680
|
-
},
|
|
32681
|
-
group: e.noise ? NOISE_HEADING : OPEN_GROUP,
|
|
32682
|
-
...e.boxes === 1 ? { checked: e.fix } : {},
|
|
32683
|
-
...e.guidance === "" ? {} : { note: e.guidance },
|
|
32684
|
-
...note?.thread !== void 0 ? { thread: note.thread } : {},
|
|
32685
|
-
body: blockNodesOfRun(content, e.bodyNodes, { skipListItem: (i) => e.notBody.has(i) })
|
|
32686
|
-
};
|
|
32687
|
-
}) };
|
|
32688
|
-
};
|
|
32689
|
-
const findingsOutline = (content) => {
|
|
32690
|
-
const lines = content.split(/\r?\n/);
|
|
32691
|
-
return [...parse(content).map((e) => ({
|
|
32692
|
-
name: e.title,
|
|
32693
|
-
detail: e.noise ? "noise" : e.fix ? "fix" : "drop",
|
|
32694
|
-
range: spanRange(lines, e.headingLine, Math.max(e.headingLine, e.endLine)),
|
|
32695
|
-
selectionRange: lineRange(lines, e.headingLine)
|
|
32696
|
-
})), ...threadOutlineNodes(content).map((t) => t.node)];
|
|
32697
|
-
};
|
|
32698
|
-
const findingsAnnotate = (content, anchor, text) => {
|
|
32699
|
-
const entries = parse(content);
|
|
32700
|
-
const lines = content.split(/\r?\n/);
|
|
32701
|
-
const resolved = (() => {
|
|
32702
|
-
if (anchor.kind === "chunk") {
|
|
32703
|
-
const e = entries[anchor.index];
|
|
32704
|
-
return e && {
|
|
32705
|
-
line: e.headingLine,
|
|
32706
|
-
blockEndLine: e.headingLine,
|
|
32707
|
-
key: `chunk:${anchor.index}`
|
|
32708
|
-
};
|
|
32709
|
-
}
|
|
32710
|
-
if (anchor.kind !== "paragraph") return void 0;
|
|
32711
|
-
const block = entries.flatMap((e) => e.bodyNodes).find((n) => anchor.line >= lineOf(n) && anchor.line <= lineOf(n, true));
|
|
32712
|
-
return block && {
|
|
32713
|
-
line: anchor.line,
|
|
32714
|
-
blockEndLine: lineOf(block, true),
|
|
32715
|
-
key: `paragraph:${anchor.line}`
|
|
32716
|
-
};
|
|
32717
|
-
})();
|
|
32718
|
-
if (resolved === void 0) return {
|
|
32719
|
-
ok: false,
|
|
32720
|
-
reason: "anchor-not-found"
|
|
32721
|
-
};
|
|
32722
|
-
const result = footnoteAttachEdits(content, {
|
|
32723
|
-
...resolved,
|
|
32724
|
-
endCharacter: (lines[resolved.line] ?? "").length
|
|
32725
|
-
}, text);
|
|
32726
|
-
return result.ok ? {
|
|
32727
|
-
ok: true,
|
|
32728
|
-
edits: result.edits
|
|
32729
|
-
} : result;
|
|
32730
|
-
};
|
|
32731
|
-
/** Ticks or unticks a finding's box; `text` replaces its guidance, and unticking drops it. */
|
|
32732
|
-
const findingsApply = (content, anchor, opts) => {
|
|
32733
|
-
if (anchor.kind !== "chunk") return {
|
|
32734
|
-
ok: false,
|
|
32735
|
-
reason: "anchor-not-found"
|
|
32736
|
-
};
|
|
32737
|
-
const e = parse(content)[anchor.index];
|
|
32738
|
-
if (e?.box === void 0 || e.boxes !== 1) return {
|
|
32739
|
-
ok: false,
|
|
32740
|
-
reason: "anchor-not-found"
|
|
32741
|
-
};
|
|
32742
|
-
const fix = opts.checked ?? e.fix;
|
|
32743
|
-
const guidance = fix ? (opts.text ?? e.guidance).trim() : "";
|
|
32744
|
-
if (fix === e.fix && guidance === e.guidance) return {
|
|
32745
|
-
ok: true,
|
|
32746
|
-
edits: []
|
|
32747
|
-
};
|
|
32748
|
-
const lines = content.split(/\r?\n/);
|
|
32749
|
-
const eol = eolOf(content);
|
|
32750
|
-
const start = lineOf(e.box);
|
|
32751
|
-
const box = `${lines[start].match(/^\s*[-*+]\s+/)[0]}[${fix ? "x" : " "}] fix`;
|
|
32752
|
-
const text = guidance === "" ? box : `${box}${eol}${eol}${guidance.split(/\r?\n/).join(eol)}`;
|
|
32753
|
-
return {
|
|
32754
|
-
ok: true,
|
|
32755
|
-
edits: [{
|
|
32756
|
-
range: {
|
|
32757
|
-
start: {
|
|
32758
|
-
line: start,
|
|
32759
|
-
character: 0
|
|
32760
|
-
},
|
|
32761
|
-
end: lineRange(lines, e.lastLine).end
|
|
32762
|
-
},
|
|
32763
|
-
newText: text
|
|
32764
|
-
}]
|
|
32765
|
-
};
|
|
32766
|
-
};
|
|
32767
|
-
//#endregion
|
|
32768
|
-
//#region src/steering/index.ts
|
|
32801
|
+
//#region src/steering/registry.ts
|
|
32769
32802
|
/** The built-in registry: mode name → its format, in registry order. The single place the built-in formats are wired into names — everything else in this module dispatches over a resolved `SteeringFormat`, never a mode-name string. */
|
|
32770
32803
|
const REGISTRY = /* @__PURE__ */ new Map([
|
|
32771
32804
|
["qa", qaDescriptor],
|
|
32772
32805
|
["review", reviewDescriptor],
|
|
32773
|
-
["findings",
|
|
32774
|
-
sample: `# Quality
|
|
32775
|
-
|
|
32776
|
-
## A finding a lens wrote[^nafind1]
|
|
32777
|
-
|
|
32778
|
-
[^nafind1]:
|
|
32779
|
-
Attached via the phone UI, this note shows a comment on a finding with a
|
|
32780
|
-
\`multi word code span\` that runs past eighty characters in total length.
|
|
32781
|
-
|
|
32782
|
-
What the lens found, and where.
|
|
32783
|
-
|
|
32784
|
-
- [x] fix
|
|
32785
|
-
|
|
32786
|
-
Fix it the simple way.
|
|
32787
|
-
|
|
32788
|
-
# Dropped as noise
|
|
32789
|
-
|
|
32790
|
-
## A finding the judge called noise
|
|
32791
|
-
|
|
32792
|
-
Why the judge dropped it.
|
|
32793
|
-
|
|
32794
|
-
- [ ] fix
|
|
32795
|
-
`,
|
|
32796
|
-
validate: (content) => [...entryFindings(content), ...threadFindings(content)],
|
|
32797
|
-
outline: findingsOutline,
|
|
32798
|
-
actions: (content, range) => threadReplyActions(content, range.start),
|
|
32799
|
-
view: findingsView,
|
|
32800
|
-
annotate: findingsAnnotate,
|
|
32801
|
-
apply: findingsApply,
|
|
32802
|
-
clearTicks: (content) => content,
|
|
32803
|
-
openThreads: openThreadsOf
|
|
32804
|
-
}]
|
|
32806
|
+
["findings", findingsDescriptor]
|
|
32805
32807
|
]);
|
|
32806
32808
|
[...REGISTRY.keys()];
|
|
32807
32809
|
/** The built-in `SteeringFormat` registered under `mode`'s name, or `undefined` when it isn't a built-in mode at all. The one place a bare mode-name string is ever looked up — every consumer takes the RESOLVED value this returns, never a name, so "mode is required, never inferred from a file's basename" is a type, not a rule to remember. */
|
package/dist/wire.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
export { ACCESS_REFUSAL, BEAT_KINDS, DEMAND_BRIEFING, EXIT_CODES, EXIT_OK, EXIT_RUNTIME_ERROR, EXIT_SIGINT, EXIT_SIGTERM, EXIT_STRAYS_FOUND, EXIT_USAGE_ERROR, UNATTRIBUTED_MODEL, WIRE_SCHEMA, beatDocument, beatKindOf, demandOf, globMatches, judgeJson, landFields, landProseText, noopText, renderBeatJson, renderBeatPlain, renderFormat, renderLandJson, renderLandPlain, resolveSession, selectPath, stallDiagnosis, statusOf };
|
|
1
|
+
import { A as WIRE_SCHEMA, C as EXIT_RUNTIME_ERROR, D as EXIT_USAGE_ERROR, E as EXIT_STRAYS_FOUND, O as ACCESS_REFUSAL, S as EXIT_OK, T as EXIT_SIGTERM, _ as BEAT_KINDS, a as renderScriptJson, b as stallDiagnosis, c as landProseText, d as renderLandPlain, f as DEMAND_BRIEFING, g as statusOf, h as renderBeatJson, i as judgeJson, j as renderFormat, k as UNATTRIBUTED_MODEL, l as noopText, m as beatDocument, n as resolveSession, o as scriptFields, p as renderBeatPlain, r as selectPath, s as landFields, t as globMatches, u as renderLandJson, v as beatKindOf, w as EXIT_SIGINT, x as EXIT_CODES, y as demandOf } from "./chunks/wire-DZ8R1RU9.mjs";
|
|
2
|
+
export { ACCESS_REFUSAL, BEAT_KINDS, DEMAND_BRIEFING, EXIT_CODES, EXIT_OK, EXIT_RUNTIME_ERROR, EXIT_SIGINT, EXIT_SIGTERM, EXIT_STRAYS_FOUND, EXIT_USAGE_ERROR, UNATTRIBUTED_MODEL, WIRE_SCHEMA, beatDocument, beatKindOf, demandOf, globMatches, judgeJson, landFields, landProseText, noopText, renderBeatJson, renderBeatPlain, renderFormat, renderLandJson, renderLandPlain, renderScriptJson, resolveSession, scriptFields, selectPath, stallDiagnosis, statusOf };
|
package/dist/workflow-kit.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as parseThreads,
|
|
1
|
+
import { B as head, F as changesSince, G as mark, P as changes, X as read, Z as refuse, d as hasThreadFor, et as scope, g as requireReplies, v as requireThreadsClosed } from "./chunks/flows-DENt0uWV.mjs";
|
|
2
|
+
import { A as parseThreads, S as unansweredNotes, b as reviewRisks, n as stripCodeThreads, v as reviewDecisions, y as reviewNotes } from "./chunks/steering-DF_BMadH.mjs";
|
|
3
3
|
//#region src/workflow-kit/health.ts
|
|
4
4
|
/** Hand a red suite that repeated attempts could not fix to a person, until `limit` rounds are spent. */
|
|
5
5
|
const escalation = async (count, steps) => {
|
|
@@ -427,4 +427,4 @@ const reviewToHandOff = async (base, p) => {
|
|
|
427
427
|
}
|
|
428
428
|
};
|
|
429
429
|
//#endregion
|
|
430
|
-
export { assertionLines, escalation, fixFindings, gate, healthy, lensEntriesOf, lensOf, lensScopes, lensesOf, qualityLap, removedAssertions, review, reviewToHandOff,
|
|
430
|
+
export { assertionLines, escalation, fixFindings, gate, healthy, lensEntriesOf, lensOf, lensScopes, lensesOf, qualityLap, removedAssertions, review, reviewToHandOff, untilSettled };
|