@femtomc/mu-agent 26.2.102 → 26.2.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ These are loaded by runtime code and are the single source of truth for default
22
22
  ## Bundled starter skills
23
23
 
24
24
  Bundled starter skills live under `packages/agent/prompts/skills/` and are bootstrapped
25
- into `~/.mu/skills/` (or `$MU_HOME/skills/`) when missing:
25
+ into `~/.mu/skills/` (or `$MU_HOME/skills/`) by the CLI store-initialization path:
26
26
 
27
27
  - `mu`
28
28
  - `memory`
@@ -36,9 +36,10 @@ into `~/.mu/skills/` (or `$MU_HOME/skills/`) when missing:
36
36
  - `setup-discord`
37
37
  - `setup-telegram`
38
38
  - `setup-neovim`
39
+ - `writing`
39
40
 
40
- Starter skills are version-synced by the CLI bootstrap path when users run `mu`
41
- commands after upgrading.
41
+ Starter skills are version-synced by CLI bootstrap. Initial bootstrap seeds missing
42
+ skills; bundled-version changes refresh installed starter skill files.
42
43
 
43
44
  ## Install
44
45
 
@@ -1 +1 @@
1
- {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../src/operator.ts"],"names":[],"mappings":"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"}
package/dist/operator.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HudDocSchema, normalizeHudDocs } from "@femtomc/mu-core";
2
- import { appendJsonl, getStorePaths, readJsonl } from "@femtomc/mu-core/node";
2
+ import { appendJsonl, getStorePaths } from "@femtomc/mu-core/node";
3
3
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
5
  import { z } from "zod";
@@ -292,93 +292,6 @@ function collectHudDocsFromToolExecutionEvent(event) {
292
292
  }
293
293
  return extractHudDocsFromToolResult(rec.result);
294
294
  }
295
- function finiteInt(value) {
296
- if (typeof value !== "number" || !Number.isFinite(value)) {
297
- return null;
298
- }
299
- return Math.trunc(value);
300
- }
301
- function stringList(value) {
302
- if (!Array.isArray(value)) {
303
- return [];
304
- }
305
- const out = [];
306
- for (const item of value) {
307
- const parsed = nonEmptyString(item);
308
- if (parsed) {
309
- out.push(parsed);
310
- }
311
- }
312
- return out;
313
- }
314
- function sessionFlashPath(repoRoot) {
315
- return join(getStorePaths(repoRoot).storeDir, "control-plane", "session_flash.jsonl");
316
- }
317
- async function loadPendingSessionFlashes(opts) {
318
- const rows = await readJsonl(sessionFlashPath(opts.repoRoot));
319
- const created = new Map();
320
- const delivered = new Set();
321
- for (const row of rows) {
322
- const rec = asRecord(row);
323
- if (!rec) {
324
- continue;
325
- }
326
- const kind = nonEmptyString(rec.kind);
327
- if (kind === "session_flash.create") {
328
- const tsMs = finiteInt(rec.ts_ms) ?? Date.now();
329
- const flashId = nonEmptyString(rec.flash_id);
330
- const sessionId = nonEmptyString(rec.session_id);
331
- const body = nonEmptyString(rec.body);
332
- if (!flashId || !sessionId || !body || sessionId !== opts.sessionId) {
333
- continue;
334
- }
335
- created.set(flashId, {
336
- flash_id: flashId,
337
- created_at_ms: tsMs,
338
- session_id: sessionId,
339
- session_kind: nonEmptyString(rec.session_kind),
340
- body,
341
- context_ids: stringList(rec.context_ids),
342
- source: nonEmptyString(rec.source),
343
- metadata: asRecord(rec.metadata) ?? {},
344
- });
345
- continue;
346
- }
347
- if (kind === "session_flash.delivery") {
348
- const flashId = nonEmptyString(rec.flash_id);
349
- const sessionId = nonEmptyString(rec.session_id);
350
- if (!flashId || !sessionId || sessionId !== opts.sessionId) {
351
- continue;
352
- }
353
- delivered.add(flashId);
354
- }
355
- }
356
- const pending = [...created.values()]
357
- .filter((row) => !delivered.has(row.flash_id))
358
- .sort((a, b) => a.created_at_ms - b.created_at_ms);
359
- const limit = Math.max(1, Math.trunc(opts.limit ?? 16));
360
- if (pending.length <= limit) {
361
- return pending;
362
- }
363
- return pending.slice(pending.length - limit);
364
- }
365
- async function markSessionFlashesDelivered(opts) {
366
- if (opts.flashIds.length === 0) {
367
- return;
368
- }
369
- const deduped = [...new Set(opts.flashIds.filter((id) => id.trim().length > 0))];
370
- for (const flashId of deduped) {
371
- const row = {
372
- kind: "session_flash.delivery",
373
- ts_ms: opts.nowMs,
374
- flash_id: flashId,
375
- session_id: opts.sessionId,
376
- delivered_by: "messaging_operator_runtime",
377
- note: null,
378
- };
379
- await appendJsonl(sessionFlashPath(opts.repoRoot), row);
380
- }
381
- }
382
295
  export class JsonFileConversationSessionStore {
383
296
  #path;
384
297
  #loaded = false;
@@ -551,46 +464,14 @@ export class MessagingOperatorRuntime {
551
464
  operatorTurnId: turnId,
552
465
  };
553
466
  }
554
- let pendingFlashes = [];
555
- try {
556
- pendingFlashes = await loadPendingSessionFlashes({
557
- repoRoot: opts.inbound.repo_root,
558
- sessionId,
559
- });
560
- }
561
- catch {
562
- pendingFlashes = [];
563
- }
564
- const inboundForBackend = pendingFlashes.length > 0
565
- ? {
566
- ...opts.inbound,
567
- metadata: {
568
- ...opts.inbound.metadata,
569
- session_flash_messages: pendingFlashes,
570
- },
571
- }
572
- : opts.inbound;
573
467
  let backendResult;
574
468
  try {
575
469
  backendResult = OperatorBackendTurnResultSchema.parse(await this.#backend.runTurn({
576
470
  sessionId,
577
471
  turnId,
578
- inbound: inboundForBackend,
472
+ inbound: opts.inbound,
579
473
  binding: opts.binding,
580
474
  }));
581
- if (pendingFlashes.length > 0) {
582
- try {
583
- await markSessionFlashesDelivered({
584
- repoRoot: opts.inbound.repo_root,
585
- sessionId,
586
- flashIds: pendingFlashes.map((row) => row.flash_id),
587
- nowMs: Date.now(),
588
- });
589
- }
590
- catch {
591
- // Best-effort delivery bookkeeping; do not fail the operator turn.
592
- }
593
- }
594
475
  }
595
476
  catch (err) {
596
477
  const failureCode = classifyBackendFailureCode(err);
@@ -707,53 +588,6 @@ function buildOperatorPromptContextBlock(metadata) {
707
588
  }
708
589
  return ["", "Client context (structured preview):", preview];
709
590
  }
710
- function extractSessionFlashPromptMessages(metadata) {
711
- const raw = metadata.session_flash_messages;
712
- if (!Array.isArray(raw)) {
713
- return [];
714
- }
715
- const out = [];
716
- for (const value of raw) {
717
- const rec = asRecord(value);
718
- if (!rec) {
719
- continue;
720
- }
721
- const flashId = nonEmptyString(rec.flash_id);
722
- const body = nonEmptyString(rec.body);
723
- const sessionId = nonEmptyString(rec.session_id);
724
- if (!flashId || !body || !sessionId) {
725
- continue;
726
- }
727
- out.push({
728
- flash_id: flashId,
729
- created_at_ms: finiteInt(rec.created_at_ms) ?? 0,
730
- session_id: sessionId,
731
- session_kind: nonEmptyString(rec.session_kind),
732
- body,
733
- context_ids: stringList(rec.context_ids),
734
- source: nonEmptyString(rec.source),
735
- metadata: asRecord(rec.metadata) ?? {},
736
- });
737
- }
738
- out.sort((a, b) => a.created_at_ms - b.created_at_ms);
739
- return out;
740
- }
741
- function buildOperatorPromptFlashBlock(metadata) {
742
- const flashes = extractSessionFlashPromptMessages(metadata);
743
- if (flashes.length === 0) {
744
- return [];
745
- }
746
- const lines = ["", `Session flash messages (${flashes.length}):`];
747
- for (const flash of flashes) {
748
- const source = flash.source ?? "unknown";
749
- const contextIds = flash.context_ids.length > 0 ? ` | context_ids=${flash.context_ids.join(",")}` : "";
750
- const bodyPreview = compactJsonPreview(flash.body, 400) ?? flash.body;
751
- lines.push(`- [${flash.flash_id}] source=${source}${contextIds}`);
752
- lines.push(` ${bodyPreview}`);
753
- }
754
- lines.push("Treat these as high-priority user-provided context for this session.");
755
- return lines;
756
- }
757
591
  function buildOperatorPrompt(input) {
758
592
  const lines = [
759
593
  `[Messaging context]`,
@@ -763,7 +597,6 @@ function buildOperatorPrompt(input) {
763
597
  ``,
764
598
  `User message: ${input.inbound.command_text}`,
765
599
  ...buildOperatorPromptContextBlock(input.inbound.metadata),
766
- ...buildOperatorPromptFlashBlock(input.inbound.metadata),
767
600
  ];
768
601
  return lines.join("\n");
769
602
  }
@@ -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,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,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"}
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,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,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;AAsCF,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAuDnF"}
@@ -1,8 +1,11 @@
1
1
  import { createBashTool, createEditTool, createReadTool, createWriteTool } from "@mariozechner/pi-coding-agent";
2
2
  import { createMuResourceLoader, resolveModel } from "./backend.js";
3
3
  import { MU_DEFAULT_THEME_NAME, MU_DEFAULT_THEME_PATH } from "./ui_defaults.js";
4
+ function resolveSessionPersistenceMode(sessionOpts) {
5
+ return sessionOpts?.mode ?? (sessionOpts?.sessionFile ? "open" : "continue-recent");
6
+ }
4
7
  function createSessionManager(SessionManager, cwd, sessionOpts) {
5
- const mode = sessionOpts?.mode ?? (sessionOpts?.sessionFile ? "open" : "continue-recent");
8
+ const mode = resolveSessionPersistenceMode(sessionOpts);
6
9
  const sessionDir = sessionOpts?.sessionDir;
7
10
  switch (mode) {
8
11
  case "continue-recent":
@@ -23,10 +26,13 @@ function createSessionManager(SessionManager, cwd, sessionOpts) {
23
26
  export async function createMuSession(opts) {
24
27
  const { AuthStorage, createAgentSession, SessionManager, SettingsManager } = await import("@mariozechner/pi-coding-agent");
25
28
  const authStorage = AuthStorage.create();
29
+ const sessionMode = resolveSessionPersistenceMode(opts.session);
30
+ const shouldRestoreSessionConfig = sessionMode === "open" && !opts.provider && !opts.model;
26
31
  const defaultModel = "gpt-5.3-codex";
27
- const modelId = opts.model ?? defaultModel;
28
- const model = resolveModel(modelId, authStorage, opts.provider);
29
- if (!model) {
32
+ const requestedModelId = opts.model?.trim();
33
+ const modelId = requestedModelId && requestedModelId.length > 0 ? requestedModelId : shouldRestoreSessionConfig ? null : defaultModel;
34
+ const model = modelId ? resolveModel(modelId, authStorage, opts.provider) : undefined;
35
+ if (modelId && !model) {
30
36
  const scope = opts.provider ? ` in provider "${opts.provider}"` : "";
31
37
  throw new Error(`Model "${modelId}" not found${scope} in pi-ai registry.`);
32
38
  }
@@ -45,15 +51,22 @@ export async function createMuSession(opts) {
45
51
  createWriteTool(opts.cwd),
46
52
  createEditTool(opts.cwd),
47
53
  ];
48
- const { session } = await createAgentSession({
54
+ const requestedThinking = opts.thinking?.trim();
55
+ const thinkingLevel = requestedThinking && requestedThinking.length > 0
56
+ ? requestedThinking
57
+ : shouldRestoreSessionConfig
58
+ ? undefined
59
+ : "minimal";
60
+ const createOpts = {
49
61
  cwd: opts.cwd,
50
- model,
62
+ ...(model ? { model } : {}),
51
63
  tools,
52
- thinkingLevel: (opts.thinking ?? "minimal"),
64
+ ...(thinkingLevel ? { thinkingLevel: thinkingLevel } : {}),
53
65
  sessionManager: createSessionManager(SessionManager, opts.cwd, opts.session),
54
66
  settingsManager,
55
67
  resourceLoader,
56
68
  authStorage,
57
- });
69
+ };
70
+ const { session } = await createAgentSession(createOpts);
58
71
  return session;
59
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session_turn.d.ts","sourceRoot":"","sources":["../src/session_turn.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjG,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AACrD,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEL,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAIlD;AAsQD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACvE,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAyCA;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+F7B"}
1
+ {"version":3,"file":"session_turn.d.ts","sourceRoot":"","sources":["../src/session_turn.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjG,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,aAAa,CAAC;AACrD,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,MAAM,CAAC;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEL,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAIlD;AAuUD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACvE,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,CAyCA;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgG7B"}
@@ -65,6 +65,29 @@ function defaultSessionDirForKind(repoRoot, sessionKind) {
65
65
  return join(storeDir, "control-plane", "operator-sessions");
66
66
  }
67
67
  }
68
+ function normalizePathForPrefixMatch(path) {
69
+ return resolve(path).replaceAll("\\", "/");
70
+ }
71
+ function inferSessionKindFromSessionDir(repoRoot, sessionDir) {
72
+ const storeDir = getStorePaths(repoRoot).storeDir;
73
+ const normalizedSessionDir = normalizePathForPrefixMatch(sessionDir);
74
+ const operatorDir = normalizePathForPrefixMatch(join(storeDir, "operator", "sessions"));
75
+ if (normalizedSessionDir === operatorDir || normalizedSessionDir.startsWith(`${operatorDir}/`)) {
76
+ return "operator";
77
+ }
78
+ const cpOperatorDir = normalizePathForPrefixMatch(join(storeDir, "control-plane", "operator-sessions"));
79
+ if (normalizedSessionDir === cpOperatorDir || normalizedSessionDir.startsWith(`${cpOperatorDir}/`)) {
80
+ return "cp_operator";
81
+ }
82
+ return null;
83
+ }
84
+ function defaultSessionLookupTargets(repoRoot) {
85
+ const storeDir = getStorePaths(repoRoot).storeDir;
86
+ return [
87
+ { sessionKind: "operator", sessionDir: join(storeDir, "operator", "sessions") },
88
+ { sessionKind: "cp_operator", sessionDir: join(storeDir, "control-plane", "operator-sessions") },
89
+ ];
90
+ }
68
91
  async function pathExists(path) {
69
92
  try {
70
93
  await stat(path);
@@ -208,9 +231,7 @@ function safeSessionFile(session) {
208
231
  return typeof value === "string" && value.trim().length > 0 ? value : null;
209
232
  }
210
233
  async function resolveSessionTarget(opts) {
211
- const sessionDir = opts.request.session_dir
212
- ? resolveRepoPath(opts.repoRoot, opts.request.session_dir)
213
- : defaultSessionDirForKind(opts.repoRoot, opts.normalizedSessionKind);
234
+ const explicitSessionDir = opts.request.session_dir ? resolveRepoPath(opts.repoRoot, opts.request.session_dir) : null;
214
235
  if (opts.request.session_file) {
215
236
  const sessionFile = resolveRepoPath(opts.repoRoot, opts.request.session_file);
216
237
  if (!(await pathExists(sessionFile))) {
@@ -223,22 +244,55 @@ async function resolveSessionTarget(opts) {
223
244
  if (headerId !== opts.request.session_id) {
224
245
  throw new SessionTurnError(409, `session_file header id mismatch (expected ${opts.request.session_id}, found ${headerId})`);
225
246
  }
247
+ const sessionDir = explicitSessionDir ?? dirname(sessionFile);
248
+ return {
249
+ sessionFile,
250
+ sessionDir,
251
+ sessionKind: opts.normalizedSessionKind ?? inferSessionKindFromSessionDir(opts.repoRoot, sessionDir),
252
+ };
253
+ }
254
+ if (explicitSessionDir || opts.normalizedSessionKind) {
255
+ const sessionDir = explicitSessionDir ?? defaultSessionDirForKind(opts.repoRoot, opts.normalizedSessionKind);
256
+ if (!(await directoryExists(sessionDir))) {
257
+ throw new SessionTurnError(404, `session directory not found: ${sessionDir}`);
258
+ }
259
+ const sessionFile = await resolveSessionFileById({
260
+ sessionDir,
261
+ sessionId: opts.request.session_id,
262
+ });
263
+ if (!sessionFile) {
264
+ throw new SessionTurnError(404, `session_id not found in ${sessionDir}: ${opts.request.session_id}`);
265
+ }
226
266
  return {
227
267
  sessionFile,
228
- sessionDir: opts.request.session_dir ? sessionDir : dirname(sessionFile),
268
+ sessionDir,
269
+ sessionKind: opts.normalizedSessionKind ?? inferSessionKindFromSessionDir(opts.repoRoot, sessionDir),
229
270
  };
230
271
  }
231
- if (!(await directoryExists(sessionDir))) {
232
- throw new SessionTurnError(404, `session directory not found: ${sessionDir}`);
272
+ const matches = [];
273
+ for (const target of defaultSessionLookupTargets(opts.repoRoot)) {
274
+ if (!(await directoryExists(target.sessionDir))) {
275
+ continue;
276
+ }
277
+ const sessionFile = await resolveSessionFileById({
278
+ sessionDir: target.sessionDir,
279
+ sessionId: opts.request.session_id,
280
+ });
281
+ if (sessionFile) {
282
+ matches.push({
283
+ sessionFile,
284
+ sessionDir: target.sessionDir,
285
+ sessionKind: target.sessionKind,
286
+ });
287
+ }
288
+ }
289
+ if (matches.length === 1) {
290
+ return matches[0];
233
291
  }
234
- const sessionFile = await resolveSessionFileById({
235
- sessionDir,
236
- sessionId: opts.request.session_id,
237
- });
238
- if (!sessionFile) {
239
- throw new SessionTurnError(404, `session_id not found in ${sessionDir}: ${opts.request.session_id}`);
292
+ if (matches.length > 1) {
293
+ throw new SessionTurnError(409, `session_id is ambiguous across operator/cp_operator stores: ${opts.request.session_id} (pass --session-kind or --session-dir)`);
240
294
  }
241
- return { sessionFile, sessionDir };
295
+ throw new SessionTurnError(404, `session_id not found in operator/cp_operator stores: ${opts.request.session_id}`);
242
296
  }
243
297
  export function parseSessionTurnRequest(body) {
244
298
  const sessionId = nonEmptyString(body.session_id);
@@ -289,18 +343,19 @@ export async function executeSessionTurn(opts) {
289
343
  request: opts.request,
290
344
  normalizedSessionKind,
291
345
  });
346
+ const effectiveSessionKind = target.sessionKind ?? normalizedSessionKind;
292
347
  const sessionFactory = opts.sessionFactory ?? createMuSession;
293
348
  const session = await sessionFactory({
294
349
  cwd: opts.repoRoot,
295
350
  systemPrompt: systemPromptForTurn({
296
- sessionKind: normalizedSessionKind,
351
+ sessionKind: effectiveSessionKind,
297
352
  extensionProfile,
298
353
  }),
299
354
  provider: opts.request.provider ?? undefined,
300
355
  model: opts.request.model ?? undefined,
301
356
  thinking: opts.request.thinking ?? undefined,
302
357
  extensionPaths: extensionPathsForTurn({
303
- sessionKind: normalizedSessionKind,
358
+ sessionKind: effectiveSessionKind,
304
359
  extensionProfile,
305
360
  }),
306
361
  session: {
@@ -365,7 +420,7 @@ export async function executeSessionTurn(opts) {
365
420
  }
366
421
  return {
367
422
  session_id: resolvedSessionId ?? opts.request.session_id,
368
- session_kind: normalizedSessionKind,
423
+ session_kind: effectiveSessionKind,
369
424
  session_file: resolvedSessionFile ?? target.sessionFile,
370
425
  context_entry_id: contextEntryId,
371
426
  reply,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@femtomc/mu-agent",
3
- "version": "26.2.102",
3
+ "version": "26.2.103",
4
4
  "description": "Shared operator runtime for mu assistant sessions and serve extensions.",
5
5
  "keywords": [
6
6
  "mu",
@@ -24,10 +24,10 @@
24
24
  "themes/**"
25
25
  ],
26
26
  "dependencies": {
27
- "@femtomc/mu-core": "26.2.102",
28
- "@mariozechner/pi-agent-core": "^0.53.0",
29
- "@mariozechner/pi-ai": "^0.53.0",
30
- "@mariozechner/pi-coding-agent": "^0.53.0",
27
+ "@femtomc/mu-core": "26.2.103",
28
+ "@mariozechner/pi-agent-core": "^0.54.2",
29
+ "@mariozechner/pi-ai": "^0.54.2",
30
+ "@mariozechner/pi-coding-agent": "^0.54.2",
31
31
  "zod": "^4.1.9"
32
32
  }
33
33
  }
@@ -19,5 +19,6 @@ Using `mu`:
19
19
  - The `mu` CLI is self-explanatory: poke around with `mu --help` to understand.
20
20
  - Use `mu memory search|timeline|stats` to access contextual memory from past interactions.
21
21
  - Use `mu memory index status|rebuild` to inspect/refresh local memory index health when needed.
22
+ - Use `mu control harness [provider] --json --pretty` to inspect harness adapter/provider/model configuration and model capability vectors.
22
23
  - Use `mu heartbeats` and `mu cron` to access persistent scheduled processes that broadcast to the user.
23
24
 
@@ -136,9 +136,6 @@ mu memory stats --pretty
136
136
  - adjust anchors (`issue_id`, `run_id`, `session_id`, `topic`, channel metadata)
137
137
  - rebuild selected sources only
138
138
 
139
- Compatibility note:
140
- - `mu context ...` remains an alias to `mu memory ...`.
141
-
142
139
  ## Evaluation scenarios
143
140
 
144
141
  1. **Issue-scoped context retrieval**
@@ -11,9 +11,10 @@ run focused execution loops, and hand off to specialized skills when needed.
11
11
  ## Contents
12
12
 
13
13
  - [Core contract](#core-contract)
14
+ - [CLI capability map](#cli-capability-map)
14
15
  - [Default bounded investigation loop](#default-bounded-investigation-loop)
15
- - [Common mutation patterns](#common-mutation-patterns)
16
- - [Session and serve surfaces](#session-and-serve-surfaces)
16
+ - [Common mutation and diagnostics patterns](#common-mutation-and-diagnostics-patterns)
17
+ - [Session, serve, and one-shot surfaces](#session-serve-and-one-shot-surfaces)
17
18
  - [Durable automation handoff](#durable-automation-handoff)
18
19
  - [Evaluation scenarios](#evaluation-scenarios)
19
20
  - [Escalation map](#escalation-map)
@@ -25,25 +26,66 @@ run focused execution loops, and hand off to specialized skills when needed.
25
26
  - Start with bounded queries (`--limit`, scoped filters), then drill into specific entities.
26
27
 
27
28
  2. **CLI-first state operations**
28
- - Read/mutate `issues`, `forum`, `memory`, and `control-plane` via `mu` CLI commands.
29
+ - Use `mu` command surfaces for state reads/writes (`issues`, `forum`, `memory`, `events`, `control`, `store`, `session`).
29
30
  - Avoid hand-editing JSONL runtime journals for normal operations.
30
31
 
31
32
  3. **Read -> act -> verify loop**
32
33
  - Before writes: inspect relevant current state.
33
34
  - After writes: re-read to confirm effect.
34
35
 
35
- 4. **Keep work reversible and explicit**
36
+ 4. **Prefer self-discovery when uncertain**
37
+ - Run `mu --help` and `mu <command> --help` instead of guessing flags/subcommands.
38
+ - Use `mu guide` for the canonical in-CLI workflow map.
39
+
40
+ 5. **Keep work reversible and explicit**
36
41
  - Prefer small, composable steps.
37
42
  - State assumptions and blockers clearly.
38
43
 
44
+ ## CLI capability map
45
+
46
+ Use these command groups as the source of truth for current capabilities:
47
+
48
+ - Orientation + summaries:
49
+ - `mu --help`
50
+ - `mu guide`
51
+ - `mu status --pretty`
52
+ - Work graph + coordination:
53
+ - `mu issues <subcmd>`
54
+ - `mu forum <subcmd>`
55
+ - Context retrieval + traces:
56
+ - `mu memory <search|timeline|stats|index ...>`
57
+ - `mu events <list|trace ...>`
58
+ - Control-plane + operator config:
59
+ - `mu control status`
60
+ - `mu control harness`
61
+ - `mu control identities`
62
+ - `mu control operator <...>`
63
+ - `mu control config <get|set|unset ...>`
64
+ - `mu control reload`
65
+ - `mu control diagnose-operator`
66
+ - Session/runtime surfaces:
67
+ - `mu serve`, `mu stop`
68
+ - `mu session [list|config|<id>]`
69
+ - `mu turn ...`
70
+ - `mu exec ...`
71
+ - Durable automation:
72
+ - `mu heartbeats <...>`
73
+ - `mu cron <...>`
74
+ - Store forensics + replay:
75
+ - `mu store <paths|ls|tail ...>`
76
+ - `mu replay <id|path>`
77
+ - Provider auth:
78
+ - `mu login [<provider>] [--list] [--logout]`
79
+
39
80
  ## Default bounded investigation loop
40
81
 
41
82
  ```bash
42
83
  mu status --pretty
43
84
  mu issues list --status open --limit 20 --pretty
44
85
  mu issues ready --limit 20 --pretty
45
- mu forum read user:context --limit 20 --pretty
86
+ mu forum topics --prefix issue: --limit 20 --pretty
46
87
  mu memory search --query "<topic>" --limit 20
88
+ mu events list --limit 20 --pretty
47
89
  mu store tail events --limit 20 --pretty
48
90
  ```
49
91
 
@@ -52,37 +94,54 @@ Then inspect concrete targets:
52
94
  ```bash
53
95
  mu issues get <id> --pretty
54
96
  mu forum read issue:<id> --limit 20 --pretty
97
+ mu memory timeline --issue-id <id> --order desc --limit 40 --pretty
55
98
  ```
56
99
 
57
- ## Common mutation patterns
100
+ ## Common mutation and diagnostics patterns
58
101
 
59
102
  Issue/forum lifecycle:
60
103
 
61
104
  ```bash
105
+ mu issues claim <id> --pretty
62
106
  mu issues update <id> --status in_progress --pretty
63
107
  mu forum post issue:<id> -m "START: <plan>" --author operator
64
108
  mu forum post issue:<id> -m "RESULT: <summary>" --author operator
65
109
  mu issues close <id> --outcome success --pretty
66
110
  ```
67
111
 
68
- Control-plane lifecycle:
112
+ Control-plane lifecycle and config:
69
113
 
70
114
  ```bash
71
115
  mu control status --pretty
116
+ mu control harness --pretty
72
117
  mu control identities --all --pretty
118
+ mu control config get --pretty
119
+ mu control operator get --pretty
73
120
  mu control reload
74
121
  ```
75
122
 
76
- Store forensics:
123
+ Targeted config/operator updates:
124
+
125
+ ```bash
126
+ mu control config set control_plane.operator.enabled false
127
+ mu control config set control_plane.memory_index.every_ms 120000
128
+ mu control operator set openai-codex gpt-5.3-codex high
129
+ mu control diagnose-operator --limit 40 --pretty
130
+ ```
131
+
132
+ Store forensics and run replay:
77
133
 
78
134
  ```bash
135
+ mu store paths --pretty
79
136
  mu store ls --pretty
80
137
  mu store tail cp_commands --limit 20 --pretty
81
138
  mu store tail cp_outbox --limit 20 --pretty
82
139
  mu store tail cp_adapter_audit --limit 20 --pretty
140
+ mu store tail cp_operator_turns --limit 20 --pretty
141
+ mu replay <issue-id-or-log-path>
83
142
  ```
84
143
 
85
- ## Session and serve surfaces
144
+ ## Session, serve, and one-shot surfaces
86
145
 
87
146
  Primary interactive surface:
88
147
 
@@ -93,9 +152,17 @@ mu serve
93
152
  Session follow-ups/handoffs:
94
153
 
95
154
  ```bash
96
- mu session list --json --pretty
155
+ mu session list --kind all --all-workspaces --limit 50 --json --pretty
97
156
  mu session <session-id>
98
157
  mu turn --session-kind operator --session-id <session-id> --body "<follow-up>"
158
+ mu session config get --session-id <session-id>
159
+ mu session config set-thinking --session-id <session-id> --thinking minimal
160
+ ```
161
+
162
+ One-shot prompt (no durable session):
163
+
164
+ ```bash
165
+ mu exec --message "<task>" --json
99
166
  ```
100
167
 
101
168
  In attached terminal operator chat, `/mu` helpers are available (`/mu events`, `/mu hud ...`, `/mu help`).
@@ -110,8 +177,8 @@ mu cron --help
110
177
  ```
111
178
 
112
179
  When work is multi-step and issue-graph driven, use `planning` to shape the DAG,
113
- then `hierarchical-work-protocol` to keep DAG semantics consistent, then
114
- `subagents` for durable execution.
180
+ then `hud` for canonical HUD behavior, then `hierarchical-work-protocol` to keep DAG
181
+ semantics consistent, then `subagents` for durable execution.
115
182
  For recurring bounded automation loops, use `heartbeats`.
116
183
  For wall-clock schedules (one-shot, interval, cron-expression), use `crons`.
117
184
 
@@ -119,20 +186,21 @@ For wall-clock schedules (one-shot, interval, cron-expression), use `crons`.
119
186
 
120
187
  1. **Bounded investigation before mutation**
121
188
  - Prompt: user asks for status + targeted change.
122
- - Expected: skill gathers scoped evidence first (`mu status`, `issues`, `forum`, `memory`), then performs minimal write and verifies post-state.
189
+ - Expected: gather scoped evidence first (`mu status`, `issues`, `forum`, `memory`/`events`), then perform minimal write and verify post-state.
123
190
 
124
191
  2. **Control-plane diagnostics loop**
125
- - Prompt: user reports messaging channel stopped responding.
126
- - Expected: skill inspects `mu control status`, identities, and adapter audit/outbox logs, then proposes smallest reversible recovery step (`reload`, relink, or config fix).
192
+ - Prompt: messaging channel stopped responding.
193
+ - Expected: inspect `mu control status`, `harness`, identities, adapter audit/outbox logs; then apply smallest reversible fix (`config set`, relink, `reload`) and verify.
127
194
 
128
- 3. **Session handoff continuity**
129
- - Prompt: user asks to continue prior operator thread.
130
- - Expected: skill inspects `mu session list`, resumes by ID, and keeps follow-up actions scoped to the resumed session context.
195
+ 3. **Session continuity + scope-safe config**
196
+ - Prompt: continue prior operator thread with model/thinking tweaks.
197
+ - Expected: use `mu session list` + `mu session <id>`/`mu turn`; when needed, apply `mu session config ...` (session-scoped) instead of mutating global defaults.
131
198
 
132
199
  ## Escalation map
133
200
 
134
201
  - Historical context retrieval and index maintenance: **`memory`**
135
202
  - Planning/decomposition and DAG review: **`planning`**
203
+ - HUD contract/state updates across surfaces: **`hud`**
136
204
  - Shared DAG semantics for planning + execution: **`hierarchical-work-protocol`**
137
205
  - Durable multi-agent orchestration: **`subagents`**
138
206
  - Recurring bounded automation scheduling: **`heartbeats`**
@@ -142,3 +210,4 @@ For wall-clock schedules (one-shot, interval, cron-expression), use `crons`.
142
210
  - **`setup-discord`**
143
211
  - **`setup-telegram`**
144
212
  - **`setup-neovim`**
213
+ - Technical writing/docs polish: **`writing`**
@@ -21,8 +21,6 @@ Goal: get Telegram bot ingress and reply delivery working with minimal user-side
21
21
  - Public webhook base URL reachable by Telegram (for example `https://mu.example.com`)
22
22
  - Telegram bot token (from BotFather)
23
23
 
24
- Optional (agent can usually discover):
25
- - Bot username
26
24
 
27
25
  ## Agent-first workflow
28
26
 
@@ -47,18 +45,10 @@ mu control identities --all --pretty
47
45
 
48
46
  If no running server is available, ask user to start `mu serve` in another terminal before reload/route checks.
49
47
 
50
- ### 2) Generate webhook secret and discover bot username
48
+ ### 2) Generate webhook secret
51
49
 
52
50
  Generate a strong webhook secret (do not expose it in final summaries).
53
51
 
54
- If outbound network is available, discover bot username:
55
-
56
- ```bash
57
- curl -sS "https://api.telegram.org/bot<bot-token>/getMe"
58
- ```
59
-
60
- Extract `result.username` when present.
61
-
62
52
  ### 3) Configure Telegram webhook (agent does this when possible)
63
53
 
64
54
  Set webhook to `https://<public-base>/webhooks/telegram` with secret token:
@@ -78,7 +68,6 @@ Use this canonical patch snippet (preserves unrelated keys):
78
68
  ```bash
79
69
  export MU_TELEGRAM_WEBHOOK_SECRET='<TELEGRAM_WEBHOOK_SECRET>'
80
70
  export MU_TELEGRAM_BOT_TOKEN='<TELEGRAM_BOT_TOKEN>'
81
- export MU_TELEGRAM_BOT_USERNAME='<TELEGRAM_BOT_USERNAME_OR_EMPTY>'
82
71
  config_path="$(mu control status --json | python3 -c 'import json,sys; print(json.load(sys.stdin)["config_path"])')"
83
72
 
84
73
  python3 - "$config_path" <<'PY'
@@ -98,8 +87,6 @@ adapters = cp.setdefault("adapters", {})
98
87
  telegram = adapters.setdefault("telegram", {})
99
88
  telegram["webhook_secret"] = os.environ["MU_TELEGRAM_WEBHOOK_SECRET"]
100
89
  telegram["bot_token"] = os.environ["MU_TELEGRAM_BOT_TOKEN"]
101
- username = os.environ.get("MU_TELEGRAM_BOT_USERNAME", "").strip()
102
- telegram["bot_username"] = username or None
103
90
 
104
91
  path.parent.mkdir(parents=True, exist_ok=True)
105
92
  path.write_text(json.dumps(data, indent=2) + "\n")
@@ -107,8 +94,7 @@ PY
107
94
  ```
108
95
 
109
96
  Replace placeholder values with secrets from the user.
110
- If bot username is unknown, leave `MU_TELEGRAM_BOT_USERNAME` empty.
111
- Then `unset MU_TELEGRAM_WEBHOOK_SECRET MU_TELEGRAM_BOT_TOKEN MU_TELEGRAM_BOT_USERNAME` after patching.
97
+ Then `unset MU_TELEGRAM_WEBHOOK_SECRET MU_TELEGRAM_BOT_TOKEN` after patching.
112
98
 
113
99
  ### 5) Reload and verify
114
100
 
@@ -160,9 +146,9 @@ Ask user to send `/mu status` (or plain status text) and verify response deliver
160
146
  - Inputs: agent cannot reach `api.telegram.org`.
161
147
  - Expected: skill hands user exact `setWebhook` command, resumes after confirmation, and still completes local config/reload verification.
162
148
 
163
- 3. **Unknown bot username fallback**
164
- - Inputs: `getMe` unavailable or username omitted.
165
- - Expected: config stores `bot_username: null` (or omitted equivalent), adapter still activates, and identity link can proceed from audit chat id.
149
+ 3. **Token+secret only configuration**
150
+ - Inputs: valid bot token + webhook secret, no username discovery step.
151
+ - Expected: adapter activates and identity link can proceed from audit chat id.
166
152
 
167
153
  ## Safety requirements
168
154
 
@@ -0,0 +1,179 @@
1
+ ---
2
+ name: writing
3
+ description: "Crafts clear, precise technical documentation. Use when writing or reviewing docs, PR descriptions, error messages, READMEs, API references, or any technical prose."
4
+ ---
5
+
6
+ # writing
7
+
8
+ Use this skill when asked to write, edit, or review technical prose. This includes documentation, READMEs, PR descriptions, error messages, comments, API references, and commit messages.
9
+
10
+ ## Contents
11
+
12
+ - [Core contract](#core-contract)
13
+ - [Writing workflows](#writing-workflows)
14
+ - [Common patterns by document type](#common-patterns-by-document-type)
15
+ - [Editing and review workflow](#editing-and-review-workflow)
16
+ - [Evaluation scenarios](#evaluation-scenarios)
17
+
18
+ ## Core contract
19
+
20
+ 1. **Audience first**
21
+ - Identify the reader's baseline knowledge before writing.
22
+ - Write for the busiest reader who needs this information.
23
+ - Honor their time: front-load the essential information.
24
+
25
+ 2. **Clarity over style**
26
+ - One idea per sentence. Complex concepts deserve their own space.
27
+ - Active voice: "The system returns an error" not "An error is returned by the system."
28
+ - Precise terminology: use the same word for the same concept throughout.
29
+ - Concrete over abstract: "200ms latency" beats "fast performance."
30
+
31
+ 3. **Structure for scanability**
32
+ - Headings should communicate the document structure without reading prose.
33
+ - Lists for parallel items (bullets for unordered, numbers for sequences).
34
+ - Code blocks and tables over prose descriptions.
35
+ - Inverted pyramid: conclusion, supporting details, background.
36
+
37
+ 4. **Actionability**
38
+ - Imperative for procedures: "Run the migration" not "The migration should be run."
39
+ - Explicit consequences: state what happens if the user does X.
40
+ - Anticipate failure modes in troubleshooting sections.
41
+
42
+ 5. **Accessibility**
43
+ - Plain language: avoid Latin abbreviations, buzzwords, metaphor-heavy descriptions.
44
+ - Sentence length: average 15-20 words. Vary rhythm but never confuse length with sophistication.
45
+ - Context for jargon: define domain-specific terms on first use or link to definitions.
46
+
47
+ 6. **Verify by reading aloud**
48
+ - Awkward phrasing surfaces when spoken.
49
+ - Test instructions by following them exactly as written.
50
+ - Delete mercilessly: if a sentence doesn't inform or direct, cut it.
51
+
52
+ ## Writing workflows
53
+
54
+ ### A) Documentation from scratch
55
+
56
+ 1. **Identify the audience and goal**
57
+ - Who will read this? What do they know? What must they do after reading?
58
+
59
+ 2. **Outline the structure**
60
+ - Opening paragraph: what this document covers and why it matters.
61
+ - Body: group related concepts, sequence procedures in order of execution.
62
+ - Closing: next steps, related resources, or troubleshooting.
63
+
64
+ 3. **Draft with constraints**
65
+ - Maximum 25 words per sentence on average.
66
+ - Active voice for all instructions.
67
+ - Code examples for any behavior described.
68
+
69
+ 4. **Review against the contract**
70
+ - Scan test: can a reader grasp structure from headings alone?
71
+ - Action test: can a reader execute procedures without asking questions?
72
+ - Deletion pass: remove sentences that don't inform or direct.
73
+
74
+ ### B) PR/commit description
75
+
76
+ 1. **What changed** (imperative, present tense)
77
+ 2. **Why it changed** (context, motivation)
78
+ 3. **How to verify** (testing steps, expected outcomes)
79
+ 4. **Breaking changes** (if any, with migration path)
80
+
81
+ Keep under 80 characters per line in the summary. Body wraps at 72 characters.
82
+
83
+ ### C) Error messages
84
+
85
+ 1. **State what happened** (not what didn't)
86
+ 2. **Explain why** (the root cause, if known)
87
+ 3. **Provide the fix** (concrete next step, not generic advice)
88
+ 4. **Include identifiers** (error codes, relevant IDs, log locations)
89
+
90
+ Example:
91
+ ```
92
+ Error: Connection refused to database 'prod-db' on port 5432.
93
+ Cause: The database service is not running or firewall blocks port 5432.
94
+ Fix: Start the service with 'sudo systemctl start postgresql' or verify firewall rules.
95
+ Log: See /var/log/postgresql/postgresql-14-main.log for details.
96
+ ```
97
+
98
+ ## Common patterns by document type
99
+
100
+ ### README.md
101
+
102
+ Structure:
103
+ 1. One-line description of what this is
104
+ 2. Installation/usage (minimal working example)
105
+ 3. Key features (bullet list)
106
+ 4. Configuration/options
107
+ 5. Contributing/troubleshooting links
108
+
109
+ ### API documentation
110
+
111
+ Per endpoint:
112
+ - Purpose (one sentence)
113
+ - HTTP method and path
114
+ - Parameters (name, type, required, description)
115
+ - Request/response examples
116
+ - Error codes and meanings
117
+
118
+ ### Inline code comments
119
+
120
+ - **Why**, not what: explain intent, not obvious behavior.
121
+ - Non-obvious side effects or assumptions.
122
+ - TODO/FIXME with issue references, not vague notes.
123
+ - Public APIs: docstrings with parameters, returns, raises.
124
+
125
+ ### Configuration docs
126
+
127
+ - Default values explicitly stated.
128
+ - Units for all numeric values (ms, bytes, percent).
129
+ - Validation constraints (min/max, allowed values).
130
+ - Impact of changing the value (what breaks, what improves).
131
+
132
+ ## Editing and review workflow
133
+
134
+ When reviewing existing prose:
135
+
136
+ 1. **Structural audit**
137
+ - Does the outline serve the reader's goal?
138
+ - Are headings descriptive? Is sequencing logical?
139
+
140
+ 2. **Sentence-level edits**
141
+ - Convert passive to active voice.
142
+ - Replace vague quantifiers ("some", "many") with specifics.
143
+ - Break long sentences (\> 25 words) into two.
144
+
145
+ 3. **Accuracy check**
146
+ - Verify all code examples execute as written.
147
+ - Confirm version numbers, paths, and URLs are current.
148
+ - Check that error messages match actual output.
149
+
150
+ 4. **Final polish**
151
+ - Read aloud for awkward rhythm.
152
+ - Consistent formatting (punctuation in lists, code fences with languages).
153
+ - Spelling and grammar (but prioritize clarity over grammatical perfection).
154
+
155
+ ## Evaluation scenarios
156
+
157
+ 1. **Drafting documentation for a new feature**
158
+ - Prompt: user asks for docs for a feature they've implemented.
159
+ - Expected: skill identifies audience, structures around user goals not implementation details, includes working examples, and ends with verification steps.
160
+
161
+ 2. **Reviewing a PR description**
162
+ - Prompt: user shares a draft PR description for feedback.
163
+ - Expected: skill checks for imperative summary line, clear what/why/how structure, and explicit breaking change notice if applicable.
164
+
165
+ 3. **Improving error messages**
166
+ - Prompt: user shares error handling code or current error text.
167
+ - Expected: skill transforms vague messages into specific what/why/fix format with actionable next steps and relevant identifiers.
168
+
169
+ 4. **Editing README for clarity**
170
+ - Prompt: user asks for help with a project's README.
171
+ - Expected: skill restructures for inverted pyramid, adds minimal working example, replaces feature paragraphs with scannable lists, and ensures installation steps are complete and ordered.
172
+
173
+ ## Quality bar
174
+
175
+ - Every sentence earns its place: informs or directs.
176
+ - No sentence requires a second reading to understand.
177
+ - A reader can act on instructions without asking clarifying questions.
178
+ - Code examples execute without modification.
179
+ - A skim reader grasps the document's purpose and structure.