@femtomc/mu-agent 26.2.73 → 26.2.74

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/dist/operator.js CHANGED
@@ -1,4 +1,4 @@
1
- import { appendJsonl } from "@femtomc/mu-core/node";
1
+ import { appendJsonl, readJsonl } from "@femtomc/mu-core/node";
2
2
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import { dirname, join } from "node:path";
4
4
  import { z } from "zod";
@@ -183,6 +183,106 @@ function buildOperatorFailureFallbackMessage(code) {
183
183
  function conversationKey(inbound, binding) {
184
184
  return `${inbound.channel}:${inbound.channel_tenant_id}:${inbound.channel_conversation_id}:${binding.binding_id}`;
185
185
  }
186
+ function asRecord(value) {
187
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
188
+ return null;
189
+ }
190
+ return value;
191
+ }
192
+ function nonEmptyString(value) {
193
+ if (typeof value !== "string") {
194
+ return null;
195
+ }
196
+ const trimmed = value.trim();
197
+ return trimmed.length > 0 ? trimmed : null;
198
+ }
199
+ function finiteInt(value) {
200
+ if (typeof value !== "number" || !Number.isFinite(value)) {
201
+ return null;
202
+ }
203
+ return Math.trunc(value);
204
+ }
205
+ function stringList(value) {
206
+ if (!Array.isArray(value)) {
207
+ return [];
208
+ }
209
+ const out = [];
210
+ for (const item of value) {
211
+ const parsed = nonEmptyString(item);
212
+ if (parsed) {
213
+ out.push(parsed);
214
+ }
215
+ }
216
+ return out;
217
+ }
218
+ function sessionFlashPath(repoRoot) {
219
+ return join(repoRoot, ".mu", "control-plane", "session_flash.jsonl");
220
+ }
221
+ async function loadPendingSessionFlashes(opts) {
222
+ const rows = await readJsonl(sessionFlashPath(opts.repoRoot));
223
+ const created = new Map();
224
+ const delivered = new Set();
225
+ for (const row of rows) {
226
+ const rec = asRecord(row);
227
+ if (!rec) {
228
+ continue;
229
+ }
230
+ const kind = nonEmptyString(rec.kind);
231
+ if (kind === "session_flash.create") {
232
+ const tsMs = finiteInt(rec.ts_ms) ?? Date.now();
233
+ const flashId = nonEmptyString(rec.flash_id);
234
+ const sessionId = nonEmptyString(rec.session_id);
235
+ const body = nonEmptyString(rec.body);
236
+ if (!flashId || !sessionId || !body || sessionId !== opts.sessionId) {
237
+ continue;
238
+ }
239
+ created.set(flashId, {
240
+ flash_id: flashId,
241
+ created_at_ms: tsMs,
242
+ session_id: sessionId,
243
+ session_kind: nonEmptyString(rec.session_kind),
244
+ body,
245
+ context_ids: stringList(rec.context_ids),
246
+ source: nonEmptyString(rec.source),
247
+ metadata: asRecord(rec.metadata) ?? {},
248
+ });
249
+ continue;
250
+ }
251
+ if (kind === "session_flash.delivery") {
252
+ const flashId = nonEmptyString(rec.flash_id);
253
+ const sessionId = nonEmptyString(rec.session_id);
254
+ if (!flashId || !sessionId || sessionId !== opts.sessionId) {
255
+ continue;
256
+ }
257
+ delivered.add(flashId);
258
+ }
259
+ }
260
+ const pending = [...created.values()]
261
+ .filter((row) => !delivered.has(row.flash_id))
262
+ .sort((a, b) => a.created_at_ms - b.created_at_ms);
263
+ const limit = Math.max(1, Math.trunc(opts.limit ?? 16));
264
+ if (pending.length <= limit) {
265
+ return pending;
266
+ }
267
+ return pending.slice(pending.length - limit);
268
+ }
269
+ async function markSessionFlashesDelivered(opts) {
270
+ if (opts.flashIds.length === 0) {
271
+ return;
272
+ }
273
+ const deduped = [...new Set(opts.flashIds.filter((id) => id.trim().length > 0))];
274
+ for (const flashId of deduped) {
275
+ const row = {
276
+ kind: "session_flash.delivery",
277
+ ts_ms: opts.nowMs,
278
+ flash_id: flashId,
279
+ session_id: opts.sessionId,
280
+ delivered_by: "messaging_operator_runtime",
281
+ note: null,
282
+ };
283
+ await appendJsonl(sessionFlashPath(opts.repoRoot), row);
284
+ }
285
+ }
186
286
  export class JsonFileConversationSessionStore {
187
287
  #path;
188
288
  #loaded = false;
@@ -323,14 +423,46 @@ export class MessagingOperatorRuntime {
323
423
  operatorTurnId: turnId,
324
424
  };
325
425
  }
426
+ let pendingFlashes = [];
427
+ try {
428
+ pendingFlashes = await loadPendingSessionFlashes({
429
+ repoRoot: opts.inbound.repo_root,
430
+ sessionId,
431
+ });
432
+ }
433
+ catch {
434
+ pendingFlashes = [];
435
+ }
436
+ const inboundForBackend = pendingFlashes.length > 0
437
+ ? {
438
+ ...opts.inbound,
439
+ metadata: {
440
+ ...opts.inbound.metadata,
441
+ session_flash_messages: pendingFlashes,
442
+ },
443
+ }
444
+ : opts.inbound;
326
445
  let backendResult;
327
446
  try {
328
447
  backendResult = OperatorBackendTurnResultSchema.parse(await this.#backend.runTurn({
329
448
  sessionId,
330
449
  turnId,
331
- inbound: opts.inbound,
450
+ inbound: inboundForBackend,
332
451
  binding: opts.binding,
333
452
  }));
453
+ if (pendingFlashes.length > 0) {
454
+ try {
455
+ await markSessionFlashesDelivered({
456
+ repoRoot: opts.inbound.repo_root,
457
+ sessionId,
458
+ flashIds: pendingFlashes.map((row) => row.flash_id),
459
+ nowMs: Date.now(),
460
+ });
461
+ }
462
+ catch {
463
+ // Best-effort delivery bookkeeping; do not fail the operator turn.
464
+ }
465
+ }
334
466
  }
335
467
  catch (err) {
336
468
  return {
@@ -385,15 +517,115 @@ export class MessagingOperatorRuntime {
385
517
  }
386
518
  export { DEFAULT_OPERATOR_SYSTEM_PROMPT };
387
519
  const COMMAND_TOOL_NAME = "command";
520
+ const OPERATOR_PROMPT_CONTEXT_MAX_CHARS = 2_500;
521
+ function compactJsonPreview(value, maxChars = OPERATOR_PROMPT_CONTEXT_MAX_CHARS) {
522
+ let raw = "";
523
+ if (typeof value === "string") {
524
+ raw = value;
525
+ }
526
+ else {
527
+ try {
528
+ raw = JSON.stringify(value);
529
+ }
530
+ catch {
531
+ return null;
532
+ }
533
+ }
534
+ const compact = raw.replace(/\s+/g, " ").trim();
535
+ if (compact.length === 0) {
536
+ return null;
537
+ }
538
+ if (compact.length <= maxChars) {
539
+ return compact;
540
+ }
541
+ const keep = Math.max(1, maxChars - 1);
542
+ return `${compact.slice(0, keep)}…`;
543
+ }
544
+ function extractPromptContext(metadata) {
545
+ for (const key of ["client_context", "context", "editor_context"]) {
546
+ if (!(key in metadata)) {
547
+ continue;
548
+ }
549
+ const value = metadata[key];
550
+ if (value == null) {
551
+ continue;
552
+ }
553
+ if (typeof value === "object" || typeof value === "string") {
554
+ return value;
555
+ }
556
+ }
557
+ return null;
558
+ }
559
+ function buildOperatorPromptContextBlock(metadata) {
560
+ const context = extractPromptContext(metadata);
561
+ if (!context) {
562
+ return [];
563
+ }
564
+ const preview = compactJsonPreview(context);
565
+ if (!preview) {
566
+ return [];
567
+ }
568
+ return ["", "Client context (structured preview):", preview];
569
+ }
570
+ function extractSessionFlashPromptMessages(metadata) {
571
+ const raw = metadata.session_flash_messages;
572
+ if (!Array.isArray(raw)) {
573
+ return [];
574
+ }
575
+ const out = [];
576
+ for (const value of raw) {
577
+ const rec = asRecord(value);
578
+ if (!rec) {
579
+ continue;
580
+ }
581
+ const flashId = nonEmptyString(rec.flash_id);
582
+ const body = nonEmptyString(rec.body);
583
+ const sessionId = nonEmptyString(rec.session_id);
584
+ if (!flashId || !body || !sessionId) {
585
+ continue;
586
+ }
587
+ out.push({
588
+ flash_id: flashId,
589
+ created_at_ms: finiteInt(rec.created_at_ms) ?? 0,
590
+ session_id: sessionId,
591
+ session_kind: nonEmptyString(rec.session_kind),
592
+ body,
593
+ context_ids: stringList(rec.context_ids),
594
+ source: nonEmptyString(rec.source),
595
+ metadata: asRecord(rec.metadata) ?? {},
596
+ });
597
+ }
598
+ out.sort((a, b) => a.created_at_ms - b.created_at_ms);
599
+ return out;
600
+ }
601
+ function buildOperatorPromptFlashBlock(metadata) {
602
+ const flashes = extractSessionFlashPromptMessages(metadata);
603
+ if (flashes.length === 0) {
604
+ return [];
605
+ }
606
+ const lines = ["", `Session flash messages (${flashes.length}):`];
607
+ for (const flash of flashes) {
608
+ const source = flash.source ?? "unknown";
609
+ const contextIds = flash.context_ids.length > 0 ? ` | context_ids=${flash.context_ids.join(",")}` : "";
610
+ const bodyPreview = compactJsonPreview(flash.body, 400) ?? flash.body;
611
+ lines.push(`- [${flash.flash_id}] source=${source}${contextIds}`);
612
+ lines.push(` ${bodyPreview}`);
613
+ }
614
+ lines.push("Treat these as high-priority user-provided context for this session.");
615
+ return lines;
616
+ }
388
617
  function buildOperatorPrompt(input) {
389
- return [
618
+ const lines = [
390
619
  `[Messaging context]`,
391
620
  `channel: ${input.inbound.channel}`,
392
621
  `request_id: ${input.inbound.request_id}`,
393
622
  `repo_root: ${input.inbound.repo_root}`,
394
623
  ``,
395
624
  `User message: ${input.inbound.command_text}`,
396
- ].join("\n");
625
+ ...buildOperatorPromptContextBlock(input.inbound.metadata),
626
+ ...buildOperatorPromptFlashBlock(input.inbound.metadata),
627
+ ];
628
+ return lines.join("\n");
397
629
  }
398
630
  function sessionFileStem(sessionId) {
399
631
  const normalized = sessionId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-");
@@ -430,7 +662,7 @@ export class PiMessagingOperatorBackend {
430
662
  this.#persistSessions = opts.persistSessions ?? true;
431
663
  this.#sessionDirForRepoRoot =
432
664
  opts.sessionDirForRepoRoot ?? ((repoRoot) => join(repoRoot, ".mu", "control-plane", "operator-sessions"));
433
- // Command execution routes through the server command pipeline via command.
665
+ // Operator turns can emit structured command proposals captured from tool events.
434
666
  }
435
667
  #disposeSession(sessionId) {
436
668
  const entry = this.#sessions.get(sessionId);
@@ -23,6 +23,11 @@ export type MuSession = {
23
23
  agent: {
24
24
  waitForIdle: () => Promise<void>;
25
25
  };
26
+ sessionId?: string;
27
+ sessionFile?: string;
28
+ sessionManager?: {
29
+ getLeafId?: () => string | null;
30
+ };
26
31
  };
27
32
  export declare function createMuSession(opts: CreateMuSessionOpts): Promise<MuSession>;
28
33
  //# sourceMappingURL=session_factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session_factory.d.ts","sourceRoot":"","sources":["../src/session_factory.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAC;AAExF,MAAM,MAAM,wBAAwB,GAAG;IACtC,IAAI,CAAC,EAAE,wBAAwB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CAC5C,CAAC;AAkCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CnF"}
1
+ {"version":3,"file":"session_factory.d.ts","sourceRoot":"","sources":["../src/session_factory.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,wBAAwB,GAAG,WAAW,GAAG,iBAAiB,GAAG,KAAK,GAAG,MAAM,CAAC;AAExF,MAAM,MAAM,wBAAwB,GAAG;IACtC,IAAI,CAAC,EAAE,wBAAwB,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC1D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE;QAChB,SAAS,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;KAChC,CAAC;CACF,CAAC;AAkCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CnF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-agent",
3
- "version": "26.2.73",
3
+ "version": "26.2.74",
4
4
  "description": "Shared agent runtime for mu chat, orchestration roles, and serve extensions.",
5
5
  "keywords": [
6
6
  "mu",
@@ -24,11 +24,10 @@
24
24
  "themes/**"
25
25
  ],
26
26
  "dependencies": {
27
- "@femtomc/mu-core": "26.2.73",
27
+ "@femtomc/mu-core": "26.2.74",
28
28
  "@mariozechner/pi-agent-core": "^0.53.0",
29
29
  "@mariozechner/pi-ai": "^0.53.0",
30
30
  "@mariozechner/pi-coding-agent": "^0.53.0",
31
- "@sinclair/typebox": "^0.34.0",
32
31
  "zod": "^4.1.9"
33
32
  }
34
33
  }
@@ -5,39 +5,34 @@ Mission:
5
5
  - Help users with any coding tasks they ask you to handle directly.
6
6
  - Help users inspect repository/control-plane state.
7
7
  - Help users choose safe next actions.
8
- - When needed, execute approved mutations using the `command` tool.
8
+ - Execute reads and mutations through direct `mu` CLI invocation when managing mu state.
9
9
 
10
10
  Available tools:
11
11
  - read: Read file contents
12
- - bash: Execute bash commands
12
+ - bash: Execute shell commands (primary path for `mu` CLI)
13
13
  - edit: Make surgical edits to files
14
14
  - write: Create or overwrite files
15
- - query: Read-only retrieval across mu state (`action=describe|get|list|search|timeline|stats|trace`)
16
- - command: Approved mutation pathway (`run_*`, `reload/update`, issue/forum lifecycle, heartbeat/cron program management)
17
15
 
18
- Hard Constraints:
19
- - Never perform mutations directly through read/write tools.
20
- - Mutating actions must flow through the `command` tool.
21
- - Use structured tool arguments; do not emit raw JSON directives in normal text replies.
16
+ CLI-first workflow:
17
+ - Use `bash` + `mu ...` for issue/forum/run/control-plane state operations.
18
+ - Prefer `--pretty` (or `--json` + targeted parsing) for clear, auditable output.
19
+ - Do not use bespoke query/command wrappers; call the CLI surface directly.
22
20
 
23
- command tool usage:
24
- - `command({ kind: "run_start", prompt: "ship release" })`
25
- - `command({ kind: "run_resume", root_issue_id: "mu-abc123", max_steps: 25 })`
26
- - `command({ kind: "issue_close", id: "mu-abc123", outcome: "success" })`
27
- - `command({ kind: "forum_post", topic: "issue:mu-abc123", body: "done", author: "operator" })`
28
- - `command({ kind: "heartbeat_create", title: "Run heartbeat", target_kind: "run", run_root_issue_id: "mu-abc123", every_ms: 15000 })`
29
- - `command({ kind: "cron_create", title: "Nightly resume", target_kind: "run", run_root_issue_id: "mu-abc123", schedule_kind: "cron", expr: "0 2 * * *", tz: "UTC" })`
30
- - `command({ kind: "reload" })`
21
+ Example invocation patterns:
22
+ - `bash("mu status --pretty")`
23
+ - `bash("mu issues list --status open --limit 20 --pretty")`
24
+ - `bash("mu forum read issue:mu-abc123 --limit 20 --pretty")`
25
+ - `bash("mu runs start \"ship release\" --max-steps 25 --pretty")`
26
+ - `bash("mu issues close mu-abc123 --outcome success --pretty")`
27
+ - `bash("mu forum post issue:mu-abc123 -m \"done\" --author operator --pretty")`
28
+ - `bash("mu control reload --pretty")`
31
29
 
32
- query tool usage:
33
- - Start with `query({ action: "describe" })` when capability discovery is needed.
34
- - Use narrow retrieval first (`limit` + filters), then targeted `get` with `id` + `fields`.
35
- - Prefer precise context windows via `query({ action: "search"|"timeline", resource: "context", ... })`.
36
-
37
- Efficiency:
30
+ Guardrails:
31
+ - Never hand-edit `.mu/*.jsonl` for normal lifecycle actions; use `mu` CLI commands.
32
+ - Prefer bounded retrieval (`--limit`, scoped filters) before broad scans.
38
33
  - Do NOT pre-fetch status/issues/events/runs at conversation start.
39
34
  - Fetch only what the user request requires.
40
- - Keep responses grounded in concrete tool results.
35
+ - Keep responses grounded in concrete command results.
41
36
 
42
37
  For normal answers:
43
38
  - Respond in plain text (no directive prefix).
@@ -7,9 +7,9 @@ Mission:
7
7
  - Move planning state forward by closing expanded planning nodes.
8
8
 
9
9
  Available tools:
10
- - query: Read-only retrieval
11
- - command: Mutation pathway
12
- - bash/read are available, but prefer query/command for issue/forum state changes.
10
+ - bash: Execute commands (use `mu` CLI for issue/forum reads + mutations)
11
+ - read: Read repository files when needed for planning context
12
+ - edit/write: available but forbidden for orchestrator execution (planning only)
13
13
 
14
14
  Hard Constraints:
15
15
  1. You MUST NOT execute work directly. No code changes, no file edits, no git commits.
@@ -22,20 +22,19 @@ If the task looks atomic, create exactly one worker child issue rather than doin
22
22
 
23
23
  Workflow:
24
24
  1. Inspect context:
25
- - `query({ action: "get", resource: "issues", id: "<id>" })`
26
- - `query({ action: "list", resource: "forum_messages", topic: "issue:<id>", limit: 20 })`
27
- - `query({ action: "list", resource: "issues", contains: "<id>", limit: 200 })` (or targeted child lookup via CLI if needed)
25
+ - `bash("mu issues get <id> --pretty")`
26
+ - `bash("mu forum read issue:<id> --limit 20 --pretty")`
27
+ - `bash("mu issues children <id> --pretty")` (or `mu issues list --root <id> --pretty`)
28
28
  2. Decompose into worker issues:
29
- - `command({ kind: "issue_create", title: "<title>", body: "<body>", tags: "node:agent,role:worker", parent_id: "<id>" })`
29
+ - `bash("mu issues create \"<title>\" --body \"<body>\" --tags \"node:agent,role:worker\" --parent <id> --pretty")`
30
30
  3. Add ordering where needed:
31
- - `command({ kind: "issue_dep", src_id: "<src>", dep_type: "blocks", dst_id: "<dst>" })`
31
+ - `bash("mu issues dep <src> blocks <dst> --pretty")`
32
32
  4. Close yourself:
33
- - `command({ kind: "issue_close", id: "<id>", outcome: "expanded" })`
34
- - (CLI equivalent: `mu issues close <id> --outcome expanded`)
33
+ - `bash("mu issues close <id> --outcome expanded --pretty")`
35
34
 
36
35
  Guardrails:
37
36
  - The only valid orchestrator close outcome is `expanded`.
38
37
  - Never close with `success`, `failure`, `needs_work`, or `skipped`.
39
38
  - Keep plans small, explicit, and testable.
40
39
  - Plans should include proposed evidence for successful completion.
41
- - Prefer bounded reads (`limit`, scoped filters) before deep inspection.
40
+ - Prefer bounded reads (`--limit`, scoped filters) before deep inspection.
@@ -7,10 +7,8 @@ Mission:
7
7
 
8
8
  Available tools:
9
9
  - read: Read files and logs
10
- - bash: Run validation commands
10
+ - bash: Run validation commands (including `mu` CLI for issue/forum actions)
11
11
  - edit/write: available but avoid changing implementation directly
12
- - query: Read-only retrieval
13
- - command: Mutation pathway
14
12
 
15
13
  Hard Constraints:
16
14
  - Do NOT create child issues. Refinement scheduling is orchestrator-owned.
@@ -19,15 +17,15 @@ Hard Constraints:
19
17
 
20
18
  Workflow:
21
19
  1. Inspect:
22
- - `query({ action: "get", resource: "issues", id: "<id>" })`
23
- - `query({ action: "list", resource: "forum_messages", topic: "issue:<id>", limit: 20 })`
20
+ - `bash("mu issues get <id> --pretty")`
21
+ - `bash("mu forum read issue:<id> --limit 20 --pretty")`
24
22
  2. Verify:
25
23
  - Re-run relevant tests/checks, inspect changed files/logs.
26
24
  3. Decide:
27
- - Accept: `command({ kind: "issue_close", id: "<id>", outcome: "success" })`
28
- - Refine: `command({ kind: "issue_close", id: "<id>", outcome: "needs_work" })`
25
+ - Accept: `bash("mu issues close <id> --outcome success --pretty")`
26
+ - Refine: `bash("mu issues close <id> --outcome needs_work --pretty")`
29
27
  4. Post rationale:
30
- - `command({ kind: "forum_post", topic: "issue:<id>", body: "<evidence + rationale>", author: "reviewer" })`
28
+ - `bash("mu forum post issue:<id> -m \"<evidence + rationale>\" --author reviewer --pretty")`
31
29
 
32
30
  Guardrails:
33
31
  - Use concrete evidence (commands/tests) over opinion.
@@ -7,11 +7,9 @@ Mission:
7
7
 
8
8
  Available tools:
9
9
  - read: Read file contents
10
- - bash: Execute bash commands
10
+ - bash: Execute commands (including direct `mu` CLI reads/mutations)
11
11
  - edit: Make surgical edits to files
12
12
  - write: Create or overwrite files
13
- - query: Read-only retrieval
14
- - command: Mutation pathway
15
13
 
16
14
  Hard Constraints:
17
15
  - Do NOT create child issues — that is the orchestrator's job.
@@ -19,19 +17,19 @@ Hard Constraints:
19
17
 
20
18
  Workflow:
21
19
  1. Inspect:
22
- - `query({ action: "get", resource: "issues", id: "<id>" })`
23
- - `query({ action: "list", resource: "forum_messages", topic: "issue:<id>", limit: 20 })`
20
+ - `bash("mu issues get <id> --pretty")`
21
+ - `bash("mu forum read issue:<id> --limit 20 --pretty")`
24
22
  2. Implement:
25
23
  - Edit files and run commands needed for this issue only.
26
24
  3. Verify:
27
25
  - Run tests/typecheck/build/lint as appropriate.
28
26
  4. Close:
29
- - `command({ kind: "issue_close", id: "<id>", outcome: "success" })` (or `failure` / `skipped` / `needs_work` when warranted)
27
+ - `bash("mu issues close <id> --outcome success --pretty")` (or `failure` / `skipped` / `needs_work` when warranted)
30
28
  5. Log key notes:
31
- - `command({ kind: "forum_post", topic: "issue:<id>", body: "...", author: "worker" })`
29
+ - `bash("mu forum post issue:<id> -m \"...\" --author worker --pretty")`
32
30
 
33
31
  Guardrails:
34
32
  - Prefer concrete evidence over claims (test output, build output, repro checks).
35
33
  - Report what changed and why.
36
- - Keep command output focused: use bounded reads first (`limit`, scoped filters) and drill into specific IDs/files next.
34
+ - Keep command output focused: use bounded reads first (`--limit`, scoped filters) and drill into specific IDs/files next.
37
35
  - Be concise.
@@ -1,9 +0,0 @@
1
- /**
2
- * mu-tools — Tool-only extension bundle for non-interactive roles.
3
- *
4
- * Registers `query` (read) and `command` (mutation).
5
- */
6
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
7
- export declare function muToolsExtension(pi: ExtensionAPI): void;
8
- export default muToolsExtension;
9
- //# sourceMappingURL=mu-tools.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mu-tools.d.ts","sourceRoot":"","sources":["../../src/extensions/mu-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,QAGhD;AAED,eAAe,gBAAgB,CAAC"}
@@ -1,12 +0,0 @@
1
- /**
2
- * mu-tools — Tool-only extension bundle for non-interactive roles.
3
- *
4
- * Registers `query` (read) and `command` (mutation).
5
- */
6
- import { operatorCommandExtension } from "./operator-command.js";
7
- import { queryExtension } from "./query.js";
8
- export function muToolsExtension(pi) {
9
- queryExtension(pi);
10
- operatorCommandExtension(pi);
11
- }
12
- export default muToolsExtension;
@@ -1,13 +0,0 @@
1
- /**
2
- * command — Approved mutation tool.
3
- *
4
- * Single execution path:
5
- * - Requires MU_SERVER_URL.
6
- * - Always POSTs to /api/commands/submit.
7
- * - Supports mutation-capable command kinds only.
8
- */
9
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
- export declare const COMMAND_TOOL_NAME = "command";
11
- export declare function operatorCommandExtension(pi: ExtensionAPI): void;
12
- export default operatorCommandExtension;
13
- //# sourceMappingURL=operator-command.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"operator-command.d.ts","sourceRoot":"","sources":["../../src/extensions/operator-command.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,eAAO,MAAM,iBAAiB,YAAY,CAAC;AA2H3C,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,YAAY,QA6FxD;AAED,eAAe,wBAAwB,CAAC"}