@femtomc/mu-agent 26.2.107 → 26.2.108
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 -7
- package/assets/mu-tui-logo.png +0 -0
- package/dist/extensions/index.d.ts +0 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +0 -1
- package/dist/extensions/mu-operator.d.ts.map +1 -1
- package/dist/extensions/mu-operator.js +0 -2
- package/dist/extensions/mu-serve.d.ts.map +1 -1
- package/dist/extensions/mu-serve.js +0 -2
- package/dist/extensions/ui.d.ts.map +1 -1
- package/dist/extensions/ui.js +745 -25
- package/dist/operator.d.ts +7 -337
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +1 -62
- package/package.json +33 -33
- package/prompts/skills/core/SKILL.md +2 -2
- package/prompts/skills/core/memory/SKILL.md +2 -2
- package/prompts/skills/core/mu/SKILL.md +18 -7
- package/prompts/skills/subagents/SKILL.md +92 -8
- package/prompts/skills/subagents/control-flow/SKILL.md +118 -13
- package/prompts/skills/subagents/execution/SKILL.md +144 -31
- package/prompts/skills/subagents/model-routing/SKILL.md +146 -13
- package/prompts/skills/subagents/planning/SKILL.md +239 -90
- package/prompts/skills/writing/SKILL.md +2 -2
- package/dist/extensions/hud.d.ts +0 -4
- package/dist/extensions/hud.d.ts.map +0 -1
- package/dist/extensions/hud.js +0 -483
- package/prompts/skills/subagents/hud/SKILL.md +0 -205
package/dist/extensions/hud.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hud.d.ts","sourceRoot":"","sources":["../../src/extensions/hud.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AA0fpF,wBAAgB,YAAY,CAAC,EAAE,EAAE,YAAY,QAuE5C;AAED,eAAe,YAAY,CAAC"}
|
package/dist/extensions/hud.js
DELETED
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
import { applyHudStylePreset, hudStylePresetWarnings, HudDocSchema, normalizeHudDocs, serializeHudDocsTextFallback, } from "@femtomc/mu-core";
|
|
2
|
-
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
3
|
-
const HUD_DISPLAY_DOCS_MAX = 16;
|
|
4
|
-
const HUD_STORE_DOCS_MAX = 64;
|
|
5
|
-
const HUD_LINE_MAX = 120;
|
|
6
|
-
const HUD_CHIPS_MAX = 8;
|
|
7
|
-
const HUD_SECTION_ITEMS_MAX = 6;
|
|
8
|
-
const HUD_ACTIONS_MAX = 4;
|
|
9
|
-
const HUD_LABEL_MAX = 28;
|
|
10
|
-
const HUD_VALUE_MAX = 84;
|
|
11
|
-
function themeBold(theme, text) {
|
|
12
|
-
if (typeof theme.bold === "function") {
|
|
13
|
-
return theme.bold(text);
|
|
14
|
-
}
|
|
15
|
-
return text;
|
|
16
|
-
}
|
|
17
|
-
function themeItalic(theme, text) {
|
|
18
|
-
if (typeof theme.italic === "function") {
|
|
19
|
-
return theme.italic(text);
|
|
20
|
-
}
|
|
21
|
-
return text;
|
|
22
|
-
}
|
|
23
|
-
function themeInverse(theme, text) {
|
|
24
|
-
if (typeof theme.inverse === "function") {
|
|
25
|
-
return theme.inverse(text);
|
|
26
|
-
}
|
|
27
|
-
return text;
|
|
28
|
-
}
|
|
29
|
-
function applyHudTextStyle(theme, text, style, opts = {}) {
|
|
30
|
-
let out = text;
|
|
31
|
-
const weight = style?.weight ?? opts.defaultWeight;
|
|
32
|
-
const italic = style?.italic ?? opts.defaultItalic ?? false;
|
|
33
|
-
const code = style?.code ?? opts.defaultCode ?? false;
|
|
34
|
-
if (code) {
|
|
35
|
-
out = themeInverse(theme, out);
|
|
36
|
-
}
|
|
37
|
-
if (italic) {
|
|
38
|
-
out = themeItalic(theme, out);
|
|
39
|
-
}
|
|
40
|
-
if (weight === "strong") {
|
|
41
|
-
out = themeBold(theme, out);
|
|
42
|
-
}
|
|
43
|
-
return out;
|
|
44
|
-
}
|
|
45
|
-
function toneColor(tone) {
|
|
46
|
-
switch (tone) {
|
|
47
|
-
case "success":
|
|
48
|
-
return "success";
|
|
49
|
-
case "warning":
|
|
50
|
-
return "warning";
|
|
51
|
-
case "error":
|
|
52
|
-
return "error";
|
|
53
|
-
case "muted":
|
|
54
|
-
return "muted";
|
|
55
|
-
case "dim":
|
|
56
|
-
return "dim";
|
|
57
|
-
case "accent":
|
|
58
|
-
return "accent";
|
|
59
|
-
case "info":
|
|
60
|
-
default:
|
|
61
|
-
return "text";
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
function actionKindColor(kind) {
|
|
65
|
-
switch (kind) {
|
|
66
|
-
case "primary":
|
|
67
|
-
return "accent";
|
|
68
|
-
case "danger":
|
|
69
|
-
return "error";
|
|
70
|
-
case "secondary":
|
|
71
|
-
default:
|
|
72
|
-
return "dim";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function sectionFallbackTitle(kind) {
|
|
76
|
-
switch (kind) {
|
|
77
|
-
case "kv":
|
|
78
|
-
return "Details";
|
|
79
|
-
case "checklist":
|
|
80
|
-
return "Checklist";
|
|
81
|
-
case "activity":
|
|
82
|
-
return "Activity";
|
|
83
|
-
case "text":
|
|
84
|
-
return "Notes";
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function createDefaultState() {
|
|
88
|
-
return {
|
|
89
|
-
enabled: false,
|
|
90
|
-
docsById: new Map(),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function parseSnapshotFormat(raw) {
|
|
94
|
-
const value = (raw ?? "compact").trim().toLowerCase();
|
|
95
|
-
return value === "multiline" ? "multiline" : "compact";
|
|
96
|
-
}
|
|
97
|
-
function short(text, max = HUD_LINE_MAX) {
|
|
98
|
-
const normalized = text.replace(/\s+/g, " ").trim();
|
|
99
|
-
if (normalized.length <= max) {
|
|
100
|
-
return normalized;
|
|
101
|
-
}
|
|
102
|
-
if (max <= 1) {
|
|
103
|
-
return "…";
|
|
104
|
-
}
|
|
105
|
-
return `${normalized.slice(0, max - 1)}…`;
|
|
106
|
-
}
|
|
107
|
-
function sectionHeading(theme, section) {
|
|
108
|
-
const title = short(section.title ?? sectionFallbackTitle(section.kind), 48);
|
|
109
|
-
const styledTitle = applyHudTextStyle(theme, title, section.title_style, { defaultWeight: "strong" });
|
|
110
|
-
return [theme.fg("accent", styledTitle), theme.fg("dim", `(${section.kind})`)].join(" ");
|
|
111
|
-
}
|
|
112
|
-
function renderHudWidgetLines(theme, docs) {
|
|
113
|
-
const lines = [];
|
|
114
|
-
for (const [docIndex, doc] of docs.entries()) {
|
|
115
|
-
if (docIndex > 0) {
|
|
116
|
-
lines.push(theme.fg("muted", "─".repeat(26)));
|
|
117
|
-
}
|
|
118
|
-
const title = short(doc.title, 64);
|
|
119
|
-
const hudId = short(doc.hud_id, 32);
|
|
120
|
-
const styledTitle = applyHudTextStyle(theme, title, doc.title_style, { defaultWeight: "strong" });
|
|
121
|
-
lines.push(`${theme.fg("accent", styledTitle)} ${theme.fg("dim", `[${hudId}]`)}`);
|
|
122
|
-
if (doc.scope) {
|
|
123
|
-
lines.push(`${theme.fg("dim", "scope:")} ${theme.fg("muted", short(doc.scope, HUD_LINE_MAX - 7))}`);
|
|
124
|
-
}
|
|
125
|
-
if (doc.chips.length > 0) {
|
|
126
|
-
const visible = doc.chips.slice(0, HUD_CHIPS_MAX);
|
|
127
|
-
const chips = visible.map((chip) => {
|
|
128
|
-
const color = toneColor(chip.tone);
|
|
129
|
-
const label = short(chip.label, 28);
|
|
130
|
-
const chipLabel = applyHudTextStyle(theme, label, chip.style, {
|
|
131
|
-
defaultWeight: color === "muted" || color === "dim" ? "normal" : "strong",
|
|
132
|
-
});
|
|
133
|
-
return theme.fg(color, chipLabel);
|
|
134
|
-
});
|
|
135
|
-
const hiddenCount = doc.chips.length - visible.length;
|
|
136
|
-
if (hiddenCount > 0) {
|
|
137
|
-
chips.push(theme.fg("dim", `+${hiddenCount}`));
|
|
138
|
-
}
|
|
139
|
-
lines.push(chips.join(theme.fg("muted", " · ")));
|
|
140
|
-
}
|
|
141
|
-
for (const section of doc.sections) {
|
|
142
|
-
lines.push(sectionHeading(theme, section));
|
|
143
|
-
switch (section.kind) {
|
|
144
|
-
case "kv": {
|
|
145
|
-
const visible = section.items.slice(0, HUD_SECTION_ITEMS_MAX);
|
|
146
|
-
for (const item of visible) {
|
|
147
|
-
const keyLabel = short(item.label, HUD_LABEL_MAX);
|
|
148
|
-
const valueMax = Math.max(16, HUD_LINE_MAX - keyLabel.length - 8);
|
|
149
|
-
const valueLabel = short(item.value, Math.min(HUD_VALUE_MAX, valueMax));
|
|
150
|
-
const styledValue = applyHudTextStyle(theme, valueLabel, item.value_style);
|
|
151
|
-
lines.push(` ${theme.fg("muted", "-")} ${theme.fg("dim", `${keyLabel}:`)} ${theme.fg(toneColor(item.tone), styledValue)}`);
|
|
152
|
-
}
|
|
153
|
-
const hiddenCount = section.items.length - visible.length;
|
|
154
|
-
if (hiddenCount > 0) {
|
|
155
|
-
lines.push(theme.fg("dim", ` … (+${hiddenCount} more)`));
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
case "checklist": {
|
|
160
|
-
const visible = section.items.slice(0, HUD_SECTION_ITEMS_MAX);
|
|
161
|
-
for (const item of visible) {
|
|
162
|
-
const marker = item.done ? theme.fg("success", "[x]") : theme.fg("dim", "[ ]");
|
|
163
|
-
const textColor = item.done ? "dim" : "text";
|
|
164
|
-
const label = short(item.label, HUD_LINE_MAX - 8);
|
|
165
|
-
const styledLabel = applyHudTextStyle(theme, label, item.style);
|
|
166
|
-
lines.push(` ${marker} ${theme.fg(textColor, styledLabel)}`);
|
|
167
|
-
}
|
|
168
|
-
const hiddenCount = section.items.length - visible.length;
|
|
169
|
-
if (hiddenCount > 0) {
|
|
170
|
-
lines.push(theme.fg("dim", ` … (+${hiddenCount} more)`));
|
|
171
|
-
}
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
case "activity": {
|
|
175
|
-
const visible = section.lines.slice(0, HUD_SECTION_ITEMS_MAX);
|
|
176
|
-
for (const line of visible) {
|
|
177
|
-
lines.push(` ${theme.fg("muted", "-")} ${theme.fg("dim", short(line, HUD_LINE_MAX - 6))}`);
|
|
178
|
-
}
|
|
179
|
-
const hiddenCount = section.lines.length - visible.length;
|
|
180
|
-
if (hiddenCount > 0) {
|
|
181
|
-
lines.push(theme.fg("dim", ` … (+${hiddenCount} more)`));
|
|
182
|
-
}
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
case "text": {
|
|
186
|
-
const text = short(section.text, HUD_LINE_MAX - 4);
|
|
187
|
-
const styledText = applyHudTextStyle(theme, text, section.style);
|
|
188
|
-
lines.push(` ${theme.fg(toneColor(section.tone), styledText)}`);
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
if (doc.actions.length > 0) {
|
|
194
|
-
lines.push(theme.fg("accent", applyHudTextStyle(theme, "Actions", undefined, { defaultWeight: "strong" })));
|
|
195
|
-
const visible = doc.actions.slice(0, HUD_ACTIONS_MAX);
|
|
196
|
-
for (const action of visible) {
|
|
197
|
-
const color = actionKindColor(action.kind);
|
|
198
|
-
const label = short(action.label, HUD_LABEL_MAX);
|
|
199
|
-
const styledLabel = applyHudTextStyle(theme, label, action.style, { defaultWeight: "strong" });
|
|
200
|
-
const commandText = short(action.command_text, HUD_VALUE_MAX);
|
|
201
|
-
lines.push(` ${theme.fg(color, styledLabel)} ${theme.fg("dim", commandText)}`);
|
|
202
|
-
}
|
|
203
|
-
const hiddenCount = doc.actions.length - visible.length;
|
|
204
|
-
if (hiddenCount > 0) {
|
|
205
|
-
lines.push(theme.fg("dim", ` … (+${hiddenCount} more)`));
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const snapshotText = short(doc.snapshot_compact, HUD_LINE_MAX - 10);
|
|
209
|
-
const styledSnapshot = applyHudTextStyle(theme, snapshotText, doc.snapshot_style, { defaultItalic: true });
|
|
210
|
-
lines.push(`${theme.fg("dim", "snapshot:")} ${theme.fg("muted", styledSnapshot)}`);
|
|
211
|
-
}
|
|
212
|
-
return lines;
|
|
213
|
-
}
|
|
214
|
-
function activeDocs(state, maxDocs = HUD_DISPLAY_DOCS_MAX) {
|
|
215
|
-
return normalizeHudDocs([...state.docsById.values()], { maxDocs });
|
|
216
|
-
}
|
|
217
|
-
function statusSummary(state) {
|
|
218
|
-
const docs = activeDocs(state, HUD_STORE_DOCS_MAX);
|
|
219
|
-
const ids = docs.map((doc) => doc.hud_id).join(", ") || "(none)";
|
|
220
|
-
return [
|
|
221
|
-
`HUD: ${state.enabled ? "enabled" : "disabled"}`,
|
|
222
|
-
`docs: ${docs.length}`,
|
|
223
|
-
`ids: ${ids}`,
|
|
224
|
-
].join("\n");
|
|
225
|
-
}
|
|
226
|
-
function renderHud(ctx, state) {
|
|
227
|
-
if (!ctx.hasUI) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
const docs = activeDocs(state);
|
|
231
|
-
if (!state.enabled || docs.length === 0) {
|
|
232
|
-
ctx.ui.setStatus("mu-hud", undefined);
|
|
233
|
-
ctx.ui.setWidget("mu-hud", undefined);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
const idLabel = short(docs.map((doc) => doc.hud_id).join(", "), 64);
|
|
237
|
-
ctx.ui.setStatus("mu-hud", [
|
|
238
|
-
ctx.ui.theme.fg("dim", "hud"),
|
|
239
|
-
ctx.ui.theme.fg("muted", "·"),
|
|
240
|
-
ctx.ui.theme.fg("accent", `${docs.length}`),
|
|
241
|
-
ctx.ui.theme.fg("muted", "·"),
|
|
242
|
-
ctx.ui.theme.fg("dim", idLabel),
|
|
243
|
-
].join(" "));
|
|
244
|
-
const docsForRender = docs.map((doc) => applyHudStylePreset(doc) ?? doc);
|
|
245
|
-
const lines = renderHudWidgetLines(ctx.ui.theme, docsForRender);
|
|
246
|
-
ctx.ui.setWidget("mu-hud", lines.length > 0 ? lines : [ctx.ui.theme.fg("dim", "(hud enabled, no docs)")], { placement: "belowEditor" });
|
|
247
|
-
}
|
|
248
|
-
function parseHudDoc(input) {
|
|
249
|
-
const parsed = HudDocSchema.safeParse(input);
|
|
250
|
-
if (!parsed.success) {
|
|
251
|
-
return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid HUD doc." };
|
|
252
|
-
}
|
|
253
|
-
return { ok: true, doc: parsed.data };
|
|
254
|
-
}
|
|
255
|
-
function parseHudDocList(input) {
|
|
256
|
-
if (!Array.isArray(input)) {
|
|
257
|
-
return { ok: false, error: "docs must be an array." };
|
|
258
|
-
}
|
|
259
|
-
const docs = [];
|
|
260
|
-
for (let idx = 0; idx < input.length; idx += 1) {
|
|
261
|
-
const parsed = parseHudDoc(input[idx]);
|
|
262
|
-
if (!parsed.ok) {
|
|
263
|
-
return { ok: false, error: `docs[${idx}]: ${parsed.error}` };
|
|
264
|
-
}
|
|
265
|
-
docs.push(parsed.doc);
|
|
266
|
-
}
|
|
267
|
-
return { ok: true, docs: normalizeHudDocs(docs, { maxDocs: HUD_STORE_DOCS_MAX }) };
|
|
268
|
-
}
|
|
269
|
-
function presetWarningsExtraForDoc(doc) {
|
|
270
|
-
const warnings = hudStylePresetWarnings(doc);
|
|
271
|
-
if (warnings.length === 0) {
|
|
272
|
-
return {};
|
|
273
|
-
}
|
|
274
|
-
return { preset_warnings: warnings };
|
|
275
|
-
}
|
|
276
|
-
function presetWarningsExtraForDocs(docs) {
|
|
277
|
-
const byHudId = {};
|
|
278
|
-
for (const doc of docs) {
|
|
279
|
-
const warnings = hudStylePresetWarnings(doc);
|
|
280
|
-
if (warnings.length > 0) {
|
|
281
|
-
byHudId[doc.hud_id] = warnings;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return Object.keys(byHudId).length > 0 ? { preset_warnings: byHudId } : {};
|
|
285
|
-
}
|
|
286
|
-
function hudToolResult(opts) {
|
|
287
|
-
const docs = activeDocs(opts.state, HUD_STORE_DOCS_MAX);
|
|
288
|
-
return {
|
|
289
|
-
content: [{ type: "text", text: opts.message }],
|
|
290
|
-
hud_docs: docs,
|
|
291
|
-
details: {
|
|
292
|
-
ok: opts.ok,
|
|
293
|
-
action: opts.action,
|
|
294
|
-
enabled: opts.state.enabled,
|
|
295
|
-
doc_count: docs.length,
|
|
296
|
-
hud_ids: docs.map((doc) => doc.hud_id),
|
|
297
|
-
...(opts.ok ? {} : { error: opts.message }),
|
|
298
|
-
...(opts.extra ?? {}),
|
|
299
|
-
},
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function applyHudAction(params, state) {
|
|
303
|
-
switch (params.action) {
|
|
304
|
-
case "status": {
|
|
305
|
-
return {
|
|
306
|
-
ok: true,
|
|
307
|
-
action: "status",
|
|
308
|
-
message: statusSummary(state),
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
case "snapshot": {
|
|
312
|
-
const mode = parseSnapshotFormat(params.snapshot_format);
|
|
313
|
-
const docs = activeDocs(state, HUD_STORE_DOCS_MAX);
|
|
314
|
-
const message = docs.length
|
|
315
|
-
? serializeHudDocsTextFallback(docs, { mode, maxDocs: HUD_STORE_DOCS_MAX, maxSectionItems: 8, maxActions: 6 })
|
|
316
|
-
: "(no HUD docs)";
|
|
317
|
-
return {
|
|
318
|
-
ok: true,
|
|
319
|
-
action: "snapshot",
|
|
320
|
-
message,
|
|
321
|
-
extra: { snapshot_format: mode },
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
case "on":
|
|
325
|
-
state.enabled = true;
|
|
326
|
-
return { ok: true, action: "on", message: "HUD enabled." };
|
|
327
|
-
case "off":
|
|
328
|
-
state.enabled = false;
|
|
329
|
-
return { ok: true, action: "off", message: "HUD disabled." };
|
|
330
|
-
case "toggle":
|
|
331
|
-
state.enabled = !state.enabled;
|
|
332
|
-
return { ok: true, action: "toggle", message: `HUD ${state.enabled ? "enabled" : "disabled"}.` };
|
|
333
|
-
case "set":
|
|
334
|
-
case "update": {
|
|
335
|
-
const parsed = parseHudDoc(params.doc);
|
|
336
|
-
if (!parsed.ok) {
|
|
337
|
-
return { ok: false, action: params.action, message: parsed.error };
|
|
338
|
-
}
|
|
339
|
-
state.enabled = true;
|
|
340
|
-
state.docsById.set(parsed.doc.hud_id, parsed.doc);
|
|
341
|
-
return {
|
|
342
|
-
ok: true,
|
|
343
|
-
action: params.action,
|
|
344
|
-
message: `HUD doc set: ${parsed.doc.hud_id}`,
|
|
345
|
-
extra: { hud_id: parsed.doc.hud_id, ...presetWarningsExtraForDoc(parsed.doc) },
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
case "replace": {
|
|
349
|
-
const parsed = parseHudDocList(params.docs);
|
|
350
|
-
if (!parsed.ok) {
|
|
351
|
-
return { ok: false, action: "replace", message: parsed.error };
|
|
352
|
-
}
|
|
353
|
-
state.docsById.clear();
|
|
354
|
-
for (const doc of parsed.docs) {
|
|
355
|
-
state.docsById.set(doc.hud_id, doc);
|
|
356
|
-
}
|
|
357
|
-
state.enabled = true;
|
|
358
|
-
return {
|
|
359
|
-
ok: true,
|
|
360
|
-
action: "replace",
|
|
361
|
-
message: `HUD docs replaced (${parsed.docs.length}).`,
|
|
362
|
-
extra: presetWarningsExtraForDocs(parsed.docs),
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
case "remove": {
|
|
366
|
-
const hudId = (params.hud_id ?? "").trim();
|
|
367
|
-
if (!hudId) {
|
|
368
|
-
return { ok: false, action: "remove", message: "Missing hud_id." };
|
|
369
|
-
}
|
|
370
|
-
const removed = state.docsById.delete(hudId);
|
|
371
|
-
if (!removed) {
|
|
372
|
-
return { ok: false, action: "remove", message: `HUD doc not found: ${hudId}` };
|
|
373
|
-
}
|
|
374
|
-
return { ok: true, action: "remove", message: `HUD doc removed: ${hudId}` };
|
|
375
|
-
}
|
|
376
|
-
case "clear":
|
|
377
|
-
state.docsById.clear();
|
|
378
|
-
return { ok: true, action: "clear", message: "HUD docs cleared." };
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
function usageText() {
|
|
382
|
-
return [
|
|
383
|
-
"Usage:",
|
|
384
|
-
" /mu hud status|snapshot",
|
|
385
|
-
" /mu hud on|off|toggle",
|
|
386
|
-
" /mu hud clear",
|
|
387
|
-
" /mu hud remove <hud-id>",
|
|
388
|
-
"",
|
|
389
|
-
"For setting/updating docs, use the mu_hud tool with action=set|update|replace.",
|
|
390
|
-
].join("\n");
|
|
391
|
-
}
|
|
392
|
-
function parseCommandAction(args) {
|
|
393
|
-
const tokens = args
|
|
394
|
-
.trim()
|
|
395
|
-
.split(/\s+/)
|
|
396
|
-
.filter((token) => token.length > 0);
|
|
397
|
-
const command = tokens[0] ?? "status";
|
|
398
|
-
switch (command) {
|
|
399
|
-
case "status":
|
|
400
|
-
return { action: "status" };
|
|
401
|
-
case "snapshot":
|
|
402
|
-
return { action: "snapshot", snapshot_format: tokens[1] };
|
|
403
|
-
case "on":
|
|
404
|
-
return { action: "on" };
|
|
405
|
-
case "off":
|
|
406
|
-
return { action: "off" };
|
|
407
|
-
case "toggle":
|
|
408
|
-
return { action: "toggle" };
|
|
409
|
-
case "clear":
|
|
410
|
-
return { action: "clear" };
|
|
411
|
-
case "remove":
|
|
412
|
-
return { action: "remove", hud_id: tokens[1] };
|
|
413
|
-
default:
|
|
414
|
-
return null;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
export function hudExtension(pi) {
|
|
418
|
-
const state = createDefaultState();
|
|
419
|
-
const refresh = (ctx) => {
|
|
420
|
-
renderHud(ctx, state);
|
|
421
|
-
};
|
|
422
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
423
|
-
refresh(ctx);
|
|
424
|
-
});
|
|
425
|
-
pi.on("session_switch", async (_event, ctx) => {
|
|
426
|
-
refresh(ctx);
|
|
427
|
-
});
|
|
428
|
-
pi.on("session_shutdown", async (_event, ctx) => {
|
|
429
|
-
if (!ctx.hasUI) {
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
ctx.ui.setStatus("mu-hud", undefined);
|
|
433
|
-
ctx.ui.setWidget("mu-hud", undefined);
|
|
434
|
-
});
|
|
435
|
-
registerMuSubcommand(pi, {
|
|
436
|
-
subcommand: "hud",
|
|
437
|
-
summary: "HUD status and rendering controls",
|
|
438
|
-
usage: "/mu hud status|snapshot|on|off|toggle|clear|remove <hud-id>",
|
|
439
|
-
handler: async (args, ctx) => {
|
|
440
|
-
const parsed = parseCommandAction(args);
|
|
441
|
-
if (!parsed) {
|
|
442
|
-
ctx.ui.notify(usageText(), "error");
|
|
443
|
-
return;
|
|
444
|
-
}
|
|
445
|
-
const result = applyHudAction(parsed, state);
|
|
446
|
-
refresh(ctx);
|
|
447
|
-
ctx.ui.notify(result.message, result.ok ? "info" : "error");
|
|
448
|
-
},
|
|
449
|
-
});
|
|
450
|
-
pi.registerTool({
|
|
451
|
-
name: "mu_hud",
|
|
452
|
-
label: "mu HUD",
|
|
453
|
-
description: "Control or inspect HUD docs rendered in the TUI and shared across channel renderers.",
|
|
454
|
-
parameters: {
|
|
455
|
-
type: "object",
|
|
456
|
-
properties: {
|
|
457
|
-
action: {
|
|
458
|
-
type: "string",
|
|
459
|
-
enum: ["status", "snapshot", "on", "off", "toggle", "set", "update", "replace", "remove", "clear"],
|
|
460
|
-
},
|
|
461
|
-
doc: { type: "object", additionalProperties: true },
|
|
462
|
-
docs: { type: "array", items: { type: "object", additionalProperties: true } },
|
|
463
|
-
hud_id: { type: "string" },
|
|
464
|
-
snapshot_format: { type: "string", enum: ["compact", "multiline"] },
|
|
465
|
-
},
|
|
466
|
-
required: ["action"],
|
|
467
|
-
additionalProperties: false,
|
|
468
|
-
},
|
|
469
|
-
execute: async (_toolCallId, paramsRaw, _signal, _onUpdate, ctx) => {
|
|
470
|
-
const params = paramsRaw;
|
|
471
|
-
const result = applyHudAction(params, state);
|
|
472
|
-
refresh(ctx);
|
|
473
|
-
return hudToolResult({
|
|
474
|
-
state,
|
|
475
|
-
ok: result.ok,
|
|
476
|
-
action: result.action,
|
|
477
|
-
message: result.message,
|
|
478
|
-
extra: result.extra,
|
|
479
|
-
});
|
|
480
|
-
},
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
export default hudExtension;
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hud
|
|
3
|
-
description: "Defines HUD usage for `mu_hud` and `/mu hud`, including doc schema patterns, deterministic update rules, and rendering-safe conventions."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# hud
|
|
7
|
-
|
|
8
|
-
Use this skill whenever you need to publish, update, or inspect HUD state.
|
|
9
|
-
|
|
10
|
-
This skill is the canonical HUD reference for:
|
|
11
|
-
|
|
12
|
-
- `mu_hud` tool calls (structured HUD state)
|
|
13
|
-
- `/mu hud ...` command usage (inspection/control)
|
|
14
|
-
- `HudDoc` conventions that render well in TUI/Slack/Telegram
|
|
15
|
-
|
|
16
|
-
## Contents
|
|
17
|
-
|
|
18
|
-
- [Core contract](#core-contract)
|
|
19
|
-
- [HudDoc shape](#huddoc-shape)
|
|
20
|
-
- [Recommended turn loop](#recommended-turn-loop)
|
|
21
|
-
- [Ownership and teardown protocol](#ownership-and-teardown-protocol)
|
|
22
|
-
- [Planning, subagents, and model-routing profiles](#planning-subagents-and-model-routing-profiles)
|
|
23
|
-
- [Determinism and rendering limits](#determinism-and-rendering-limits)
|
|
24
|
-
- [Evaluation scenarios](#evaluation-scenarios)
|
|
25
|
-
|
|
26
|
-
## Core contract
|
|
27
|
-
|
|
28
|
-
### Tool (`mu_hud`)
|
|
29
|
-
|
|
30
|
-
Actions:
|
|
31
|
-
|
|
32
|
-
- `status`, `snapshot`
|
|
33
|
-
- `on`, `off`, `toggle`
|
|
34
|
-
- `set`, `update`, `replace`, `remove`, `clear`
|
|
35
|
-
|
|
36
|
-
Key params:
|
|
37
|
-
|
|
38
|
-
- `doc` (for `set`/`update`)
|
|
39
|
-
- `docs` (for `replace`)
|
|
40
|
-
- `hud_id` (for `remove`)
|
|
41
|
-
- `snapshot_format` (`compact` or `multiline`)
|
|
42
|
-
|
|
43
|
-
Notes:
|
|
44
|
-
|
|
45
|
-
- `set` and `update` are both upsert-style single-doc writes.
|
|
46
|
-
- `replace` is whole-inventory replacement.
|
|
47
|
-
- Tool results include normalized `hud_docs` for downstream transport/rendering.
|
|
48
|
-
- Advisory preset-shape warnings are surfaced in tool `details.preset_warnings` when `metadata.style_preset` and doc shape diverge (non-blocking).
|
|
49
|
-
|
|
50
|
-
### Command (`/mu hud ...`)
|
|
51
|
-
|
|
52
|
-
Supported subcommands:
|
|
53
|
-
|
|
54
|
-
- `/mu hud status`
|
|
55
|
-
- `/mu hud snapshot [compact|multiline]`
|
|
56
|
-
- `/mu hud on|off|toggle`
|
|
57
|
-
- `/mu hud clear`
|
|
58
|
-
- `/mu hud remove <hud-id>`
|
|
59
|
-
|
|
60
|
-
Use the tool (`mu_hud`) for structured doc writes.
|
|
61
|
-
|
|
62
|
-
## HudDoc shape
|
|
63
|
-
|
|
64
|
-
HUD docs are validated against `HudDoc` (`@femtomc/mu-core`).
|
|
65
|
-
|
|
66
|
-
Minimum practical fields:
|
|
67
|
-
|
|
68
|
-
- `v: 1`
|
|
69
|
-
- `hud_id: <non-empty>`
|
|
70
|
-
- `title: <non-empty>`
|
|
71
|
-
- `snapshot_compact: <non-empty>`
|
|
72
|
-
- `updated_at_ms: <int>`
|
|
73
|
-
|
|
74
|
-
Common optional fields:
|
|
75
|
-
|
|
76
|
-
- `scope` (for root/session/issue scoping)
|
|
77
|
-
- `title_style` / `snapshot_style` (`{weight?:"normal|strong", italic?:boolean, code?:boolean}`)
|
|
78
|
-
- `chips` (`[{key,label,tone?,style?}]`)
|
|
79
|
-
- `sections`:
|
|
80
|
-
- `kv` (key/value; supports `title_style` + item `value_style`)
|
|
81
|
-
- `checklist` (checkbox-style progress; supports `title_style` + item `style`)
|
|
82
|
-
- `activity` (recent lines; supports `title_style`)
|
|
83
|
-
- `text` (free text; supports `title_style` + `style`)
|
|
84
|
-
- `actions` (`[{id,label,command_text,kind?,style?}]`)
|
|
85
|
-
- `metadata` (machine-readable extras; optional `style_preset` convention: `planning|subagents`)
|
|
86
|
-
|
|
87
|
-
Example checklist doc:
|
|
88
|
-
|
|
89
|
-
```json
|
|
90
|
-
{
|
|
91
|
-
"action": "set",
|
|
92
|
-
"doc": {
|
|
93
|
-
"v": 1,
|
|
94
|
-
"hud_id": "planning",
|
|
95
|
-
"title": "Planning HUD",
|
|
96
|
-
"scope": "mu-root-123",
|
|
97
|
-
"chips": [
|
|
98
|
-
{ "key": "phase", "label": "phase:drafting", "tone": "accent", "style": { "weight": "strong" } },
|
|
99
|
-
{ "key": "steps", "label": "steps:2/5", "tone": "dim" }
|
|
100
|
-
],
|
|
101
|
-
"sections": [
|
|
102
|
-
{
|
|
103
|
-
"kind": "checklist",
|
|
104
|
-
"title": "Checklist",
|
|
105
|
-
"items": [
|
|
106
|
-
{ "id": "1", "label": "Investigate", "done": true },
|
|
107
|
-
{ "id": "2", "label": "Draft DAG", "done": true },
|
|
108
|
-
{ "id": "3", "label": "Review", "done": false }
|
|
109
|
-
]
|
|
110
|
-
}
|
|
111
|
-
],
|
|
112
|
-
"actions": [
|
|
113
|
-
{ "id": "snapshot", "label": "Snapshot", "command_text": "/mu hud snapshot", "kind": "secondary", "style": { "italic": true } }
|
|
114
|
-
],
|
|
115
|
-
"snapshot_compact": "HUD(plan) · phase=drafting · steps=2/5",
|
|
116
|
-
"updated_at_ms": 1771853115000,
|
|
117
|
-
"metadata": { "style_preset": "planning", "phase": "drafting" }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
## Recommended turn loop
|
|
123
|
-
|
|
124
|
-
1. Ensure HUD is on:
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
{"action":"on"}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
2. Upsert exactly the docs you own (`set`/`update`).
|
|
131
|
-
3. Emit compact snapshot for user-facing status:
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{"action":"snapshot","snapshot_format":"compact"}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
4. Keep response text and HUD state aligned (no contradictions).
|
|
138
|
-
|
|
139
|
-
## Ownership and teardown protocol
|
|
140
|
-
|
|
141
|
-
HUD-using skills should treat HUD state as owned, explicit, and non-optional when
|
|
142
|
-
that skill declares HUD-required behavior.
|
|
143
|
-
|
|
144
|
-
1. **Own explicit `hud_id` values**
|
|
145
|
-
- Each active skill owns one canonical doc id (for example `planning`,
|
|
146
|
-
`subagents`, `control-flow`, `model-routing`).
|
|
147
|
-
- Prefer `remove <hud_id>` over `clear` to avoid deleting other skills’ docs.
|
|
148
|
-
|
|
149
|
-
2. **Teardown is mandatory at skill end**
|
|
150
|
-
- When a HUD-owning skill completes, remove its doc(s).
|
|
151
|
-
- If no other HUD-owning skill is active next, turn HUD off.
|
|
152
|
-
|
|
153
|
-
3. **Teardown during HUD-to-HUD handoff**
|
|
154
|
-
- Remove current skill doc(s), keep HUD enabled, then let next skill set its doc.
|
|
155
|
-
- Never leave stale docs from prior skills during handoff.
|
|
156
|
-
|
|
157
|
-
Example teardown/handoff calls:
|
|
158
|
-
|
|
159
|
-
```json
|
|
160
|
-
{"action":"remove","hud_id":"planning"}
|
|
161
|
-
{"action":"set","doc":{"v":1,"hud_id":"subagents","title":"Subagents HUD","snapshot_compact":"HUD(subagents)","updated_at_ms":1771853115001}}
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Example full teardown (no next HUD skill):
|
|
165
|
-
|
|
166
|
-
```json
|
|
167
|
-
{"action":"remove","hud_id":"subagents"}
|
|
168
|
-
{"action":"off"}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Planning, subagents, and model-routing profiles
|
|
172
|
-
|
|
173
|
-
Use profile-specific `hud_id` values:
|
|
174
|
-
|
|
175
|
-
- planning profile: `hud_id: "planning"`
|
|
176
|
-
- subagents profile: `hud_id: "subagents"`
|
|
177
|
-
- model-routing profile: `hud_id: "model-routing"`
|
|
178
|
-
|
|
179
|
-
Treat these as conventions layered on top of this generic contract.
|
|
180
|
-
|
|
181
|
-
## Determinism and rendering limits
|
|
182
|
-
|
|
183
|
-
- Keep one canonical doc per `hud_id`.
|
|
184
|
-
- Keep `updated_at_ms` monotonic for each `hud_id`.
|
|
185
|
-
- Prefer a small doc set (usually 1–3 docs total) for channel readability.
|
|
186
|
-
- Keep command actions concise; long commands may degrade to text-only fallbacks on some channels.
|
|
187
|
-
- Assume channel renderers cap docs/actions/lines; put critical state in `snapshot_compact` and first section items.
|
|
188
|
-
|
|
189
|
-
If behavior is unclear, inspect implementation/tests before guessing:
|
|
190
|
-
|
|
191
|
-
- `packages/core/src/hud.ts`
|
|
192
|
-
- `packages/agent/src/extensions/hud.ts`
|
|
193
|
-
- `packages/server/src/control_plane.ts`
|
|
194
|
-
- `packages/agent/test/hud_tool.test.ts`
|
|
195
|
-
|
|
196
|
-
## Evaluation scenarios
|
|
197
|
-
|
|
198
|
-
1. **Planning review turn**
|
|
199
|
-
- Expected: `planning` doc updates phase/checklist/waiting state, then emits compact snapshot.
|
|
200
|
-
|
|
201
|
-
2. **Subagents orchestration pass**
|
|
202
|
-
- Expected: `subagents` doc updates queue/activity/chips after each bounded pass.
|
|
203
|
-
|
|
204
|
-
3. **HUD reset handoff**
|
|
205
|
-
- Expected: after phase completion, owned HUD docs are removed; HUD is turned off when no next HUD skill is active, or ownership cleanly hands off to the next skill with no stale docs.
|