@design-ai/cli 4.56.0 → 4.57.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 +109 -25
- package/CHANGELOG.md +36 -0
- package/README.ko.md +9 -26
- package/README.md +10 -23
- package/cli/bin/design-ai.mjs +1 -0
- package/cli/commands/help.mjs +7 -4
- package/cli/commands/index.mjs +292 -0
- package/cli/commands/learn-help.mjs +149 -0
- package/cli/commands/learn-print-profile.mjs +373 -0
- package/cli/commands/learn-print-restore.mjs +349 -0
- package/cli/commands/learn-print-signals.mjs +453 -0
- package/cli/commands/learn.mjs +60 -1281
- package/cli/commands/pack.mjs +7 -3
- package/cli/commands/prompt.mjs +7 -3
- package/cli/commands/search.mjs +94 -1
- package/cli/lib/dispatch.mjs +3 -0
- package/cli/lib/embedding-index.mjs +199 -0
- package/cli/lib/embedding-provider.mjs +121 -0
- package/cli/lib/embedding-rerank.mjs +52 -0
- package/cli/lib/learn-args.mjs +490 -0
- package/cli/lib/learn-backup.mjs +748 -0
- package/cli/lib/learn-curation.mjs +612 -0
- package/cli/lib/learn-eval.mjs +459 -0
- package/cli/lib/learn-profile.mjs +763 -0
- package/cli/lib/learn-select.mjs +223 -0
- package/cli/lib/learn-shared.mjs +158 -0
- package/cli/lib/learn-test-support.mjs +218 -0
- package/cli/lib/learn-usage.mjs +360 -0
- package/cli/lib/learn.mjs +83 -3694
- package/cli/lib/lexical.mjs +137 -0
- package/cli/lib/local-config.mjs +110 -0
- package/cli/lib/mcp-server.mjs +13 -3
- package/cli/lib/pack.mjs +31 -4
- package/cli/lib/prompt.mjs +42 -4
- package/cli/lib/recall.mjs +193 -0
- package/cli/lib/retrieval-index.mjs +0 -0
- package/cli/lib/search-ranked.mjs +170 -0
- package/cli/lib/search.mjs +11 -1
- package/cli/lib/signals-backlog-commands.mjs +673 -0
- package/cli/lib/signals-backlog.mjs +361 -0
- package/cli/lib/signals-eval.mjs +176 -0
- package/cli/lib/signals-readiness.mjs +247 -0
- package/cli/lib/signals-registry.mjs +377 -0
- package/cli/lib/signals-render.mjs +478 -0
- package/cli/lib/signals-shared.mjs +75 -0
- package/cli/lib/signals.mjs +16 -2337
- package/cli/lib/site-bundle-handoff-expected.mjs +173 -0
- package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
- package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
- package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
- package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
- package/cli/lib/site-bundle-handoff-runbook.mjs +44 -931
- package/cli/lib/site-test-support.mjs +68 -0
- package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
- package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
- package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
- package/cli/lib/skill-proposals-generate.mjs +298 -0
- package/cli/lib/skill-proposals-render.mjs +532 -0
- package/cli/lib/skill-proposals-review.mjs +262 -0
- package/cli/lib/skill-proposals.mjs +15 -2046
- package/cli/lib/workspace-args.mjs +83 -0
- package/cli/lib/workspace-git.mjs +169 -0
- package/cli/lib/workspace-learning.mjs +483 -0
- package/cli/lib/workspace-repo.mjs +139 -0
- package/cli/lib/workspace-report.mjs +283 -0
- package/cli/lib/workspace-test-support.mjs +99 -0
- package/cli/lib/workspace.mjs +30 -1056
- package/docs/AI-LEARNING-PHASE2.md +215 -0
- package/docs/DISTRIBUTION.ko.md +2 -2
- package/docs/DISTRIBUTION.md +2 -2
- package/docs/NEXT-SURFACE-DECISION.md +125 -0
- package/docs/PRODUCT-READINESS.md +3 -3
- package/docs/RELEASE-GATES.ko.md +35 -0
- package/docs/RELEASE-GATES.md +234 -0
- package/docs/ROADMAP.md +53 -8
- package/docs/external-status.md +6 -8
- package/docs/inspection-20260630.md +169 -0
- package/docs/integrations/design-ai-mcp-server.md +4 -4
- package/docs/reference/ant-design.md +413 -0
- package/docs/reference/awesome-design-md.md +439 -0
- package/docs/reference/mui.md +783 -0
- package/docs/reference/shadcn-ui.md +298 -0
- package/examples/component-accordion-actions.md +5 -5
- package/examples/component-accordion-details.md +5 -5
- package/examples/component-accordion-summary.md +5 -5
- package/examples/component-accordion.md +3 -3
- package/examples/component-affix.md +1 -1
- package/examples/component-alert-dialog.md +1 -1
- package/examples/component-alert-title.md +1 -1
- package/examples/component-alert.md +3 -3
- package/examples/component-anchor.md +1 -1
- package/examples/component-app-bar.md +1 -1
- package/examples/component-aspect-ratio.md +1 -1
- package/examples/component-auto-complete.md +2 -2
- package/examples/component-avatar-group.md +5 -5
- package/examples/component-avatar.md +3 -3
- package/examples/component-back-top.md +1 -1
- package/examples/component-backdrop.md +1 -1
- package/examples/component-badge.md +3 -3
- package/examples/component-border-beam.md +3 -3
- package/examples/component-bottom-navigation.md +1 -1
- package/examples/component-box.md +1 -1
- package/examples/component-breadcrumb.md +3 -3
- package/examples/component-button-base.md +3 -3
- package/examples/component-button-group.md +2 -2
- package/examples/component-button.md +3 -3
- package/examples/component-calendar.md +2 -2
- package/examples/component-card-actions.md +1 -1
- package/examples/component-card-content.md +1 -1
- package/examples/component-card-header.md +1 -1
- package/examples/component-card-media.md +1 -1
- package/examples/component-card.md +3 -3
- package/examples/component-carousel.md +2 -2
- package/examples/component-cascader.md +1 -1
- package/examples/component-chart.md +1 -1
- package/examples/component-checkbox.md +3 -3
- package/examples/component-click-away-listener.md +1 -1
- package/examples/component-code.md +1 -1
- package/examples/component-collapsible.md +1 -1
- package/examples/component-color-picker.md +1 -1
- package/examples/component-combobox.md +1 -1
- package/examples/component-command.md +1 -1
- package/examples/component-config-provider.md +3 -3
- package/examples/component-context-menu.md +1 -1
- package/examples/component-css-baseline.md +3 -3
- package/examples/component-date-picker.md +2 -2
- package/examples/component-descriptions.md +1 -1
- package/examples/component-dialog-actions.md +1 -1
- package/examples/component-dialog-content-text.md +1 -1
- package/examples/component-dialog-content.md +1 -1
- package/examples/component-dialog-title.md +1 -1
- package/examples/component-dialog.md +2 -2
- package/examples/component-divider.md +3 -3
- package/examples/component-drawer.md +3 -3
- package/examples/component-dropdown.md +3 -3
- package/examples/component-empty.md +2 -2
- package/examples/component-fade.md +1 -1
- package/examples/component-field.md +1 -1
- package/examples/component-filled-input.md +1 -1
- package/examples/component-flex.md +1 -1
- package/examples/component-float-button.md +2 -2
- package/examples/component-form-control-label.md +1 -1
- package/examples/component-form-control.md +1 -1
- package/examples/component-form-controls.md +1 -1
- package/examples/component-form-group.md +1 -1
- package/examples/component-form-helper-text.md +1 -1
- package/examples/component-form-label.md +1 -1
- package/examples/component-form.md +2 -2
- package/examples/component-grid.md +2 -2
- package/examples/component-grow.md +1 -1
- package/examples/component-hover-card.md +1 -1
- package/examples/component-icon-button.md +1 -1
- package/examples/component-icon.md +1 -1
- package/examples/component-image-list.md +1 -1
- package/examples/component-image.md +1 -1
- package/examples/component-input-adornment.md +1 -1
- package/examples/component-input-base.md +1 -1
- package/examples/component-input-number.md +1 -1
- package/examples/component-input-otp.md +2 -2
- package/examples/component-input.md +3 -3
- package/examples/component-item.md +1 -1
- package/examples/component-label.md +1 -1
- package/examples/component-layout.md +1 -1
- package/examples/component-link.md +1 -1
- package/examples/component-list-item-avatar.md +1 -1
- package/examples/component-list-item-button.md +1 -1
- package/examples/component-list-item-icon.md +1 -1
- package/examples/component-list-item-text.md +1 -1
- package/examples/component-list-item.md +1 -1
- package/examples/component-list-subheader.md +1 -1
- package/examples/component-list.md +2 -2
- package/examples/component-masonry.md +1 -1
- package/examples/component-mentions.md +1 -1
- package/examples/component-menu-item.md +1 -1
- package/examples/component-menu-list.md +1 -1
- package/examples/component-menu.md +1 -1
- package/examples/component-menubar.md +1 -1
- package/examples/component-message.md +1 -1
- package/examples/component-mobile-stepper.md +1 -1
- package/examples/component-modal.md +5 -5
- package/examples/component-navigation-menu.md +1 -1
- package/examples/component-notification.md +1 -1
- package/examples/component-outlined-input.md +1 -1
- package/examples/component-pagination.md +3 -3
- package/examples/component-paper.md +1 -1
- package/examples/component-popconfirm.md +1 -1
- package/examples/component-popover.md +3 -3
- package/examples/component-popper.md +1 -1
- package/examples/component-progress.md +2 -2
- package/examples/component-qr-code.md +1 -1
- package/examples/component-radio.md +3 -3
- package/examples/component-rate.md +2 -2
- package/examples/component-resizable.md +1 -1
- package/examples/component-result.md +1 -1
- package/examples/component-scroll-area.md +1 -1
- package/examples/component-segmented.md +3 -3
- package/examples/component-select.md +3 -3
- package/examples/component-separator.md +1 -1
- package/examples/component-sheet.md +1 -1
- package/examples/component-sidebar.md +1 -1
- package/examples/component-skeleton.md +3 -3
- package/examples/component-slide.md +1 -1
- package/examples/component-slider.md +3 -3
- package/examples/component-snackbar-content.md +1 -1
- package/examples/component-snackbar.md +1 -1
- package/examples/component-sonner.md +1 -1
- package/examples/component-space.md +1 -1
- package/examples/component-speed-dial-action.md +1 -1
- package/examples/component-speed-dial.md +1 -1
- package/examples/component-spin.md +2 -2
- package/examples/component-spinner.md +1 -1
- package/examples/component-splitter.md +2 -2
- package/examples/component-stack.md +1 -1
- package/examples/component-statistic.md +1 -1
- package/examples/component-step-button.md +4 -4
- package/examples/component-step-connector.md +4 -4
- package/examples/component-step-content.md +1 -1
- package/examples/component-step-icon.md +1 -1
- package/examples/component-step-label.md +1 -1
- package/examples/component-step.md +2 -2
- package/examples/component-steps.md +2 -2
- package/examples/component-swipeable-drawer.md +1 -1
- package/examples/component-switch.md +3 -3
- package/examples/component-tab-scroll-button.md +5 -5
- package/examples/component-tab.md +1 -1
- package/examples/component-table-body.md +1 -1
- package/examples/component-table-cell.md +1 -1
- package/examples/component-table-container.md +1 -1
- package/examples/component-table-footer.md +1 -1
- package/examples/component-table-head.md +1 -1
- package/examples/component-table-pagination.md +1 -1
- package/examples/component-table-row.md +1 -1
- package/examples/component-table-sort-label.md +1 -1
- package/examples/component-table.md +2 -2
- package/examples/component-tabs.md +3 -3
- package/examples/component-tag-badge.md +2 -2
- package/examples/component-tag.md +1 -1
- package/examples/component-textarea-autosize.md +1 -1
- package/examples/component-textarea.md +1 -1
- package/examples/component-time-picker.md +1 -1
- package/examples/component-timeline.md +1 -1
- package/examples/component-toast.md +4 -4
- package/examples/component-toggle-button.md +1 -1
- package/examples/component-toggle.md +1 -1
- package/examples/component-toolbar.md +1 -1
- package/examples/component-tooltip.md +3 -3
- package/examples/component-tour.md +1 -1
- package/examples/component-transfer.md +1 -1
- package/examples/component-tree-select.md +1 -1
- package/examples/component-tree.md +1 -1
- package/examples/component-typography.md +2 -2
- package/examples/component-upload.md +1 -1
- package/examples/component-watermark.md +1 -1
- package/examples/component-zoom.md +1 -1
- package/knowledge/COVERAGE.md +4 -3
- package/knowledge/components/INDEX.md +202 -202
- package/knowledge/patterns/brand-references.md +72 -72
- package/package.json +1 -1
- package/tools/audit/local-ci.py +16 -1
- package/tools/audit/package-smoke.py +611 -9
- package/tools/audit/registry-smoke.py +534 -0
- package/tools/audit/release-metadata.py +47 -1
- package/tools/audit/smoke_assertions.py +915 -13
- package/tools/migrations/refs-links-to-reference-pages.py +179 -0
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
// Agent backlog command safety classification, effect review, and execution queue.
|
|
2
|
+
|
|
3
|
+
import { commandSpec } from "./signals-shared.mjs";
|
|
4
|
+
|
|
5
|
+
export function classifyAgentBacklogCommand(command = "") {
|
|
6
|
+
const text = String(command || "");
|
|
7
|
+
const detectedFlags = [];
|
|
8
|
+
if (/\s--out(?:\s|=|$)/.test(text)) detectedFlags.push("--out");
|
|
9
|
+
if (/\s--yes(?:\s|$)/.test(text)) detectedFlags.push("--yes");
|
|
10
|
+
if (/\s--fix(?:\s|$)/.test(text)) detectedFlags.push("--fix");
|
|
11
|
+
if (/\s--force(?:\s|$)/.test(text)) detectedFlags.push("--force");
|
|
12
|
+
if (/\s--with-learning(?:\s|$)/.test(text)) detectedFlags.push("--with-learning");
|
|
13
|
+
const outputTargets = extractAgentBacklogFlagTargets(text, "--out");
|
|
14
|
+
const profileTargets = [
|
|
15
|
+
...extractAgentBacklogFlagTargets(text, "--file"),
|
|
16
|
+
...extractAgentBacklogFlagTargets(text, "--learning-file"),
|
|
17
|
+
...extractAgentBacklogEnvTargets(text, "DESIGN_AI_LEARNING_FILE"),
|
|
18
|
+
];
|
|
19
|
+
const usageTargets = [
|
|
20
|
+
...extractAgentBacklogFlagTargets(text, "--usage-file"),
|
|
21
|
+
...extractAgentBacklogFlagTargets(text, "--learning-usage"),
|
|
22
|
+
...extractAgentBacklogEnvTargets(text, "DESIGN_AI_LEARNING_USAGE_FILE"),
|
|
23
|
+
];
|
|
24
|
+
const mutationFlags = detectedFlags.filter((flag) => flag === "--yes" || flag === "--fix" || flag === "--force" || flag === "--with-learning");
|
|
25
|
+
const writesLocalFiles = detectedFlags.includes("--out");
|
|
26
|
+
const mutatesLocalState = detectedFlags.some((flag) => flag === "--yes" || flag === "--fix" || flag === "--with-learning");
|
|
27
|
+
const level = mutatesLocalState ? "mutates-local-state" : writesLocalFiles ? "writes-local-file" : "read-only";
|
|
28
|
+
const reason = mutatesLocalState
|
|
29
|
+
? detectedFlags.includes("--with-learning")
|
|
30
|
+
? "Command records local learning usage sidecar metadata."
|
|
31
|
+
: "Command includes an apply/fix flag that can mutate local state."
|
|
32
|
+
: writesLocalFiles
|
|
33
|
+
? "Command writes an explicit local output file."
|
|
34
|
+
: "Command is preview/report oriented and has no detected mutation flags.";
|
|
35
|
+
return {
|
|
36
|
+
level,
|
|
37
|
+
writesLocalFiles,
|
|
38
|
+
mutatesLocalState,
|
|
39
|
+
requiresCleanWorkspace: writesLocalFiles || mutatesLocalState,
|
|
40
|
+
detectedFlags,
|
|
41
|
+
outputTargets,
|
|
42
|
+
profileTargets,
|
|
43
|
+
usageTargets,
|
|
44
|
+
mutationFlags,
|
|
45
|
+
reason,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function extractAgentBacklogFlagTargets(command = "", flag = "") {
|
|
50
|
+
if (!flag) return [];
|
|
51
|
+
const escaped = flag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
52
|
+
const pattern = new RegExp(`(?:^|\\s)${escaped}(?:=|\\s+)(?:"([^"]+)"|'([^']+)'|(\\S+))`, "g");
|
|
53
|
+
const targets = [];
|
|
54
|
+
let match = pattern.exec(command);
|
|
55
|
+
while (match) {
|
|
56
|
+
const value = match[1] || match[2] || match[3] || "";
|
|
57
|
+
if (value) targets.push({ flag, value });
|
|
58
|
+
match = pattern.exec(command);
|
|
59
|
+
}
|
|
60
|
+
return targets;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function extractAgentBacklogEnvTargets(command = "", name = "") {
|
|
64
|
+
if (!name) return [];
|
|
65
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66
|
+
const pattern = new RegExp(`(?:^|\\s)${escaped}=(?:"([^"]+)"|'([^']+)'|(\\S+))`, "g");
|
|
67
|
+
const targets = [];
|
|
68
|
+
let match = pattern.exec(command);
|
|
69
|
+
while (match) {
|
|
70
|
+
const value = match[1] || match[2] || match[3] || "";
|
|
71
|
+
if (value) targets.push({ flag: name, value });
|
|
72
|
+
match = pattern.exec(command);
|
|
73
|
+
}
|
|
74
|
+
return targets;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function summarizeAgentBacklogCommandSafety(steps = []) {
|
|
78
|
+
const summary = {
|
|
79
|
+
total: steps.length,
|
|
80
|
+
readOnly: 0,
|
|
81
|
+
writesLocalFile: 0,
|
|
82
|
+
mutatesLocalState: 0,
|
|
83
|
+
requiresCleanWorkspace: 0,
|
|
84
|
+
requiresReviewBeforeMutation: 0,
|
|
85
|
+
};
|
|
86
|
+
for (const step of steps) {
|
|
87
|
+
const safety = step.commandSafety && typeof step.commandSafety === "object" ? step.commandSafety : {};
|
|
88
|
+
if (safety.level === "read-only") summary.readOnly += 1;
|
|
89
|
+
if (safety.level === "writes-local-file") summary.writesLocalFile += 1;
|
|
90
|
+
if (safety.level === "mutates-local-state") summary.mutatesLocalState += 1;
|
|
91
|
+
if (safety.requiresCleanWorkspace) summary.requiresCleanWorkspace += 1;
|
|
92
|
+
if (step.requiresReviewBeforeMutation) summary.requiresReviewBeforeMutation += 1;
|
|
93
|
+
}
|
|
94
|
+
return summary;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function agentBacklogRunPolicyForSafetyLevel(safetyLevel = "unknown") {
|
|
98
|
+
if (safetyLevel === "read-only") return "preview-only";
|
|
99
|
+
if (safetyLevel === "writes-local-file") return "review-before-file-write";
|
|
100
|
+
if (safetyLevel === "mutates-local-state") return "review-before-mutation";
|
|
101
|
+
return "manual-review";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function buildAgentBacklogCommandEffects(commandSafety = {}) {
|
|
105
|
+
const outputTargets = Array.isArray(commandSafety.outputTargets) ? commandSafety.outputTargets : [];
|
|
106
|
+
const profileTargets = Array.isArray(commandSafety.profileTargets) ? commandSafety.profileTargets : [];
|
|
107
|
+
const usageTargets = Array.isArray(commandSafety.usageTargets) ? commandSafety.usageTargets : [];
|
|
108
|
+
const mutationFlags = Array.isArray(commandSafety.mutationFlags) ? commandSafety.mutationFlags : [];
|
|
109
|
+
const detectedFlags = Array.isArray(commandSafety.detectedFlags) ? commandSafety.detectedFlags : [];
|
|
110
|
+
return {
|
|
111
|
+
writesLocalFiles: Boolean(commandSafety.writesLocalFiles),
|
|
112
|
+
mutatesLocalState: Boolean(commandSafety.mutatesLocalState),
|
|
113
|
+
requiresCleanWorkspace: Boolean(commandSafety.requiresCleanWorkspace),
|
|
114
|
+
detectedFlags,
|
|
115
|
+
mutationFlags,
|
|
116
|
+
outputTargets,
|
|
117
|
+
profileTargets,
|
|
118
|
+
usageTargets,
|
|
119
|
+
reviewReason: commandSafety.reason || "",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function summarizeAgentBacklogCommandEffects(effects = {}) {
|
|
124
|
+
const parts = [];
|
|
125
|
+
const outputTargets = Array.isArray(effects.outputTargets) ? effects.outputTargets : [];
|
|
126
|
+
const profileTargets = Array.isArray(effects.profileTargets) ? effects.profileTargets : [];
|
|
127
|
+
const usageTargets = Array.isArray(effects.usageTargets) ? effects.usageTargets : [];
|
|
128
|
+
const mutationFlags = Array.isArray(effects.mutationFlags) ? effects.mutationFlags : [];
|
|
129
|
+
if (outputTargets.length > 0) parts.push(`out ${outputTargets.map((item) => item.value).join(", ")}`);
|
|
130
|
+
if (profileTargets.length > 0) parts.push(`profile ${profileTargets.map((item) => item.value).join(", ")}`);
|
|
131
|
+
if (usageTargets.length > 0) parts.push(`usage ${usageTargets.map((item) => item.value).join(", ")}`);
|
|
132
|
+
if (mutationFlags.length > 0) parts.push(`flags ${mutationFlags.join(", ")}`);
|
|
133
|
+
if (parts.length > 0) return parts.join("; ");
|
|
134
|
+
if (effects.mutatesLocalState) return "implicit local mutation target";
|
|
135
|
+
if (effects.writesLocalFiles) return "local file write target";
|
|
136
|
+
return "read-only";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function uniqueAgentBacklogTargets(targets = []) {
|
|
140
|
+
const seen = new Set();
|
|
141
|
+
const unique = [];
|
|
142
|
+
for (const target of targets) {
|
|
143
|
+
if (!target || typeof target !== "object") continue;
|
|
144
|
+
const flag = String(target.flag || "");
|
|
145
|
+
const value = String(target.value || "");
|
|
146
|
+
if (!flag || !value) continue;
|
|
147
|
+
const key = `${flag}\n${value}`;
|
|
148
|
+
if (seen.has(key)) continue;
|
|
149
|
+
seen.add(key);
|
|
150
|
+
unique.push({ flag, value });
|
|
151
|
+
}
|
|
152
|
+
return unique;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function uniqueAgentBacklogFlags(flags = []) {
|
|
156
|
+
return [...new Set(flags.filter(Boolean).map((flag) => String(flag)))];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function summarizeAgentBacklogCommandEffectManifest(commandManifest = []) {
|
|
160
|
+
const effects = commandManifest
|
|
161
|
+
.map((item) => (item && typeof item.commandEffects === "object" ? item.commandEffects : {}));
|
|
162
|
+
const outputTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
|
|
163
|
+
Array.isArray(item.outputTargets) ? item.outputTargets : []
|
|
164
|
+
)));
|
|
165
|
+
const profileTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
|
|
166
|
+
Array.isArray(item.profileTargets) ? item.profileTargets : []
|
|
167
|
+
)));
|
|
168
|
+
const usageTargets = uniqueAgentBacklogTargets(effects.flatMap((item) => (
|
|
169
|
+
Array.isArray(item.usageTargets) ? item.usageTargets : []
|
|
170
|
+
)));
|
|
171
|
+
const mutationFlags = uniqueAgentBacklogFlags(effects.flatMap((item) => (
|
|
172
|
+
Array.isArray(item.mutationFlags) ? item.mutationFlags : []
|
|
173
|
+
)));
|
|
174
|
+
return {
|
|
175
|
+
totalCommands: commandManifest.length,
|
|
176
|
+
writesLocalFileCount: effects.filter((item) => item.writesLocalFiles).length,
|
|
177
|
+
mutatesLocalStateCount: effects.filter((item) => item.mutatesLocalState).length,
|
|
178
|
+
requiresCleanWorkspaceCount: effects.filter((item) => item.requiresCleanWorkspace).length,
|
|
179
|
+
outputTargetCount: outputTargets.length,
|
|
180
|
+
profileTargetCount: profileTargets.length,
|
|
181
|
+
usageTargetCount: usageTargets.length,
|
|
182
|
+
mutationFlagCount: mutationFlags.length,
|
|
183
|
+
outputTargets,
|
|
184
|
+
profileTargets,
|
|
185
|
+
usageTargets,
|
|
186
|
+
mutationFlags,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function buildAgentBacklogCommandEffectReview(summary = {}, {
|
|
191
|
+
refreshCommandArgs = ["design-ai", "learn", "--agent-backlog", "--strict", "--json"],
|
|
192
|
+
} = {}) {
|
|
193
|
+
const hasMutation = Number(summary.mutatesLocalStateCount || 0) > 0 || Number(summary.mutationFlagCount || 0) > 0;
|
|
194
|
+
const hasFileWrite = Number(summary.writesLocalFileCount || 0) > 0 || Number(summary.outputTargetCount || 0) > 0;
|
|
195
|
+
const hasProfileOrUsage = Number(summary.profileTargetCount || 0) > 0 || Number(summary.usageTargetCount || 0) > 0;
|
|
196
|
+
const hasCommand = Number(summary.totalCommands || 0) > 0;
|
|
197
|
+
const checklist = [];
|
|
198
|
+
if (hasMutation) {
|
|
199
|
+
checklist.push("Review mutation flags and run in a clean workspace before applying.");
|
|
200
|
+
}
|
|
201
|
+
if (hasFileWrite) {
|
|
202
|
+
checklist.push("Inspect explicit output targets before committing generated files.");
|
|
203
|
+
}
|
|
204
|
+
if (hasProfileOrUsage) {
|
|
205
|
+
checklist.push("Confirm learning profile and usage sidecar targets are intentional.");
|
|
206
|
+
}
|
|
207
|
+
if (checklist.length === 0) {
|
|
208
|
+
checklist.push("No command target or mutation flag exposure detected.");
|
|
209
|
+
}
|
|
210
|
+
const level = hasMutation
|
|
211
|
+
? "mutation-review"
|
|
212
|
+
: hasFileWrite || hasProfileOrUsage
|
|
213
|
+
? "target-review"
|
|
214
|
+
: "clear";
|
|
215
|
+
const headline = level === "mutation-review"
|
|
216
|
+
? "Mutation-capable commands require operator review before execution."
|
|
217
|
+
: level === "target-review"
|
|
218
|
+
? "Command targets are explicit and should be reviewed before file changes."
|
|
219
|
+
: "No command target or mutation flag exposure detected.";
|
|
220
|
+
const gateCommands = [];
|
|
221
|
+
if (level !== "clear") {
|
|
222
|
+
gateCommands.push({
|
|
223
|
+
phase: "before",
|
|
224
|
+
label: "Confirm clean workspace before execution",
|
|
225
|
+
...commandSpec(["git", "status", "--short"]),
|
|
226
|
+
required: true,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (hasMutation || hasFileWrite) {
|
|
230
|
+
gateCommands.push({
|
|
231
|
+
phase: "after",
|
|
232
|
+
label: "Inspect local file changes after execution",
|
|
233
|
+
...commandSpec(["git", "diff", "--stat"]),
|
|
234
|
+
required: true,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
gateCommands.push({
|
|
238
|
+
phase: "refresh",
|
|
239
|
+
label: "Refresh focused agent backlog after review",
|
|
240
|
+
...commandSpec(refreshCommandArgs),
|
|
241
|
+
required: hasCommand,
|
|
242
|
+
});
|
|
243
|
+
const gatePhaseSummary = summarizeAgentBacklogGateCommands(gateCommands);
|
|
244
|
+
const gateRunbook = groupAgentBacklogGateCommands(gateCommands);
|
|
245
|
+
return {
|
|
246
|
+
level,
|
|
247
|
+
requiresOperatorReview: level !== "clear",
|
|
248
|
+
headline,
|
|
249
|
+
checklist,
|
|
250
|
+
gatePhaseSummary,
|
|
251
|
+
gateRunbook,
|
|
252
|
+
gateCommands,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function summarizeAgentBacklogGateCommands(gateCommands = []) {
|
|
257
|
+
const validCommands = Array.isArray(gateCommands)
|
|
258
|
+
? gateCommands.filter((item) => item && typeof item === "object")
|
|
259
|
+
: [];
|
|
260
|
+
const phases = [...new Set(validCommands.map((item) => String(item.phase || "")).filter(Boolean))];
|
|
261
|
+
const requiredCount = validCommands.filter((item) => item.required === true).length;
|
|
262
|
+
return {
|
|
263
|
+
count: validCommands.length,
|
|
264
|
+
requiredCount,
|
|
265
|
+
optionalCount: validCommands.length - requiredCount,
|
|
266
|
+
phases,
|
|
267
|
+
hasBefore: phases.includes("before"),
|
|
268
|
+
hasAfter: phases.includes("after"),
|
|
269
|
+
hasRefresh: phases.includes("refresh"),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function groupAgentBacklogGateCommands(gateCommands = []) {
|
|
274
|
+
const groups = {
|
|
275
|
+
before: [],
|
|
276
|
+
after: [],
|
|
277
|
+
refresh: [],
|
|
278
|
+
other: [],
|
|
279
|
+
};
|
|
280
|
+
const validCommands = Array.isArray(gateCommands)
|
|
281
|
+
? gateCommands.filter((item) => item && typeof item === "object")
|
|
282
|
+
: [];
|
|
283
|
+
for (const item of validCommands) {
|
|
284
|
+
const phase = String(item.phase || "");
|
|
285
|
+
if (phase === "before" || phase === "after" || phase === "refresh") {
|
|
286
|
+
groups[phase].push(item);
|
|
287
|
+
} else {
|
|
288
|
+
groups.other.push(item);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return groups;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function buildAgentBacklogOperatorRunbook({
|
|
295
|
+
commandManifest = [],
|
|
296
|
+
commandEffectReview = {},
|
|
297
|
+
} = {}) {
|
|
298
|
+
const gateRunbook = commandEffectReview?.gateRunbook && typeof commandEffectReview.gateRunbook === "object"
|
|
299
|
+
? commandEffectReview.gateRunbook
|
|
300
|
+
: {};
|
|
301
|
+
const gateCommandsFor = (phase) => (
|
|
302
|
+
Array.isArray(gateRunbook[phase]) ? gateRunbook[phase] : []
|
|
303
|
+
);
|
|
304
|
+
const executeCommands = Array.isArray(commandManifest)
|
|
305
|
+
? commandManifest
|
|
306
|
+
.filter((item) => item && typeof item === "object" && item.command)
|
|
307
|
+
.map((item) => ({
|
|
308
|
+
phase: "execute",
|
|
309
|
+
rank: item.rank,
|
|
310
|
+
actionId: item.actionId || "",
|
|
311
|
+
label: item.actionId ? `Run ${item.actionId}` : "Run backlog command",
|
|
312
|
+
command: item.command,
|
|
313
|
+
commandArgs: Array.isArray(item.commandArgs) ? item.commandArgs : [],
|
|
314
|
+
required: true,
|
|
315
|
+
safetyLevel: item.safetyLevel || "unknown",
|
|
316
|
+
runPolicy: item.runPolicy || "manual-review",
|
|
317
|
+
requiresReviewBeforeMutation: Boolean(item.requiresReviewBeforeMutation),
|
|
318
|
+
}))
|
|
319
|
+
: [];
|
|
320
|
+
const stages = [
|
|
321
|
+
{
|
|
322
|
+
phase: "before",
|
|
323
|
+
label: "Run before executing backlog commands",
|
|
324
|
+
commands: gateCommandsFor("before"),
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
phase: "execute",
|
|
328
|
+
label: "Execute reviewed backlog commands",
|
|
329
|
+
commands: executeCommands,
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
phase: "after",
|
|
333
|
+
label: "Run after executing backlog commands",
|
|
334
|
+
commands: gateCommandsFor("after"),
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
phase: "refresh",
|
|
338
|
+
label: "Refresh backlog status after execution",
|
|
339
|
+
commands: gateCommandsFor("refresh"),
|
|
340
|
+
},
|
|
341
|
+
].map((stage) => {
|
|
342
|
+
const commands = Array.isArray(stage.commands) ? stage.commands : [];
|
|
343
|
+
return {
|
|
344
|
+
...stage,
|
|
345
|
+
commandCount: commands.length,
|
|
346
|
+
requiredCount: commands.filter((item) => item && item.required === true).length,
|
|
347
|
+
commands,
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
const allCommands = stages.flatMap((stage) => stage.commands);
|
|
351
|
+
const nextStage = stages.find((stage) => stage.commands.length > 0) || null;
|
|
352
|
+
const nextCommand = nextStage?.commands?.[0] || null;
|
|
353
|
+
const stageOrder = ["before", "execute", "after", "refresh"];
|
|
354
|
+
const nonRefreshCommandCount = stages
|
|
355
|
+
.filter((stage) => stage.phase !== "refresh")
|
|
356
|
+
.reduce((count, stage) => count + stage.commands.length, 0);
|
|
357
|
+
const optionalRefreshOnlyNextCommand = Boolean(
|
|
358
|
+
nextCommand
|
|
359
|
+
&& nextStage?.phase === "refresh"
|
|
360
|
+
&& nextCommand.required !== true
|
|
361
|
+
&& nonRefreshCommandCount === 0,
|
|
362
|
+
);
|
|
363
|
+
const nextCommandSelection = {
|
|
364
|
+
strategy: "first-command-in-operator-runbook-stage-order",
|
|
365
|
+
stageOrder,
|
|
366
|
+
stage: nextStage?.phase || "",
|
|
367
|
+
label: nextCommand?.label || "",
|
|
368
|
+
command: nextCommand?.command || "",
|
|
369
|
+
commandArgs: Array.isArray(nextCommand?.commandArgs) ? nextCommand.commandArgs : [],
|
|
370
|
+
actionId: nextCommand?.actionId || "",
|
|
371
|
+
rank: nextCommand?.rank ?? null,
|
|
372
|
+
required: Boolean(nextCommand?.required),
|
|
373
|
+
runPolicy: nextCommand?.runPolicy || "",
|
|
374
|
+
reason: optionalRefreshOnlyNextCommand
|
|
375
|
+
? "Optional refresh command is available as status metadata; no executable backlog handoff command is selected."
|
|
376
|
+
: nextCommand
|
|
377
|
+
? `Selected the first command in the ${nextStage?.phase || "unknown"} stage using operator runbook stage order.`
|
|
378
|
+
: "No operator runbook command is available.",
|
|
379
|
+
};
|
|
380
|
+
return {
|
|
381
|
+
version: 1,
|
|
382
|
+
stageCount: stages.length,
|
|
383
|
+
commandCount: allCommands.length,
|
|
384
|
+
requiredCommandCount: allCommands.filter((item) => item && item.required === true).length,
|
|
385
|
+
reviewLevel: commandEffectReview?.level || "unknown",
|
|
386
|
+
requiresOperatorReview: Boolean(commandEffectReview?.requiresOperatorReview),
|
|
387
|
+
phases: stages.map((stage) => stage.phase),
|
|
388
|
+
nextStage: nextStage?.phase || "",
|
|
389
|
+
nextCommandLabel: nextCommand?.label || "",
|
|
390
|
+
nextCommand: nextCommand?.command || "",
|
|
391
|
+
nextCommandArgs: Array.isArray(nextCommand?.commandArgs) ? nextCommand.commandArgs : [],
|
|
392
|
+
nextCommandRequired: Boolean(nextCommand?.required),
|
|
393
|
+
nextCommandRunPolicy: nextCommand?.runPolicy || "",
|
|
394
|
+
nextCommandSelection,
|
|
395
|
+
stages,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function buildAgentBacklogNextCommandAlignment({
|
|
400
|
+
operatorRunbook = {},
|
|
401
|
+
nextCommandItem = null,
|
|
402
|
+
rankedNextStep = null,
|
|
403
|
+
} = {}) {
|
|
404
|
+
const operatorSelection = operatorRunbook?.nextCommandSelection && typeof operatorRunbook.nextCommandSelection === "object"
|
|
405
|
+
? operatorRunbook.nextCommandSelection
|
|
406
|
+
: {};
|
|
407
|
+
const operatorCommand = operatorRunbook?.nextCommand || operatorSelection.command || "";
|
|
408
|
+
const operatorActionId = operatorSelection.actionId || "";
|
|
409
|
+
const operatorStage = operatorRunbook?.nextStage || operatorSelection.stage || "";
|
|
410
|
+
const queueCommand = nextCommandItem?.command || "";
|
|
411
|
+
const queueActionId = nextCommandItem?.actionId || "";
|
|
412
|
+
const rankedActionId = rankedNextStep?.actionId || "";
|
|
413
|
+
const matchesQueueNextCommand = Boolean(operatorCommand && queueCommand && operatorCommand === queueCommand);
|
|
414
|
+
const matchesQueueNextAction = Boolean(operatorActionId && queueActionId && operatorActionId === queueActionId);
|
|
415
|
+
const operatorRunsBeforeQueueCommand = Boolean(operatorCommand && queueCommand && !matchesQueueNextCommand && operatorStage === "before");
|
|
416
|
+
const queueMatchesRankedNextAction = Boolean(queueActionId && rankedActionId && queueActionId === rankedActionId);
|
|
417
|
+
let reason = "No operator runbook command or queue command is available.";
|
|
418
|
+
if (matchesQueueNextCommand) {
|
|
419
|
+
reason = "Operator runbook starts with the same command as the safety-ordered execution queue.";
|
|
420
|
+
} else if (operatorRunsBeforeQueueCommand) {
|
|
421
|
+
reason = "Operator runbook starts with a before-stage gate before the safety-ordered queue command.";
|
|
422
|
+
} else if (operatorCommand && queueCommand) {
|
|
423
|
+
reason = "Operator runbook and safety-ordered queue selected different first commands.";
|
|
424
|
+
} else if (operatorCommand) {
|
|
425
|
+
reason = operatorStage === "refresh"
|
|
426
|
+
? "Operator runbook exposes an optional refresh command while the safety-ordered execution queue is empty."
|
|
427
|
+
: "Operator runbook exposes a command while the safety-ordered execution queue is empty.";
|
|
428
|
+
} else if (queueCommand) {
|
|
429
|
+
reason = "Safety-ordered execution queue exposes a command while the operator runbook has no command.";
|
|
430
|
+
}
|
|
431
|
+
return {
|
|
432
|
+
strategy: "compare-operator-runbook-next-command-to-execution-queue-next-command",
|
|
433
|
+
operatorStage,
|
|
434
|
+
operatorActionId,
|
|
435
|
+
operatorCommand,
|
|
436
|
+
operatorCommandArgs: Array.isArray(operatorRunbook?.nextCommandArgs) ? operatorRunbook.nextCommandArgs : [],
|
|
437
|
+
queueActionId,
|
|
438
|
+
queueCommand,
|
|
439
|
+
queueCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
|
|
440
|
+
rankedNextActionId: rankedActionId,
|
|
441
|
+
matchesQueueNextCommand,
|
|
442
|
+
matchesQueueNextAction,
|
|
443
|
+
operatorRunsBeforeQueueCommand,
|
|
444
|
+
queueMatchesRankedNextAction,
|
|
445
|
+
reason,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function buildAgentBacklogOperatorHandoff({
|
|
450
|
+
operatorRunbook = {},
|
|
451
|
+
nextCommandItem = null,
|
|
452
|
+
nextCommandAlignment = {},
|
|
453
|
+
} = {}) {
|
|
454
|
+
const operatorSelection = operatorRunbook?.nextCommandSelection && typeof operatorRunbook.nextCommandSelection === "object"
|
|
455
|
+
? operatorRunbook.nextCommandSelection
|
|
456
|
+
: {};
|
|
457
|
+
const hasOperatorCommand = Boolean(operatorRunbook?.nextCommand);
|
|
458
|
+
const hasQueueCommand = Boolean(nextCommandItem?.command);
|
|
459
|
+
const refreshStage = Array.isArray(operatorRunbook?.stages)
|
|
460
|
+
? operatorRunbook.stages.find((stage) => stage && stage.phase === "refresh")
|
|
461
|
+
: null;
|
|
462
|
+
const refreshCommandItem = Array.isArray(refreshStage?.commands)
|
|
463
|
+
? refreshStage.commands.find((item) => item && item.command) || null
|
|
464
|
+
: null;
|
|
465
|
+
const operatorPhase = hasOperatorCommand ? operatorRunbook.nextStage || "" : "";
|
|
466
|
+
const operatorCommandIsGate = Boolean(hasOperatorCommand && operatorPhase && operatorPhase !== "execute");
|
|
467
|
+
const nextQueueCommandRequiresGate = Boolean(nextCommandItem?.requiresReviewBeforeMutation);
|
|
468
|
+
const operatorGateAppliesToNextQueueAction = Boolean(operatorCommandIsGate && hasQueueCommand && nextQueueCommandRequiresGate);
|
|
469
|
+
const shouldUseOperatorCommand = Boolean(hasOperatorCommand && (!operatorCommandIsGate || operatorGateAppliesToNextQueueAction));
|
|
470
|
+
const source = shouldUseOperatorCommand ? "operator-runbook" : hasQueueCommand ? "execution-queue" : "";
|
|
471
|
+
const phase = shouldUseOperatorCommand ? operatorRunbook.nextStage || "" : hasQueueCommand ? "execute" : "";
|
|
472
|
+
const command = shouldUseOperatorCommand ? operatorRunbook.nextCommand || "" : nextCommandItem?.command || "";
|
|
473
|
+
const commandArgs = shouldUseOperatorCommand
|
|
474
|
+
? Array.isArray(operatorRunbook?.nextCommandArgs) ? operatorRunbook.nextCommandArgs : []
|
|
475
|
+
: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [];
|
|
476
|
+
const isGate = Boolean(shouldUseOperatorCommand && phase && phase !== "execute");
|
|
477
|
+
const nextQueueActionBlockedByGate = Boolean(isGate && hasQueueCommand);
|
|
478
|
+
const requiresOperatorReviewForHandoff = shouldUseOperatorCommand
|
|
479
|
+
? Boolean(operatorRunbook?.requiresOperatorReview)
|
|
480
|
+
: Boolean(nextCommandItem?.requiresReviewBeforeMutation);
|
|
481
|
+
const reviewLevelForHandoff = shouldUseOperatorCommand
|
|
482
|
+
? operatorRunbook?.reviewLevel || "unknown"
|
|
483
|
+
: requiresOperatorReviewForHandoff ? operatorRunbook?.reviewLevel || "unknown" : "clear";
|
|
484
|
+
let decision = "none";
|
|
485
|
+
let reason = refreshCommandItem?.command
|
|
486
|
+
? "No handoff command is required; optional refresh command remains available as status metadata."
|
|
487
|
+
: "No operator or queue command is available for handoff.";
|
|
488
|
+
if (nextQueueActionBlockedByGate) {
|
|
489
|
+
decision = "run-operator-gate";
|
|
490
|
+
reason = "Run the operator gate before executing the safety-ordered queue command.";
|
|
491
|
+
} else if (nextCommandAlignment?.matchesQueueNextCommand) {
|
|
492
|
+
decision = "run-shared-command";
|
|
493
|
+
reason = "Run the shared operator and queue command next.";
|
|
494
|
+
} else if (shouldUseOperatorCommand) {
|
|
495
|
+
decision = isGate ? "run-operator-gate" : "run-operator-command";
|
|
496
|
+
reason = "Run the operator runbook command next.";
|
|
497
|
+
} else if (hasQueueCommand) {
|
|
498
|
+
decision = "run-queue-command";
|
|
499
|
+
reason = "Run the safety-ordered queue command next.";
|
|
500
|
+
}
|
|
501
|
+
let stateStatus = "no-command";
|
|
502
|
+
let stateSummary = "No operator or queue command is available for handoff.";
|
|
503
|
+
if (command) {
|
|
504
|
+
if (nextQueueActionBlockedByGate) {
|
|
505
|
+
stateStatus = "gate-required";
|
|
506
|
+
stateSummary = "Run the required operator gate before the safety-ordered queue command.";
|
|
507
|
+
} else if (requiresOperatorReviewForHandoff) {
|
|
508
|
+
stateStatus = "review-required";
|
|
509
|
+
stateSummary = "Review command targets and mutation exposure before running the handoff command.";
|
|
510
|
+
} else {
|
|
511
|
+
stateStatus = "ready";
|
|
512
|
+
stateSummary = "The handoff command can be presented or run, then refreshed with the focused backlog check.";
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
stateSummary = "Focused agent backlog is clear; no handoff command is required.";
|
|
516
|
+
}
|
|
517
|
+
const hasHandoffCommand = Boolean(command);
|
|
518
|
+
const state = {
|
|
519
|
+
version: 1,
|
|
520
|
+
status: stateStatus,
|
|
521
|
+
ready: hasHandoffCommand || stateStatus === "no-command",
|
|
522
|
+
hasCommand: hasHandoffCommand,
|
|
523
|
+
complete: !hasHandoffCommand,
|
|
524
|
+
canRunWithoutReview: Boolean(hasHandoffCommand && !nextQueueActionBlockedByGate && !requiresOperatorReviewForHandoff),
|
|
525
|
+
requiresGate: nextQueueActionBlockedByGate,
|
|
526
|
+
requiresRefresh: Boolean(hasHandoffCommand && refreshCommandItem?.required),
|
|
527
|
+
summary: stateSummary,
|
|
528
|
+
};
|
|
529
|
+
return {
|
|
530
|
+
version: 1,
|
|
531
|
+
decision,
|
|
532
|
+
state,
|
|
533
|
+
source,
|
|
534
|
+
phase,
|
|
535
|
+
label: shouldUseOperatorCommand ? operatorRunbook.nextCommandLabel || "" : nextCommandItem?.actionId || "",
|
|
536
|
+
command,
|
|
537
|
+
commandArgs,
|
|
538
|
+
actionId: shouldUseOperatorCommand ? operatorSelection.actionId || "" : nextCommandItem?.actionId || "",
|
|
539
|
+
rank: shouldUseOperatorCommand ? operatorSelection.rank ?? null : nextCommandItem?.rank ?? null,
|
|
540
|
+
runPolicy: shouldUseOperatorCommand ? operatorRunbook.nextCommandRunPolicy || "" : nextCommandItem?.runPolicy || "",
|
|
541
|
+
required: shouldUseOperatorCommand ? Boolean(operatorRunbook.nextCommandRequired) : Boolean(hasQueueCommand),
|
|
542
|
+
isGate,
|
|
543
|
+
nextQueueActionId: nextCommandItem?.actionId || "",
|
|
544
|
+
nextQueueCommand: nextCommandItem?.command || "",
|
|
545
|
+
nextQueueCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
|
|
546
|
+
nextQueueCommandRequiresGate,
|
|
547
|
+
operatorGateAppliesToNextQueueAction,
|
|
548
|
+
nextQueueActionBlockedByGate,
|
|
549
|
+
refreshCommand: refreshCommandItem?.command || "",
|
|
550
|
+
refreshCommandArgs: Array.isArray(refreshCommandItem?.commandArgs) ? refreshCommandItem.commandArgs : [],
|
|
551
|
+
refreshCommandLabel: refreshCommandItem?.label || "",
|
|
552
|
+
refreshCommandRequired: Boolean(hasHandoffCommand && refreshCommandItem?.required),
|
|
553
|
+
reviewLevel: reviewLevelForHandoff,
|
|
554
|
+
requiresOperatorReview: requiresOperatorReviewForHandoff,
|
|
555
|
+
reason,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
export function buildAgentBacklogExecutionQueue(steps = [], {
|
|
560
|
+
refreshCommandArgs = ["design-ai", "learn", "--agent-backlog", "--strict", "--json"],
|
|
561
|
+
} = {}) {
|
|
562
|
+
const toQueueItem = (step) => {
|
|
563
|
+
const commandSafety = step.commandSafety && typeof step.commandSafety === "object" ? step.commandSafety : {};
|
|
564
|
+
const safetyLevel = commandSafety.level || "unknown";
|
|
565
|
+
const commandEffects = buildAgentBacklogCommandEffects(commandSafety);
|
|
566
|
+
const applyCommandSafety = step.applyCommandSafety && typeof step.applyCommandSafety === "object"
|
|
567
|
+
? {
|
|
568
|
+
level: step.applyCommandSafety.level || "unknown",
|
|
569
|
+
...buildAgentBacklogCommandEffects(step.applyCommandSafety),
|
|
570
|
+
}
|
|
571
|
+
: null;
|
|
572
|
+
return {
|
|
573
|
+
rank: step.rank,
|
|
574
|
+
actionId: step.actionId || "",
|
|
575
|
+
priority: step.priority || "p3",
|
|
576
|
+
category: step.category || "other",
|
|
577
|
+
title: step.title || "",
|
|
578
|
+
command: step.command || "",
|
|
579
|
+
commandArgs: Array.isArray(step.commandArgs) ? step.commandArgs : [],
|
|
580
|
+
applyCommand: step.applyCommand || "",
|
|
581
|
+
applyCommandArgs: Array.isArray(step.applyCommandArgs) ? step.applyCommandArgs : [],
|
|
582
|
+
applyCommandSafety,
|
|
583
|
+
applyRequiresReviewBeforeMutation: Boolean(step.applyRequiresReviewBeforeMutation),
|
|
584
|
+
safetyLevel,
|
|
585
|
+
runPolicy: agentBacklogRunPolicyForSafetyLevel(safetyLevel),
|
|
586
|
+
commandEffects,
|
|
587
|
+
requiresReviewBeforeMutation: Boolean(step.requiresReviewBeforeMutation),
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
const preview = steps.filter((step) => step.commandSafety?.level === "read-only").map(toQueueItem);
|
|
591
|
+
const fileWriteReview = steps.filter((step) => step.commandSafety?.level === "writes-local-file").map(toQueueItem);
|
|
592
|
+
const mutationReview = steps.filter((step) => step.commandSafety?.level === "mutates-local-state").map(toQueueItem);
|
|
593
|
+
const ordered = [...preview, ...fileWriteReview, ...mutationReview];
|
|
594
|
+
const commandManifest = ordered
|
|
595
|
+
.filter((item) => item.command)
|
|
596
|
+
.map((item) => ({
|
|
597
|
+
rank: item.rank,
|
|
598
|
+
actionId: item.actionId,
|
|
599
|
+
command: item.command,
|
|
600
|
+
commandArgs: item.commandArgs,
|
|
601
|
+
applyCommand: item.applyCommand,
|
|
602
|
+
applyCommandArgs: item.applyCommandArgs,
|
|
603
|
+
applyCommandSafety: item.applyCommandSafety,
|
|
604
|
+
applyRequiresReviewBeforeMutation: item.applyRequiresReviewBeforeMutation,
|
|
605
|
+
safetyLevel: item.safetyLevel,
|
|
606
|
+
runPolicy: item.runPolicy,
|
|
607
|
+
commandEffects: item.commandEffects,
|
|
608
|
+
requiresReviewBeforeMutation: item.requiresReviewBeforeMutation,
|
|
609
|
+
}));
|
|
610
|
+
const nextCommandItem = ordered.find((item) => item.command) || null;
|
|
611
|
+
const rankedNextStep = steps[0] || null;
|
|
612
|
+
const commandEffectSummary = summarizeAgentBacklogCommandEffectManifest(commandManifest);
|
|
613
|
+
const commandEffectReview = buildAgentBacklogCommandEffectReview(commandEffectSummary, {
|
|
614
|
+
refreshCommandArgs,
|
|
615
|
+
});
|
|
616
|
+
const operatorRunbook = buildAgentBacklogOperatorRunbook({
|
|
617
|
+
commandManifest,
|
|
618
|
+
commandEffectReview,
|
|
619
|
+
});
|
|
620
|
+
const nextCommandMatchesRankedStep = Boolean(
|
|
621
|
+
nextCommandItem?.actionId
|
|
622
|
+
&& rankedNextStep?.actionId
|
|
623
|
+
&& nextCommandItem.actionId === rankedNextStep.actionId,
|
|
624
|
+
);
|
|
625
|
+
const nextCommandSelection = {
|
|
626
|
+
strategy: "first-command-in-safety-ordered-queue",
|
|
627
|
+
safetyOrder: ["read-only", "writes-local-file", "mutates-local-state"],
|
|
628
|
+
actionId: nextCommandItem?.actionId || "",
|
|
629
|
+
rank: nextCommandItem?.rank ?? null,
|
|
630
|
+
safetyLevel: nextCommandItem?.safetyLevel || "",
|
|
631
|
+
runPolicy: nextCommandItem?.runPolicy || "",
|
|
632
|
+
planNextActionId: rankedNextStep?.actionId || "",
|
|
633
|
+
planNextActionRank: rankedNextStep?.rank ?? null,
|
|
634
|
+
matchesPlanNextAction: nextCommandMatchesRankedStep,
|
|
635
|
+
reason: nextCommandItem
|
|
636
|
+
? nextCommandMatchesRankedStep
|
|
637
|
+
? "Selected the ranked next action because it is first in the safety-ordered queue."
|
|
638
|
+
: "Selected the first command in the safety-ordered queue before higher-risk ranked actions."
|
|
639
|
+
: "No command-bearing backlog action is available.",
|
|
640
|
+
};
|
|
641
|
+
const nextCommandAlignment = buildAgentBacklogNextCommandAlignment({
|
|
642
|
+
operatorRunbook,
|
|
643
|
+
nextCommandItem,
|
|
644
|
+
rankedNextStep,
|
|
645
|
+
});
|
|
646
|
+
const operatorHandoff = buildAgentBacklogOperatorHandoff({
|
|
647
|
+
operatorRunbook,
|
|
648
|
+
nextCommandItem,
|
|
649
|
+
nextCommandAlignment,
|
|
650
|
+
});
|
|
651
|
+
return {
|
|
652
|
+
orderedCount: ordered.length,
|
|
653
|
+
commandManifestCount: commandManifest.length,
|
|
654
|
+
previewCount: preview.length,
|
|
655
|
+
fileWriteReviewCount: fileWriteReview.length,
|
|
656
|
+
mutationReviewCount: mutationReview.length,
|
|
657
|
+
nextActionId: ordered[0]?.actionId || "",
|
|
658
|
+
nextCommand: nextCommandItem?.command || "",
|
|
659
|
+
nextCommandArgs: Array.isArray(nextCommandItem?.commandArgs) ? nextCommandItem.commandArgs : [],
|
|
660
|
+
nextCommandRunPolicy: nextCommandItem?.runPolicy || "",
|
|
661
|
+
nextCommandSelection,
|
|
662
|
+
nextCommandAlignment,
|
|
663
|
+
operatorHandoff,
|
|
664
|
+
commandEffectSummary,
|
|
665
|
+
commandEffectReview,
|
|
666
|
+
operatorRunbook,
|
|
667
|
+
ordered,
|
|
668
|
+
commandManifest,
|
|
669
|
+
preview,
|
|
670
|
+
fileWriteReview,
|
|
671
|
+
mutationReview,
|
|
672
|
+
};
|
|
673
|
+
}
|