@femtomc/mu-agent 26.2.68 → 26.2.70
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 +20 -1
- package/dist/backend.d.ts +1 -0
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +11 -2
- package/dist/extensions/activities.d.ts +4 -1
- package/dist/extensions/activities.d.ts.map +1 -1
- package/dist/extensions/activities.js +127 -16
- package/dist/extensions/branding.d.ts +2 -3
- package/dist/extensions/branding.d.ts.map +1 -1
- package/dist/extensions/branding.js +73 -58
- package/dist/extensions/cron.d.ts +4 -1
- package/dist/extensions/cron.d.ts.map +1 -1
- package/dist/extensions/cron.js +74 -14
- package/dist/extensions/heartbeats.d.ts +4 -1
- package/dist/extensions/heartbeats.d.ts.map +1 -1
- package/dist/extensions/heartbeats.js +60 -17
- package/dist/extensions/index.d.ts +13 -3
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +13 -3
- package/dist/extensions/messaging-setup.d.ts +4 -1
- package/dist/extensions/messaging-setup.d.ts.map +1 -1
- package/dist/extensions/messaging-setup.js +47 -10
- package/dist/extensions/mu-full-tools.d.ts +10 -0
- package/dist/extensions/mu-full-tools.d.ts.map +1 -0
- package/dist/extensions/mu-full-tools.js +25 -0
- package/dist/extensions/mu-operator.d.ts.map +1 -1
- package/dist/extensions/mu-operator.js +2 -6
- package/dist/extensions/mu-query-tools.d.ts +10 -0
- package/dist/extensions/mu-query-tools.d.ts.map +1 -0
- package/dist/extensions/mu-query-tools.js +11 -0
- package/dist/extensions/operator-command.d.ts +5 -5
- package/dist/extensions/operator-command.d.ts.map +1 -1
- package/dist/extensions/operator-command.js +112 -21
- package/dist/extensions/orchestration-runs-readonly.d.ts.map +1 -1
- package/dist/extensions/orchestration-runs-readonly.js +180 -10
- package/dist/extensions/orchestration-runs.d.ts.map +1 -1
- package/dist/extensions/orchestration-runs.js +206 -14
- package/dist/extensions/server-tools.d.ts +10 -0
- package/dist/extensions/server-tools.d.ts.map +1 -1
- package/dist/extensions/server-tools.js +688 -290
- package/dist/extensions/shared.d.ts +11 -0
- package/dist/extensions/shared.d.ts.map +1 -1
- package/dist/extensions/shared.js +81 -0
- package/dist/operator.d.ts.map +1 -1
- package/dist/operator.js +1 -3
- package/dist/session_factory.d.ts.map +1 -1
- package/dist/session_factory.js +3 -1
- package/dist/ui_defaults.d.ts +4 -0
- package/dist/ui_defaults.d.ts.map +1 -0
- package/dist/ui_defaults.js +18 -0
- package/package.json +4 -3
- package/prompts/roles/operator.md +5 -0
- package/prompts/roles/orchestrator.md +1 -0
- package/prompts/roles/worker.md +10 -4
- package/themes/mu-gruvbox-dark.json +90 -0
package/README.md
CHANGED
|
@@ -45,13 +45,15 @@ not anonymous inline factories).
|
|
|
45
45
|
|
|
46
46
|
Current stack:
|
|
47
47
|
|
|
48
|
-
- `brandingExtension` — mu header/footer
|
|
48
|
+
- `brandingExtension` — mu compact header/footer branding + default theme
|
|
49
49
|
- `serverToolsExtension` — status + issues/forum/events/control-plane tools
|
|
50
50
|
- `eventLogExtension` — event tail + watch widget
|
|
51
51
|
- `messagingSetupExtension` — adapter diagnostics and setup guidance
|
|
52
52
|
|
|
53
53
|
`mu serve` sets `MU_SERVER_URL` automatically for these extensions.
|
|
54
54
|
|
|
55
|
+
Default operator UI theme is `mu-gruvbox-dark`.
|
|
56
|
+
|
|
55
57
|
## Slash commands (operator-facing)
|
|
56
58
|
|
|
57
59
|
- `/mu status` — concise server status
|
|
@@ -81,6 +83,23 @@ Current stack:
|
|
|
81
83
|
- `action`: `check | preflight | guide | plan | apply | verify`
|
|
82
84
|
- `adapter`: `slack | discord | telegram | gmail`
|
|
83
85
|
|
|
86
|
+
### Query contract (context-safe by default)
|
|
87
|
+
|
|
88
|
+
Read-heavy `mu_*` tools are designed to be summary-first, with explicit narrowing:
|
|
89
|
+
|
|
90
|
+
- `limit` controls result size (default is usually `20` for list/query/read actions).
|
|
91
|
+
- `contains` performs case-insensitive content filtering where relevant.
|
|
92
|
+
- `fields` (comma-separated paths) supports precise retrieval on targeted actions (`get`, `status`, `trace`, etc.).
|
|
93
|
+
|
|
94
|
+
Recommended flow:
|
|
95
|
+
|
|
96
|
+
1. Discover with bounded list/query (`limit` + filters).
|
|
97
|
+
2. Select a concrete ID.
|
|
98
|
+
3. Retrieve only required fields via `fields`.
|
|
99
|
+
|
|
100
|
+
Tool `details` may still include richer payloads for diagnostics, but `content` is
|
|
101
|
+
kept compact to reduce context pollution.
|
|
102
|
+
|
|
84
103
|
## Messaging setup notes
|
|
85
104
|
|
|
86
105
|
- Runtime setup state comes from `GET /api/config` and `.mu/config.json`.
|
package/dist/backend.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export type CreateMuResourceLoaderOpts = {
|
|
|
40
40
|
settingsManager?: SettingsManager;
|
|
41
41
|
additionalExtensionPaths?: string[];
|
|
42
42
|
additionalSkillPaths?: string[];
|
|
43
|
+
additionalThemePaths?: string[];
|
|
43
44
|
};
|
|
44
45
|
export declare function createMuResourceLoader(opts: CreateMuResourceLoaderOpts): DefaultResourceLoader;
|
|
45
46
|
//# sourceMappingURL=backend.d.ts.map
|
package/dist/backend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EACN,WAAW,EAOX,qBAAqB,EAErB,eAAe,EACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EACN,WAAW,EAOX,qBAAqB,EAErB,eAAe,EACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAI5C,MAAM,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC7B,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CA4BpD;AAGD;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,WAAW,EACxB,kBAAkB,CAAC,EAAE,MAAM,GACzB,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CA6BxB;AAED;;;;GAIG;AACH,qBAAa,UAAW,YAAW,aAAa;IACzC,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;CAmGhD;AAGD,MAAM,MAAM,0BAA0B,GAAG;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,0BAA0B,GAAG,qBAAqB,CA6B9F"}
|
package/dist/backend.js
CHANGED
|
@@ -3,6 +3,8 @@ import { mkdir, open } from "node:fs/promises";
|
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { getModels, getProviders } from "@mariozechner/pi-ai";
|
|
5
5
|
import { AuthStorage, createAgentSession, createBashTool, createEditTool, createReadTool, createWriteTool, DefaultResourceLoader, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
|
|
6
|
+
import { orchestratorToolExtensionPaths, workerToolExtensionPaths } from "./extensions/index.js";
|
|
7
|
+
import { MU_DEFAULT_THEME_NAME, MU_DEFAULT_THEME_PATH } from "./ui_defaults.js";
|
|
6
8
|
export function streamHasError(line) {
|
|
7
9
|
let event;
|
|
8
10
|
try {
|
|
@@ -75,11 +77,13 @@ export class SdkBackend {
|
|
|
75
77
|
const scope = opts.provider ? ` in provider "${opts.provider}"` : "";
|
|
76
78
|
throw new Error(`Model "${opts.model}" not found${scope} in pi-ai registry.`);
|
|
77
79
|
}
|
|
78
|
-
const settingsManager = SettingsManager.inMemory();
|
|
80
|
+
const settingsManager = SettingsManager.inMemory({ theme: MU_DEFAULT_THEME_NAME, quietStartup: true });
|
|
81
|
+
const roleExtensionPaths = opts.role === "worker" ? workerToolExtensionPaths : orchestratorToolExtensionPaths;
|
|
79
82
|
const resourceLoader = createMuResourceLoader({
|
|
80
83
|
cwd: opts.cwd,
|
|
81
84
|
systemPrompt: opts.systemPrompt,
|
|
82
85
|
settingsManager,
|
|
86
|
+
additionalExtensionPaths: roleExtensionPaths,
|
|
83
87
|
});
|
|
84
88
|
await resourceLoader.reload();
|
|
85
89
|
const tools = [
|
|
@@ -159,6 +163,10 @@ export function createMuResourceLoader(opts) {
|
|
|
159
163
|
for (const p of opts.additionalSkillPaths ?? []) {
|
|
160
164
|
skillPaths.add(p);
|
|
161
165
|
}
|
|
166
|
+
const themePaths = new Set([MU_DEFAULT_THEME_PATH]);
|
|
167
|
+
for (const p of opts.additionalThemePaths ?? []) {
|
|
168
|
+
themePaths.add(p);
|
|
169
|
+
}
|
|
162
170
|
// If a repo has a top-level `skills/` dir (like workshop/), load it.
|
|
163
171
|
const repoSkills = join(opts.cwd, "skills");
|
|
164
172
|
if (existsSync(repoSkills)) {
|
|
@@ -167,9 +175,10 @@ export function createMuResourceLoader(opts) {
|
|
|
167
175
|
return new DefaultResourceLoader({
|
|
168
176
|
cwd: opts.cwd,
|
|
169
177
|
agentDir: opts.agentDir,
|
|
170
|
-
settingsManager: opts.settingsManager ?? SettingsManager.inMemory(),
|
|
178
|
+
settingsManager: opts.settingsManager ?? SettingsManager.inMemory({ theme: MU_DEFAULT_THEME_NAME, quietStartup: true }),
|
|
171
179
|
additionalExtensionPaths: opts.additionalExtensionPaths,
|
|
172
180
|
additionalSkillPaths: [...skillPaths],
|
|
181
|
+
additionalThemePaths: [...themePaths],
|
|
173
182
|
systemPromptOverride: (_base) => opts.systemPrompt,
|
|
174
183
|
agentsFilesOverride: (base) => ({
|
|
175
184
|
agentsFiles: base.agentsFiles.filter((f) => basename(f.path) === "AGENTS.md"),
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
export
|
|
2
|
+
export type ActivitiesExtensionOpts = {
|
|
3
|
+
allowMutations?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function activitiesExtension(pi: ExtensionAPI, opts?: ActivitiesExtensionOpts): void;
|
|
3
6
|
export default activitiesExtension;
|
|
4
7
|
//# sourceMappingURL=activities.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activities.d.ts","sourceRoot":"","sources":["../../src/extensions/activities.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"activities.d.ts","sourceRoot":"","sources":["../../src/extensions/activities.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAsDlE,MAAM,MAAM,uBAAuB,GAAG;IACrC,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,GAAE,uBAA4B,QAqMvF;AAED,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,28 +1,65 @@
|
|
|
1
1
|
import { StringEnum } from "@mariozechner/pi-ai";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
|
-
import { clampInt, fetchMuJson, textResult, toJsonText } from "./shared.js";
|
|
3
|
+
import { asArray, asNumber, asRecord, asString, clampInt, fetchMuJson, parseFieldPaths, previewText, selectFields, textResult, toJsonText, } from "./shared.js";
|
|
4
4
|
function trimOrNull(value) {
|
|
5
5
|
if (value == null)
|
|
6
6
|
return null;
|
|
7
7
|
const trimmed = value.trim();
|
|
8
8
|
return trimmed.length > 0 ? trimmed : null;
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
function summarizeActivity(activity) {
|
|
11
|
+
return {
|
|
12
|
+
activity_id: asString(activity.activity_id),
|
|
13
|
+
title: previewText(activity.title, 140),
|
|
14
|
+
kind: asString(activity.kind),
|
|
15
|
+
status: asString(activity.status),
|
|
16
|
+
message_preview: previewText(activity.message, 180),
|
|
17
|
+
started_at_ms: asNumber(activity.started_at_ms),
|
|
18
|
+
updated_at_ms: asNumber(activity.updated_at_ms),
|
|
19
|
+
finished_at_ms: asNumber(activity.finished_at_ms),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function summarizeActivityEvent(event) {
|
|
23
|
+
return {
|
|
24
|
+
ts_ms: asNumber(event.ts_ms),
|
|
25
|
+
status: asString(event.status),
|
|
26
|
+
message_preview: previewText(event.message, 220),
|
|
27
|
+
reason: asString(event.reason),
|
|
28
|
+
source: asString(event.source),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function summarizeActivityMutation(payload) {
|
|
32
|
+
const activity = asRecord(payload.activity) ?? asRecord(payload.target_activity) ?? asRecord(payload.result);
|
|
33
|
+
return {
|
|
34
|
+
ok: payload.ok ?? null,
|
|
35
|
+
reason: asString(payload.reason),
|
|
36
|
+
activity: activity ? summarizeActivity(activity) : null,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function activitiesExtension(pi, opts = {}) {
|
|
40
|
+
const allowMutations = opts.allowMutations ?? true;
|
|
41
|
+
const activityActions = allowMutations
|
|
42
|
+
? ["list", "get", "start", "progress", "heartbeat", "complete", "fail", "cancel", "events"]
|
|
43
|
+
: ["list", "get", "events"];
|
|
11
44
|
const ActivitiesParams = Type.Object({
|
|
12
|
-
action: StringEnum(
|
|
45
|
+
action: StringEnum(activityActions),
|
|
13
46
|
activity_id: Type.Optional(Type.String({ description: "Activity ID" })),
|
|
14
47
|
title: Type.Optional(Type.String({ description: "Title for start" })),
|
|
15
48
|
kind: Type.Optional(Type.String({ description: "Activity kind for start/list filtering" })),
|
|
16
49
|
heartbeat_every_ms: Type.Optional(Type.Number({ description: "Heartbeat interval in ms for start" })),
|
|
17
50
|
status: Type.Optional(Type.String({ description: "Status filter for list" })),
|
|
51
|
+
contains: Type.Optional(Type.String({ description: "Case-insensitive search text for list/event messages" })),
|
|
18
52
|
message: Type.Optional(Type.String({ description: "Progress/final message" })),
|
|
19
53
|
reason: Type.Optional(Type.String({ description: "Heartbeat reason" })),
|
|
20
|
-
|
|
54
|
+
fields: Type.Optional(Type.String({ description: "Comma-separated fields for get/events selection" })),
|
|
55
|
+
limit: Type.Optional(Type.Number({ description: "Optional limit for list/events (default: 20)" })),
|
|
21
56
|
});
|
|
22
57
|
pi.registerTool({
|
|
23
58
|
name: "mu_activities",
|
|
24
59
|
label: "Activities",
|
|
25
|
-
description:
|
|
60
|
+
description: allowMutations
|
|
61
|
+
? "Manage generic long-running activities. Actions: list, get, start, progress, heartbeat, complete, fail, cancel, events. List/events are summary-first; use fields for precise retrieval."
|
|
62
|
+
: "Read-only activity inspection. Actions: list, get, events. Mutation actions are disabled in query-only mode.",
|
|
26
63
|
parameters: ActivitiesParams,
|
|
27
64
|
async execute(_toolCallId, params) {
|
|
28
65
|
switch (params.action) {
|
|
@@ -30,31 +67,72 @@ export function activitiesExtension(pi) {
|
|
|
30
67
|
const query = new URLSearchParams();
|
|
31
68
|
const status = trimOrNull(params.status);
|
|
32
69
|
const kind = trimOrNull(params.kind);
|
|
33
|
-
const
|
|
70
|
+
const contains = trimOrNull(params.contains);
|
|
71
|
+
const limit = clampInt(params.limit, 20, 1, 500);
|
|
34
72
|
if (status)
|
|
35
73
|
query.set("status", status);
|
|
36
74
|
if (kind)
|
|
37
75
|
query.set("kind", kind);
|
|
38
|
-
query.set("limit", String(limit));
|
|
76
|
+
query.set("limit", String(Math.max(limit, 100)));
|
|
39
77
|
const payload = await fetchMuJson(`/api/activities?${query.toString()}`);
|
|
40
|
-
|
|
78
|
+
const activities = asArray(payload.activities)
|
|
79
|
+
.map((activity) => asRecord(activity))
|
|
80
|
+
.filter((activity) => activity != null)
|
|
81
|
+
.filter((activity) => {
|
|
82
|
+
if (!contains)
|
|
83
|
+
return true;
|
|
84
|
+
const haystack = `${previewText(activity.title, 500)}\n${previewText(activity.message, 500)}`.toLowerCase();
|
|
85
|
+
return haystack.includes(contains.toLowerCase());
|
|
86
|
+
});
|
|
87
|
+
const sliced = activities.slice(0, limit);
|
|
88
|
+
return textResult(toJsonText({
|
|
89
|
+
total: activities.length,
|
|
90
|
+
returned: sliced.length,
|
|
91
|
+
truncated: sliced.length < activities.length,
|
|
92
|
+
activities: sliced.map((activity) => summarizeActivity(activity)),
|
|
93
|
+
}), { action: "list", status, kind, contains, limit, payload });
|
|
41
94
|
}
|
|
42
95
|
case "get": {
|
|
43
96
|
const activityId = trimOrNull(params.activity_id);
|
|
44
97
|
if (!activityId)
|
|
45
98
|
return textResult("get requires activity_id");
|
|
46
99
|
const payload = await fetchMuJson(`/api/activities/${encodeURIComponent(activityId)}`);
|
|
47
|
-
|
|
100
|
+
const fields = parseFieldPaths(trimOrNull(params.fields) ?? undefined);
|
|
101
|
+
const content = fields.length > 0
|
|
102
|
+
? { activity_id: activityId, selected: selectFields(payload, fields) }
|
|
103
|
+
: { activity: summarizeActivity(payload) };
|
|
104
|
+
return textResult(toJsonText(content), { action: "get", activityId, fields, payload });
|
|
48
105
|
}
|
|
49
106
|
case "events": {
|
|
50
107
|
const activityId = trimOrNull(params.activity_id);
|
|
51
108
|
if (!activityId)
|
|
52
109
|
return textResult("events requires activity_id");
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
110
|
+
const contains = trimOrNull(params.contains);
|
|
111
|
+
const limit = clampInt(params.limit, 20, 1, 500);
|
|
112
|
+
const payload = await fetchMuJson(`/api/activities/${encodeURIComponent(activityId)}/events?limit=${Math.max(limit, 100)}`);
|
|
113
|
+
const fields = parseFieldPaths(trimOrNull(params.fields) ?? undefined);
|
|
114
|
+
const records = asArray(payload.events)
|
|
115
|
+
.map((event) => asRecord(event))
|
|
116
|
+
.filter((event) => event != null)
|
|
117
|
+
.filter((event) => {
|
|
118
|
+
if (!contains)
|
|
119
|
+
return true;
|
|
120
|
+
const haystack = `${previewText(event.message, 500)}\n${previewText(event.reason, 500)}\n${previewText(event.status, 500)}`.toLowerCase();
|
|
121
|
+
return haystack.includes(contains.toLowerCase());
|
|
122
|
+
});
|
|
123
|
+
const sliced = records.slice(-limit);
|
|
124
|
+
const content = fields.length > 0
|
|
125
|
+
? { activity_id: activityId, selected: sliced.map((event) => selectFields(event, fields)) }
|
|
126
|
+
: { activity_id: activityId, events: sliced.map((event) => summarizeActivityEvent(event)) };
|
|
127
|
+
return textResult(toJsonText(content), { action: "events", activityId, contains, limit, fields, payload });
|
|
56
128
|
}
|
|
57
129
|
case "start": {
|
|
130
|
+
if (!allowMutations) {
|
|
131
|
+
return textResult("activity mutations are disabled in query-only mode.", {
|
|
132
|
+
blocked: true,
|
|
133
|
+
reason: "activities_query_only_mode",
|
|
134
|
+
});
|
|
135
|
+
}
|
|
58
136
|
const title = trimOrNull(params.title);
|
|
59
137
|
if (!title)
|
|
60
138
|
return textResult("start requires title");
|
|
@@ -70,9 +148,21 @@ export function activitiesExtension(pi) {
|
|
|
70
148
|
heartbeat_every_ms: heartbeatEveryMs,
|
|
71
149
|
},
|
|
72
150
|
});
|
|
73
|
-
return textResult(toJsonText(payload), {
|
|
151
|
+
return textResult(toJsonText(summarizeActivityMutation(payload)), {
|
|
152
|
+
action: "start",
|
|
153
|
+
title,
|
|
154
|
+
kind,
|
|
155
|
+
heartbeatEveryMs,
|
|
156
|
+
payload,
|
|
157
|
+
});
|
|
74
158
|
}
|
|
75
159
|
case "progress": {
|
|
160
|
+
if (!allowMutations) {
|
|
161
|
+
return textResult("activity mutations are disabled in query-only mode.", {
|
|
162
|
+
blocked: true,
|
|
163
|
+
reason: "activities_query_only_mode",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
76
166
|
const activityId = trimOrNull(params.activity_id);
|
|
77
167
|
if (!activityId)
|
|
78
168
|
return textResult("progress requires activity_id");
|
|
@@ -84,9 +174,20 @@ export function activitiesExtension(pi) {
|
|
|
84
174
|
message,
|
|
85
175
|
},
|
|
86
176
|
});
|
|
87
|
-
return textResult(toJsonText(payload), {
|
|
177
|
+
return textResult(toJsonText(summarizeActivityMutation(payload)), {
|
|
178
|
+
action: "progress",
|
|
179
|
+
activityId,
|
|
180
|
+
message,
|
|
181
|
+
payload,
|
|
182
|
+
});
|
|
88
183
|
}
|
|
89
184
|
case "heartbeat": {
|
|
185
|
+
if (!allowMutations) {
|
|
186
|
+
return textResult("activity mutations are disabled in query-only mode.", {
|
|
187
|
+
blocked: true,
|
|
188
|
+
reason: "activities_query_only_mode",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
90
191
|
const activityId = trimOrNull(params.activity_id);
|
|
91
192
|
if (!activityId)
|
|
92
193
|
return textResult("heartbeat requires activity_id");
|
|
@@ -98,7 +199,12 @@ export function activitiesExtension(pi) {
|
|
|
98
199
|
reason,
|
|
99
200
|
},
|
|
100
201
|
});
|
|
101
|
-
return textResult(toJsonText(payload), {
|
|
202
|
+
return textResult(toJsonText(summarizeActivityMutation(payload)), {
|
|
203
|
+
action: "heartbeat",
|
|
204
|
+
activityId,
|
|
205
|
+
reason,
|
|
206
|
+
payload,
|
|
207
|
+
});
|
|
102
208
|
}
|
|
103
209
|
case "complete":
|
|
104
210
|
case "fail":
|
|
@@ -114,7 +220,12 @@ export function activitiesExtension(pi) {
|
|
|
114
220
|
message,
|
|
115
221
|
},
|
|
116
222
|
});
|
|
117
|
-
return textResult(toJsonText(payload), {
|
|
223
|
+
return textResult(toJsonText(summarizeActivityMutation(payload)), {
|
|
224
|
+
action: params.action,
|
|
225
|
+
activityId,
|
|
226
|
+
message,
|
|
227
|
+
payload,
|
|
228
|
+
});
|
|
118
229
|
}
|
|
119
230
|
default:
|
|
120
231
|
return textResult(`unknown action: ${params.action}`);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* mu-branding — Custom serve-mode TUI chrome for mu.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* - Quick command widget above the editor
|
|
4
|
+
* Defaults to a minimal, information-dense layout:
|
|
5
|
+
* - Compact header + footer
|
|
7
6
|
* - Terminal title and working message branding
|
|
8
7
|
* - Lightweight periodic status refresh (open/ready/control-plane)
|
|
9
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/extensions/branding.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/extensions/branding.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAwEpF,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,YAAY,QAmOjD;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* mu-branding — Custom serve-mode TUI chrome for mu.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* - Quick command widget above the editor
|
|
4
|
+
* Defaults to a minimal, information-dense layout:
|
|
5
|
+
* - Compact header + footer
|
|
7
6
|
* - Terminal title and working message branding
|
|
8
7
|
* - Lightweight periodic status refresh (open/ready/control-plane)
|
|
9
8
|
*/
|
|
10
9
|
import { basename } from "node:path";
|
|
10
|
+
import { MU_DEFAULT_THEME_NAME, MU_VERSION } from "../ui_defaults.js";
|
|
11
11
|
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
12
12
|
import { fetchMuStatus, muServerUrl } from "./shared.js";
|
|
13
13
|
const EMPTY_SNAPSHOT = {
|
|
@@ -17,7 +17,7 @@ const EMPTY_SNAPSHOT = {
|
|
|
17
17
|
adapters: [],
|
|
18
18
|
error: null,
|
|
19
19
|
};
|
|
20
|
-
const ANSI_RE =
|
|
20
|
+
const ANSI_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
21
21
|
function visibleWidth(text) {
|
|
22
22
|
return text.replace(ANSI_RE, "").length;
|
|
23
23
|
}
|
|
@@ -31,61 +31,75 @@ function truncateToWidth(text, width) {
|
|
|
31
31
|
return plain.slice(0, 1);
|
|
32
32
|
return `${plain.slice(0, width - 1)}…`;
|
|
33
33
|
}
|
|
34
|
+
function centerLine(text, width) {
|
|
35
|
+
const vw = visibleWidth(text);
|
|
36
|
+
if (vw >= width)
|
|
37
|
+
return truncateToWidth(text, width);
|
|
38
|
+
const pad = Math.floor((width - vw) / 2);
|
|
39
|
+
return " ".repeat(pad) + text;
|
|
40
|
+
}
|
|
34
41
|
function routesFromStatus(adapters, routes) {
|
|
35
42
|
if (routes && routes.length > 0)
|
|
36
43
|
return routes;
|
|
37
44
|
return adapters.map((name) => ({ name, route: `/webhooks/${name}` }));
|
|
38
45
|
}
|
|
39
|
-
function
|
|
46
|
+
function shortModelLabel(ctx) {
|
|
40
47
|
if (!ctx.model)
|
|
41
|
-
return "
|
|
42
|
-
return
|
|
48
|
+
return "?";
|
|
49
|
+
return ctx.model.id;
|
|
50
|
+
}
|
|
51
|
+
function shortModelLabelFromEvent(model) {
|
|
52
|
+
return model.id;
|
|
43
53
|
}
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
const BAR_CHARS = ["░", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
|
|
55
|
+
function contextBar(percent, barWidth) {
|
|
56
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
57
|
+
const filled = (clamped / 100) * barWidth;
|
|
58
|
+
const full = Math.floor(filled);
|
|
59
|
+
const frac = filled - full;
|
|
60
|
+
const fracIdx = Math.round(frac * (BAR_CHARS.length - 1));
|
|
61
|
+
const empty = barWidth - full - (fracIdx > 0 ? 1 : 0);
|
|
62
|
+
return (BAR_CHARS[BAR_CHARS.length - 1].repeat(full) +
|
|
63
|
+
(fracIdx > 0 ? BAR_CHARS[fracIdx] : "") +
|
|
64
|
+
BAR_CHARS[0].repeat(Math.max(0, empty)));
|
|
46
65
|
}
|
|
47
66
|
export function brandingExtension(pi) {
|
|
48
67
|
let enabled = true;
|
|
49
68
|
let repoName = "mu";
|
|
50
|
-
let
|
|
69
|
+
let currentModelLabel = "?";
|
|
51
70
|
let snapshot = { ...EMPTY_SNAPSHOT };
|
|
52
71
|
let activeCtx = null;
|
|
53
72
|
let pollTimer = null;
|
|
54
73
|
let footerRequestRender = null;
|
|
55
|
-
function
|
|
74
|
+
function applyDefaultTheme(ctx) {
|
|
56
75
|
if (!ctx.hasUI)
|
|
57
76
|
return;
|
|
58
|
-
ctx.ui.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
: snapshot.controlPlaneActive
|
|
63
|
-
? theme.fg("success", `cp ${snapshot.adapters.join(",") || "on"}`)
|
|
64
|
-
: theme.fg("muted", "cp off");
|
|
65
|
-
const line1 = `${theme.fg("accent", "μ")}${theme.fg("dim", " quick actions")}: ${theme.fg("muted", "/mu status /mu control /mu setup /mu events")}`;
|
|
66
|
-
const line2 = `${theme.fg("dim", `open ${snapshot.openCount} · ready ${snapshot.readyCount}`)} · ${cpState}`;
|
|
67
|
-
return [truncateToWidth(line1, width), truncateToWidth(line2, width)];
|
|
68
|
-
},
|
|
69
|
-
invalidate() { },
|
|
70
|
-
}));
|
|
77
|
+
const result = ctx.ui.setTheme(MU_DEFAULT_THEME_NAME);
|
|
78
|
+
if (!result.success) {
|
|
79
|
+
ctx.ui.notify(`failed to apply ${MU_DEFAULT_THEME_NAME}: ${result.error ?? "unknown error"}`, "warning");
|
|
80
|
+
}
|
|
71
81
|
}
|
|
72
82
|
function applyChrome(ctx) {
|
|
73
83
|
if (!ctx.hasUI || !enabled)
|
|
74
84
|
return;
|
|
75
|
-
ctx.ui.setTitle(`mu
|
|
76
|
-
ctx.ui.setWorkingMessage("
|
|
85
|
+
ctx.ui.setTitle(`mu · ${repoName}`);
|
|
86
|
+
ctx.ui.setWorkingMessage("working…");
|
|
87
|
+
ctx.ui.setWidget("mu-quick-actions", undefined);
|
|
77
88
|
ctx.ui.setHeader((_tui, theme) => ({
|
|
78
89
|
render(width) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"",
|
|
88
|
-
|
|
90
|
+
const cpPart = snapshot.error
|
|
91
|
+
? ""
|
|
92
|
+
: snapshot.controlPlaneActive
|
|
93
|
+
? ` ${theme.fg("muted", "·")} ${theme.fg("success", `cp:${snapshot.adapters.join(",") || "on"}`)}`
|
|
94
|
+
: "";
|
|
95
|
+
const line = [
|
|
96
|
+
theme.fg("accent", theme.bold("μ")),
|
|
97
|
+
theme.fg("muted", "·"),
|
|
98
|
+
theme.fg("dim", `v${MU_VERSION}`),
|
|
99
|
+
theme.fg("muted", "·"),
|
|
100
|
+
theme.fg("dim", repoName),
|
|
101
|
+
].join(" ") + cpPart;
|
|
102
|
+
return [centerLine(line, width)];
|
|
89
103
|
},
|
|
90
104
|
invalidate() { },
|
|
91
105
|
}));
|
|
@@ -102,27 +116,24 @@ export function brandingExtension(pi) {
|
|
|
102
116
|
},
|
|
103
117
|
invalidate() { },
|
|
104
118
|
render(width) {
|
|
105
|
-
const
|
|
106
|
-
? theme.fg("warning", "cp:unavailable")
|
|
107
|
-
: snapshot.controlPlaneActive
|
|
108
|
-
? theme.fg("success", `cp:${snapshot.adapters.join(",") || "on"}`)
|
|
109
|
-
: theme.fg("muted", "cp:off");
|
|
110
|
-
const left = [
|
|
111
|
-
theme.fg("accent", "μ"),
|
|
112
|
-
theme.fg("dim", repoName),
|
|
113
|
-
theme.fg("muted", "|"),
|
|
114
|
-
theme.fg("dim", `open ${snapshot.openCount} ready ${snapshot.readyCount}`),
|
|
115
|
-
theme.fg("muted", "|"),
|
|
116
|
-
cpLabel,
|
|
117
|
-
].join(" ");
|
|
119
|
+
const parts = [theme.fg("dim", currentModelLabel)];
|
|
118
120
|
const branch = footerData.getGitBranch();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if (branch) {
|
|
122
|
+
parts.push(theme.fg("muted", "·"), theme.fg("dim", branch));
|
|
123
|
+
}
|
|
124
|
+
const usage = activeCtx?.getContextUsage();
|
|
125
|
+
if (usage && usage.percent != null) {
|
|
126
|
+
const pct = Math.round(usage.percent);
|
|
127
|
+
const barColor = pct >= 80 ? "warning" : pct >= 60 ? "muted" : "dim";
|
|
128
|
+
parts.push(theme.fg("muted", "·"), theme.fg(barColor, `ctx ${pct}%`), theme.fg(barColor, contextBar(pct, 10)));
|
|
129
|
+
}
|
|
130
|
+
if (snapshot.openCount > 0 || snapshot.readyCount > 0) {
|
|
131
|
+
parts.push(theme.fg("muted", "·"), theme.fg("dim", `open ${snapshot.openCount} ready ${snapshot.readyCount}`));
|
|
132
|
+
}
|
|
133
|
+
return [centerLine(parts.join(" "), width)];
|
|
122
134
|
},
|
|
123
135
|
};
|
|
124
136
|
});
|
|
125
|
-
refreshWidget(ctx);
|
|
126
137
|
}
|
|
127
138
|
function clearChrome(ctx) {
|
|
128
139
|
if (!ctx.hasUI)
|
|
@@ -142,7 +153,6 @@ export function brandingExtension(pi) {
|
|
|
142
153
|
error: "MU_SERVER_URL not set",
|
|
143
154
|
};
|
|
144
155
|
ctx.ui.setStatus("mu-overview", ctx.ui.theme.fg("warning", "μ server unavailable"));
|
|
145
|
-
refreshWidget(ctx);
|
|
146
156
|
footerRequestRender?.();
|
|
147
157
|
return;
|
|
148
158
|
}
|
|
@@ -169,7 +179,6 @@ export function brandingExtension(pi) {
|
|
|
169
179
|
};
|
|
170
180
|
ctx.ui.setStatus("mu-overview", ctx.ui.theme.fg("warning", "μ status refresh failed"));
|
|
171
181
|
}
|
|
172
|
-
refreshWidget(ctx);
|
|
173
182
|
footerRequestRender?.();
|
|
174
183
|
}
|
|
175
184
|
function ensurePolling() {
|
|
@@ -190,10 +199,11 @@ export function brandingExtension(pi) {
|
|
|
190
199
|
async function initialize(ctx) {
|
|
191
200
|
activeCtx = ctx;
|
|
192
201
|
repoName = basename(ctx.cwd);
|
|
193
|
-
|
|
202
|
+
currentModelLabel = shortModelLabel(ctx);
|
|
194
203
|
if (!ctx.hasUI)
|
|
195
204
|
return;
|
|
196
205
|
if (enabled) {
|
|
206
|
+
applyDefaultTheme(ctx);
|
|
197
207
|
applyChrome(ctx);
|
|
198
208
|
await refreshStatus(ctx);
|
|
199
209
|
ensurePolling();
|
|
@@ -210,10 +220,14 @@ export function brandingExtension(pi) {
|
|
|
210
220
|
await initialize(ctx);
|
|
211
221
|
});
|
|
212
222
|
pi.on("model_select", async (event, ctx) => {
|
|
213
|
-
|
|
223
|
+
currentModelLabel = shortModelLabelFromEvent(event.model);
|
|
224
|
+
if (!ctx.hasUI || !enabled)
|
|
225
|
+
return;
|
|
226
|
+
footerRequestRender?.();
|
|
227
|
+
});
|
|
228
|
+
pi.on("turn_end", async (_event, ctx) => {
|
|
214
229
|
if (!ctx.hasUI || !enabled)
|
|
215
230
|
return;
|
|
216
|
-
ctx.ui.setStatus("mu-model", ctx.ui.theme.fg("dim", currentModel));
|
|
217
231
|
footerRequestRender?.();
|
|
218
232
|
});
|
|
219
233
|
pi.on("session_shutdown", async () => {
|
|
@@ -241,6 +255,7 @@ export function brandingExtension(pi) {
|
|
|
241
255
|
return;
|
|
242
256
|
}
|
|
243
257
|
if (enabled) {
|
|
258
|
+
applyDefaultTheme(ctx);
|
|
244
259
|
applyChrome(ctx);
|
|
245
260
|
await refreshStatus(ctx);
|
|
246
261
|
ensurePolling();
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
export
|
|
2
|
+
export type CronExtensionOpts = {
|
|
3
|
+
allowMutations?: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function cronExtension(pi: ExtensionAPI, opts?: CronExtensionOpts): void;
|
|
3
6
|
export default cronExtension;
|
|
4
7
|
//# sourceMappingURL=cron.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/extensions/cron.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/extensions/cron.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA0DlE,MAAM,MAAM,iBAAiB,GAAG;IAC/B,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,GAAE,iBAAsB,QAqM3E;AAED,eAAe,aAAa,CAAC"}
|