From 6bb3d66004d108d31c925848d271c6c5fc25bb8c Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sun, 9 Aug 2026 18:22:52 +0200 Subject: [PATCH] docs: Read My Mind user guide (enable, capture rules, privacy, API, troubleshooting) docs/readmymind.md covers phase 1 as a user guide: how to enable the synced readMyMindEnabled setting via the API (no UI checkbox until phase 2), exactly what is and is not captured, the hooks dependency (Docker bridge / remote-SSH caveats), storage and wipe paths, curl examples for the three endpoints, the agent-skill ground rules, and a troubleshooting table. Cross-linked from the CLAUDE.md Key Patterns entry and the api-reference section. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 2 +- docs/api-reference.md | 3 +- docs/readmymind.md | 86 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 docs/readmymind.md diff --git a/CLAUDE.md b/CLAUDE.md index 094efaa4..81bc048b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -210,7 +210,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph **Approvals Inbox** (cross-session queue of prompts waiting on a human; `approvalsInboxEnabled`, SYNCED, default OFF: every surface is opt-in; only the store and answer endpoints run regardless, so flipping it ON shows anything already pending): `web/approval-inbox.ts` is a `sessionWaits`-style singleton fed by `/api/hook-event`, holding at most ONE item per session (a new prompt supersedes), claude-mode only, in-memory. Cards are answered via `POST /api/approvals/:id/answer`, which sends a digit / Esc / idle-prompt text through `writeViaMux` (menu answers never carry `\r`). ⚠️ `option` digits are accepted ONLY when they match options parsed from the captured pane frame, and the answer path RE-CAPTURES the pane first (a dialog that no longer parses on screen means the keystroke would land in the composer, so refuse with 409). ⚠️ Resolution on the heuristic `working` signal is restricted to `idle` items; permission/question items clear only on definitive signals (`stop`, `elicitation_complete`/`elicitation_response`, exit/delete, answer, supersede, 12h TTL). The frontend seeds from `GET /api/approvals` in `handleInit` (which is what makes tab alerts survive reloads), but only with the setting ON; push Approve/Deny buttons are also gated on it (`sendPushNotifications` strips `actions`/`approvalId` when OFF) and are answered from `sw.js` directly so they work with no tab open. Surfaces (all gated on the setting): header bell (marker-hidden until count > 0, phones never show it) + drawer (`approvals-ui.js`), phone overview NEEDS YOU answer strips (`mobile-overview.js`). Design: `docs/approvals-inbox-plan.md`. -**Read My Mind intent profiles** (phase 1 of `docs/readmymind-plan.md`; `readMyMindEnabled`, SYNCED, default OFF): per-CASE profiles (user-stated `goals` + the user's recent real prompts), keyed by owner + realpath(workingDir) so they survive `/clear`/respawns and multi-user scoping is structural. Capture rides the transcript (`transcript:user_prompt` from `transcript-watcher.ts`), NOT the input paths: `POST /input` sees only programmatic prompts and the WS channel is raw keystrokes. The listener lives inside `startTranscriptWatcher()`'s `if (!watcher)` block (outside it would duplicate per hook event) and is claude-only + gated on the setting per event. Store: `src/intent-store.ts` singleton, `intents.json` written 0600 tmp+rename (prompts can contain secrets; never fed to `/api/search`). Endpoints: GET/PUT/DELETE `/api/sessions/:id/intent` (`readmymind-routes.ts`, ownership via `findSessionOrFail` WITH `req`). The predictor/button are phase 2; nothing auto-sends, ever. +**Read My Mind intent profiles** (phase 1 of `docs/readmymind-plan.md`; `readMyMindEnabled`, SYNCED, default OFF): per-CASE profiles (user-stated `goals` + the user's recent real prompts), keyed by owner + realpath(workingDir) so they survive `/clear`/respawns and multi-user scoping is structural. Capture rides the transcript (`transcript:user_prompt` from `transcript-watcher.ts`), NOT the input paths: `POST /input` sees only programmatic prompts and the WS channel is raw keystrokes. The listener lives inside `startTranscriptWatcher()`'s `if (!watcher)` block (outside it would duplicate per hook event) and is claude-only + gated on the setting per event. Store: `src/intent-store.ts` singleton, `intents.json` written 0600 tmp+rename (prompts can contain secrets; never fed to `/api/search`). Endpoints: GET/PUT/DELETE `/api/sessions/:id/intent` (`readmymind-routes.ts`, ownership via `findSessionOrFail` WITH `req`). The predictor/button are phase 2; nothing auto-sends, ever. User guide: `docs/readmymind.md`. **Agent Teams**: `TeamWatcher` polls `~/.claude/teams/`, matches to sessions via `leadSessionId`. Teammates are in-process threads appearing as subagents. Enable: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. See `docs/agent-teams/`. diff --git a/docs/api-reference.md b/docs/api-reference.md index f22a9fad..369fc806 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -441,7 +441,8 @@ goals plus the user's recently submitted prompts, captured from the Claude session transcript while the opt-in `readMyMindEnabled` setting is on (default OFF). Keyed by owner + workingDir, so the profile survives `/clear`, respawns, and session churn. Stored in `~/.codeman/intents.json` (mode 0600); never fed -into `/api/v1/search`. Design: [`readmymind-plan.md`](readmymind-plan.md). +into `/api/v1/search`. Design: [`readmymind-plan.md`](readmymind-plan.md); +user guide: [`readmymind.md`](readmymind.md). - `GET /api/v1/sessions/:id/intent` -> `{ intent: IntentProfile }` for the session's case. `IntentProfile`: `{ key, workingDir, updatedAt, goals, diff --git a/docs/readmymind.md b/docs/readmymind.md new file mode 100644 index 00000000..0e780e31 --- /dev/null +++ b/docs/readmymind.md @@ -0,0 +1,86 @@ +# Read My Mind + +Codeman's per-case memory of what you are trying to accomplish. Each case gets an **intent profile**: a freeform `goals` text (written by you or your agent) plus the prompts you actually submitted, captured automatically while the feature is on. Phase 1 (this document) ships the profile itself, its API, and the agent-skill verbs. Phase 2 adds the 🧠 button that turns the profile into a predicted next prompt you can accept, edit, or rethink; the design for that lives in [`readmymind-plan.md`](readmymind-plan.md). Nothing is ever sent to a session automatically, in any phase. + +## What it does today (phase 1) + +- Captures the prompts you submit in Claude sessions into a per-case history (50 most recent, bounded). +- Lets you (or your agent) record explicit goals per case. +- Exposes the profile over the HTTP API, and to agents through the `codeman` skill, so an agent can ground its work in what you actually want instead of guessing from the last screenful. + +## Turning it on + +The synced setting `readMyMindEnabled` (default **OFF**) gates capture. There is no App Settings checkbox yet (that arrives with the phase-2 UI), so flip it over the API: + +```bash +curl -sk -X PUT https://localhost:3000/api/settings \ + -H 'Content-Type: application/json' \ + -d '{"readMyMindEnabled": true}' +``` + +Add `-u user:password` if your install has `CODEMAN_PASSWORD` set, and drop `-k`/use `http://` for a plain-HTTP dev server. Turning it OFF stops capture immediately; existing profiles stay until you delete them (below). + +## What gets captured, exactly + +Capture reads the Claude session transcript, not your keystrokes: when a user turn lands in the transcript, its text is folded into the case's profile. Filters applied on the way in: + +- **Claude-mode sessions only.** Shell, OpenCode, Codex, Gemini, and Antigravity sessions are never captured (they have no transcript watcher). +- Tool results, local slash-command echo (`/model` and friends), system wrappers, and interrupt markers are skipped. +- Entries shorter than 3 characters are skipped (menu digits, Esc artifacts). +- Consecutive duplicates collapse (auto-resume's "continue" spam counts once per run). +- Each prompt is stored as one line, truncated to 500 characters; the history caps at 50 prompts FIFO. + +Because the transcript path arrives via Claude Code hooks, capture needs hooks to reach the server, the same condition as hook-based idle detection. Docker cases against a loopback-only server need `CODEMAN_DOCKER_BRIDGE_HOOKS=1`; remote-SSH cases do not capture. + +## What is never captured + +- Anything while `readMyMindEnabled` is OFF (capture is not retroactive). +- Terminal output, keystrokes, passwords typed into shells: only submitted Claude prompts are read. +- Nothing leaves the machine, and profiles are never fed into `/api/search`. + +## Where it lives, and how to wipe it + +Profiles live in `~/.codeman/intents.json`, written atomically at mode 0600 (captured prompts can contain secrets). The file is per Codeman instance. Keys derive from owner + the case's resolved working directory, so profiles survive `/clear`, respawn cycles, and session churn, and in multi-user mode two owners of the same directory get separate profiles. + +Forget one case: `DELETE /api/sessions/:id/intent` (below). Forget everything: stop the server and delete `~/.codeman/intents.json`. + +## The API + +Three endpoints, session-scoped so ownership is enforced by the session itself (`/api/v1/` aliases work too; full spec in [`api-reference.md`](api-reference.md)): + +```bash +# Read the profile for a session's case +curl -sk https://localhost:3000/api/sessions/$SID/intent | jq '.data.intent' + +# Record goals (REPLACES the text: read + merge if you want to append) +curl -sk -X PUT https://localhost:3000/api/sessions/$SID/intent \ + -H 'Content-Type: application/json' \ + -d '{"goals":"ship 1.17; then mobile polish"}' + +# Forget the case +curl -sk -X DELETE https://localhost:3000/api/sessions/$SID/intent +``` + +A case with nothing recorded answers an empty profile with `updatedAt: 0`; reads never persist anything. Goals cap at 8192 characters and the schema is strict, so unknown fields or over-long goals answer `400 INVALID_INPUT`. A session you do not own answers `404 NOT_FOUND`, indistinguishable from a nonexistent one. + +## For agents (the skill) + +The `codeman` agent skill documents the same three verbs (SKILL.md §3 plus `reference/endpoints.md`), with the ground rules: read the profile to understand what the user wants, record goals the user actually stated, merge instead of blind-writing (PUT replaces), and never delete a profile unprompted. It is the user's memory, not the agent's. + +## What phase 2 adds + +The 🧠 button and the predictor: a context assembler feeds the profile, the last assistant turn, tool activity, git state, away context, and any pending approval dialog to a one-shot opus call, and the suggested next prompt appears in an approval dialog (Send / Insert to edit / Rethink with a steer note / Dismiss). See [`readmymind-plan.md`](readmymind-plan.md) for the full design, including the trust-tier rules that keep terminal output from steering suggestions. + +## Troubleshooting + +| Symptom | Cause / fix | +| ------- | ----------- | +| Profile stays empty although I am prompting | `readMyMindEnabled` was OFF at the time (capture is not retroactive), the session is not claude-mode, or hooks are not reaching the server (Docker case on a loopback bind without `CODEMAN_DOCKER_BRIDGE_HOOKS=1`, or a remote-SSH case) | +| Short answers I typed are missing | Entries under 3 characters are filtered by design (menu digits, Esc artifacts) | +| My goals text vanished after an agent wrote to it | PUT replaces the whole text; the skill tells agents to read + merge, but a blind write wins. Re-state the goals; consider phrasing them in the session so capture keeps the evidence | +| Two profiles for what I think is one case | Different owners in multi-user mode, or genuinely different directories; paths are realpath-resolved, so symlink spellings converge but distinct checkouts do not | +| `400 INVALID_INPUT` on PUT | Goals over 8192 chars, or an extra field in the body (strict schema) | + +## Where the code lives + +`src/intent-store.ts` (store + pure helpers, singleton), the `transcript:user_prompt` event in `src/transcript-watcher.ts`, capture wiring in `src/web/server.ts` (`captureIntentPrompt`), routes in `src/web/routes/readmymind-routes.ts`, schema in `src/web/schemas.ts`. Tests: `test/intent-store.test.ts`, `test/routes/readmymind-routes.test.ts`, and the capture cases in `test/transcript-watcher.test.ts`.