iterate-plugin 3.11.0 → 3.13.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/README.md +10 -9
- package/README.zh-CN.md +11 -10
- package/dist/index.js +8 -5
- package/dist/tools/config.js +109 -9
- package/dist/tools/preflight.js +280 -0
- package/dist/validation-detect.js +193 -0
- package/lib/client.js +352 -5
- package/lib/parse.js +172 -2
- package/package.json +5 -4
- package/src/client/i18n.ts +43 -1
- package/src/client/index.ts +221 -0
- package/src/index.ts +8 -5
- package/src/tools/config.ts +113 -10
- package/src/tools/preflight.ts +337 -0
- package/src/validation-detect.ts +207 -0
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* iterate-plugin client source-digest:
|
|
1
|
+
/* iterate-plugin client source-digest: 909db487a440ae131a6de59f253a9d84b240e5fbd55bce9ae83a9876b711a9a2 */
|
|
2
2
|
window.__ModuleLoader__.load({ id: "iterate-plugin", factory: (require) => {
|
|
3
3
|
var module = { exports: {} };
|
|
4
4
|
var exports = module.exports;
|
|
@@ -854,6 +854,62 @@ function scanSessionForDefenseEvents(session) {
|
|
|
854
854
|
const loose = findFirstInObject(session, isDefenseEventsResult);
|
|
855
855
|
return loose ? normalizeDefenseEventsResult(loose) : null;
|
|
856
856
|
}
|
|
857
|
+
function isPreflightReport(obj) {
|
|
858
|
+
if (!obj || typeof obj !== "object") return false;
|
|
859
|
+
const o = (
|
|
860
|
+
/** @type {Record<string, unknown>} */
|
|
861
|
+
obj
|
|
862
|
+
);
|
|
863
|
+
const ready = safeGet(o, "ready");
|
|
864
|
+
const checks = safeGet(o, "checks");
|
|
865
|
+
const blocking = safeGet(o, "blocking");
|
|
866
|
+
const warnings = safeGet(o, "warnings");
|
|
867
|
+
return typeof ready === "boolean" && Array.isArray(checks) && (typeof blocking === "number" || typeof warnings === "number");
|
|
868
|
+
}
|
|
869
|
+
function findPreflightReportInObject(obj) {
|
|
870
|
+
return findFirstInObject(obj, (o) => isPreflightReport(o));
|
|
871
|
+
}
|
|
872
|
+
function normalizePreflightReport(raw) {
|
|
873
|
+
const src = raw && typeof raw === "object" ? (
|
|
874
|
+
/** @type {Record<string, unknown>} */
|
|
875
|
+
raw
|
|
876
|
+
) : {};
|
|
877
|
+
const asNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
878
|
+
const asStr = (v) => typeof v === "string" ? v : "";
|
|
879
|
+
const checks = Array.isArray(safeGet(src, "checks")) ? safeGet(src, "checks") : [];
|
|
880
|
+
return {
|
|
881
|
+
ok: safeGet(src, "ok") !== false,
|
|
882
|
+
ready: safeGet(src, "ready") === true,
|
|
883
|
+
blocking: asNum(safeGet(src, "blocking")),
|
|
884
|
+
warnings: asNum(safeGet(src, "warnings")),
|
|
885
|
+
error: asStr(safeGet(src, "error")) || null,
|
|
886
|
+
checks: (
|
|
887
|
+
/** @type {unknown[]} */
|
|
888
|
+
checks.map((c) => {
|
|
889
|
+
const rec = (
|
|
890
|
+
/** @type {Record<string, unknown>} */
|
|
891
|
+
c && typeof c === "object" ? c : {}
|
|
892
|
+
);
|
|
893
|
+
const status = safeGet(rec, "status");
|
|
894
|
+
const detail = safeGet(rec, "detail");
|
|
895
|
+
return {
|
|
896
|
+
id: asStr(safeGet(rec, "id")),
|
|
897
|
+
status: status === "pass" || status === "warn" || status === "fail" ? status : "warn",
|
|
898
|
+
detail: detail && typeof detail === "object" ? detail : {}
|
|
899
|
+
};
|
|
900
|
+
})
|
|
901
|
+
)
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
function scanSessionForPreflight(session) {
|
|
905
|
+
const node = latestToolResultNode(session, "iterate_preflight", findPreflightReportInObject);
|
|
906
|
+
if (node !== null) {
|
|
907
|
+
const found = findPreflightReportInObject(node);
|
|
908
|
+
if (found) return normalizePreflightReport(found);
|
|
909
|
+
}
|
|
910
|
+
const loose = findFirstInObject(session, isPreflightReport);
|
|
911
|
+
return loose ? normalizePreflightReport(loose) : null;
|
|
912
|
+
}
|
|
857
913
|
function isRunSummary(obj) {
|
|
858
914
|
if (!obj || typeof obj !== "object") return false;
|
|
859
915
|
const o = (
|
|
@@ -1280,6 +1336,25 @@ function buildQualityGateClearInstruction() {
|
|
|
1280
1336
|
${JSON.stringify({ operation: "clear" }, null, 2)}
|
|
1281
1337
|
\`\`\``;
|
|
1282
1338
|
}
|
|
1339
|
+
function buildPreflightInstruction() {
|
|
1340
|
+
return `\u8BF7\u8C03\u7528 \`iterate_preflight\` \u8FD0\u884C\u5F00\u8DD1\u524D\u9884\u68C0\uFF08\u53EA\u8BFB\uFF1A\u6821\u9A8C validation.commands\u3001git\u3001\u810F\u5DE5\u4F5C\u533A\u3001\u9648\u65E7\u8D28\u91CF\u95E8\u7981\u4E0E\u4E2D\u65AD\u65AD\u70B9\uFF09\uFF1A
|
|
1341
|
+
|
|
1342
|
+
\`\`\`json
|
|
1343
|
+
${JSON.stringify({}, null, 2)}
|
|
1344
|
+
\`\`\``;
|
|
1345
|
+
}
|
|
1346
|
+
function buildValidationApplyInstruction(commands) {
|
|
1347
|
+
const list = Array.isArray(commands) ? commands.filter((c) => typeof c === "string" && c.length > 0) : [];
|
|
1348
|
+
const payload = {
|
|
1349
|
+
operation: "write",
|
|
1350
|
+
updates: { validation: { commands: { default: list } } }
|
|
1351
|
+
};
|
|
1352
|
+
return `\u8BF7\u8C03\u7528 \`iterate_config\` \u628A\u68C0\u6D4B\u5230\u7684\u9A8C\u8BC1\u547D\u4EE4\u5199\u5165 \`validation.commands.default\`\uFF08\u4F1A\u5148\u5907\u4EFD\u914D\u7F6E\uFF09\uFF1A
|
|
1353
|
+
|
|
1354
|
+
\`\`\`json
|
|
1355
|
+
${JSON.stringify(payload, null, 2)}
|
|
1356
|
+
\`\`\``;
|
|
1357
|
+
}
|
|
1283
1358
|
function buildRollbackInstruction(id) {
|
|
1284
1359
|
return `\u8BF7\u8C03\u7528 \`iterate_rollback\` \u56DE\u6EDA\u4EE5\u4E0B\u4FEE\u590D\uFF1A
|
|
1285
1360
|
|
|
@@ -1555,13 +1630,35 @@ function buildCompletionSummary(report, lang = "zh") {
|
|
|
1555
1630
|
const stats = severityStats(report);
|
|
1556
1631
|
const converged = conv.converged === true;
|
|
1557
1632
|
const totalFindings = stats.critical + stats.high + stats.medium + stats.low;
|
|
1633
|
+
const stopped = typeof conv.stoppedReason === "string" ? conv.stoppedReason : typeof report.stoppedReason === "string" ? report.stoppedReason : "";
|
|
1634
|
+
const reason = stopped ? completionReason(stopped, lang, total) : converged ? lang === "en" ? "converged" : "\u5DF2\u6536\u655B" : lang === "en" ? `reached the round limit ${total}` : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1558
1635
|
if (lang === "en") {
|
|
1559
|
-
|
|
1560
|
-
return `iterate review complete \xB7 ${rounds}/${total} rounds \xB7 ${totalFindings} findings \xB7 ${reason2}`;
|
|
1636
|
+
return `iterate review complete \xB7 ${rounds}/${total} rounds \xB7 ${totalFindings} findings \xB7 ${reason}`;
|
|
1561
1637
|
}
|
|
1562
|
-
const reason = converged ? "\u5DF2\u6536\u655B" : `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`;
|
|
1563
1638
|
return `iterate \u8BC4\u5BA1\u5B8C\u6210 \xB7 ${rounds}/${total} \u8F6E \xB7 ${totalFindings} \u9879\u53D1\u73B0 \xB7 ${reason}`;
|
|
1564
1639
|
}
|
|
1640
|
+
function completionReason(reason, lang, total) {
|
|
1641
|
+
const zh = {
|
|
1642
|
+
converged: "\u5DF2\u6536\u655B",
|
|
1643
|
+
max_rounds_reached: `\u5DF2\u8FBE\u6700\u5927\u8F6E\u6570 ${total}`,
|
|
1644
|
+
aborted_by_validation: "\u9A8C\u8BC1\u5931\u8D25\u540E\u56DE\u6EDA",
|
|
1645
|
+
aborted_by_config: "\u9A8C\u8BC1\u547D\u4EE4\u672A\u5728\u767D\u540D\u5355",
|
|
1646
|
+
inconclusive: "\u8F6E\u6B21\u7ED3\u8BBA\u4E0D\u660E",
|
|
1647
|
+
schema_invalid: "\u5BA1\u67E5\u8F93\u51FA schema \u6821\u9A8C\u5931\u8D25",
|
|
1648
|
+
no_usable_reviewer_output: "\u65E0\u53EF\u7528\u5BA1\u67E5\u8F93\u51FA"
|
|
1649
|
+
};
|
|
1650
|
+
const en = {
|
|
1651
|
+
converged: "converged",
|
|
1652
|
+
max_rounds_reached: `reached the round limit ${total}`,
|
|
1653
|
+
aborted_by_validation: "rolled back after a validation failure",
|
|
1654
|
+
aborted_by_config: "validation command not allow-listed",
|
|
1655
|
+
inconclusive: "inconclusive rounds",
|
|
1656
|
+
schema_invalid: "review output failed schema validation",
|
|
1657
|
+
no_usable_reviewer_output: "no usable review output"
|
|
1658
|
+
};
|
|
1659
|
+
const table = lang === "en" ? en : zh;
|
|
1660
|
+
return Object.hasOwn(table, reason) ? table[reason] : reason;
|
|
1661
|
+
}
|
|
1565
1662
|
var CONFIG_EDIT_FIELDS = [
|
|
1566
1663
|
{
|
|
1567
1664
|
key: "goal",
|
|
@@ -2658,6 +2755,40 @@ var EN = {
|
|
|
2658
2755
|
\u6E05\u9664\u7C7B\u578B\u7B5B\u9009: "Clear type filter",
|
|
2659
2756
|
"\u9632\u7EBF\uFF1A{a0}": "Defense: {a0}",
|
|
2660
2757
|
\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6: "No defense events of this type",
|
|
2758
|
+
// ── F11 preflight ────────────────────────────────────────────────────────
|
|
2759
|
+
\u5F00\u8DD1\u9884\u68C0: "Preflight",
|
|
2760
|
+
\u5F85\u9884\u68C0: "Not run yet",
|
|
2761
|
+
\u672A\u5C31\u7EEA: "Not ready",
|
|
2762
|
+
"\u590D\u5236 iterate_preflight \u9884\u68C0\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09": "Copy the iterate_preflight readiness instruction (JSON arguments included)",
|
|
2763
|
+
\u8FD0\u884C\u9884\u68C0: "Run preflight",
|
|
2764
|
+
\u5F00\u8DD1\u9884\u68C0\u6E05\u5355: "Preflight checklist",
|
|
2765
|
+
\u9884\u68C0\u7ED3\u679C: "Preflight result",
|
|
2766
|
+
"\u5F00\u8DD1\u524D\u5148\u9884\u68C0\uFF1A\u786E\u8BA4\u9A8C\u8BC1\u547D\u4EE4\u3001Git\u3001\u5DE5\u4F5C\u533A\u3001\u9648\u65E7\u95E8\u7981\u4E0E\u4E2D\u65AD\u65AD\u70B9\u90FD\u5DF2\u5C31\u7EEA\uFF0C\u907F\u514D\u5F00\u8DD1\u540E\u624D\u53D1\u73B0\u73AF\u5883\u95EE\u9898\u3002": "Run preflight first: confirm validation commands, git, the working tree, a stale gate and an interrupted checkpoint are ready, so you do not discover an environment problem mid-run.",
|
|
2767
|
+
\u590D\u5236\u9884\u68C0\u6307\u4EE4: "Copy preflight instruction",
|
|
2768
|
+
\u9A8C\u8BC1\u547D\u4EE4: "Validation commands",
|
|
2769
|
+
"\u5DF2\u914D\u7F6E {a0} \u6761\u9A8C\u8BC1\u547D\u4EE4": "{a0} validation command(s) configured",
|
|
2770
|
+
"\u672A\u914D\u7F6E validation.commands\uFF08\u963B\u65AD\u9879\uFF09\u2014\u2014 \u68C0\u6D4B\u5230 {a0} \u9879\u76EE\uFF0C\u5EFA\u8BAE\uFF1A{a1}": "validation.commands is not configured (blocking) \u2014 detected {a0} project; suggested: {a1}",
|
|
2771
|
+
"\u672A\u914D\u7F6E validation.commands \u2014\u2014 \u672C\u8F6E\u65E0\u6CD5\u8BC1\u660E\u4FEE\u590D\uFF0C\u5C5E\u963B\u65AD\u9879": "validation.commands is not configured \u2014 the run cannot prove fixes (blocking)",
|
|
2772
|
+
"Git \u4ED3\u5E93": "Git repository",
|
|
2773
|
+
"\u5DF2\u68C0\u6D4B\u5230 Git \u4ED3\u5E93": "Git repository detected",
|
|
2774
|
+
"\u4E0D\u662F Git \u4ED3\u5E93 \u2014\u2014 changed-only \u8303\u56F4\u4E0E iterate_diff \u9700\u8981\u5B83": "Not a git repository \u2014 changed-only scope and iterate_diff need one",
|
|
2775
|
+
\u5DE5\u4F5C\u533A: "Working tree",
|
|
2776
|
+
"\u672A\u63D0\u4EA4\u6539\u52A8\uFF1A\u672A\u77E5": "Uncommitted changes: unknown",
|
|
2777
|
+
"{a0} \u4E2A\u672A\u63D0\u4EA4/\u672A\u8DDF\u8E2A\u6587\u4EF6 \u2014\u2014 \u5EFA\u8BAE\u5148 commit \u6216 stash": "{a0} uncommitted/untracked file(s) \u2014 commit or stash first",
|
|
2778
|
+
\u5DE5\u4F5C\u533A\u5E72\u51C0: "Working tree clean",
|
|
2779
|
+
\u9648\u65E7\u95E8\u7981: "Stale gate",
|
|
2780
|
+
"\u78C1\u76D8\u4E0A\u6B8B\u7559 FAIL \u8D28\u91CF\u95E8\u7981 \u2014\u2014 \u5F00\u65B0\u8F6E\u524D\u5148\u6E05\u9664": "A stale FAIL quality gate is persisted \u2014 clear it before a fresh run",
|
|
2781
|
+
"\u8D28\u91CF\u95E8\u7981\uFF1A{a0}": "Quality gate: {a0}",
|
|
2782
|
+
\u65E0\u6301\u4E45\u5316\u8D28\u91CF\u95E8\u7981: "No persisted quality gate",
|
|
2783
|
+
\u4E2D\u65AD\u65AD\u70B9: "Interrupted checkpoint",
|
|
2784
|
+
"\u5B58\u5728\u4E2D\u65AD\u65AD\u70B9\uFF08\u5DF2\u6062\u590D {a0} \u6B21\uFF09\u2014\u2014 \u6062\u590D\u6216\u6E05\u9664\u540E\u518D\u5F00\u8DD1": "An interrupted checkpoint is waiting (resumed {a0}x) \u2014 resume or clear it first",
|
|
2785
|
+
\u65E0\u4E2D\u65AD\u65AD\u70B9: "No interrupted checkpoint",
|
|
2786
|
+
\u914D\u7F6E\u9A8C\u8BC1\u547D\u4EE4: "Configure validation",
|
|
2787
|
+
\u5E94\u7528\u5EFA\u8BAE: "Apply suggestion",
|
|
2788
|
+
\u6062\u590D\u65AD\u70B9: "Resume checkpoint",
|
|
2789
|
+
"\u963B\u65AD {a0} \xB7 \u8B66\u544A {a1}": "Blocking {a0} \xB7 warnings {a1}",
|
|
2790
|
+
"\u9884\u68C0\u5931\u8D25\uFF1A{a0}": "Preflight failed: {a0}",
|
|
2791
|
+
"\u9884\u68C0\u53EA\u8BFB\uFF0C\u4E0D\u4F1A\u4FEE\u6539\u914D\u7F6E\u6216\u6587\u4EF6\u3002": "Preflight is read-only; it never modifies config or files.",
|
|
2661
2792
|
// ── copy feedback ────────────────────────────────────────────────────────
|
|
2662
2793
|
\u5DF2\u590D\u5236: "Copied",
|
|
2663
2794
|
\u590D\u5236\u5931\u8D25: "Copy failed"
|
|
@@ -2907,6 +3038,7 @@ var ITERATE_CSS = `
|
|
|
2907
3038
|
.iterate-gate[data-status="pass"] { color: var(--dsw-alias-state-success-primary); background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 28%, transparent); }
|
|
2908
3039
|
.iterate-gate[data-status="fail"] { color: var(--dsw-alias-state-error-primary); background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 28%, transparent); }
|
|
2909
3040
|
.iterate-gate[data-status="pending"] { color: var(--dsw-alias-label-secondary); background: color-mix(in srgb, var(--dsw-alias-label-secondary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dsw-alias-label-secondary) 28%, transparent); }
|
|
3041
|
+
.iterate-gate[data-status="warn"] { color: var(--dsw-alias-state-warn-primary); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 28%, transparent); }
|
|
2910
3042
|
|
|
2911
3043
|
/* v3.0: Experience bank hit highlight */
|
|
2912
3044
|
.iterate-exp-hit { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px; border-radius: 6px; background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 28%, transparent); font-size: 11px; color: var(--dsw-alias-state-success-primary); }
|
|
@@ -4343,7 +4475,9 @@ var OBS_TABS = [
|
|
|
4343
4475
|
// v3.0: Quality Command Center tabs
|
|
4344
4476
|
{ key: "f8", label: { zh: "\u8D28\u91CF\u95E8\u7981", en: "Quality gate" }, hotkey: "8" },
|
|
4345
4477
|
{ key: "f9", label: { zh: "\u7ECF\u9A8C\u94F6\u884C", en: "Experience bank" }, hotkey: "9" },
|
|
4346
|
-
{ key: "f10", label: { zh: "\u9632\u5FA1\u4E8B\u4EF6", en: "Defense events" }, hotkey: "0" }
|
|
4478
|
+
{ key: "f10", label: { zh: "\u9632\u5FA1\u4E8B\u4EF6", en: "Defense events" }, hotkey: "0" },
|
|
4479
|
+
// v3.12: pre-run readiness checklist (validation/git/dirty-tree/stale-gate/checkpoint)
|
|
4480
|
+
{ key: "f11", label: { zh: "\u5F00\u8DD1\u9884\u68C0", en: "Preflight" }, hotkey: "" }
|
|
4347
4481
|
];
|
|
4348
4482
|
var OBS_HOTKEY_TABS = {
|
|
4349
4483
|
1: "f1",
|
|
@@ -4414,6 +4548,10 @@ function latestDefenseEvents(session) {
|
|
|
4414
4548
|
if (!session) return null;
|
|
4415
4549
|
return scanSessionForDefenseEvents(session);
|
|
4416
4550
|
}
|
|
4551
|
+
function latestPreflight(session) {
|
|
4552
|
+
if (!session) return null;
|
|
4553
|
+
return scanSessionForPreflight(session);
|
|
4554
|
+
}
|
|
4417
4555
|
function buildObsFixInstruction(f) {
|
|
4418
4556
|
return buildFixInstruction(f);
|
|
4419
4557
|
}
|
|
@@ -4426,6 +4564,7 @@ function ObservatoryPanel(props) {
|
|
|
4426
4564
|
const qualityGate = latestQualityGate(session);
|
|
4427
4565
|
const experienceBank = latestExperienceBank(session);
|
|
4428
4566
|
const defenseEvents = latestDefenseEvents(session);
|
|
4567
|
+
const preflight = latestPreflight(session);
|
|
4429
4568
|
const [open, setOpen] = React.useState(false);
|
|
4430
4569
|
const [tab, setTab] = React.useState("live");
|
|
4431
4570
|
const [expandedThreads, setExpandedThreads] = React.useState(/* @__PURE__ */ new Set());
|
|
@@ -5577,6 +5716,212 @@ function ObservatoryPanel(props) {
|
|
|
5577
5716
|
eventRows.length > 0 ? React.createElement("div", {}, ...eventRows) : React.createElement("div", { className: "iterate-obs-empty" }, t("\u6CA1\u6709\u5339\u914D\u8BE5\u7C7B\u578B\u7684\u9632\u5FA1\u4E8B\u4EF6"))
|
|
5578
5717
|
);
|
|
5579
5718
|
};
|
|
5719
|
+
const renderPreflight = () => {
|
|
5720
|
+
const preflightInstruction = buildPreflightInstruction();
|
|
5721
|
+
const report = preflight;
|
|
5722
|
+
const hasReport = report !== null && Array.isArray(report.checks);
|
|
5723
|
+
const ready = report !== null && report.ready === true;
|
|
5724
|
+
const blocking = Number(report && report.blocking != null ? report.blocking : 0);
|
|
5725
|
+
const warnings = Number(report && report.warnings != null ? report.warnings : 0);
|
|
5726
|
+
const statusLabel = !hasReport ? t("\u5F85\u9884\u68C0") : ready ? t("\u5C31\u7EEA") : t("\u672A\u5C31\u7EEA");
|
|
5727
|
+
const statusTone = !hasReport ? "pending" : ready ? "pass" : "fail";
|
|
5728
|
+
const headerBar = React.createElement(
|
|
5729
|
+
"div",
|
|
5730
|
+
{ className: "iterate-obs-bar", style: { marginBottom: 8 } },
|
|
5731
|
+
React.createElement("b", {}, t("\u5F00\u8DD1\u9884\u68C0")),
|
|
5732
|
+
React.createElement("span", { className: "iterate-gate", "data-status": statusTone }, statusLabel),
|
|
5733
|
+
React.createElement("button", {
|
|
5734
|
+
className: "iterate-btn",
|
|
5735
|
+
"data-primary": "",
|
|
5736
|
+
"data-copied": copiedKey === "preflight" ? "" : void 0,
|
|
5737
|
+
onClick: () => copyInstruction("preflight", preflightInstruction),
|
|
5738
|
+
title: t("\u590D\u5236 iterate_preflight \u9884\u68C0\u6307\u4EE4\uFF08\u542B JSON \u53C2\u6570\uFF09")
|
|
5739
|
+
}, copiedKey === "preflight" ? t("\u5DF2\u590D\u5236") : t("\u8FD0\u884C\u9884\u68C0"))
|
|
5740
|
+
);
|
|
5741
|
+
if (!hasReport) {
|
|
5742
|
+
return React.createElement(
|
|
5743
|
+
"div",
|
|
5744
|
+
{},
|
|
5745
|
+
headerBar,
|
|
5746
|
+
React.createElement(
|
|
5747
|
+
"div",
|
|
5748
|
+
{ className: "iterate-obs-block" },
|
|
5749
|
+
React.createElement(
|
|
5750
|
+
"div",
|
|
5751
|
+
{ className: "iterate-obs-block-head" },
|
|
5752
|
+
React.createElement("span", {}, t("\u5F00\u8DD1\u9884\u68C0\u6E05\u5355")),
|
|
5753
|
+
React.createElement("span", { className: "iterate-obs-badge" }, "v3.12")
|
|
5754
|
+
),
|
|
5755
|
+
React.createElement(
|
|
5756
|
+
"div",
|
|
5757
|
+
{ className: "iterate-obs-block-body" },
|
|
5758
|
+
React.createElement(
|
|
5759
|
+
"div",
|
|
5760
|
+
{ className: "iterate-obs-msg" },
|
|
5761
|
+
// Gap #4: separate "pulled and empty" from "never pulled".
|
|
5762
|
+
diskEmptyStateText(toolCalledInSession(session, "iterate_preflight"), t("\u9884\u68C0\u7ED3\u679C"), getLang())
|
|
5763
|
+
),
|
|
5764
|
+
React.createElement(
|
|
5765
|
+
"div",
|
|
5766
|
+
{ className: "iterate-obs-msg", style: { marginTop: 6 } },
|
|
5767
|
+
t("\u5F00\u8DD1\u524D\u5148\u9884\u68C0\uFF1A\u786E\u8BA4\u9A8C\u8BC1\u547D\u4EE4\u3001Git\u3001\u5DE5\u4F5C\u533A\u3001\u9648\u65E7\u95E8\u7981\u4E0E\u4E2D\u65AD\u65AD\u70B9\u90FD\u5DF2\u5C31\u7EEA\uFF0C\u907F\u514D\u5F00\u8DD1\u540E\u624D\u53D1\u73B0\u73AF\u5883\u95EE\u9898\u3002")
|
|
5768
|
+
),
|
|
5769
|
+
React.createElement(
|
|
5770
|
+
"div",
|
|
5771
|
+
{ className: "iterate-obs-bar", style: { marginTop: 8 } },
|
|
5772
|
+
React.createElement("button", {
|
|
5773
|
+
className: "iterate-btn",
|
|
5774
|
+
"data-copied": copiedKey === "preflight-instr" ? "" : void 0,
|
|
5775
|
+
onClick: () => copyInstruction("preflight-instr", preflightInstruction),
|
|
5776
|
+
title: t("\u590D\u5236\u9884\u68C0\u6307\u4EE4")
|
|
5777
|
+
}, copiedKey === "preflight-instr" ? t("\u5DF2\u590D\u5236") : t("\u590D\u5236\u9884\u68C0\u6307\u4EE4"))
|
|
5778
|
+
)
|
|
5779
|
+
)
|
|
5780
|
+
)
|
|
5781
|
+
);
|
|
5782
|
+
}
|
|
5783
|
+
if (report && report.ok === false) {
|
|
5784
|
+
return React.createElement(
|
|
5785
|
+
"div",
|
|
5786
|
+
{},
|
|
5787
|
+
headerBar,
|
|
5788
|
+
React.createElement(
|
|
5789
|
+
"div",
|
|
5790
|
+
{ className: "iterate-obs-block" },
|
|
5791
|
+
React.createElement(
|
|
5792
|
+
"div",
|
|
5793
|
+
{ className: "iterate-obs-block-head" },
|
|
5794
|
+
React.createElement("span", {}, t("\u5F00\u8DD1\u9884\u68C0\u6E05\u5355")),
|
|
5795
|
+
React.createElement("span", { className: "iterate-obs-badge" }, "v3.12")
|
|
5796
|
+
),
|
|
5797
|
+
React.createElement(
|
|
5798
|
+
"div",
|
|
5799
|
+
{ className: "iterate-obs-block-body" },
|
|
5800
|
+
React.createElement(
|
|
5801
|
+
"div",
|
|
5802
|
+
{ className: "iterate-obs-msg" },
|
|
5803
|
+
tf("\u9884\u68C0\u5931\u8D25\uFF1A{a0}", { a0: String(report.error || "") })
|
|
5804
|
+
)
|
|
5805
|
+
)
|
|
5806
|
+
)
|
|
5807
|
+
);
|
|
5808
|
+
}
|
|
5809
|
+
const CHECK_META = {
|
|
5810
|
+
validation: {
|
|
5811
|
+
label: t("\u9A8C\u8BC1\u547D\u4EE4"),
|
|
5812
|
+
detail: (d) => {
|
|
5813
|
+
const n = Number(d.commandCount ?? 0);
|
|
5814
|
+
if (n > 0) return tf("\u5DF2\u914D\u7F6E {a0} \u6761\u9A8C\u8BC1\u547D\u4EE4", { a0: n });
|
|
5815
|
+
const suggested = Array.isArray(d.suggestedCommands) ? d.suggestedCommands.map((c) => String(c)).filter((c) => c.length > 0) : [];
|
|
5816
|
+
if (suggested.length > 0) {
|
|
5817
|
+
return tf("\u672A\u914D\u7F6E validation.commands\uFF08\u963B\u65AD\u9879\uFF09\u2014\u2014 \u68C0\u6D4B\u5230 {a0} \u9879\u76EE\uFF0C\u5EFA\u8BAE\uFF1A{a1}", {
|
|
5818
|
+
a0: String(d.ecosystem || ""),
|
|
5819
|
+
a1: suggested.join(" \xB7 ")
|
|
5820
|
+
});
|
|
5821
|
+
}
|
|
5822
|
+
return t("\u672A\u914D\u7F6E validation.commands \u2014\u2014 \u672C\u8F6E\u65E0\u6CD5\u8BC1\u660E\u4FEE\u590D\uFF0C\u5C5E\u963B\u65AD\u9879");
|
|
5823
|
+
}
|
|
5824
|
+
},
|
|
5825
|
+
git: {
|
|
5826
|
+
label: t("Git \u4ED3\u5E93"),
|
|
5827
|
+
detail: (d) => d.isRepo === true ? t("\u5DF2\u68C0\u6D4B\u5230 Git \u4ED3\u5E93") : t("\u4E0D\u662F Git \u4ED3\u5E93 \u2014\u2014 changed-only \u8303\u56F4\u4E0E iterate_diff \u9700\u8981\u5B83")
|
|
5828
|
+
},
|
|
5829
|
+
"dirty-tree": {
|
|
5830
|
+
label: t("\u5DE5\u4F5C\u533A"),
|
|
5831
|
+
detail: (d) => {
|
|
5832
|
+
const n = d.changedFiles;
|
|
5833
|
+
if (n === null || n === void 0) return t("\u672A\u63D0\u4EA4\u6539\u52A8\uFF1A\u672A\u77E5");
|
|
5834
|
+
const num = Number(n);
|
|
5835
|
+
return num > 0 ? tf("{a0} \u4E2A\u672A\u63D0\u4EA4/\u672A\u8DDF\u8E2A\u6587\u4EF6 \u2014\u2014 \u5EFA\u8BAE\u5148 commit \u6216 stash", { a0: num }) : t("\u5DE5\u4F5C\u533A\u5E72\u51C0");
|
|
5836
|
+
}
|
|
5837
|
+
},
|
|
5838
|
+
"stale-gate": {
|
|
5839
|
+
label: t("\u9648\u65E7\u95E8\u7981"),
|
|
5840
|
+
detail: (d) => {
|
|
5841
|
+
const s = d.overallStatus;
|
|
5842
|
+
if (s === "fail") return t("\u78C1\u76D8\u4E0A\u6B8B\u7559 FAIL \u8D28\u91CF\u95E8\u7981 \u2014\u2014 \u5F00\u65B0\u8F6E\u524D\u5148\u6E05\u9664");
|
|
5843
|
+
return s ? tf("\u8D28\u91CF\u95E8\u7981\uFF1A{a0}", { a0: String(s) }) : t("\u65E0\u6301\u4E45\u5316\u8D28\u91CF\u95E8\u7981");
|
|
5844
|
+
}
|
|
5845
|
+
},
|
|
5846
|
+
"interrupted-checkpoint": {
|
|
5847
|
+
label: t("\u4E2D\u65AD\u65AD\u70B9"),
|
|
5848
|
+
detail: (d) => {
|
|
5849
|
+
if (d.interrupted === true) {
|
|
5850
|
+
const rc = Number(d.resumeCount ?? 0);
|
|
5851
|
+
return tf("\u5B58\u5728\u4E2D\u65AD\u65AD\u70B9\uFF08\u5DF2\u6062\u590D {a0} \u6B21\uFF09\u2014\u2014 \u6062\u590D\u6216\u6E05\u9664\u540E\u518D\u5F00\u8DD1", { a0: rc });
|
|
5852
|
+
}
|
|
5853
|
+
return t("\u65E0\u4E2D\u65AD\u65AD\u70B9");
|
|
5854
|
+
}
|
|
5855
|
+
}
|
|
5856
|
+
};
|
|
5857
|
+
const fixFor = (id, detail) => {
|
|
5858
|
+
switch (id) {
|
|
5859
|
+
case "validation": {
|
|
5860
|
+
const suggested = Array.isArray(detail.suggestedCommands) ? detail.suggestedCommands.map((c) => String(c)).filter((c) => c.length > 0) : [];
|
|
5861
|
+
if (suggested.length > 0) {
|
|
5862
|
+
return {
|
|
5863
|
+
key: "preflight-fix-validation",
|
|
5864
|
+
label: t("\u5E94\u7528\u5EFA\u8BAE"),
|
|
5865
|
+
instruction: buildValidationApplyInstruction(suggested)
|
|
5866
|
+
};
|
|
5867
|
+
}
|
|
5868
|
+
return { key: "preflight-fix-validation", label: t("\u914D\u7F6E\u9A8C\u8BC1\u547D\u4EE4"), instruction: buildConfigFieldInstruction("validation.commands") };
|
|
5869
|
+
}
|
|
5870
|
+
case "stale-gate":
|
|
5871
|
+
return { key: "preflight-fix-gate", label: t("\u6E05\u9664\u95E8\u7981"), instruction: buildQualityGateClearInstruction() };
|
|
5872
|
+
case "interrupted-checkpoint":
|
|
5873
|
+
return { key: "preflight-fix-checkpoint", label: t("\u6062\u590D\u65AD\u70B9"), instruction: buildCheckpointResumeInstruction() };
|
|
5874
|
+
default:
|
|
5875
|
+
return null;
|
|
5876
|
+
}
|
|
5877
|
+
};
|
|
5878
|
+
const rows = (report.checks || []).map((c, i) => {
|
|
5879
|
+
const id = String(c.id || "");
|
|
5880
|
+
const status = String(c.status || "warn");
|
|
5881
|
+
const meta = CHECK_META[id];
|
|
5882
|
+
const label = meta ? meta.label : id || "?";
|
|
5883
|
+
const detailText = meta ? meta.detail(c.detail || {}) : "";
|
|
5884
|
+
const fix = status !== "pass" ? fixFor(id, c.detail || {}) : null;
|
|
5885
|
+
return React.createElement(
|
|
5886
|
+
"div",
|
|
5887
|
+
{ key: `pf-${i}`, className: "iterate-obs-row" },
|
|
5888
|
+
React.createElement("span", { className: "iterate-gate", "data-status": status }, status.toUpperCase()),
|
|
5889
|
+
React.createElement("b", {}, label),
|
|
5890
|
+
React.createElement("span", { className: "iterate-obs-msg" }, detailText),
|
|
5891
|
+
fix ? React.createElement("button", {
|
|
5892
|
+
className: "iterate-btn",
|
|
5893
|
+
"data-copied": copiedKey === fix.key ? "" : void 0,
|
|
5894
|
+
onClick: () => copyInstruction(fix.key, fix.instruction),
|
|
5895
|
+
title: fix.instruction
|
|
5896
|
+
}, copiedKey === fix.key ? t("\u5DF2\u590D\u5236") : fix.label) : null
|
|
5897
|
+
);
|
|
5898
|
+
});
|
|
5899
|
+
return React.createElement(
|
|
5900
|
+
"div",
|
|
5901
|
+
{},
|
|
5902
|
+
headerBar,
|
|
5903
|
+
React.createElement(
|
|
5904
|
+
"div",
|
|
5905
|
+
{ className: "iterate-obs-block" },
|
|
5906
|
+
React.createElement(
|
|
5907
|
+
"div",
|
|
5908
|
+
{ className: "iterate-obs-block-head" },
|
|
5909
|
+
React.createElement("span", {}, t("\u5F00\u8DD1\u9884\u68C0\u6E05\u5355")),
|
|
5910
|
+
React.createElement(
|
|
5911
|
+
"span",
|
|
5912
|
+
{ className: "iterate-obs-head-meta" },
|
|
5913
|
+
tf("\u963B\u65AD {a0} \xB7 \u8B66\u544A {a1}", { a0: blocking, a1: warnings })
|
|
5914
|
+
)
|
|
5915
|
+
),
|
|
5916
|
+
React.createElement(
|
|
5917
|
+
"div",
|
|
5918
|
+
{ className: "iterate-obs-block-body" },
|
|
5919
|
+
React.createElement("div", { className: "iterate-obs-msg" }, t("\u9884\u68C0\u53EA\u8BFB\uFF0C\u4E0D\u4F1A\u4FEE\u6539\u914D\u7F6E\u6216\u6587\u4EF6\u3002")),
|
|
5920
|
+
...rows
|
|
5921
|
+
)
|
|
5922
|
+
)
|
|
5923
|
+
);
|
|
5924
|
+
};
|
|
5580
5925
|
const renderLive = () => {
|
|
5581
5926
|
const liveEntries = manifest.live || [];
|
|
5582
5927
|
if (liveEntries.length === 0) {
|
|
@@ -5673,6 +6018,8 @@ function ObservatoryPanel(props) {
|
|
|
5673
6018
|
return renderExperienceBank();
|
|
5674
6019
|
case "f10":
|
|
5675
6020
|
return renderDefenseEvents();
|
|
6021
|
+
case "f11":
|
|
6022
|
+
return renderPreflight();
|
|
5676
6023
|
default:
|
|
5677
6024
|
return null;
|
|
5678
6025
|
}
|
package/lib/parse.js
CHANGED
|
@@ -1124,6 +1124,88 @@ export function scanSessionForDefenseEvents(session) {
|
|
|
1124
1124
|
return loose ? normalizeDefenseEventsResult(loose) : null
|
|
1125
1125
|
}
|
|
1126
1126
|
|
|
1127
|
+
// ─── Preflight readiness report detection ────────────────────────────────────
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Check whether `obj` is an `iterate_preflight` report.
|
|
1131
|
+
*
|
|
1132
|
+
* Discriminator: a boolean `ready`, an array `checks`, and a numeric
|
|
1133
|
+
* `blocking`/`warnings` counter. The `ok:false` error shape (empty checks)
|
|
1134
|
+
* still matches so the panel can render the failure instead of claiming
|
|
1135
|
+
* "not pulled yet".
|
|
1136
|
+
*
|
|
1137
|
+
* @param {unknown} obj
|
|
1138
|
+
* @returns {boolean}
|
|
1139
|
+
*/
|
|
1140
|
+
export function isPreflightReport(obj) {
|
|
1141
|
+
if (!obj || typeof obj !== 'object') return false
|
|
1142
|
+
const o = /** @type {Record<string, unknown>} */ (obj)
|
|
1143
|
+
const ready = safeGet(o, 'ready')
|
|
1144
|
+
const checks = safeGet(o, 'checks')
|
|
1145
|
+
const blocking = safeGet(o, 'blocking')
|
|
1146
|
+
const warnings = safeGet(o, 'warnings')
|
|
1147
|
+
return (
|
|
1148
|
+
typeof ready === 'boolean' &&
|
|
1149
|
+
Array.isArray(checks) &&
|
|
1150
|
+
(typeof blocking === 'number' || typeof warnings === 'number')
|
|
1151
|
+
)
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/** Deep-find the first `iterate_preflight` report node inside `obj`. */
|
|
1155
|
+
function findPreflightReportInObject(obj) {
|
|
1156
|
+
return findFirstInObject(obj, (o) => isPreflightReport(o))
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Normalize an `iterate_preflight` report into a JSON-safe object.
|
|
1161
|
+
*
|
|
1162
|
+
* @param {Record<string, unknown> | null | undefined} raw
|
|
1163
|
+
* @returns {Record<string, unknown>}
|
|
1164
|
+
*/
|
|
1165
|
+
export function normalizePreflightReport(raw) {
|
|
1166
|
+
const src = raw && typeof raw === 'object'
|
|
1167
|
+
? /** @type {Record<string, unknown>} */ (raw)
|
|
1168
|
+
: {}
|
|
1169
|
+
const asNum = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0)
|
|
1170
|
+
const asStr = (v) => (typeof v === 'string' ? v : '')
|
|
1171
|
+
const checks = Array.isArray(safeGet(src, 'checks')) ? safeGet(src, 'checks') : []
|
|
1172
|
+
return {
|
|
1173
|
+
ok: safeGet(src, 'ok') !== false,
|
|
1174
|
+
ready: safeGet(src, 'ready') === true,
|
|
1175
|
+
blocking: asNum(safeGet(src, 'blocking')),
|
|
1176
|
+
warnings: asNum(safeGet(src, 'warnings')),
|
|
1177
|
+
error: asStr(safeGet(src, 'error')) || null,
|
|
1178
|
+
checks: (/** @type {unknown[]} */ (checks)).map((c) => {
|
|
1179
|
+
const rec = /** @type {Record<string, unknown>} */ (c && typeof c === 'object' ? c : {})
|
|
1180
|
+
const status = safeGet(rec, 'status')
|
|
1181
|
+
const detail = safeGet(rec, 'detail')
|
|
1182
|
+
return {
|
|
1183
|
+
id: asStr(safeGet(rec, 'id')),
|
|
1184
|
+
status: status === 'pass' || status === 'warn' || status === 'fail' ? status : 'warn',
|
|
1185
|
+
detail: detail && typeof detail === 'object' ? detail : {},
|
|
1186
|
+
}
|
|
1187
|
+
}),
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Scan a session snapshot for the latest `iterate_preflight` result and return
|
|
1193
|
+
* its normalized report (or null).
|
|
1194
|
+
*
|
|
1195
|
+
* @param {unknown} session
|
|
1196
|
+
* @returns {Record<string, unknown> | null}
|
|
1197
|
+
*/
|
|
1198
|
+
export function scanSessionForPreflight(session) {
|
|
1199
|
+
const node = latestToolResultNode(session, 'iterate_preflight', findPreflightReportInObject)
|
|
1200
|
+
if (node !== null) {
|
|
1201
|
+
const found = findPreflightReportInObject(node)
|
|
1202
|
+
if (found) return normalizePreflightReport(found)
|
|
1203
|
+
}
|
|
1204
|
+
// Same deep-scan fallback as the quality-gate scanner (see above).
|
|
1205
|
+
const loose = findFirstInObject(session, isPreflightReport)
|
|
1206
|
+
return loose ? normalizePreflightReport(loose) : null
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1127
1209
|
// ─── Run-summary / meta-review verdict detection ─────────────────────────────
|
|
1128
1210
|
|
|
1129
1211
|
/**
|
|
@@ -1857,6 +1939,45 @@ export function buildQualityGateClearInstruction() {
|
|
|
1857
1939
|
)
|
|
1858
1940
|
}
|
|
1859
1941
|
|
|
1942
|
+
/**
|
|
1943
|
+
* `iterate_preflight` readiness-check payload (runs the pre-run checklist).
|
|
1944
|
+
*
|
|
1945
|
+
* @returns {string}
|
|
1946
|
+
*/
|
|
1947
|
+
export function buildPreflightInstruction() {
|
|
1948
|
+
return (
|
|
1949
|
+
'请调用 `iterate_preflight` 运行开跑前预检(只读:校验 validation.commands、git、脏工作区、' +
|
|
1950
|
+
'陈旧质量门禁与中断断点):\n\n' +
|
|
1951
|
+
'```json\n' +
|
|
1952
|
+
`${JSON.stringify({}, null, 2)}\n` +
|
|
1953
|
+
'```'
|
|
1954
|
+
)
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* `iterate_config` write instruction that installs the validation commands the
|
|
1959
|
+
* preflight panel detected. Copies the exact command set the user saw, so the
|
|
1960
|
+
* model applies it deterministically instead of re-detecting (and drifting).
|
|
1961
|
+
*
|
|
1962
|
+
* @param {string[]} commands
|
|
1963
|
+
* @returns {string}
|
|
1964
|
+
*/
|
|
1965
|
+
export function buildValidationApplyInstruction(commands) {
|
|
1966
|
+
const list = Array.isArray(commands)
|
|
1967
|
+
? commands.filter((c) => typeof c === 'string' && c.length > 0)
|
|
1968
|
+
: []
|
|
1969
|
+
const payload = {
|
|
1970
|
+
operation: 'write',
|
|
1971
|
+
updates: { validation: { commands: { default: list } } },
|
|
1972
|
+
}
|
|
1973
|
+
return (
|
|
1974
|
+
'请调用 `iterate_config` 把检测到的验证命令写入 `validation.commands.default`(会先备份配置):\n\n' +
|
|
1975
|
+
'```json\n' +
|
|
1976
|
+
`${JSON.stringify(payload, null, 2)}\n` +
|
|
1977
|
+
'```'
|
|
1978
|
+
)
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1860
1981
|
/**
|
|
1861
1982
|
* `iterate_rollback` payload for one applied fix id.
|
|
1862
1983
|
*
|
|
@@ -2310,14 +2431,63 @@ export function buildCompletionSummary(report, lang = 'zh') {
|
|
|
2310
2431
|
const stats = severityStats(report)
|
|
2311
2432
|
const converged = conv.converged === true
|
|
2312
2433
|
const totalFindings = stats.critical + stats.high + stats.medium + stats.low
|
|
2434
|
+
// The persisted `stoppedReason` is the authoritative "why" — a validation
|
|
2435
|
+
// abort or a schema failure must NOT be reported as "reached the round
|
|
2436
|
+
// limit". A normalized report nests it under `convergence.stoppedReason`
|
|
2437
|
+
// (normalizeReport); a raw harness report may carry it at the top level.
|
|
2438
|
+
// Fall back to the convergence flag only when no reason was stored (older
|
|
2439
|
+
// transcripts predate the field).
|
|
2440
|
+
const stopped = typeof conv.stoppedReason === 'string'
|
|
2441
|
+
? conv.stoppedReason
|
|
2442
|
+
: typeof report.stoppedReason === 'string'
|
|
2443
|
+
? report.stoppedReason
|
|
2444
|
+
: ''
|
|
2445
|
+
const reason = stopped
|
|
2446
|
+
? completionReason(stopped, lang, total)
|
|
2447
|
+
: converged
|
|
2448
|
+
? (lang === 'en' ? 'converged' : '已收敛')
|
|
2449
|
+
: (lang === 'en' ? `reached the round limit ${total}` : `已达最大轮数 ${total}`)
|
|
2313
2450
|
if (lang === 'en') {
|
|
2314
|
-
const reason = converged ? 'converged' : `reached the round limit ${total}`
|
|
2315
2451
|
return `iterate review complete \u00b7 ${rounds}/${total} rounds \u00b7 ${totalFindings} findings \u00b7 ${reason}`
|
|
2316
2452
|
}
|
|
2317
|
-
const reason = converged ? '已收敛' : `已达最大轮数 ${total}`
|
|
2318
2453
|
return `iterate 评审完成 · ${rounds}/${total} 轮 · ${totalFindings} 项发现 · ${reason}`
|
|
2319
2454
|
}
|
|
2320
2455
|
|
|
2456
|
+
/**
|
|
2457
|
+
* Compact "why the run ended" phrase for the completion summary.
|
|
2458
|
+
* Mirrors `stoppedReasonLabel` but drops the "已结束 · " prefix so it reads
|
|
2459
|
+
* inside the one-line summary. Unknown reasons degrade to the raw value.
|
|
2460
|
+
*
|
|
2461
|
+
* @param {string} reason
|
|
2462
|
+
* @param {string} lang
|
|
2463
|
+
* @param {number} total
|
|
2464
|
+
* @returns {string}
|
|
2465
|
+
*/
|
|
2466
|
+
function completionReason(reason, lang, total) {
|
|
2467
|
+
const zh = {
|
|
2468
|
+
converged: '已收敛',
|
|
2469
|
+
max_rounds_reached: `已达最大轮数 ${total}`,
|
|
2470
|
+
aborted_by_validation: '验证失败后回滚',
|
|
2471
|
+
aborted_by_config: '验证命令未在白名单',
|
|
2472
|
+
inconclusive: '轮次结论不明',
|
|
2473
|
+
schema_invalid: '审查输出 schema 校验失败',
|
|
2474
|
+
no_usable_reviewer_output: '无可用审查输出',
|
|
2475
|
+
}
|
|
2476
|
+
const en = {
|
|
2477
|
+
converged: 'converged',
|
|
2478
|
+
max_rounds_reached: `reached the round limit ${total}`,
|
|
2479
|
+
aborted_by_validation: 'rolled back after a validation failure',
|
|
2480
|
+
aborted_by_config: 'validation command not allow-listed',
|
|
2481
|
+
inconclusive: 'inconclusive rounds',
|
|
2482
|
+
schema_invalid: 'review output failed schema validation',
|
|
2483
|
+
no_usable_reviewer_output: 'no usable review output',
|
|
2484
|
+
}
|
|
2485
|
+
const table = lang === 'en' ? en : zh
|
|
2486
|
+
// Object.hasOwn, NOT `table[reason]`: a hand-edited `__proto__` reason must
|
|
2487
|
+
// not resolve through the prototype chain to a non-string.
|
|
2488
|
+
return Object.hasOwn(table, reason) ? table[reason] : reason
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2321
2491
|
// ─── Config edit guidance ───────────────────────────────────────────────────
|
|
2322
2492
|
|
|
2323
2493
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.13.0",
|
|
4
4
|
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness with quality command center and experience bank (v3.3). Features: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus dry-run pure-review mode, quality gate compute/persist, writable experience bank, defense events stream (record + bilingual labels), and native command buttons — with a live quality command center (F8/F9/F10 render real session data), §8 assign-fix instruction, and task_mode indicator wired end to end.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"0.2.1-rc.2": "compatible",
|
|
55
55
|
"0.2.1-rc.3": "compatible",
|
|
56
56
|
"0.2.1-rc.4": "compatible",
|
|
57
|
+
"0.2.1-rc.5": "compatible",
|
|
57
58
|
"0.2.2-alpha.2": "compatible"
|
|
58
59
|
}
|
|
59
60
|
},
|
|
@@ -96,9 +97,9 @@
|
|
|
96
97
|
},
|
|
97
98
|
"peerDependencies": {
|
|
98
99
|
"@deepseek-ai/cordis": "^4.0.5-alpha.2 || ~4.0.6-alpha.1",
|
|
99
|
-
"@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2",
|
|
100
|
-
"@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2",
|
|
101
|
-
"@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.2-alpha.2"
|
|
100
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-alpha.2",
|
|
101
|
+
"@deepseek-ai/dsh-tools": "0.1.1-rc.1 || 0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-alpha.2",
|
|
102
|
+
"@deepseek-ai/dsh-util-values": "0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.5-alpha.1 || 0.1.5-alpha.2 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.4 || 0.2.1-rc.5 || 0.2.2-alpha.2"
|
|
102
103
|
},
|
|
103
104
|
"peerDependenciesMeta": {
|
|
104
105
|
"@deepseek-ai/cordis": {
|