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.
- package/AGENTS.md +12 -10
- package/README.md +237 -53
- package/agents/README.md +3 -3
- package/agents/assistant.md +1 -1
- package/agents/coach.md +26 -4
- package/agents/designer.md +1 -1
- package/agents/planner.md +2 -2
- package/hotmilk.json +11 -0
- package/package.json +43 -24
- package/prompts/prompt-eval.md +241 -0
- package/prompts/tidy.md +3 -1
- package/prompts/translate.md +3 -1
- package/skills/make-docs/SKILL.md +43 -0
- package/skills/pioneer/SKILL.md +135 -131
- package/skills/pioneer/references/autoresearch-routing.md +114 -0
- package/skills/pioneer/references/chat-plan.md +50 -0
- package/skills/pioneer/references/goal-gate.md +54 -0
- package/skills/pioneer/references/graph-recon-gate.md +50 -0
- package/skills/pioneer/references/observational-memory-routing.md +90 -0
- package/skills/pioneer/references/openspec-routing.md +50 -0
- package/skills/pioneer/references/plannotator-routing.md +83 -0
- package/skills/pioneer/references/prompt-eval-gate.md +38 -0
- package/skills/pioneer/references/shazam-routing.md +58 -0
- package/skills/recommend-research/SKILL.md +94 -6
- package/skills/recommend-research/references/example-narrow.md +53 -0
- package/skills/recommend-research/references/scope-gate.md +46 -0
- package/skills/recommend-research/references/shortlist-gate.md +69 -0
- package/skills/recommend-research/references/verify-gate.md +73 -0
- package/skills/update-docs/SKILL.md +86 -0
- package/skills/update-docs/references/doc-inventory.md +69 -0
- package/skills/update-docs/references/drift-verification.md +59 -0
- package/src/bootstrap/btw.ts +49 -0
- package/src/bootstrap/context-stack.ts +71 -14
- package/src/bootstrap/dashboard-settings.ts +16 -2
- package/src/bootstrap/dashboard.ts +85 -10
- package/src/bootstrap/defaults.ts +26 -0
- package/src/bootstrap/extensions.ts +10 -1
- package/src/bootstrap/global-extension-sources.ts +43 -4
- package/src/bootstrap/graph.ts +12 -0
- package/src/bootstrap/project-trust.ts +55 -0
- package/src/bootstrap/resolve-bundled.ts +28 -4
- package/src/bootstrap/session.ts +61 -1
- package/src/bootstrap/subagents-doctor.ts +12 -0
- package/src/config/bundled-extensions.ts +42 -0
- package/src/config/bundled-package-registry.ts +3 -0
- package/src/config/hotmilk.ts +76 -1
- package/src/config/mcp.ts +45 -12
- package/src/config/resolve.ts +21 -0
- package/src/config/runtime.ts +6 -0
- package/src/controller/input.ts +15 -0
- package/src/controller/mode.ts +32 -0
- package/src/index.ts +5 -0
- package/src/ui/footer.ts +63 -1
- package/src/ui/github-user.ts +52 -4
- package/src/ui/session-logo.ts +31 -3
- package/themes/monokai.json +1 -1
- 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)
|
package/src/bootstrap/btw.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
46
|
-
*
|
|
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();
|