@femtomc/mu-agent 26.2.106 → 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 +41 -21
- package/assets/mu-tui-logo.png +0 -0
- package/dist/extensions/index.d.ts +1 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +1 -1
- package/dist/extensions/mu-command-dispatcher.d.ts +0 -1
- package/dist/extensions/mu-command-dispatcher.d.ts.map +1 -1
- package/dist/extensions/mu-command-dispatcher.js +5 -42
- package/dist/extensions/mu-operator.js +2 -2
- package/dist/extensions/mu-serve.js +2 -2
- package/dist/extensions/ui.d.ts +4 -0
- package/dist/extensions/ui.d.ts.map +1 -0
- package/dist/extensions/ui.js +1055 -0
- package/dist/operator.d.ts +93 -255
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +41 -32
- package/package.json +33 -33
- package/prompts/skills/automation/SKILL.md +25 -0
- package/prompts/skills/{crons → automation/crons}/SKILL.md +2 -2
- package/prompts/skills/{heartbeats → automation/heartbeats}/SKILL.md +2 -2
- package/prompts/skills/core/SKILL.md +28 -0
- package/prompts/skills/{code-mode → core/code-mode}/SKILL.md +1 -1
- package/prompts/skills/{memory → core/memory}/SKILL.md +2 -2
- package/prompts/skills/{mu → core/mu}/SKILL.md +52 -9
- package/prompts/skills/{tmux → core/tmux}/SKILL.md +1 -1
- package/prompts/skills/messaging/SKILL.md +27 -0
- package/prompts/skills/subagents/SKILL.md +93 -243
- package/prompts/skills/{control-flow → subagents/control-flow}/SKILL.md +122 -17
- package/prompts/skills/subagents/execution/SKILL.md +428 -0
- package/prompts/skills/{model-routing → subagents/model-routing}/SKILL.md +179 -19
- package/prompts/skills/subagents/planning/SKILL.md +393 -0
- package/prompts/skills/{orchestration → subagents/protocol}/SKILL.md +7 -10
- package/prompts/skills/writing/SKILL.md +3 -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/hud/SKILL.md +0 -205
- package/prompts/skills/planning/SKILL.md +0 -244
- /package/prompts/skills/{setup-discord → messaging/setup-discord}/SKILL.md +0 -0
- /package/prompts/skills/{setup-neovim → messaging/setup-neovim}/SKILL.md +0 -0
- /package/prompts/skills/{setup-slack → messaging/setup-slack}/SKILL.md +0 -0
- /package/prompts/skills/{setup-telegram → messaging/setup-telegram}/SKILL.md +0 -0
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: "Defines the shared planning/execution
|
|
2
|
+
name: protocol
|
|
3
|
+
description: "Defines the shared planning/execution protocol for issue-DAG work. Use when creating, validating, or executing protocol-driven DAG work."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# protocol
|
|
7
7
|
|
|
8
8
|
Use this skill when work should flow through one shared protocol from planning to execution.
|
|
9
9
|
|
|
10
|
-
This skill supersedes the previous `hierarchical-work-protocol` skill name.
|
|
11
|
-
|
|
12
10
|
## Contents
|
|
13
11
|
|
|
14
12
|
- [Protocol identity](#protocol-identity)
|
|
@@ -26,7 +24,6 @@ This skill supersedes the previous `hierarchical-work-protocol` skill name.
|
|
|
26
24
|
- Protocol ID: `hierarchical-work.protocol/v1`
|
|
27
25
|
- Required issue tag on all protocol nodes: `proto:hierarchical-work-v1`
|
|
28
26
|
|
|
29
|
-
This system does **not** use backward-compatibility aliases for older protocol names.
|
|
30
27
|
Use only the protocol ID and tag above.
|
|
31
28
|
|
|
32
29
|
## Canonical tags and node roles
|
|
@@ -70,7 +67,7 @@ Node role rules:
|
|
|
70
67
|
Policy overlays are layered on top of this protocol and should not redefine
|
|
71
68
|
protocol primitives or `kind:*` semantics.
|
|
72
69
|
|
|
73
|
-
- Keep
|
|
70
|
+
- Keep protocol tags/kinds as source-of-truth for structure.
|
|
74
71
|
- Represent policy-specific behavior with overlay tags/metadata:
|
|
75
72
|
- loop/termination policy (for example review gates, retry rounds,
|
|
76
73
|
escalation thresholds): `flow:*`
|
|
@@ -192,7 +189,7 @@ mu issues dep <step-a> blocks <step-b>
|
|
|
192
189
|
|
|
193
190
|
## Planning handoff contract
|
|
194
191
|
|
|
195
|
-
Before handoff from planning to
|
|
192
|
+
Before handoff from planning to execution supervision:
|
|
196
193
|
|
|
197
194
|
1. Root exists and is tagged `node:root`, `kind:root`, `proto:hierarchical-work-v1`.
|
|
198
195
|
2. Every in-scope node carries `proto:hierarchical-work-v1`.
|
|
@@ -212,7 +209,7 @@ mu issues validate <root-id>
|
|
|
212
209
|
Worker/orchestrator passes always choose one primitive at a time:
|
|
213
210
|
|
|
214
211
|
1. `read_tree`
|
|
215
|
-
2. Choose one primitive (`ask` | `expand` | `complete` |
|
|
212
|
+
2. Choose one primitive (`ask` | `expand` | `complete` | protocol primitive)
|
|
216
213
|
3. Apply
|
|
217
214
|
4. Verify (`get`, `children`, `ready`, `validate`)
|
|
218
215
|
5. Log human-facing progress to forum as a titled narrative update (context -> milestone moved -> impact -> overall progress -> next), using the reusable status-voice style from `heartbeats`
|
|
@@ -245,7 +242,7 @@ mu forum post issue:"$goal_id" -m "<goal brief + acceptance criteria>" --author
|
|
|
245
242
|
|
|
246
243
|
1. **Planning-to-execution continuity**
|
|
247
244
|
- Setup: a freshly planned DAG.
|
|
248
|
-
- Expected: all nodes satisfy protocol tag/kind/context rules and can be consumed by
|
|
245
|
+
- Expected: all nodes satisfy protocol tag/kind/context rules and can be consumed by `execution` without re-shaping.
|
|
249
246
|
|
|
250
247
|
2. **Decomposition with synthesis fan-in**
|
|
251
248
|
- Setup: worker expands a complex node.
|
|
@@ -14,6 +14,7 @@ Use this skill when asked to write, edit, or review technical prose. This includ
|
|
|
14
14
|
- [Common patterns by document type](#common-patterns-by-document-type)
|
|
15
15
|
- [Editing and review workflow](#editing-and-review-workflow)
|
|
16
16
|
- [Evaluation scenarios](#evaluation-scenarios)
|
|
17
|
+
- [Quality bar](#quality-bar)
|
|
17
18
|
|
|
18
19
|
## Core contract
|
|
19
20
|
|
|
@@ -35,7 +36,7 @@ Use this skill when asked to write, edit, or review technical prose. This includ
|
|
|
35
36
|
- Inverted pyramid: conclusion, supporting details, background.
|
|
36
37
|
|
|
37
38
|
4. **Actionability**
|
|
38
|
-
- Imperative for procedures: "Run the
|
|
39
|
+
- Imperative for procedures: "Run the command" not "The command should be run."
|
|
39
40
|
- Explicit consequences: state what happens if the user does X.
|
|
40
41
|
- Anticipate failure modes in troubleshooting sections.
|
|
41
42
|
|
|
@@ -76,7 +77,7 @@ Use this skill when asked to write, edit, or review technical prose. This includ
|
|
|
76
77
|
1. **What changed** (imperative, present tense)
|
|
77
78
|
2. **Why it changed** (context, motivation)
|
|
78
79
|
3. **How to verify** (testing steps, expected outcomes)
|
|
79
|
-
4. **Breaking changes** (if any, with
|
|
80
|
+
4. **Breaking changes** (if any, with explicit adoption guidance)
|
|
80
81
|
|
|
81
82
|
Keep under 80 characters per line in the summary. Body wraps at 72 characters.
|
|
82
83
|
|
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;
|