@femtomc/mu-agent 26.2.85 → 26.2.87

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 CHANGED
@@ -48,6 +48,8 @@ Current stack:
48
48
 
49
49
  - `brandingExtension` — mu compact header/footer branding + default theme
50
50
  - `eventLogExtension` — event tail + watch widget
51
+ - `planningUiExtension` — planning phase/checklist HUD widget (`/mu plan ...`)
52
+ - `subagentsUiExtension` — tmux subagent monitor/spawner widget (`/mu subagents ...`)
51
53
 
52
54
  Default operator UI theme is `mu-gruvbox-dark`.
53
55
 
@@ -56,6 +58,8 @@ Default operator UI theme is `mu-gruvbox-dark`.
56
58
  - `/mu events [n]` / `/mu events tail [n]` — event log tail
57
59
  - `/mu events watch on|off` — toggle event watch widget
58
60
  - `/mu brand on|off|toggle` — enable/disable UI branding
61
+ - `/mu plan on|off|status|phase|root|check|uncheck|toggle-step|reset` — planning HUD
62
+ - `/mu subagents on|off|status|refresh|prefix|root|role|spawn` — tmux + issue queue monitor/spawner
59
63
  - `/mu help` — dispatcher catalog of registered `/mu` subcommands
60
64
 
61
65
  ## Tooling model (CLI-first)
@@ -2,6 +2,8 @@ export { brandingExtension } from "./branding.js";
2
2
  export { eventLogExtension } from "./event-log.js";
3
3
  export { muOperatorExtension } from "./mu-operator.js";
4
4
  export { muServeExtension } from "./mu-serve.js";
5
+ export { planningUiExtension } from "./planning-ui.js";
6
+ export { subagentsUiExtension } from "./subagents-ui.js";
5
7
  /**
6
8
  * Serve-mode extension — single facade that bundles all serve extensions.
7
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAQjD;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAA4C,CAAC;AAE7E;;GAEG;AACH,eAAO,MAAM,sBAAsB,UAA+C,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,EAAO,CAAC;AAC3D,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extensions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAQzD;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAA4C,CAAC;AAE7E;;GAEG;AACH,eAAO,MAAM,sBAAsB,UAA+C,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,MAAM,EAAO,CAAC;AAC3D,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAAO,CAAC"}
@@ -2,6 +2,8 @@ export { brandingExtension } from "./branding.js";
2
2
  export { eventLogExtension } from "./event-log.js";
3
3
  export { muOperatorExtension } from "./mu-operator.js";
4
4
  export { muServeExtension } from "./mu-serve.js";
5
+ export { planningUiExtension } from "./planning-ui.js";
6
+ export { subagentsUiExtension } from "./subagents-ui.js";
5
7
  const RUNTIME_EXTENSION = import.meta.url.endsWith(".ts") ? "ts" : "js";
6
8
  function resolveBundledExtensionPath(moduleBasename) {
7
9
  return new URL(`./${moduleBasename}.${RUNTIME_EXTENSION}`, import.meta.url).pathname;
@@ -1 +1 @@
1
- {"version":3,"file":"mu-operator.d.ts","sourceRoot":"","sources":["../../src/extensions/mu-operator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QAGnD;AAED,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"mu-operator.d.ts","sourceRoot":"","sources":["../../src/extensions/mu-operator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMlE,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QAKnD;AAED,eAAe,mBAAmB,CAAC"}
@@ -6,8 +6,12 @@
6
6
  */
7
7
  import { brandingExtension } from "./branding.js";
8
8
  import { eventLogExtension } from "./event-log.js";
9
+ import { planningUiExtension } from "./planning-ui.js";
10
+ import { subagentsUiExtension } from "./subagents-ui.js";
9
11
  export function muOperatorExtension(pi) {
10
12
  brandingExtension(pi);
11
13
  eventLogExtension(pi);
14
+ planningUiExtension(pi);
15
+ subagentsUiExtension(pi);
12
16
  }
13
17
  export default muOperatorExtension;
@@ -1 +1 @@
1
- {"version":3,"file":"mu-serve.d.ts","sourceRoot":"","sources":["../../src/extensions/mu-serve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,QAGhD;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"mu-serve.d.ts","sourceRoot":"","sources":["../../src/extensions/mu-serve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAMlE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,QAKhD;AAED,eAAe,gBAAgB,CAAC"}
@@ -6,8 +6,12 @@
6
6
  */
7
7
  import { brandingExtension } from "./branding.js";
8
8
  import { eventLogExtension } from "./event-log.js";
9
+ import { planningUiExtension } from "./planning-ui.js";
10
+ import { subagentsUiExtension } from "./subagents-ui.js";
9
11
  export function muServeExtension(pi) {
10
12
  brandingExtension(pi);
11
13
  eventLogExtension(pi);
14
+ planningUiExtension(pi);
15
+ subagentsUiExtension(pi);
12
16
  }
13
17
  export default muServeExtension;
@@ -0,0 +1,4 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ export declare function planningUiExtension(pi: ExtensionAPI): void;
3
+ export default planningUiExtension;
4
+ //# sourceMappingURL=planning-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planning-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/planning-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA4LpF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,QA8KnD;AAED,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,302 @@
1
+ import { registerMuSubcommand } from "./mu-command-dispatcher.js";
2
+ const DEFAULT_STEPS = [
3
+ "Investigate relevant code/docs/state",
4
+ "Create root issue + decomposed child issues",
5
+ "Present plan with IDs, ordering, risks",
6
+ "Refine with user feedback until approved",
7
+ ];
8
+ const BAR_CHARS = ["░", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
9
+ function phaseTone(phase) {
10
+ switch (phase) {
11
+ case "investigating":
12
+ return "dim";
13
+ case "drafting":
14
+ return "accent";
15
+ case "reviewing":
16
+ return "warning";
17
+ case "approved":
18
+ return "success";
19
+ }
20
+ }
21
+ function progressBar(done, total, width = 10) {
22
+ if (width <= 0 || total <= 0) {
23
+ return "";
24
+ }
25
+ const clampedDone = Math.max(0, Math.min(total, done));
26
+ const filled = (clampedDone / total) * width;
27
+ const full = Math.floor(filled);
28
+ const frac = filled - full;
29
+ const fracIdx = Math.round(frac * (BAR_CHARS.length - 1));
30
+ const empty = width - full - (fracIdx > 0 ? 1 : 0);
31
+ return (BAR_CHARS[BAR_CHARS.length - 1].repeat(full) +
32
+ (fracIdx > 0 ? BAR_CHARS[fracIdx] : "") +
33
+ BAR_CHARS[0].repeat(Math.max(0, empty)));
34
+ }
35
+ function createDefaultState() {
36
+ return {
37
+ enabled: false,
38
+ phase: "investigating",
39
+ rootIssueId: null,
40
+ steps: DEFAULT_STEPS.map((label) => ({ label, done: false })),
41
+ };
42
+ }
43
+ function summarizePhase(phase) {
44
+ switch (phase) {
45
+ case "investigating":
46
+ return "investigating";
47
+ case "drafting":
48
+ return "drafting";
49
+ case "reviewing":
50
+ return "reviewing";
51
+ case "approved":
52
+ return "approved";
53
+ }
54
+ }
55
+ function renderPlanningUi(ctx, state) {
56
+ if (!ctx.hasUI) {
57
+ return;
58
+ }
59
+ if (!state.enabled) {
60
+ ctx.ui.setStatus("mu-planning", undefined);
61
+ ctx.ui.setWidget("mu-planning", undefined);
62
+ return;
63
+ }
64
+ const done = state.steps.filter((step) => step.done).length;
65
+ const total = state.steps.length;
66
+ const phase = summarizePhase(state.phase);
67
+ const phaseColor = phaseTone(state.phase);
68
+ const rootLabel = state.rootIssueId ?? "(unset)";
69
+ const meter = progressBar(done, total, 10);
70
+ ctx.ui.setStatus("mu-planning", [
71
+ ctx.ui.theme.fg("dim", "plan"),
72
+ ctx.ui.theme.fg(phaseColor, phase),
73
+ ctx.ui.theme.fg("dim", `${done}/${total}`),
74
+ ctx.ui.theme.fg(phaseColor, meter),
75
+ ctx.ui.theme.fg("muted", `root:${rootLabel}`),
76
+ ].join(` ${ctx.ui.theme.fg("muted", "·")} `));
77
+ const lines = [
78
+ ctx.ui.theme.fg("accent", ctx.ui.theme.bold("Planning board")),
79
+ ` ${ctx.ui.theme.fg("muted", "phase:")} ${ctx.ui.theme.fg(phaseColor, phase)}`,
80
+ ` ${ctx.ui.theme.fg("muted", "progress:")} ${ctx.ui.theme.fg("dim", `${done}/${total}`)} ${ctx.ui.theme.fg(phaseColor, meter)}`,
81
+ ` ${ctx.ui.theme.fg("muted", "root:")} ${ctx.ui.theme.fg("dim", rootLabel)}`,
82
+ ` ${ctx.ui.theme.fg("dim", "────────────────────────────")}`,
83
+ ...state.steps.map((step, index) => {
84
+ const mark = step.done ? ctx.ui.theme.fg("success", "☑") : ctx.ui.theme.fg("muted", "☐");
85
+ const label = step.done ? ctx.ui.theme.fg("dim", step.label) : ctx.ui.theme.fg("text", step.label);
86
+ return `${mark} ${ctx.ui.theme.fg("muted", `${index + 1}.`)} ${label}`;
87
+ }),
88
+ ctx.ui.theme.fg("muted", " /mu plan status · /mu plan phase <...>"),
89
+ ];
90
+ ctx.ui.setWidget("mu-planning", lines, { placement: "belowEditor" });
91
+ }
92
+ function planningUsageText() {
93
+ return [
94
+ "Usage:",
95
+ " /mu plan on|off|toggle|status|reset",
96
+ " /mu plan phase <investigating|drafting|reviewing|approved>",
97
+ " /mu plan root <issue-id|clear>",
98
+ " /mu plan check <n> | /mu plan uncheck <n> | /mu plan toggle-step <n>",
99
+ ].join("\n");
100
+ }
101
+ function parsePlanningPhase(raw) {
102
+ const value = raw.trim().toLowerCase();
103
+ if (value === "investigating" || value === "drafting" || value === "reviewing" || value === "approved") {
104
+ return value;
105
+ }
106
+ return null;
107
+ }
108
+ function planningDetails(state) {
109
+ return {
110
+ enabled: state.enabled,
111
+ phase: state.phase,
112
+ root_issue_id: state.rootIssueId,
113
+ steps: state.steps.map((step, index) => ({
114
+ index: index + 1,
115
+ label: step.label,
116
+ done: step.done,
117
+ })),
118
+ };
119
+ }
120
+ function planningStatusSummary(state) {
121
+ const done = state.steps.filter((step) => step.done).length;
122
+ const root = state.rootIssueId ?? "(unset)";
123
+ return `Planning HUD: ${state.enabled ? "enabled" : "disabled"}\nphase: ${state.phase}\nroot: ${root}\nsteps: ${done}/${state.steps.length}`;
124
+ }
125
+ function planningToolError(message) {
126
+ return {
127
+ content: [{ type: "text", text: message }],
128
+ details: {
129
+ ok: false,
130
+ error: message,
131
+ },
132
+ };
133
+ }
134
+ export function planningUiExtension(pi) {
135
+ let state = createDefaultState();
136
+ const notify = (ctx, message, level = "info") => {
137
+ ctx.ui.notify(`${message}\n\n${planningUsageText()}`, level);
138
+ };
139
+ const refresh = (ctx) => {
140
+ renderPlanningUi(ctx, state);
141
+ };
142
+ const applyPlanningAction = (params) => {
143
+ switch (params.action) {
144
+ case "status":
145
+ return { ok: true, message: planningStatusSummary(state), level: "info" };
146
+ case "on":
147
+ state.enabled = true;
148
+ return { ok: true, message: "Planning HUD enabled.", level: "info" };
149
+ case "off":
150
+ state.enabled = false;
151
+ return { ok: true, message: "Planning HUD disabled.", level: "info" };
152
+ case "toggle":
153
+ state.enabled = !state.enabled;
154
+ return { ok: true, message: `Planning HUD ${state.enabled ? "enabled" : "disabled"}.`, level: "info" };
155
+ case "reset":
156
+ state = createDefaultState();
157
+ return { ok: true, message: "Planning HUD state reset.", level: "info" };
158
+ case "phase": {
159
+ const phase = parsePlanningPhase(params.phase ?? "");
160
+ if (!phase) {
161
+ return { ok: false, message: "Invalid phase.", level: "error" };
162
+ }
163
+ state.phase = phase;
164
+ state.enabled = true;
165
+ return { ok: true, message: `Planning phase set to ${phase}.`, level: "info" };
166
+ }
167
+ case "root": {
168
+ const root = params.root_issue_id?.trim();
169
+ if (!root) {
170
+ return { ok: false, message: "Missing root issue id.", level: "error" };
171
+ }
172
+ state.rootIssueId = root.toLowerCase() === "clear" ? null : root;
173
+ state.enabled = true;
174
+ return { ok: true, message: `Planning root set to ${state.rootIssueId ?? "(unset)"}.`, level: "info" };
175
+ }
176
+ case "check":
177
+ case "uncheck":
178
+ case "toggle_step": {
179
+ const step = params.step;
180
+ if (typeof step !== "number" || !Number.isFinite(step)) {
181
+ return { ok: false, message: "Step index must be a number.", level: "error" };
182
+ }
183
+ const parsed = Math.trunc(step);
184
+ if (parsed < 1 || parsed > state.steps.length) {
185
+ return { ok: false, message: `Step index out of range (1-${state.steps.length}).`, level: "error" };
186
+ }
187
+ const index = parsed - 1;
188
+ if (params.action === "check") {
189
+ state.steps[index].done = true;
190
+ }
191
+ else if (params.action === "uncheck") {
192
+ state.steps[index].done = false;
193
+ }
194
+ else {
195
+ state.steps[index].done = !state.steps[index].done;
196
+ }
197
+ state.enabled = true;
198
+ return { ok: true, message: `Planning step ${index + 1} updated.`, level: "info" };
199
+ }
200
+ }
201
+ };
202
+ pi.on("session_start", async (_event, ctx) => {
203
+ refresh(ctx);
204
+ });
205
+ pi.on("session_switch", async (_event, ctx) => {
206
+ refresh(ctx);
207
+ });
208
+ registerMuSubcommand(pi, {
209
+ subcommand: "plan",
210
+ summary: "Planning HUD: phase + checklist widget for planning workflows",
211
+ usage: "/mu plan on|off|toggle|status|phase|root|check|uncheck|toggle-step|reset",
212
+ handler: async (args, ctx) => {
213
+ const tokens = args
214
+ .trim()
215
+ .split(/\s+/)
216
+ .filter((token) => token.length > 0);
217
+ const command = tokens[0] ?? "status";
218
+ let params;
219
+ switch (command) {
220
+ case "on":
221
+ params = { action: "on" };
222
+ break;
223
+ case "off":
224
+ params = { action: "off" };
225
+ break;
226
+ case "toggle":
227
+ params = { action: "toggle" };
228
+ break;
229
+ case "status":
230
+ params = { action: "status" };
231
+ break;
232
+ case "reset":
233
+ params = { action: "reset" };
234
+ break;
235
+ case "phase":
236
+ params = { action: "phase", phase: tokens[1] };
237
+ break;
238
+ case "root":
239
+ params = { action: "root", root_issue_id: tokens[1] };
240
+ break;
241
+ case "check":
242
+ params = { action: "check", step: Number.parseInt(tokens[1] ?? "", 10) };
243
+ break;
244
+ case "uncheck":
245
+ params = { action: "uncheck", step: Number.parseInt(tokens[1] ?? "", 10) };
246
+ break;
247
+ case "toggle-step":
248
+ params = { action: "toggle_step", step: Number.parseInt(tokens[1] ?? "", 10) };
249
+ break;
250
+ default:
251
+ notify(ctx, `Unknown plan command: ${command}`, "error");
252
+ return;
253
+ }
254
+ const result = applyPlanningAction(params);
255
+ refresh(ctx);
256
+ if (!result.ok) {
257
+ notify(ctx, result.message, result.level ?? "error");
258
+ return;
259
+ }
260
+ ctx.ui.notify(result.message, result.level ?? "info");
261
+ },
262
+ });
263
+ pi.registerTool({
264
+ name: "mu_planning_hud",
265
+ label: "mu planning HUD",
266
+ description: "Control or inspect planning HUD state (phase, root issue, checklist progress).",
267
+ parameters: {
268
+ type: "object",
269
+ properties: {
270
+ action: {
271
+ type: "string",
272
+ enum: ["status", "on", "off", "toggle", "reset", "phase", "root", "check", "uncheck", "toggle_step"],
273
+ },
274
+ phase: {
275
+ type: "string",
276
+ enum: ["investigating", "drafting", "reviewing", "approved"],
277
+ },
278
+ root_issue_id: { type: "string" },
279
+ step: { type: "integer", minimum: 1 },
280
+ },
281
+ required: ["action"],
282
+ additionalProperties: false,
283
+ },
284
+ execute: async (_toolCallId, paramsRaw, _signal, _onUpdate, ctx) => {
285
+ const params = paramsRaw;
286
+ const result = applyPlanningAction(params);
287
+ refresh(ctx);
288
+ if (!result.ok) {
289
+ return planningToolError(result.message);
290
+ }
291
+ return {
292
+ content: [{ type: "text", text: `${result.message}\n\n${planningStatusSummary(state)}` }],
293
+ details: {
294
+ ok: true,
295
+ action: params.action,
296
+ ...planningDetails(state),
297
+ },
298
+ };
299
+ },
300
+ });
301
+ }
302
+ export default planningUiExtension;
@@ -0,0 +1,4 @@
1
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
+ export declare function subagentsUiExtension(pi: ExtensionAPI): void;
3
+ export default subagentsUiExtension;
4
+ //# sourceMappingURL=subagents-ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagents-ui.d.ts","sourceRoot":"","sources":["../../src/extensions/subagents-ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA+fpF,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,QAwWpD;AAED,eAAe,oBAAoB,CAAC"}