hotmilk 0.1.13 → 0.1.15

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.
Files changed (57) hide show
  1. package/AGENTS.md +12 -10
  2. package/README.md +237 -53
  3. package/agents/README.md +3 -3
  4. package/agents/assistant.md +1 -1
  5. package/agents/coach.md +26 -4
  6. package/agents/designer.md +1 -1
  7. package/agents/planner.md +2 -2
  8. package/hotmilk.json +11 -0
  9. package/package.json +43 -24
  10. package/prompts/prompt-eval.md +241 -0
  11. package/prompts/tidy.md +3 -1
  12. package/prompts/translate.md +3 -1
  13. package/skills/make-docs/SKILL.md +43 -0
  14. package/skills/pioneer/SKILL.md +135 -131
  15. package/skills/pioneer/references/autoresearch-routing.md +114 -0
  16. package/skills/pioneer/references/chat-plan.md +50 -0
  17. package/skills/pioneer/references/goal-gate.md +54 -0
  18. package/skills/pioneer/references/graph-recon-gate.md +50 -0
  19. package/skills/pioneer/references/observational-memory-routing.md +90 -0
  20. package/skills/pioneer/references/openspec-routing.md +50 -0
  21. package/skills/pioneer/references/plannotator-routing.md +83 -0
  22. package/skills/pioneer/references/prompt-eval-gate.md +38 -0
  23. package/skills/pioneer/references/shazam-routing.md +58 -0
  24. package/skills/recommend-research/SKILL.md +94 -6
  25. package/skills/recommend-research/references/example-narrow.md +53 -0
  26. package/skills/recommend-research/references/scope-gate.md +46 -0
  27. package/skills/recommend-research/references/shortlist-gate.md +69 -0
  28. package/skills/recommend-research/references/verify-gate.md +73 -0
  29. package/skills/update-docs/SKILL.md +86 -0
  30. package/skills/update-docs/references/doc-inventory.md +69 -0
  31. package/skills/update-docs/references/drift-verification.md +59 -0
  32. package/src/bootstrap/btw.ts +49 -0
  33. package/src/bootstrap/context-stack.ts +71 -14
  34. package/src/bootstrap/dashboard-settings.ts +16 -2
  35. package/src/bootstrap/dashboard.ts +85 -10
  36. package/src/bootstrap/defaults.ts +26 -0
  37. package/src/bootstrap/extensions.ts +10 -1
  38. package/src/bootstrap/global-extension-sources.ts +43 -4
  39. package/src/bootstrap/graph.ts +12 -0
  40. package/src/bootstrap/project-trust.ts +55 -0
  41. package/src/bootstrap/resolve-bundled.ts +28 -4
  42. package/src/bootstrap/session.ts +61 -1
  43. package/src/bootstrap/subagents-doctor.ts +12 -0
  44. package/src/config/bundled-extensions.ts +42 -0
  45. package/src/config/bundled-package-registry.ts +3 -0
  46. package/src/config/hotmilk.ts +76 -1
  47. package/src/config/mcp.ts +45 -12
  48. package/src/config/resolve.ts +21 -0
  49. package/src/config/runtime.ts +6 -0
  50. package/src/controller/input.ts +15 -0
  51. package/src/controller/mode.ts +32 -0
  52. package/src/index.ts +5 -0
  53. package/src/ui/footer.ts +63 -1
  54. package/src/ui/github-user.ts +52 -4
  55. package/src/ui/session-logo.ts +31 -3
  56. package/themes/monokai.json +1 -1
  57. package/skills/empirical-prompt-tuning/SKILL.md +0 -232
@@ -0,0 +1,73 @@
1
+ # Verify gate (Phase 3)
2
+
3
+ **Load when:** Phase 3 runs — finalists exist and evidence-backed recommendation is next.
4
+
5
+ **Do NOT load when:** Scope not pinned (scope-gate). No finalists yet (shortlist-gate). User only asked for a link or doc lookup with no recommendation — use skill resolution ladder inline.
6
+
7
+ ## Verify ladder (per finalist)
8
+
9
+ Run passes in order. Stop early when **done** criteria pass for the asked question depth.
10
+
11
+ | Pass | Source | Skill / tool | Proves |
12
+ | ---- | -------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
13
+ | 1 | Official docs | fetch / web-search / brave-search | API contract, supported versions, install, license statement |
14
+ | 2 | Changelog / releases | same | Breaking changes, release cadence |
15
+ | 3 | Repository | **`/skill:deepwiki`** or README | Architecture, scope, documented limits |
16
+ | 4 | Source / history | **`/skill:librarian`** or **`/skill:opensrc`** + `rg`/`find` on `$(opensrc path <pkg>)` | Internals, edge cases, performance, "why" — source **search**, not candidate discovery |
17
+ | 5 | Issues / security | librarian / gh / advisory DB | Open blockers, CVE status |
18
+
19
+ **Contract questions** → pass 1–2 often enough. **Internals / performance** → pass 4 required. Do not skip pass 1 because pass 4 is tempting.
20
+
21
+ ## Holistic verify (cross-layer picks)
22
+
23
+ When scope-gate chose **Holistic**, run per-finalist passes **and** this integration table before Phase 4:
24
+
25
+ | Row | Check |
26
+ | ----------------------- | ----------------------------------------------------------- |
27
+ | API compatibility | Shared types, auth tokens, error shapes across layers |
28
+ | Shared auth / identity | One login story; no duplicate secret stores |
29
+ | Deployment coupling | Can layers deploy independently? Single rollback path? |
30
+ | Data / schema alignment | Migrations, IDs, event contracts between components |
31
+ | Ops surface | One observability story; avoid N unrelated agents |
32
+ | Rollback | Partial failure — which layer reverts without orphan state? |
33
+
34
+ Holistic **done** requires at least one integrated stack story (not independent per-layer winners) with citations per layer.
35
+
36
+ ## Per-finalist checklist
37
+
38
+ | Row | Required for production pick |
39
+ | ----------- | ------------------------------------------------------------- |
40
+ | License | SPDX or README license — compatible with stated use |
41
+ | Maintenance | Last release or commit within acceptable window for risk tier |
42
+ | API fit | Maps to must-haves from scope-gate with doc citation |
43
+ | Ops fit | Install size, runtime deps, platform support |
44
+ | Exit path | Migration or wrap cost if pick fails |
45
+
46
+ ## Unavailable source fallbacks
47
+
48
+ | Situation | Fallback |
49
+ | ------------------------------- | --------------------------------------------------------------------- |
50
+ | Docs site down | Cached README, deepwiki, tagged release tree; say source is secondary |
51
+ | Private repo | Only public evidence; flag "cannot verify internals" |
52
+ | Archived project | Recommend against unless user explicitly accepts unmaintained risk |
53
+ | opensrc / librarian unavailable | Official docs + changelog only; widen **Evidence gaps** in verdict |
54
+ | Conflicting blog vs docs | **Docs win** unless source inspection proves blog correct |
55
+
56
+ ## Done criteria (Phase 3 complete)
57
+
58
+ Verify gate is **done** when:
59
+
60
+ 1. Every **finalist** has pass 1 complete; passes 2–5 match question depth
61
+ 2. At least one finalist has clear **pick** support with citations (doc URL or permalink)
62
+ 3. Alternatives have explicit **why not** or tie-breaker
63
+ 4. **Evidence gaps** listed — empty only when passes required for scope are satisfied
64
+
65
+ If done criteria fail, do **not** emit Phase 4 recommendation. State missing evidence and offer next verify step or user waiver.
66
+
67
+ ## Handoff after verify
68
+
69
+ | Next user intent | Skill |
70
+ | --------------------------------- | ------------------------------------- |
71
+ | Implement integration in hotmilk | **`/skill:pioneer`** |
72
+ | Large multi-agent integration bet | **`/skill:tcz-agent-converge`** |
73
+ | Prompt/skill edit from research | **`/prompt-eval <path>`** before ship |
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: update-docs
3
+ description: Sync documentation to code and config — README, CONTRIBUTING, AGENTS.md, CLAUDE.md, docs/, manifests, CI, config templates (e.g. hotmilk.json), and post-merge doc drift. Use for /update-docs, update docs, sync docs, docs drift, stale README, or after changes to commands, defaults, toggles, dependencies, or module layout.
4
+ ---
5
+
6
+ # Update docs
7
+
8
+ Reconcile docs with **code and config as source of truth**. Fix drift; do not rewrite for voice or expand scope.
9
+
10
+ ## Scenario router (read first)
11
+
12
+ | User signal | Route |
13
+ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
14
+ | "Sync all docs" / post-release / manifest work | **Full** — Phase 0 → 4; read entire [`doc-inventory.md`](references/doc-inventory.md) |
15
+ | Named file only (`README`, one doc file) | **Targeted** — Phase 1–3 on that file + its truth sources from inventory |
16
+ | User just shipped code; docs maybe stale | **Post-ship** — read ONLY **Post-ship triggers** in `doc-inventory.md`; do NOT load `drift-verification.md` until Phase 4 |
17
+ | User wants new docs / rewrite tone | **Stop** — sync fixes truth; ask whether to write new content separately |
18
+
19
+ **References:** Do **not** read `references/` until a phase below requires it. Each reference states load conditions at the top.
20
+
21
+ ## Phase map
22
+
23
+ Skip phases that do not apply. State which phase was skipped and why.
24
+
25
+ ```text
26
+ 0. Scope → doc-inventory.md (Full sync — MANDATORY READ ENTIRE FILE)
27
+ 1. Recon → truth files + target docs (read-only)
28
+ 2. Diff → drift list only (no edits yet)
29
+ 3. Patch → minimal surgical edits (one theme per pass unless user asked batch)
30
+ 4. Verify → drift-verification.md (MANDATORY READ ENTIRE FILE before reporting done)
31
+ ```
32
+
33
+ ## Recon rules (Phase 1)
34
+
35
+ | Rule | Detail |
36
+ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
37
+ | Truth first | Read listed source-of-truth files before editing any doc |
38
+ | No invention | If truth is silent, report the gap — do not guess commands, paths, or defaults |
39
+ | Truth conflicts | When doc, manifest, and CI disagree: **CI > manifest scripts/targets > source comments**; report the conflict instead of picking silently |
40
+ | Scope | Touch only doc files implicated by the diff; no drive-by style edits |
41
+ | Language | Match each file's existing language and heading style |
42
+
43
+ ## Patch rules (Phase 3)
44
+
45
+ | Do | Do not |
46
+ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
47
+ | Fix factual drift (commands, paths, defaults, version pins, module layout) | Rewrite unrelated sections for "clarity" |
48
+ | Keep tables and lists aligned when two docs cover the same fact | Duplicate long prose — prefer one canonical section + link |
49
+ | Update config samples when **code** defaults changed | Change runtime defaults in code while "fixing" docs unless user asked |
50
+ | Preserve technical terms and command literals | Translate or rephrase during drift sync unless user asked for localization |
51
+
52
+ ## Output (Phase 4)
53
+
54
+ Emit after the verify checklist passes (or list blockers):
55
+
56
+ ```markdown
57
+ ## Doc sync report
58
+
59
+ **Scope:** full | targeted | post-ship
60
+ **Truth sources read:** …
61
+ **Files patched:** … (or none)
62
+
63
+ ### Drifts fixed
64
+
65
+ - …
66
+
67
+ ### Gaps (no truth — not patched)
68
+
69
+ - …
70
+
71
+ ### Verify
72
+
73
+ - [ ] …
74
+ ```
75
+
76
+ ## Anti-patterns
77
+
78
+ | Do not | Why | Do instead |
79
+ | ------------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------ |
80
+ | Read `references/` without a matching route | Token waste | Router row first |
81
+ | Edit docs before reading truth files | Propagates stale claims | Phase 1 recon |
82
+ | Full README rewrite on a targeted request | Review noise | Named file only |
83
+ | Add a `docs/` tree when the repo has none | Scope creep | Skip; mention only if user wants new docs |
84
+ | "Improve" prose without drift | Style ≠ sync | Diff list must cite a truth mismatch |
85
+ | Skip verify after patch | Drift returns unnoticed | Phase 4 checklist |
86
+ | Copy root manifest commands into every package README in a monorepo | Wrong scope; breaks package-local truth | Root ↔ root manifest; package ↔ package manifest |
@@ -0,0 +1,69 @@
1
+ # Doc inventory
2
+
3
+ **Load when:** Phase 0 (full sync), or you need the truth → doc matrix for an unfamiliar drift.
4
+
5
+ **Do NOT load when:** Post-ship route already names the row (read that row here only if unsure), or the user scoped a single file you already know.
6
+
7
+ Code and config win. Docs below are reconciliation targets. **Adapt rows to the repo** — skip rows that do not exist.
8
+
9
+ ## Detect project type (Phase 0)
10
+
11
+ Read one manifest first, then use the matching rows:
12
+
13
+ | Signal file | Project type |
14
+ | ----------------------------- | -------------- |
15
+ | `package.json` | Node / JS / TS |
16
+ | `Cargo.toml` | Rust |
17
+ | `pyproject.toml` / `setup.py` | Python |
18
+ | `go.mod` | Go |
19
+ | `pom.xml` / `build.gradle*` | JVM |
20
+
21
+ Also note doc roots if present: `README.md`, `CONTRIBUTING.md`, `docs/`, `CHANGELOG.md`, and any root instruction files (`AGENTS.md`, `CLAUDE.md`, …).
22
+
23
+ **Monorepo / multi-manifest:** When root and package manifests both exist, treat **root manifest scripts + root README/CONTRIBUTING** as canonical for repo-wide commands. Package READMEs get package-local scripts and paths only — do not copy root commands into every package README unless that package owns them.
24
+
25
+ ## Truth → doc matrix
26
+
27
+ | Source of truth | What to extract | Doc targets |
28
+ | ------------------------------------------------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
29
+ | Package manifest (`package.json`, `Cargo.toml`, `pyproject.toml`, `go.mod`, …) | Scripts/commands, deps, version, entry points, published files | `README.md`, `CONTRIBUTING.md` |
30
+ | Source layout (`src/`, `lib/`, `cmd/`, …) | Module roles, public API surface | README architecture section, `docs/` |
31
+ | CI config (`.github/workflows/`, `.gitlab-ci.yml`, …) | Test/lint/build commands | `README.md`, `CONTRIBUTING.md` |
32
+ | Config templates in repo root | Default values users copy | README config sample, setup docs |
33
+ | `CHANGELOG.md` / release tags | Version, breaking changes | README version notes |
34
+ | `docs/` (when directory exists) | Topic pages, API reference | Cross-link from README; do not create `docs/` during sync |
35
+ | Root instruction files (when present) | Commands, layout tables, contributor rules | That file only — do not invent paths |
36
+ | Workspace / monorepo (root + package manifests) | Root scripts for repo-wide ops; package scripts for package-local ops | Root README/CONTRIBUTING ↔ root manifest; package README ↔ that package manifest |
37
+
38
+ ## Post-ship triggers (targeted recon)
39
+
40
+ When the user changed code recently, scan **only** rows touched by the change:
41
+
42
+ | Change area | Minimum doc pass |
43
+ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
44
+ | New dependency or entry point | Manifest ↔ README (+ CONTRIBUTING if it lists deps) |
45
+ | Default config flip or toggle default change | Config template ↔ README sample and feature/toggle tables |
46
+ | Lazy-loaded or opt-in feature (doc mentions it; default off in template) | Template defaults ↔ README — mark opt-in/experimental when default is false |
47
+ | New script or test command | Manifest scripts ↔ README + CONTRIBUTING |
48
+ | Version / peer bump | README version note, manifest version constraints |
49
+ | Public API or module layout change | Layout tables, `docs/` API pages |
50
+ | CI workflow change | CONTRIBUTING / README verify commands |
51
+
52
+ ## Full-sync order
53
+
54
+ 1. Detect project type; read truth files for applicable matrix rows.
55
+ 2. Read every doc target listed for those rows.
56
+ 3. Build a diff list — fact mismatches only.
57
+ 4. Patch the smallest set of files that restores consistency.
58
+ 5. Run [`drift-verification.md`](drift-verification.md).
59
+
60
+ ## Common drifts (quick scan)
61
+
62
+ | Symptom | Likely fix |
63
+ | ---------------------------------------------- | ----------------------------------------------------------------------------- |
64
+ | README command does not exist in manifest | Align with manifest scripts/targets |
65
+ | Doc paths point to removed modules | Update layout table from current tree |
66
+ | Config sample shows stale defaults | Copy from repo config template after confirming code |
67
+ | Doc lists a feature not in manifest/config | Remove or mark as external/optional |
68
+ | Duplicate facts disagree across two docs | Pick one canonical source; link from the other |
69
+ | Doc, manifest, and CI disagree on same command | Apply precedence (CI > manifest > source); report conflict if still ambiguous |
@@ -0,0 +1,59 @@
1
+ # Drift verification
2
+
3
+ **Load when:** Phase 4 — after any patch, or when reporting "docs already in sync."
4
+
5
+ **Do NOT load when:** Recon-only pass with zero edits planned — skip to a short "no drift found" note instead.
6
+
7
+ **Truth conflicts:** When sources disagree, apply **CI > manifest scripts/targets > source comments**. Report unresolved conflicts in **Gaps** — do not patch until the user picks a winner.
8
+
9
+ ## Checklist
10
+
11
+ Mark each item that applies to scope. All applicable items must pass before "done."
12
+
13
+ ### Always (any patch)
14
+
15
+ - [ ] Every edited claim traces to a truth file read this session (path named in report)
16
+ - [ ] No code or config files changed unless the user explicitly asked to fix defaults
17
+ - [ ] Diff stayed within scoped doc files — no cosmetic-only edits elsewhere
18
+
19
+ ### Full sync
20
+
21
+ - [ ] README commands match manifest scripts/targets (or language equivalent); in monorepos, root README uses root manifest — package READMEs use package manifest only
22
+ - [ ] Config template in docs matches repo config defaults
23
+ - [ ] Path references in edited docs still exist on disk
24
+ - [ ] Duplicate facts across docs agree — one canonical source preferred
25
+
26
+ ### Optional (when root instruction files were in scope)
27
+
28
+ - [ ] Command and layout tables match manifest, CI, and current tree
29
+
30
+ ### Post-ship: dependency / manifest
31
+
32
+ - [ ] New manifest entries have matching README mention when user-facing
33
+ - [ ] Opt-in or experimental features documented when defaults are off
34
+
35
+ ## Commands (adapt to project)
36
+
37
+ ```bash
38
+ # Which doc files changed
39
+ git diff --name-only -- '*.md'
40
+
41
+ # Node: list scripts
42
+ node -e "console.log(require('./package.json').scripts)"
43
+
44
+ # Rust: list bin targets
45
+ cargo metadata --no-deps --format-version 1 | head
46
+
47
+ # Python: project scripts (when pyproject.toml exists)
48
+ python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb')).get('project',{}).get('scripts',{}))" 2>/dev/null || true
49
+ ```
50
+
51
+ Run the project's test/lint command when code changed in the same task (from README, CONTRIBUTING, CI config, or manifest scripts). Markdown-only sync does not require a full test run.
52
+
53
+ ## Report blockers
54
+
55
+ If a checklist item fails, report **blocked** with:
56
+
57
+ - which item failed
58
+ - what truth vs doc say
59
+ - whether user decision is needed (e.g. intentional undocumented feature)
@@ -1,3 +1,11 @@
1
+ /**
2
+ * BTW (side-thread) injection for hotmilk.
3
+ *
4
+ * Strips main-session harness sections, replaces append prompts, and proxies
5
+ * context-mode/graphify tools into pi-btw sub-sessions without loading bundled
6
+ * extensions there.
7
+ */
8
+
1
9
  import { execFileSync } from "node:child_process";
2
10
  import { existsSync } from "node:fs";
3
11
  import { join } from "node:path";
@@ -17,16 +25,22 @@ import { bundledImportUrl } from "./resolve-bundled.ts";
17
25
 
18
26
  // --- Session config (injected before btw extension loads) ---
19
27
 
28
+ /** Per-session config injected into the BTW extension before it loads. */
20
29
  export type HotmilkBtwConfig = {
21
30
  extensionToggles: Readonly<Record<BundledExtensionId, boolean>>;
22
31
  };
23
32
 
24
33
  let activeConfig: HotmilkBtwConfig | null = null;
25
34
 
35
+ /** Inject the active hotmilk config for the upcoming BTW session. */
26
36
  export function setHotmilkBtwConfig(config: HotmilkBtwConfig): void {
27
37
  activeConfig = config;
28
38
  }
29
39
 
40
+ /**
41
+ * Return the config injected by {@link setHotmilkBtwConfig}.
42
+ * @throws when called before registration.
43
+ */
30
44
  export function getHotmilkBtwConfig(): HotmilkBtwConfig {
31
45
  if (!activeConfig) {
32
46
  throw new Error(
@@ -36,12 +50,14 @@ export function getHotmilkBtwConfig(): HotmilkBtwConfig {
36
50
  return activeConfig;
37
51
  }
38
52
 
53
+ /** Clear the active config so tests start from a clean state. */
39
54
  export function resetHotmilkBtwConfigForTests(): void {
40
55
  activeConfig = null;
41
56
  }
42
57
 
43
58
  // --- Prompt shaping ---
44
59
 
60
+ /** Remove dynamically appended date/time/cwd footer lines from a system prompt. */
45
61
  export function stripDynamicSystemPromptFooter(systemPrompt: string): string {
46
62
  return systemPrompt
47
63
  .replace(/\nCurrent date and time:[^\n]*(?:\nCurrent working directory:[^\n]*)?$/u, "")
@@ -59,6 +75,7 @@ const MAIN_SESSION_SECTION_MARKERS = [
59
75
  /^<behavioral_directive>/im,
60
76
  ] as const;
61
77
 
78
+ /** Strip hotmilk-specific harness sections from the main session prompt. */
62
79
  export function stripHotmilkMainSessionHarness(systemPrompt: string): string {
63
80
  let next = systemPrompt;
64
81
  for (const marker of MAIN_SESSION_SECTION_MARKERS) {
@@ -69,6 +86,7 @@ export function stripHotmilkMainSessionHarness(systemPrompt: string): string {
69
86
  return next.trim();
70
87
  }
71
88
 
89
+ /** Base system prompt used for BTW side threads. */
72
90
  export const HOTMILK_BTW_SYSTEM_PROMPT = [
73
91
  "You are having an aside conversation with the user, separate from their main working session.",
74
92
  "If main session messages are provided, they are for context only — that work is being handled by another agent.",
@@ -80,6 +98,11 @@ export const HOTMILK_BTW_SYSTEM_PROMPT = [
80
98
  "ctx_search is available as a read-only proxy to the main session knowledge base — use it for timeline recall, prior decisions, and indexed content. Do not use ctx_execute, ctx_batch_execute, or ctx_purge from BTW; loading context-mode here would spawn a second MCP child.",
81
99
  ].join(" ");
82
100
 
101
+ /**
102
+ * Build the extra instructions appended to BTW sessions.
103
+ * @param options - toggles that determine which routing hints to include
104
+ * @returns ordered instruction lines
105
+ */
83
106
  export function buildHotmilkBtwAppendPrompt(options: {
84
107
  graphifyEnabled: boolean;
85
108
  subagentsEnabled: boolean;
@@ -126,6 +149,13 @@ function isBtwSummarizeSession(loader: ResourceLoader): boolean {
126
149
  return text.includes("Summarize the side conversation");
127
150
  }
128
151
 
152
+ /**
153
+ * Wrap a Pi resource loader so BTW gets a neutral loader: no bundled extensions,
154
+ * a stripped system prompt, and hotmilk-tailored append instructions.
155
+ *
156
+ * @param loader - upstream pi-btw loader
157
+ * @param config - hotmilk toggle config
158
+ */
129
159
  export function adaptBtwResourceLoaderForHotmilk(
130
160
  loader: ResourceLoader,
131
161
  config: HotmilkBtwConfig,
@@ -175,6 +205,10 @@ type MainCtxSearchTool = {
175
205
  let mainCtxSearchTool: MainCtxSearchTool | null = null;
176
206
  let ctxSearchCaptureInstalled = false;
177
207
 
208
+ /**
209
+ * Store the main-session `ctx_search` tool definition so BTW can proxy it.
210
+ * Ignores tools whose name is not `ctx_search`.
211
+ */
178
212
  export function captureMainCtxSearchTool(tool: {
179
213
  name: string;
180
214
  description: string;
@@ -190,10 +224,12 @@ export function captureMainCtxSearchTool(tool: {
190
224
  };
191
225
  }
192
226
 
227
+ /** @returns the captured tool, or null if none has been registered. */
193
228
  export function getMainCtxSearchToolForTests(): MainCtxSearchTool | null {
194
229
  return mainCtxSearchTool;
195
230
  }
196
231
 
232
+ /** Reset captured tool and installation guard for tests. */
197
233
  export function resetMainCtxSearchCaptureForTests(): void {
198
234
  mainCtxSearchTool = null;
199
235
  ctxSearchCaptureInstalled = false;
@@ -254,10 +290,18 @@ const CTX_SEARCH_FALLBACK_PARAMS = Type.Object({
254
290
  ),
255
291
  });
256
292
 
293
+ /** Check whether a graphify graph exists in the working directory. */
257
294
  export function graphifyGraphExists(cwd = process.cwd()): boolean {
258
295
  return existsSync(join(cwd, GRAPH_JSON));
259
296
  }
260
297
 
298
+ /**
299
+ * Choose the base tool set exposed in BTW.
300
+ *
301
+ * Read-biased when subagents are enabled; otherwise keeps edit/write tools.
302
+ *
303
+ * @param config - hotmilk toggle config
304
+ */
261
305
  export function resolveHotmilkBtwTools(config: HotmilkBtwConfig): string[] {
262
306
  if (config.extensionToggles.subagents === true) {
263
307
  return ["read", "grep", "find", "ls", "bash"];
@@ -293,6 +337,11 @@ function createCtxSearchProxyTool(mainTool: MainCtxSearchTool | null): ToolDefin
293
337
  });
294
338
  }
295
339
 
340
+ /**
341
+ * Build BTW custom tools (`ctx_search` proxy, `graphify_query`) based on toggles.
342
+ *
343
+ * @param config - hotmilk toggle config
344
+ */
296
345
  export function createHotmilkBtwCustomTools(config: HotmilkBtwConfig): ToolDefinition[] {
297
346
  const tools: ToolDefinition[] = [];
298
347
 
@@ -1,3 +1,10 @@
1
+ /**
2
+ * RTK-optimizer / context-mode coexistence bootstrap.
3
+ *
4
+ * Seeds and synchronizes pi-rtk-optimizer config so it works alongside
5
+ * context-mode, and prunes stale context-mode MCP server entries.
6
+ */
7
+
1
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
9
  import { dirname } from "node:path";
3
10
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
@@ -8,15 +15,25 @@ import type { BundledExtensionId } from "../config/hotmilk.ts";
8
15
  const RTK_EXTENSION_DIR = "pi-rtk-optimizer";
9
16
  const RTK_CONFIG_FILENAME = "config.json";
10
17
 
18
+ /** @returns the absolute path to pi-rtk-optimizer's `config.json`. */
11
19
  export function getRtkOptimizerConfigPath(): string {
12
20
  return `${getAgentDir()}/extensions/${RTK_EXTENSION_DIR}/${RTK_CONFIG_FILENAME}`;
13
21
  }
14
22
 
23
+ /**
24
+ * Decide the RTK mode that best coexists with context-mode.
25
+ *
26
+ * @returns `"suggest"` when context-mode is on, `"rewrite"` otherwise.
27
+ */
15
28
  export function expectedRtkMode(contextModeEnabled: boolean): "suggest" | "rewrite" {
16
29
  return contextModeEnabled ? "suggest" : "rewrite";
17
30
  }
18
31
 
19
- /** Defaults when hotmilk seeds pi-rtk-optimizer config for the first time. */
32
+ /**
33
+ * Build the default pi-rtk-optimizer config shaped for hotmilk.
34
+ *
35
+ * @param contextModeEnabled - whether context-mode is toggled on
36
+ */
20
37
  export function buildHotmilkRtkConfig(contextModeEnabled: boolean): Record<string, unknown> {
21
38
  return {
22
39
  enabled: true,
@@ -53,17 +70,32 @@ function writeRtkConfig(configPath: string, config: unknown): void {
53
70
  writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
54
71
  }
55
72
 
73
+ /**
74
+ * Create pi-rtk-optimizer config if it does not already exist.
75
+ *
76
+ * @param contextModeEnabled - whether context-mode is toggled on
77
+ * @param configPath - override config path (default: agent extension dir)
78
+ * @returns seed result, with `error` set on filesystem failure
79
+ */
56
80
  export function seedRtkConfigIfMissing(
57
81
  contextModeEnabled: boolean,
58
82
  configPath = getRtkOptimizerConfigPath(),
59
- ): { seeded: boolean; path: string } {
83
+ ): { seeded: boolean; path: string; error?: string } {
60
84
  if (existsSync(configPath)) {
61
85
  return { seeded: false, path: configPath };
62
86
  }
63
87
 
64
- mkdirSync(dirname(configPath), { recursive: true });
65
- writeRtkConfig(configPath, buildHotmilkRtkConfig(contextModeEnabled));
66
- return { seeded: true, path: configPath };
88
+ try {
89
+ mkdirSync(dirname(configPath), { recursive: true });
90
+ writeRtkConfig(configPath, buildHotmilkRtkConfig(contextModeEnabled));
91
+ return { seeded: true, path: configPath };
92
+ } catch (error) {
93
+ return {
94
+ seeded: false,
95
+ path: configPath,
96
+ error: error instanceof Error ? error.message : String(error),
97
+ };
98
+ }
67
99
  }
68
100
 
69
101
  /** Hotmilk-managed fields only — does not touch Pi auto-compaction (settings.json). */
@@ -87,30 +119,55 @@ function alignRtkConfigWithContextMode(
87
119
  return changed;
88
120
  }
89
121
 
122
+ /**
123
+ * Keep pi-rtk-optimizer config aligned with the context-mode toggle.
124
+ *
125
+ * Seeds when missing, otherwise updates `mode` and `readCompaction`.
126
+ *
127
+ * @param contextModeEnabled - whether context-mode is toggled on
128
+ * @param rtkEnabled - whether rtk-optimizer is toggled on
129
+ * @param configPath - override config path (default: agent extension dir)
130
+ */
90
131
  export function syncRtkConfigForContextStack(
91
132
  contextModeEnabled: boolean,
92
133
  rtkEnabled: boolean,
93
134
  configPath = getRtkOptimizerConfigPath(),
94
- ): { updated: boolean; seeded: boolean; path: string } {
135
+ ): { updated: boolean; seeded: boolean; path: string; error?: string } {
95
136
  if (!rtkEnabled) {
96
137
  return { updated: false, seeded: false, path: configPath };
97
138
  }
98
139
 
99
140
  if (!existsSync(configPath)) {
100
- seedRtkConfigIfMissing(contextModeEnabled, configPath);
141
+ const seeded = seedRtkConfigIfMissing(contextModeEnabled, configPath);
142
+ if (seeded.error) {
143
+ return { updated: false, seeded: false, path: configPath, error: seeded.error };
144
+ }
101
145
  return { updated: true, seeded: true, path: configPath };
102
146
  }
103
147
 
104
- const config = JSON.parse(readFileSync(configPath, "utf8")) as RtkConfigRecord;
105
- const changed = alignRtkConfigWithContextMode(config, contextModeEnabled);
106
- if (changed) {
107
- writeRtkConfig(configPath, config);
108
- }
148
+ try {
149
+ const config = JSON.parse(readFileSync(configPath, "utf8")) as RtkConfigRecord;
150
+ const changed = alignRtkConfigWithContextMode(config, contextModeEnabled);
151
+ if (changed) {
152
+ writeRtkConfig(configPath, config);
153
+ }
109
154
 
110
- return { updated: changed, seeded: false, path: configPath };
155
+ return { updated: changed, seeded: false, path: configPath };
156
+ } catch (error) {
157
+ return {
158
+ updated: false,
159
+ seeded: false,
160
+ path: configPath,
161
+ error: error instanceof Error ? error.message : String(error),
162
+ };
163
+ }
111
164
  }
112
165
 
113
- /** Run before bundled extensions register (rtk seed/sync when that toggle is on). */
166
+ /**
167
+ * Eagerly seed/sync RTK config before bundled extensions register.
168
+ *
169
+ * @param extensionToggles - resolved bundled-extension toggle state
170
+ */
114
171
  export function prepareContextStack(extensionToggles: Record<BundledExtensionId, boolean>): void {
115
172
  if (extensionToggles["rtk-optimizer"]) {
116
173
  syncRtkConfigForContextStack(extensionToggles["context-mode"], true);
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Dashboard bridge settings cleanup.
3
+ *
4
+ * Removes redundant standalone dashboard bridge entries from Pi agent settings
5
+ * when hotmilk already bundles the agent-dashboard extension.
6
+ */
7
+
1
8
  import fs from "node:fs";
2
9
  import os from "node:os";
3
10
  import path from "node:path";
@@ -37,13 +44,20 @@ function isBundledDashboardExtensionEntry(entry: string): boolean {
37
44
  return readPackageName(entry) === DASHBOARD_EXTENSION_NAME;
38
45
  }
39
46
 
47
+ /** Options for {@link pruneRedundantDashboardPackage}. */
40
48
  export interface PruneDashboardSettingsOptions {
49
+ /** Override `$HOME` resolution. */
41
50
  homedir?: string;
42
51
  }
43
52
 
44
53
  /**
45
- * When hotmilk bundles agent-dashboard, drop standalone bridge paths from
46
- * ~/.pi/agent/settings.json that pi-dashboard start auto-registers.
54
+ * Drop standalone dashboard-bridge paths from `~/.pi/agent/settings.json`.
55
+ *
56
+ * The bundled agent-dashboard extension already provides the bridge, so the
57
+ * legacy standalone package entry is redundant.
58
+ *
59
+ * @param opts - optional overrides
60
+ * @returns whether any entries were removed
47
61
  */
48
62
  export function pruneRedundantDashboardPackage(opts: PruneDashboardSettingsOptions = {}): boolean {
49
63
  const home = opts.homedir ?? process.env.HOME ?? process.env.USERPROFILE ?? os.homedir();