@femtomc/mu-agent 26.2.100 → 26.2.102
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 +5 -4
- package/dist/extensions/hud.d.ts +4 -0
- package/dist/extensions/hud.d.ts.map +1 -0
- package/dist/extensions/hud.js +483 -0
- package/dist/extensions/index.d.ts +1 -2
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +1 -2
- package/dist/extensions/mu-operator.d.ts.map +1 -1
- package/dist/extensions/mu-operator.js +2 -4
- package/dist/extensions/mu-serve.d.ts.map +1 -1
- package/dist/extensions/mu-serve.js +2 -4
- package/dist/operator.d.ts +331 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +66 -2
- package/package.json +2 -2
- package/prompts/skills/heartbeats/SKILL.md +30 -5
- package/prompts/skills/hierarchical-work-protocol/SKILL.md +2 -1
- package/prompts/skills/hud/SKILL.md +171 -0
- package/prompts/skills/mu/SKILL.md +1 -1
- package/prompts/skills/planning/SKILL.md +43 -45
- package/prompts/skills/subagents/SKILL.md +40 -16
- package/dist/extensions/hud-mode.d.ts +0 -8
- package/dist/extensions/hud-mode.d.ts.map +0 -1
- package/dist/extensions/hud-mode.js +0 -21
- package/dist/extensions/planning-ui.d.ts +0 -4
- package/dist/extensions/planning-ui.d.ts.map +0 -1
- package/dist/extensions/planning-ui.js +0 -866
- package/dist/extensions/subagents-ui.d.ts +0 -4
- package/dist/extensions/subagents-ui.d.ts.map +0 -1
- package/dist/extensions/subagents-ui.js +0 -1409
|
@@ -1,866 +0,0 @@
|
|
|
1
|
-
import { clearHudMode, setActiveHudMode, syncHudModeStatus } from "./hud-mode.js";
|
|
2
|
-
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
3
|
-
const DEFAULT_STEPS = [
|
|
4
|
-
"Investigate relevant code/docs/state",
|
|
5
|
-
"Create root issue + decomposed child issues",
|
|
6
|
-
"Present plan with IDs, ordering, risks",
|
|
7
|
-
"Refine with user feedback until approved",
|
|
8
|
-
];
|
|
9
|
-
const BAR_CHARS = ["░", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
|
|
10
|
-
const WIDGET_STEP_LIMIT = 4;
|
|
11
|
-
const WIDGET_STEP_LABEL_MAX = 60;
|
|
12
|
-
const WIDGET_ROOT_MAX = 24;
|
|
13
|
-
const WIDGET_NEXT_MAX = 76;
|
|
14
|
-
const WIDGET_BLOCKER_MAX = 76;
|
|
15
|
-
function phaseTone(phase) {
|
|
16
|
-
switch (phase) {
|
|
17
|
-
case "investigating":
|
|
18
|
-
return "dim";
|
|
19
|
-
case "drafting":
|
|
20
|
-
return "accent";
|
|
21
|
-
case "reviewing":
|
|
22
|
-
return "warning";
|
|
23
|
-
case "waiting_user":
|
|
24
|
-
return "warning";
|
|
25
|
-
case "blocked":
|
|
26
|
-
return "warning";
|
|
27
|
-
case "executing":
|
|
28
|
-
return "accent";
|
|
29
|
-
case "approved":
|
|
30
|
-
return "success";
|
|
31
|
-
case "done":
|
|
32
|
-
return "success";
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function confidenceTone(confidence) {
|
|
36
|
-
switch (confidence) {
|
|
37
|
-
case "low":
|
|
38
|
-
return "warning";
|
|
39
|
-
case "medium":
|
|
40
|
-
return "accent";
|
|
41
|
-
case "high":
|
|
42
|
-
return "success";
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function progressBar(done, total, width = 10) {
|
|
46
|
-
if (width <= 0 || total <= 0) {
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
|
-
const clampedDone = Math.max(0, Math.min(total, done));
|
|
50
|
-
const filled = (clampedDone / total) * width;
|
|
51
|
-
const full = Math.floor(filled);
|
|
52
|
-
const frac = filled - full;
|
|
53
|
-
const fracIdx = Math.round(frac * (BAR_CHARS.length - 1));
|
|
54
|
-
const empty = width - full - (fracIdx > 0 ? 1 : 0);
|
|
55
|
-
return (BAR_CHARS[BAR_CHARS.length - 1].repeat(full) +
|
|
56
|
-
(fracIdx > 0 ? BAR_CHARS[fracIdx] : "") +
|
|
57
|
-
BAR_CHARS[0].repeat(Math.max(0, empty)));
|
|
58
|
-
}
|
|
59
|
-
function createDefaultState() {
|
|
60
|
-
return {
|
|
61
|
-
enabled: false,
|
|
62
|
-
phase: "investigating",
|
|
63
|
-
rootIssueId: null,
|
|
64
|
-
steps: DEFAULT_STEPS.map((label) => ({ label, done: false })),
|
|
65
|
-
waitingOnUser: false,
|
|
66
|
-
nextAction: null,
|
|
67
|
-
blocker: null,
|
|
68
|
-
confidence: "medium",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function summarizePhase(phase) {
|
|
72
|
-
switch (phase) {
|
|
73
|
-
case "investigating":
|
|
74
|
-
return "investigating";
|
|
75
|
-
case "drafting":
|
|
76
|
-
return "drafting";
|
|
77
|
-
case "reviewing":
|
|
78
|
-
return "reviewing";
|
|
79
|
-
case "waiting_user":
|
|
80
|
-
return "waiting-user";
|
|
81
|
-
case "blocked":
|
|
82
|
-
return "blocked";
|
|
83
|
-
case "executing":
|
|
84
|
-
return "executing";
|
|
85
|
-
case "approved":
|
|
86
|
-
return "approved";
|
|
87
|
-
case "done":
|
|
88
|
-
return "done";
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function parsePlanningPhase(raw) {
|
|
92
|
-
const value = raw.trim().toLowerCase();
|
|
93
|
-
if (value === "investigating" ||
|
|
94
|
-
value === "drafting" ||
|
|
95
|
-
value === "reviewing" ||
|
|
96
|
-
value === "waiting_user" ||
|
|
97
|
-
value === "waiting-user" ||
|
|
98
|
-
value === "blocked" ||
|
|
99
|
-
value === "executing" ||
|
|
100
|
-
value === "approved" ||
|
|
101
|
-
value === "done") {
|
|
102
|
-
return value === "waiting-user" ? "waiting_user" : value;
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
function parsePlanningConfidence(raw) {
|
|
107
|
-
const value = raw.trim().toLowerCase();
|
|
108
|
-
if (value === "low" || value === "medium" || value === "high") {
|
|
109
|
-
return value;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
function parseSnapshotFormat(raw) {
|
|
114
|
-
const value = (raw ?? "compact").trim().toLowerCase();
|
|
115
|
-
return value === "multiline" ? "multiline" : "compact";
|
|
116
|
-
}
|
|
117
|
-
function normalizeMaybeClear(raw) {
|
|
118
|
-
const trimmed = raw.trim();
|
|
119
|
-
if (trimmed.length === 0) {
|
|
120
|
-
return { ok: false, error: "Value must not be empty." };
|
|
121
|
-
}
|
|
122
|
-
if (trimmed.toLowerCase() === "clear") {
|
|
123
|
-
return { ok: true, value: null };
|
|
124
|
-
}
|
|
125
|
-
return { ok: true, value: trimmed };
|
|
126
|
-
}
|
|
127
|
-
function normalizeSteps(labelsRaw) {
|
|
128
|
-
if (!Array.isArray(labelsRaw)) {
|
|
129
|
-
return { ok: false, error: "Steps must be an array of strings." };
|
|
130
|
-
}
|
|
131
|
-
const labels = [];
|
|
132
|
-
for (let i = 0; i < labelsRaw.length; i += 1) {
|
|
133
|
-
const value = labelsRaw[i];
|
|
134
|
-
if (typeof value !== "string") {
|
|
135
|
-
return { ok: false, error: `Step ${i + 1} must be a string.` };
|
|
136
|
-
}
|
|
137
|
-
const trimmed = value.trim();
|
|
138
|
-
if (trimmed.length === 0) {
|
|
139
|
-
return { ok: false, error: `Step ${i + 1} must not be empty.` };
|
|
140
|
-
}
|
|
141
|
-
labels.push(trimmed);
|
|
142
|
-
}
|
|
143
|
-
return { ok: true, labels };
|
|
144
|
-
}
|
|
145
|
-
function validateStepIndex(step, max, allowAppend = false) {
|
|
146
|
-
if (typeof step !== "number" || !Number.isFinite(step)) {
|
|
147
|
-
return { ok: false, error: "Step index must be a number." };
|
|
148
|
-
}
|
|
149
|
-
const parsed = Math.trunc(step);
|
|
150
|
-
const upperBound = allowAppend ? max + 1 : max;
|
|
151
|
-
if (parsed < 1 || parsed > upperBound) {
|
|
152
|
-
return { ok: false, error: `Step index out of range (1-${upperBound}).` };
|
|
153
|
-
}
|
|
154
|
-
return { ok: true, index: parsed - 1 };
|
|
155
|
-
}
|
|
156
|
-
function applyStepUpdates(state, updatesRaw) {
|
|
157
|
-
if (!Array.isArray(updatesRaw)) {
|
|
158
|
-
return { ok: false, error: "step_updates must be an array." };
|
|
159
|
-
}
|
|
160
|
-
let changed = 0;
|
|
161
|
-
for (let i = 0; i < updatesRaw.length; i += 1) {
|
|
162
|
-
const raw = updatesRaw[i];
|
|
163
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
164
|
-
return { ok: false, error: `step_updates[${i}] must be an object.` };
|
|
165
|
-
}
|
|
166
|
-
const update = raw;
|
|
167
|
-
const indexRaw = update.index;
|
|
168
|
-
if (typeof indexRaw !== "number" || !Number.isFinite(indexRaw)) {
|
|
169
|
-
return { ok: false, error: `step_updates[${i}].index must be a number.` };
|
|
170
|
-
}
|
|
171
|
-
const stepIndex = Math.trunc(indexRaw);
|
|
172
|
-
if (stepIndex < 1 || stepIndex > state.steps.length) {
|
|
173
|
-
return { ok: false, error: `step_updates[${i}].index out of range (1-${state.steps.length}).` };
|
|
174
|
-
}
|
|
175
|
-
const doneRaw = update.done;
|
|
176
|
-
const labelRaw = update.label;
|
|
177
|
-
if (doneRaw === undefined && labelRaw === undefined) {
|
|
178
|
-
return { ok: false, error: `step_updates[${i}] must include done and/or label.` };
|
|
179
|
-
}
|
|
180
|
-
const step = state.steps[stepIndex - 1];
|
|
181
|
-
if (!step) {
|
|
182
|
-
return { ok: false, error: `step_updates[${i}] references missing step.` };
|
|
183
|
-
}
|
|
184
|
-
if (doneRaw !== undefined) {
|
|
185
|
-
if (typeof doneRaw !== "boolean") {
|
|
186
|
-
return { ok: false, error: `step_updates[${i}].done must be a boolean.` };
|
|
187
|
-
}
|
|
188
|
-
if (step.done !== doneRaw) {
|
|
189
|
-
step.done = doneRaw;
|
|
190
|
-
changed += 1;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (labelRaw !== undefined) {
|
|
194
|
-
if (typeof labelRaw !== "string") {
|
|
195
|
-
return { ok: false, error: `step_updates[${i}].label must be a string.` };
|
|
196
|
-
}
|
|
197
|
-
const trimmed = labelRaw.trim();
|
|
198
|
-
if (trimmed.length === 0) {
|
|
199
|
-
return { ok: false, error: `step_updates[${i}].label must not be empty.` };
|
|
200
|
-
}
|
|
201
|
-
if (step.label !== trimmed) {
|
|
202
|
-
step.label = trimmed;
|
|
203
|
-
changed += 1;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return { ok: true, changed };
|
|
208
|
-
}
|
|
209
|
-
function shortLabel(value, fallback, maxLen = 48) {
|
|
210
|
-
if (!value || value.trim().length === 0) {
|
|
211
|
-
return fallback;
|
|
212
|
-
}
|
|
213
|
-
const compact = value.replace(/\s+/g, " ").trim();
|
|
214
|
-
if (compact.length <= maxLen) {
|
|
215
|
-
return compact;
|
|
216
|
-
}
|
|
217
|
-
return `${compact.slice(0, Math.max(0, maxLen - 1))}…`;
|
|
218
|
-
}
|
|
219
|
-
function planningSnapshot(state, format) {
|
|
220
|
-
const done = state.steps.filter((step) => step.done).length;
|
|
221
|
-
const total = state.steps.length;
|
|
222
|
-
const phase = summarizePhase(state.phase);
|
|
223
|
-
const root = state.rootIssueId ?? "(unset)";
|
|
224
|
-
const waiting = state.waitingOnUser ? "yes" : "no";
|
|
225
|
-
const next = shortLabel(state.nextAction, "(unset)");
|
|
226
|
-
const blocker = shortLabel(state.blocker, "(none)");
|
|
227
|
-
if (format === "multiline") {
|
|
228
|
-
return [
|
|
229
|
-
`Planning HUD snapshot`,
|
|
230
|
-
`phase: ${phase}`,
|
|
231
|
-
`root: ${root}`,
|
|
232
|
-
`steps: ${done}/${total}`,
|
|
233
|
-
`waiting_on_user: ${waiting}`,
|
|
234
|
-
`confidence: ${state.confidence}`,
|
|
235
|
-
`next_action: ${next}`,
|
|
236
|
-
`blocker: ${blocker}`,
|
|
237
|
-
].join("\n");
|
|
238
|
-
}
|
|
239
|
-
return [
|
|
240
|
-
`HUD(plan)`,
|
|
241
|
-
`phase=${phase}`,
|
|
242
|
-
`root=${root}`,
|
|
243
|
-
`steps=${done}/${total}`,
|
|
244
|
-
`waiting=${waiting}`,
|
|
245
|
-
`confidence=${state.confidence}`,
|
|
246
|
-
`next=${next}`,
|
|
247
|
-
`blocker=${blocker}`,
|
|
248
|
-
].join(" · ");
|
|
249
|
-
}
|
|
250
|
-
function renderPlanningUi(ctx, state) {
|
|
251
|
-
if (!ctx.hasUI) {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
if (!state.enabled) {
|
|
255
|
-
ctx.ui.setStatus("mu-planning", undefined);
|
|
256
|
-
ctx.ui.setStatus("mu-planning-meta", undefined);
|
|
257
|
-
ctx.ui.setWidget("mu-planning", undefined);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
const done = state.steps.filter((step) => step.done).length;
|
|
261
|
-
const total = state.steps.length;
|
|
262
|
-
const phase = summarizePhase(state.phase);
|
|
263
|
-
const phaseColor = phaseTone(state.phase);
|
|
264
|
-
const confidenceColor = confidenceTone(state.confidence);
|
|
265
|
-
const meter = progressBar(done, total, 10);
|
|
266
|
-
const waitingLabel = state.waitingOnUser ? "awaiting-user" : "active";
|
|
267
|
-
const waitingColor = state.waitingOnUser ? "warning" : "dim";
|
|
268
|
-
const rootCompact = state.rootIssueId ? shortLabel(state.rootIssueId, "", WIDGET_ROOT_MAX) : null;
|
|
269
|
-
const nextLabel = state.waitingOnUser ? "ask" : "next";
|
|
270
|
-
const nextFallback = state.waitingOnUser ? "(describe required user input)" : "(unset)";
|
|
271
|
-
const nextTone = state.waitingOnUser ? "warning" : "dim";
|
|
272
|
-
const nextCompact = shortLabel(state.nextAction, nextFallback, WIDGET_NEXT_MAX);
|
|
273
|
-
const blockerCompact = shortLabel(state.blocker, "(none)", WIDGET_BLOCKER_MAX);
|
|
274
|
-
const blockerColor = state.blocker ? "warning" : "dim";
|
|
275
|
-
const statusParts = [
|
|
276
|
-
ctx.ui.theme.fg("dim", "plan"),
|
|
277
|
-
ctx.ui.theme.fg(phaseColor, phase),
|
|
278
|
-
ctx.ui.theme.fg("dim", `${done}/${total}`),
|
|
279
|
-
ctx.ui.theme.fg(phaseColor, meter),
|
|
280
|
-
];
|
|
281
|
-
if (state.waitingOnUser) {
|
|
282
|
-
statusParts.push(ctx.ui.theme.fg("warning", "ask:user"));
|
|
283
|
-
}
|
|
284
|
-
if (state.blocker) {
|
|
285
|
-
statusParts.push(ctx.ui.theme.fg("warning", "blocked"));
|
|
286
|
-
}
|
|
287
|
-
ctx.ui.setStatus("mu-planning", statusParts.join(` ${ctx.ui.theme.fg("muted", "·")} `));
|
|
288
|
-
ctx.ui.setStatus("mu-planning-meta", undefined);
|
|
289
|
-
const lines = [
|
|
290
|
-
[
|
|
291
|
-
ctx.ui.theme.fg("accent", ctx.ui.theme.bold("Planning")),
|
|
292
|
-
ctx.ui.theme.fg("muted", "·"),
|
|
293
|
-
ctx.ui.theme.fg(phaseColor, phase),
|
|
294
|
-
ctx.ui.theme.fg("muted", "·"),
|
|
295
|
-
ctx.ui.theme.fg(waitingColor, waitingLabel),
|
|
296
|
-
ctx.ui.theme.fg("muted", "·"),
|
|
297
|
-
ctx.ui.theme.fg("dim", `${done}/${total}`),
|
|
298
|
-
ctx.ui.theme.fg(phaseColor, meter),
|
|
299
|
-
].join(" "),
|
|
300
|
-
];
|
|
301
|
-
const chips = [];
|
|
302
|
-
if (rootCompact) {
|
|
303
|
-
chips.push(ctx.ui.theme.fg("muted", `root:${rootCompact}`));
|
|
304
|
-
}
|
|
305
|
-
if (state.confidence !== "medium") {
|
|
306
|
-
chips.push(ctx.ui.theme.fg(confidenceColor, `conf:${state.confidence}`));
|
|
307
|
-
}
|
|
308
|
-
if (chips.length > 0) {
|
|
309
|
-
lines.push(chips.join(` ${ctx.ui.theme.fg("muted", "·")} `));
|
|
310
|
-
}
|
|
311
|
-
lines.push(`${ctx.ui.theme.fg("muted", `${nextLabel}:`)} ${ctx.ui.theme.fg(nextTone, nextCompact)}`);
|
|
312
|
-
if (state.blocker) {
|
|
313
|
-
lines.push(`${ctx.ui.theme.fg("muted", "blocker:")} ${ctx.ui.theme.fg(blockerColor, blockerCompact)}`);
|
|
314
|
-
}
|
|
315
|
-
lines.push(ctx.ui.theme.fg("dim", "────────────────────────────"));
|
|
316
|
-
if (state.steps.length === 0) {
|
|
317
|
-
lines.push(ctx.ui.theme.fg("muted", "(no checklist steps configured)"));
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
const shownSteps = state.steps.slice(0, WIDGET_STEP_LIMIT);
|
|
321
|
-
for (let index = 0; index < shownSteps.length; index += 1) {
|
|
322
|
-
const step = shownSteps[index];
|
|
323
|
-
const mark = step.done ? ctx.ui.theme.fg("success", "[x]") : ctx.ui.theme.fg("muted", "[ ]");
|
|
324
|
-
const labelText = shortLabel(step.label, "(empty)", WIDGET_STEP_LABEL_MAX);
|
|
325
|
-
const label = step.done ? ctx.ui.theme.fg("dim", labelText) : ctx.ui.theme.fg("text", labelText);
|
|
326
|
-
lines.push(`${mark} ${ctx.ui.theme.fg("muted", `${index + 1}.`)} ${label}`);
|
|
327
|
-
}
|
|
328
|
-
if (state.steps.length > shownSteps.length) {
|
|
329
|
-
lines.push(ctx.ui.theme.fg("muted", `... +${state.steps.length - shownSteps.length} more steps`));
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
ctx.ui.setWidget("mu-planning", lines, { placement: "belowEditor" });
|
|
333
|
-
}
|
|
334
|
-
function planningUsageText() {
|
|
335
|
-
return [
|
|
336
|
-
"Usage:",
|
|
337
|
-
" /mu plan on|off|toggle|status|reset|snapshot",
|
|
338
|
-
" /mu plan phase <investigating|drafting|reviewing|waiting-user|blocked|executing|approved|done>",
|
|
339
|
-
" /mu plan root <issue-id|clear>",
|
|
340
|
-
" /mu plan check <n> | /mu plan uncheck <n> | /mu plan toggle-step <n>",
|
|
341
|
-
" /mu plan add-step <label> | remove-step <n> | relabel-step <n> <label>",
|
|
342
|
-
" /mu plan waiting <on|off> | confidence <low|medium|high>",
|
|
343
|
-
" /mu plan next <text|clear> | blocker <text|clear>",
|
|
344
|
-
].join("\n");
|
|
345
|
-
}
|
|
346
|
-
function parseOnOff(raw) {
|
|
347
|
-
const value = (raw ?? "").trim().toLowerCase();
|
|
348
|
-
if (value === "on" || value === "yes" || value === "true" || value === "1") {
|
|
349
|
-
return true;
|
|
350
|
-
}
|
|
351
|
-
if (value === "off" || value === "no" || value === "false" || value === "0") {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
return null;
|
|
355
|
-
}
|
|
356
|
-
function planningDetails(state) {
|
|
357
|
-
return {
|
|
358
|
-
enabled: state.enabled,
|
|
359
|
-
phase: state.phase,
|
|
360
|
-
root_issue_id: state.rootIssueId,
|
|
361
|
-
waiting_on_user: state.waitingOnUser,
|
|
362
|
-
next_action: state.nextAction,
|
|
363
|
-
blocker: state.blocker,
|
|
364
|
-
confidence: state.confidence,
|
|
365
|
-
steps: state.steps.map((step, index) => ({
|
|
366
|
-
index: index + 1,
|
|
367
|
-
label: step.label,
|
|
368
|
-
done: step.done,
|
|
369
|
-
})),
|
|
370
|
-
snapshot_compact: planningSnapshot(state, "compact"),
|
|
371
|
-
snapshot_multiline: planningSnapshot(state, "multiline"),
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
function planningStatusSummary(state) {
|
|
375
|
-
const done = state.steps.filter((step) => step.done).length;
|
|
376
|
-
const root = state.rootIssueId ?? "(unset)";
|
|
377
|
-
return [
|
|
378
|
-
`Planning HUD: ${state.enabled ? "enabled" : "disabled"}`,
|
|
379
|
-
`phase: ${state.phase}`,
|
|
380
|
-
`root: ${root}`,
|
|
381
|
-
`steps: ${done}/${state.steps.length}`,
|
|
382
|
-
`waiting_on_user: ${state.waitingOnUser ? "yes" : "no"}`,
|
|
383
|
-
`confidence: ${state.confidence}`,
|
|
384
|
-
`next_action: ${shortLabel(state.nextAction, "(unset)", 120)}`,
|
|
385
|
-
`blocker: ${shortLabel(state.blocker, "(none)", 120)}`,
|
|
386
|
-
].join("\n");
|
|
387
|
-
}
|
|
388
|
-
function planningToolError(message) {
|
|
389
|
-
return {
|
|
390
|
-
content: [{ type: "text", text: message }],
|
|
391
|
-
details: {
|
|
392
|
-
ok: false,
|
|
393
|
-
error: message,
|
|
394
|
-
},
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
export function planningUiExtension(pi) {
|
|
398
|
-
let state = createDefaultState();
|
|
399
|
-
const notify = (ctx, message, level = "info") => {
|
|
400
|
-
ctx.ui.notify(`${message}\n\n${planningUsageText()}`, level);
|
|
401
|
-
};
|
|
402
|
-
const refresh = (ctx) => {
|
|
403
|
-
renderPlanningUi(ctx, state);
|
|
404
|
-
};
|
|
405
|
-
const syncPlanningMode = (ctx, action) => {
|
|
406
|
-
const passiveAction = action === "status" || action === "snapshot";
|
|
407
|
-
if (!state.enabled) {
|
|
408
|
-
clearHudMode("planning");
|
|
409
|
-
}
|
|
410
|
-
else if (!passiveAction) {
|
|
411
|
-
setActiveHudMode("planning");
|
|
412
|
-
}
|
|
413
|
-
syncHudModeStatus(ctx);
|
|
414
|
-
};
|
|
415
|
-
const applyPlanningAction = (params) => {
|
|
416
|
-
switch (params.action) {
|
|
417
|
-
case "status":
|
|
418
|
-
return { ok: true, message: planningStatusSummary(state), level: "info" };
|
|
419
|
-
case "snapshot": {
|
|
420
|
-
const format = parseSnapshotFormat(params.snapshot_format);
|
|
421
|
-
return { ok: true, message: planningSnapshot(state, format), level: "info" };
|
|
422
|
-
}
|
|
423
|
-
case "on":
|
|
424
|
-
state.enabled = true;
|
|
425
|
-
return { ok: true, message: "Planning HUD enabled.", level: "info" };
|
|
426
|
-
case "off":
|
|
427
|
-
state.enabled = false;
|
|
428
|
-
return { ok: true, message: "Planning HUD disabled.", level: "info" };
|
|
429
|
-
case "toggle":
|
|
430
|
-
state.enabled = !state.enabled;
|
|
431
|
-
return { ok: true, message: `Planning HUD ${state.enabled ? "enabled" : "disabled"}.`, level: "info" };
|
|
432
|
-
case "reset":
|
|
433
|
-
state = createDefaultState();
|
|
434
|
-
return { ok: true, message: "Planning HUD state reset.", level: "info" };
|
|
435
|
-
case "phase": {
|
|
436
|
-
const phase = parsePlanningPhase(params.phase ?? "");
|
|
437
|
-
if (!phase) {
|
|
438
|
-
return { ok: false, message: "Invalid phase.", level: "error" };
|
|
439
|
-
}
|
|
440
|
-
state.phase = phase;
|
|
441
|
-
state.enabled = true;
|
|
442
|
-
return { ok: true, message: `Planning phase set to ${phase}.`, level: "info" };
|
|
443
|
-
}
|
|
444
|
-
case "root": {
|
|
445
|
-
const rootRaw = params.root_issue_id;
|
|
446
|
-
if (typeof rootRaw !== "string") {
|
|
447
|
-
return { ok: false, message: "Missing root issue id.", level: "error" };
|
|
448
|
-
}
|
|
449
|
-
const normalized = normalizeMaybeClear(rootRaw);
|
|
450
|
-
if (!normalized.ok) {
|
|
451
|
-
return { ok: false, message: "Missing root issue id.", level: "error" };
|
|
452
|
-
}
|
|
453
|
-
state.rootIssueId = normalized.value;
|
|
454
|
-
state.enabled = true;
|
|
455
|
-
return { ok: true, message: `Planning root set to ${state.rootIssueId ?? "(unset)"}.`, level: "info" };
|
|
456
|
-
}
|
|
457
|
-
case "check":
|
|
458
|
-
case "uncheck":
|
|
459
|
-
case "toggle_step": {
|
|
460
|
-
const parsedIndex = validateStepIndex(params.step, state.steps.length);
|
|
461
|
-
if (!parsedIndex.ok) {
|
|
462
|
-
return { ok: false, message: parsedIndex.error, level: "error" };
|
|
463
|
-
}
|
|
464
|
-
const step = state.steps[parsedIndex.index];
|
|
465
|
-
if (!step) {
|
|
466
|
-
return { ok: false, message: "Step index out of range.", level: "error" };
|
|
467
|
-
}
|
|
468
|
-
if (params.action === "check") {
|
|
469
|
-
step.done = true;
|
|
470
|
-
}
|
|
471
|
-
else if (params.action === "uncheck") {
|
|
472
|
-
step.done = false;
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
step.done = !step.done;
|
|
476
|
-
}
|
|
477
|
-
state.enabled = true;
|
|
478
|
-
return { ok: true, message: `Planning step ${parsedIndex.index + 1} updated.`, level: "info" };
|
|
479
|
-
}
|
|
480
|
-
case "set_steps": {
|
|
481
|
-
const normalized = normalizeSteps(params.steps);
|
|
482
|
-
if (!normalized.ok) {
|
|
483
|
-
return { ok: false, message: normalized.error, level: "error" };
|
|
484
|
-
}
|
|
485
|
-
state.steps = normalized.labels.map((label) => ({ label, done: false }));
|
|
486
|
-
state.enabled = true;
|
|
487
|
-
return { ok: true, message: `Planning checklist replaced (${state.steps.length} steps).`, level: "info" };
|
|
488
|
-
}
|
|
489
|
-
case "add_step": {
|
|
490
|
-
const labelRaw = params.label;
|
|
491
|
-
if (typeof labelRaw !== "string" || labelRaw.trim().length === 0) {
|
|
492
|
-
return { ok: false, message: "Missing step label.", level: "error" };
|
|
493
|
-
}
|
|
494
|
-
const label = labelRaw.trim();
|
|
495
|
-
let insertIndex = state.steps.length;
|
|
496
|
-
if (params.step !== undefined) {
|
|
497
|
-
const parsedIndex = validateStepIndex(params.step, state.steps.length, true);
|
|
498
|
-
if (!parsedIndex.ok) {
|
|
499
|
-
return { ok: false, message: parsedIndex.error, level: "error" };
|
|
500
|
-
}
|
|
501
|
-
insertIndex = parsedIndex.index;
|
|
502
|
-
}
|
|
503
|
-
state.steps.splice(insertIndex, 0, { label, done: false });
|
|
504
|
-
state.enabled = true;
|
|
505
|
-
return { ok: true, message: `Added planning step ${insertIndex + 1}.`, level: "info" };
|
|
506
|
-
}
|
|
507
|
-
case "remove_step": {
|
|
508
|
-
const parsedIndex = validateStepIndex(params.step, state.steps.length);
|
|
509
|
-
if (!parsedIndex.ok) {
|
|
510
|
-
return { ok: false, message: parsedIndex.error, level: "error" };
|
|
511
|
-
}
|
|
512
|
-
state.steps.splice(parsedIndex.index, 1);
|
|
513
|
-
state.enabled = true;
|
|
514
|
-
return { ok: true, message: `Removed planning step ${parsedIndex.index + 1}.`, level: "info" };
|
|
515
|
-
}
|
|
516
|
-
case "set_step_label": {
|
|
517
|
-
const parsedIndex = validateStepIndex(params.step, state.steps.length);
|
|
518
|
-
if (!parsedIndex.ok) {
|
|
519
|
-
return { ok: false, message: parsedIndex.error, level: "error" };
|
|
520
|
-
}
|
|
521
|
-
const labelRaw = params.label;
|
|
522
|
-
if (typeof labelRaw !== "string" || labelRaw.trim().length === 0) {
|
|
523
|
-
return { ok: false, message: "Missing step label.", level: "error" };
|
|
524
|
-
}
|
|
525
|
-
const step = state.steps[parsedIndex.index];
|
|
526
|
-
if (!step) {
|
|
527
|
-
return { ok: false, message: "Step index out of range.", level: "error" };
|
|
528
|
-
}
|
|
529
|
-
step.label = labelRaw.trim();
|
|
530
|
-
state.enabled = true;
|
|
531
|
-
return { ok: true, message: `Planning step ${parsedIndex.index + 1} relabeled.`, level: "info" };
|
|
532
|
-
}
|
|
533
|
-
case "set_waiting": {
|
|
534
|
-
if (typeof params.waiting_on_user !== "boolean") {
|
|
535
|
-
return { ok: false, message: "waiting_on_user must be a boolean.", level: "error" };
|
|
536
|
-
}
|
|
537
|
-
state.waitingOnUser = params.waiting_on_user;
|
|
538
|
-
state.enabled = true;
|
|
539
|
-
return {
|
|
540
|
-
ok: true,
|
|
541
|
-
message: `Planning waiting_on_user set to ${state.waitingOnUser ? "yes" : "no"}.`,
|
|
542
|
-
level: "info",
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
case "set_next": {
|
|
546
|
-
const nextRaw = params.next_action;
|
|
547
|
-
if (typeof nextRaw !== "string") {
|
|
548
|
-
return { ok: false, message: "Missing next_action value.", level: "error" };
|
|
549
|
-
}
|
|
550
|
-
const normalized = normalizeMaybeClear(nextRaw);
|
|
551
|
-
if (!normalized.ok) {
|
|
552
|
-
return { ok: false, message: "Missing next_action value.", level: "error" };
|
|
553
|
-
}
|
|
554
|
-
state.nextAction = normalized.value;
|
|
555
|
-
state.enabled = true;
|
|
556
|
-
return {
|
|
557
|
-
ok: true,
|
|
558
|
-
message: `Planning next_action set to ${shortLabel(state.nextAction, "(unset)")}.`,
|
|
559
|
-
level: "info",
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
case "set_blocker": {
|
|
563
|
-
const blockerRaw = params.blocker;
|
|
564
|
-
if (typeof blockerRaw !== "string") {
|
|
565
|
-
return { ok: false, message: "Missing blocker value.", level: "error" };
|
|
566
|
-
}
|
|
567
|
-
const normalized = normalizeMaybeClear(blockerRaw);
|
|
568
|
-
if (!normalized.ok) {
|
|
569
|
-
return { ok: false, message: "Missing blocker value.", level: "error" };
|
|
570
|
-
}
|
|
571
|
-
state.blocker = normalized.value;
|
|
572
|
-
state.enabled = true;
|
|
573
|
-
return {
|
|
574
|
-
ok: true,
|
|
575
|
-
message: `Planning blocker set to ${shortLabel(state.blocker, "(none)")}.`,
|
|
576
|
-
level: "info",
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
case "set_confidence": {
|
|
580
|
-
const confidence = parsePlanningConfidence(params.confidence ?? "");
|
|
581
|
-
if (!confidence) {
|
|
582
|
-
return { ok: false, message: "Invalid confidence.", level: "error" };
|
|
583
|
-
}
|
|
584
|
-
state.confidence = confidence;
|
|
585
|
-
state.enabled = true;
|
|
586
|
-
return { ok: true, message: `Planning confidence set to ${confidence}.`, level: "info" };
|
|
587
|
-
}
|
|
588
|
-
case "update": {
|
|
589
|
-
const changed = [];
|
|
590
|
-
if (params.phase !== undefined) {
|
|
591
|
-
const phase = parsePlanningPhase(params.phase);
|
|
592
|
-
if (!phase) {
|
|
593
|
-
return { ok: false, message: "Invalid phase.", level: "error" };
|
|
594
|
-
}
|
|
595
|
-
state.phase = phase;
|
|
596
|
-
changed.push("phase");
|
|
597
|
-
}
|
|
598
|
-
if (params.root_issue_id !== undefined) {
|
|
599
|
-
if (typeof params.root_issue_id !== "string") {
|
|
600
|
-
return { ok: false, message: "root_issue_id must be a string.", level: "error" };
|
|
601
|
-
}
|
|
602
|
-
const normalized = normalizeMaybeClear(params.root_issue_id);
|
|
603
|
-
if (!normalized.ok) {
|
|
604
|
-
return { ok: false, message: "root_issue_id must not be empty.", level: "error" };
|
|
605
|
-
}
|
|
606
|
-
state.rootIssueId = normalized.value;
|
|
607
|
-
changed.push("root_issue_id");
|
|
608
|
-
}
|
|
609
|
-
if (params.waiting_on_user !== undefined) {
|
|
610
|
-
if (typeof params.waiting_on_user !== "boolean") {
|
|
611
|
-
return { ok: false, message: "waiting_on_user must be a boolean.", level: "error" };
|
|
612
|
-
}
|
|
613
|
-
state.waitingOnUser = params.waiting_on_user;
|
|
614
|
-
changed.push("waiting_on_user");
|
|
615
|
-
}
|
|
616
|
-
if (params.next_action !== undefined) {
|
|
617
|
-
if (typeof params.next_action !== "string") {
|
|
618
|
-
return { ok: false, message: "next_action must be a string.", level: "error" };
|
|
619
|
-
}
|
|
620
|
-
const normalized = normalizeMaybeClear(params.next_action);
|
|
621
|
-
if (!normalized.ok) {
|
|
622
|
-
return { ok: false, message: "next_action must not be empty.", level: "error" };
|
|
623
|
-
}
|
|
624
|
-
state.nextAction = normalized.value;
|
|
625
|
-
changed.push("next_action");
|
|
626
|
-
}
|
|
627
|
-
if (params.blocker !== undefined) {
|
|
628
|
-
if (typeof params.blocker !== "string") {
|
|
629
|
-
return { ok: false, message: "blocker must be a string.", level: "error" };
|
|
630
|
-
}
|
|
631
|
-
const normalized = normalizeMaybeClear(params.blocker);
|
|
632
|
-
if (!normalized.ok) {
|
|
633
|
-
return { ok: false, message: "blocker must not be empty.", level: "error" };
|
|
634
|
-
}
|
|
635
|
-
state.blocker = normalized.value;
|
|
636
|
-
changed.push("blocker");
|
|
637
|
-
}
|
|
638
|
-
if (params.confidence !== undefined) {
|
|
639
|
-
const confidence = parsePlanningConfidence(params.confidence);
|
|
640
|
-
if (!confidence) {
|
|
641
|
-
return { ok: false, message: "Invalid confidence.", level: "error" };
|
|
642
|
-
}
|
|
643
|
-
state.confidence = confidence;
|
|
644
|
-
changed.push("confidence");
|
|
645
|
-
}
|
|
646
|
-
if (params.steps !== undefined) {
|
|
647
|
-
const normalized = normalizeSteps(params.steps);
|
|
648
|
-
if (!normalized.ok) {
|
|
649
|
-
return { ok: false, message: normalized.error, level: "error" };
|
|
650
|
-
}
|
|
651
|
-
state.steps = normalized.labels.map((label) => ({ label, done: false }));
|
|
652
|
-
changed.push("steps");
|
|
653
|
-
}
|
|
654
|
-
if (params.step_updates !== undefined) {
|
|
655
|
-
const updated = applyStepUpdates(state, params.step_updates);
|
|
656
|
-
if (!updated.ok) {
|
|
657
|
-
return { ok: false, message: updated.error, level: "error" };
|
|
658
|
-
}
|
|
659
|
-
changed.push("step_updates");
|
|
660
|
-
}
|
|
661
|
-
if (changed.length === 0) {
|
|
662
|
-
return { ok: false, message: "No update fields provided.", level: "error" };
|
|
663
|
-
}
|
|
664
|
-
state.enabled = true;
|
|
665
|
-
return {
|
|
666
|
-
ok: true,
|
|
667
|
-
message: `Planning HUD updated (${changed.join(", ")}).`,
|
|
668
|
-
level: "info",
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
};
|
|
673
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
674
|
-
refresh(ctx);
|
|
675
|
-
syncHudModeStatus(ctx);
|
|
676
|
-
});
|
|
677
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
678
|
-
refresh(ctx);
|
|
679
|
-
syncHudModeStatus(ctx);
|
|
680
|
-
});
|
|
681
|
-
registerMuSubcommand(pi, {
|
|
682
|
-
subcommand: "plan",
|
|
683
|
-
summary: "Planning HUD: phase + checklist + communication state for planning workflows",
|
|
684
|
-
usage: "/mu plan on|off|toggle|status|reset|snapshot|phase|root|check|uncheck|toggle-step|add-step|remove-step|relabel-step|waiting|next|blocker|confidence",
|
|
685
|
-
handler: async (args, ctx) => {
|
|
686
|
-
const tokens = args
|
|
687
|
-
.trim()
|
|
688
|
-
.split(/\s+/)
|
|
689
|
-
.filter((token) => token.length > 0);
|
|
690
|
-
const command = tokens[0] ?? "status";
|
|
691
|
-
let params;
|
|
692
|
-
switch (command) {
|
|
693
|
-
case "on":
|
|
694
|
-
params = { action: "on" };
|
|
695
|
-
break;
|
|
696
|
-
case "off":
|
|
697
|
-
params = { action: "off" };
|
|
698
|
-
break;
|
|
699
|
-
case "toggle":
|
|
700
|
-
params = { action: "toggle" };
|
|
701
|
-
break;
|
|
702
|
-
case "status":
|
|
703
|
-
params = { action: "status" };
|
|
704
|
-
break;
|
|
705
|
-
case "snapshot":
|
|
706
|
-
params = { action: "snapshot", snapshot_format: tokens[1] };
|
|
707
|
-
break;
|
|
708
|
-
case "reset":
|
|
709
|
-
params = { action: "reset" };
|
|
710
|
-
break;
|
|
711
|
-
case "phase":
|
|
712
|
-
params = { action: "phase", phase: tokens[1] };
|
|
713
|
-
break;
|
|
714
|
-
case "root":
|
|
715
|
-
params = { action: "root", root_issue_id: tokens[1] };
|
|
716
|
-
break;
|
|
717
|
-
case "check":
|
|
718
|
-
params = { action: "check", step: Number.parseInt(tokens[1] ?? "", 10) };
|
|
719
|
-
break;
|
|
720
|
-
case "uncheck":
|
|
721
|
-
params = { action: "uncheck", step: Number.parseInt(tokens[1] ?? "", 10) };
|
|
722
|
-
break;
|
|
723
|
-
case "toggle-step":
|
|
724
|
-
params = { action: "toggle_step", step: Number.parseInt(tokens[1] ?? "", 10) };
|
|
725
|
-
break;
|
|
726
|
-
case "add-step":
|
|
727
|
-
params = { action: "add_step", label: tokens.slice(1).join(" ") };
|
|
728
|
-
break;
|
|
729
|
-
case "remove-step":
|
|
730
|
-
params = { action: "remove_step", step: Number.parseInt(tokens[1] ?? "", 10) };
|
|
731
|
-
break;
|
|
732
|
-
case "relabel-step":
|
|
733
|
-
params = {
|
|
734
|
-
action: "set_step_label",
|
|
735
|
-
step: Number.parseInt(tokens[1] ?? "", 10),
|
|
736
|
-
label: tokens.slice(2).join(" "),
|
|
737
|
-
};
|
|
738
|
-
break;
|
|
739
|
-
case "waiting": {
|
|
740
|
-
const parsed = parseOnOff(tokens[1]);
|
|
741
|
-
params = { action: "set_waiting", waiting_on_user: parsed ?? undefined };
|
|
742
|
-
break;
|
|
743
|
-
}
|
|
744
|
-
case "next":
|
|
745
|
-
params = { action: "set_next", next_action: tokens.slice(1).join(" ") };
|
|
746
|
-
break;
|
|
747
|
-
case "blocker":
|
|
748
|
-
params = { action: "set_blocker", blocker: tokens.slice(1).join(" ") };
|
|
749
|
-
break;
|
|
750
|
-
case "confidence":
|
|
751
|
-
params = { action: "set_confidence", confidence: tokens[1] };
|
|
752
|
-
break;
|
|
753
|
-
default:
|
|
754
|
-
notify(ctx, `Unknown plan command: ${command}`, "error");
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
const result = applyPlanningAction(params);
|
|
758
|
-
refresh(ctx);
|
|
759
|
-
if (!result.ok) {
|
|
760
|
-
notify(ctx, result.message, result.level ?? "error");
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
syncPlanningMode(ctx, params.action);
|
|
764
|
-
ctx.ui.notify(result.message, result.level ?? "info");
|
|
765
|
-
},
|
|
766
|
-
});
|
|
767
|
-
pi.registerTool({
|
|
768
|
-
name: "mu_planning_hud",
|
|
769
|
-
label: "mu planning HUD",
|
|
770
|
-
description: "Control or inspect planning HUD state (phase, root issue, checklist, and communication metadata).",
|
|
771
|
-
parameters: {
|
|
772
|
-
type: "object",
|
|
773
|
-
properties: {
|
|
774
|
-
action: {
|
|
775
|
-
type: "string",
|
|
776
|
-
enum: [
|
|
777
|
-
"status",
|
|
778
|
-
"on",
|
|
779
|
-
"off",
|
|
780
|
-
"toggle",
|
|
781
|
-
"reset",
|
|
782
|
-
"phase",
|
|
783
|
-
"root",
|
|
784
|
-
"check",
|
|
785
|
-
"uncheck",
|
|
786
|
-
"toggle_step",
|
|
787
|
-
"set_steps",
|
|
788
|
-
"add_step",
|
|
789
|
-
"remove_step",
|
|
790
|
-
"set_step_label",
|
|
791
|
-
"set_waiting",
|
|
792
|
-
"set_next",
|
|
793
|
-
"set_blocker",
|
|
794
|
-
"set_confidence",
|
|
795
|
-
"update",
|
|
796
|
-
"snapshot",
|
|
797
|
-
],
|
|
798
|
-
},
|
|
799
|
-
phase: {
|
|
800
|
-
type: "string",
|
|
801
|
-
enum: [
|
|
802
|
-
"investigating",
|
|
803
|
-
"drafting",
|
|
804
|
-
"reviewing",
|
|
805
|
-
"waiting_user",
|
|
806
|
-
"blocked",
|
|
807
|
-
"executing",
|
|
808
|
-
"approved",
|
|
809
|
-
"done",
|
|
810
|
-
],
|
|
811
|
-
},
|
|
812
|
-
root_issue_id: { type: "string" },
|
|
813
|
-
step: { type: "integer", minimum: 1 },
|
|
814
|
-
label: { type: "string" },
|
|
815
|
-
waiting_on_user: { type: "boolean" },
|
|
816
|
-
next_action: { type: "string" },
|
|
817
|
-
blocker: { type: "string" },
|
|
818
|
-
confidence: {
|
|
819
|
-
type: "string",
|
|
820
|
-
enum: ["low", "medium", "high"],
|
|
821
|
-
},
|
|
822
|
-
steps: {
|
|
823
|
-
type: "array",
|
|
824
|
-
items: { type: "string" },
|
|
825
|
-
},
|
|
826
|
-
step_updates: {
|
|
827
|
-
type: "array",
|
|
828
|
-
items: {
|
|
829
|
-
type: "object",
|
|
830
|
-
properties: {
|
|
831
|
-
index: { type: "integer", minimum: 1 },
|
|
832
|
-
done: { type: "boolean" },
|
|
833
|
-
label: { type: "string" },
|
|
834
|
-
},
|
|
835
|
-
required: ["index"],
|
|
836
|
-
additionalProperties: false,
|
|
837
|
-
},
|
|
838
|
-
},
|
|
839
|
-
snapshot_format: {
|
|
840
|
-
type: "string",
|
|
841
|
-
enum: ["compact", "multiline"],
|
|
842
|
-
},
|
|
843
|
-
},
|
|
844
|
-
required: ["action"],
|
|
845
|
-
additionalProperties: false,
|
|
846
|
-
},
|
|
847
|
-
execute: async (_toolCallId, paramsRaw, _signal, _onUpdate, ctx) => {
|
|
848
|
-
const params = paramsRaw;
|
|
849
|
-
const result = applyPlanningAction(params);
|
|
850
|
-
refresh(ctx);
|
|
851
|
-
if (!result.ok) {
|
|
852
|
-
return planningToolError(result.message);
|
|
853
|
-
}
|
|
854
|
-
syncPlanningMode(ctx, params.action);
|
|
855
|
-
return {
|
|
856
|
-
content: [{ type: "text", text: `${result.message}\n\n${planningStatusSummary(state)}` }],
|
|
857
|
-
details: {
|
|
858
|
-
ok: true,
|
|
859
|
-
action: params.action,
|
|
860
|
-
...planningDetails(state),
|
|
861
|
-
},
|
|
862
|
-
};
|
|
863
|
-
},
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
export default planningUiExtension;
|