@draht/coding-agent 2026.3.5 → 2026.3.11-1

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 (193) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +6 -2
  3. package/agents/architect.md +1 -0
  4. package/agents/debugger.md +1 -0
  5. package/agents/git-committer.md +1 -0
  6. package/agents/implementer.md +1 -0
  7. package/agents/reviewer.md +1 -0
  8. package/agents/security-auditor.md +1 -0
  9. package/agents/verifier.md +1 -0
  10. package/dist/agents/architect.md +1 -0
  11. package/dist/agents/debugger.md +1 -0
  12. package/dist/agents/git-committer.md +1 -0
  13. package/dist/agents/implementer.md +1 -0
  14. package/dist/agents/reviewer.md +1 -0
  15. package/dist/agents/security-auditor.md +1 -0
  16. package/dist/agents/verifier.md +1 -0
  17. package/dist/cli/args.d.ts +6 -0
  18. package/dist/cli/args.d.ts.map +1 -1
  19. package/dist/cli/args.js +24 -0
  20. package/dist/cli/args.js.map +1 -1
  21. package/dist/cli/attach-mode.d.ts +13 -0
  22. package/dist/cli/attach-mode.d.ts.map +1 -0
  23. package/dist/cli/attach-mode.js +97 -0
  24. package/dist/cli/attach-mode.js.map +1 -0
  25. package/dist/cli/list-sessions.d.ts +8 -0
  26. package/dist/cli/list-sessions.d.ts.map +1 -0
  27. package/dist/cli/list-sessions.js +52 -0
  28. package/dist/cli/list-sessions.js.map +1 -0
  29. package/dist/config.d.ts +7 -0
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/config.js +15 -0
  32. package/dist/config.js.map +1 -1
  33. package/dist/core/agent-session.d.ts +1 -0
  34. package/dist/core/agent-session.d.ts.map +1 -1
  35. package/dist/core/agent-session.js +50 -17
  36. package/dist/core/agent-session.js.map +1 -1
  37. package/dist/core/auth-storage.d.ts +2 -1
  38. package/dist/core/auth-storage.d.ts.map +1 -1
  39. package/dist/core/auth-storage.js +25 -1
  40. package/dist/core/auth-storage.js.map +1 -1
  41. package/dist/core/compaction/utils.d.ts +3 -0
  42. package/dist/core/compaction/utils.d.ts.map +1 -1
  43. package/dist/core/compaction/utils.js +16 -1
  44. package/dist/core/compaction/utils.js.map +1 -1
  45. package/dist/core/export-html/index.d.ts +5 -2
  46. package/dist/core/export-html/index.d.ts.map +1 -1
  47. package/dist/core/export-html/index.js +4 -3
  48. package/dist/core/export-html/index.js.map +1 -1
  49. package/dist/core/export-html/template.js +11 -14
  50. package/dist/core/export-html/tool-renderer.d.ts +5 -2
  51. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  52. package/dist/core/export-html/tool-renderer.js +12 -5
  53. package/dist/core/export-html/tool-renderer.js.map +1 -1
  54. package/dist/core/extensions/index.d.ts +1 -1
  55. package/dist/core/extensions/index.d.ts.map +1 -1
  56. package/dist/core/extensions/index.js.map +1 -1
  57. package/dist/core/extensions/loader.d.ts.map +1 -1
  58. package/dist/core/extensions/loader.js +6 -6
  59. package/dist/core/extensions/loader.js.map +1 -1
  60. package/dist/core/extensions/runner.d.ts +3 -2
  61. package/dist/core/extensions/runner.d.ts.map +1 -1
  62. package/dist/core/extensions/runner.js +32 -0
  63. package/dist/core/extensions/runner.js.map +1 -1
  64. package/dist/core/extensions/types.d.ts +21 -2
  65. package/dist/core/extensions/types.d.ts.map +1 -1
  66. package/dist/core/extensions/types.js.map +1 -1
  67. package/dist/core/model-resolver.d.ts.map +1 -1
  68. package/dist/core/model-resolver.js +2 -2
  69. package/dist/core/model-resolver.js.map +1 -1
  70. package/dist/core/package-manager.d.ts.map +1 -1
  71. package/dist/core/package-manager.js +8 -8
  72. package/dist/core/package-manager.js.map +1 -1
  73. package/dist/core/prompt-templates.d.ts.map +1 -1
  74. package/dist/core/prompt-templates.js +4 -4
  75. package/dist/core/prompt-templates.js.map +1 -1
  76. package/dist/core/resource-loader.d.ts.map +1 -1
  77. package/dist/core/resource-loader.js +10 -9
  78. package/dist/core/resource-loader.js.map +1 -1
  79. package/dist/core/sdk.d.ts.map +1 -1
  80. package/dist/core/sdk.js +7 -0
  81. package/dist/core/sdk.js.map +1 -1
  82. package/dist/core/settings-manager.d.ts +4 -0
  83. package/dist/core/settings-manager.d.ts.map +1 -1
  84. package/dist/core/settings-manager.js +38 -4
  85. package/dist/core/settings-manager.js.map +1 -1
  86. package/dist/core/skills.d.ts.map +1 -1
  87. package/dist/core/skills.js +3 -3
  88. package/dist/core/skills.js.map +1 -1
  89. package/dist/core/socket-server/discovery.d.ts +19 -0
  90. package/dist/core/socket-server/discovery.d.ts.map +1 -0
  91. package/dist/core/socket-server/discovery.js +91 -0
  92. package/dist/core/socket-server/discovery.js.map +1 -0
  93. package/dist/core/socket-server/index.d.ts +13 -0
  94. package/dist/core/socket-server/index.d.ts.map +1 -0
  95. package/dist/core/socket-server/index.js +11 -0
  96. package/dist/core/socket-server/index.js.map +1 -0
  97. package/dist/core/socket-server/session-integration.d.ts +17 -0
  98. package/dist/core/socket-server/session-integration.d.ts.map +1 -0
  99. package/dist/core/socket-server/session-integration.js +77 -0
  100. package/dist/core/socket-server/session-integration.js.map +1 -0
  101. package/dist/core/socket-server/socket-client.d.ts +65 -0
  102. package/dist/core/socket-server/socket-client.d.ts.map +1 -0
  103. package/dist/core/socket-server/socket-client.js +197 -0
  104. package/dist/core/socket-server/socket-client.js.map +1 -0
  105. package/dist/core/socket-server/socket-server.d.ts +60 -0
  106. package/dist/core/socket-server/socket-server.d.ts.map +1 -0
  107. package/dist/core/socket-server/socket-server.js +273 -0
  108. package/dist/core/socket-server/socket-server.js.map +1 -0
  109. package/dist/core/socket-server/types.d.ts +81 -0
  110. package/dist/core/socket-server/types.d.ts.map +1 -0
  111. package/dist/core/socket-server/types.js +8 -0
  112. package/dist/core/socket-server/types.js.map +1 -0
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js.map +1 -1
  116. package/dist/main.d.ts.map +1 -1
  117. package/dist/main.js +76 -11
  118. package/dist/main.js.map +1 -1
  119. package/dist/migrations.d.ts.map +1 -1
  120. package/dist/migrations.js +2 -2
  121. package/dist/migrations.js.map +1 -1
  122. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  123. package/dist/modes/interactive/components/config-selector.js +3 -3
  124. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  125. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  126. package/dist/modes/interactive/components/extension-editor.js +1 -0
  127. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  128. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  129. package/dist/modes/interactive/components/footer.js +8 -23
  130. package/dist/modes/interactive/components/footer.js.map +1 -1
  131. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  132. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  133. package/dist/modes/interactive/components/settings-selector.js +10 -0
  134. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  135. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  136. package/dist/modes/interactive/components/tool-execution.js +14 -4
  137. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  138. package/dist/modes/interactive/components/tree-selector.d.ts +21 -2
  139. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  140. package/dist/modes/interactive/components/tree-selector.js +115 -9
  141. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  142. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  143. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  144. package/dist/modes/interactive/interactive-mode.js +64 -5
  145. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  146. package/dist/modes/rpc/jsonl.d.ts +17 -0
  147. package/dist/modes/rpc/jsonl.d.ts.map +1 -0
  148. package/dist/modes/rpc/jsonl.js +49 -0
  149. package/dist/modes/rpc/jsonl.js.map +1 -0
  150. package/dist/modes/rpc/rpc-client.d.ts +1 -1
  151. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  152. package/dist/modes/rpc/rpc-client.js +7 -11
  153. package/dist/modes/rpc/rpc-client.js.map +1 -1
  154. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  155. package/dist/modes/rpc/rpc-mode.js +9 -11
  156. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  157. package/dist/prompts/commands/discuss-phase.md +10 -0
  158. package/dist/prompts/commands/execute-phase.md +51 -34
  159. package/dist/prompts/commands/fix.md +8 -6
  160. package/dist/prompts/commands/init-project.md +12 -0
  161. package/dist/prompts/commands/map-codebase.md +17 -18
  162. package/dist/prompts/commands/new-project.md +12 -0
  163. package/dist/prompts/commands/next-milestone.md +5 -3
  164. package/dist/prompts/commands/plan-phase.md +27 -5
  165. package/dist/prompts/commands/quick.md +12 -5
  166. package/dist/prompts/commands/review.md +10 -10
  167. package/dist/prompts/commands/verify-work.md +31 -17
  168. package/docs/compaction.md +2 -0
  169. package/docs/custom-provider.md +11 -7
  170. package/docs/extensions.md +55 -3
  171. package/docs/keybindings.md +9 -1
  172. package/docs/models.md +5 -1
  173. package/docs/rpc.md +40 -3
  174. package/docs/session.md +2 -2
  175. package/docs/settings.md +1 -0
  176. package/docs/terminal-setup.md +28 -3
  177. package/docs/tmux.md +61 -0
  178. package/docs/tree.md +9 -0
  179. package/examples/extensions/overlay-qa-tests.ts +468 -1
  180. package/examples/extensions/provider-payload.ts +14 -0
  181. package/examples/extensions/with-deps/index.ts +1 -5
  182. package/package.json +7 -5
  183. package/prompts/commands/discuss-phase.md +10 -0
  184. package/prompts/commands/execute-phase.md +51 -34
  185. package/prompts/commands/fix.md +8 -6
  186. package/prompts/commands/init-project.md +12 -0
  187. package/prompts/commands/map-codebase.md +17 -18
  188. package/prompts/commands/new-project.md +12 -0
  189. package/prompts/commands/next-milestone.md +5 -3
  190. package/prompts/commands/plan-phase.md +27 -5
  191. package/prompts/commands/quick.md +12 -5
  192. package/prompts/commands/review.md +10 -10
  193. package/prompts/commands/verify-work.md +31 -17
@@ -4,7 +4,7 @@ description: "Acceptance testing of completed phase work"
4
4
 
5
5
  # /verify-work
6
6
 
7
- Walk through acceptance testing of completed phase work.
7
+ Walk through acceptance testing of completed phase work, using subagents for parallel verification.
8
8
 
9
9
  ## Usage
10
10
  ```
@@ -14,20 +14,34 @@ Walk through acceptance testing of completed phase work.
14
14
  Phase: $1
15
15
 
16
16
  ## Steps
17
- 1. Run full test suite and capture results:
18
- - Execute all tests (`bun test` or project-specific runner)
19
- - Record pass/fail counts verification cannot proceed if tests are failing
20
- 2. Check domain language violations:
21
- - Load `.planning/DOMAIN.md` and extract all defined terms
22
- - Scan source files for PascalCase identifiers not present in the glossary
23
- - Flag any bounded context boundary violations (cross-context direct imports)
24
- 3. Run quality gate: `draht-tools quality-gate --strict`
25
- 4. Run `draht-tools extract-deliverables $1` to get testable items
26
- 5. Walk user through each deliverable one at a time
27
- 6. Record results (pass/fail/partially/skip)
28
- 7. For failures: diagnose and create fix plans via `draht-tools create-fix-plan $1 P`
17
+ 1. Run `draht-tools extract-deliverables $1` to get testable items
18
+ 2. **Run parallel verification via subagents:**
19
+ Use the `subagent` tool in **parallel mode** with these tasks:
20
+ - `verifier` agent: "Run the full test suite for this project. Check package.json for the test command. Record pass/fail counts. Then run any available lint and typecheck commands (e.g. npm run check, npm run lint, npx tsc --noEmit). Report all results with error details. Do NOT run draht, draht-tools, or pi commands."
21
+ - `security-auditor` agent: "Audit the recent code changes (use git log and git diff to find them). Check for injection risks, auth bypasses, secrets in code, unsafe patterns. Report findings by severity. Do NOT run draht, draht-tools, or pi commands."
22
+ - `reviewer` agent: "Review the recent code changes (use git log and git diff). Check domain language compliance against `.planning/DOMAIN.md` if it exists — scan for identifiers not in the glossary and cross-context boundary violations. Report findings. Do NOT run draht, draht-tools, or pi commands."
23
+
24
+ 3. Collect results from all subagents
25
+ 4. Walk user through each deliverable one at a time, incorporating findings from the parallel checks
26
+ 5. Record results (pass/fail/partially/skip)
27
+ 6. For failures: diagnose and create fix plans via `draht-tools create-fix-plan $1 P`
29
28
  - Fix plans MUST include a reproducing test that demonstrates the failure before any implementation
30
- 8. Write UAT report: `draht-tools write-uat $1`
31
- - Report must include: test health summary (pass/fail/coverage), domain model status (any glossary violations), deliverable results
32
- 9. If all passed: mark phase complete
33
- 10. If failures: route to `execute-phase $1 --gaps-only`
29
+ 7. Write UAT report: `draht-tools write-uat $1`
30
+ - Report must include: test health summary (pass/fail/coverage), security audit results, domain model status (any glossary violations), deliverable results
31
+ 8. If all passed: mark phase complete.
32
+ - If more phases remain in the milestone: tell the user to start a new session and run `/discuss-phase N+1`.
33
+ - If ALL phases in the milestone are complete: tell the user to start a new session and run `/next-milestone`.
34
+ 9. If failures: route to `execute-phase $1 --gaps-only`
35
+
36
+ ## Workflow
37
+ This is the last step in the per-phase cycle. Each step runs in its own session (`/new` between steps):
38
+
39
+ ```
40
+ /discuss-phase N → /new → /plan-phase N → /new → /execute-phase N → /new → /verify-work N
41
+ ```
42
+
43
+ After verify-work passes:
44
+ - More phases remaining → `/new` → `/discuss-phase N+1`
45
+ - ALL phases in milestone verified → `/new` → `/next-milestone`
46
+
47
+ `/next-milestone` is ONLY for generating new phases after every phase in the current milestone is complete.
@@ -262,6 +262,8 @@ Before summarization, messages are serialized to text via [`serializeConversatio
262
262
 
263
263
  This prevents the model from treating it as a conversation to continue.
264
264
 
265
+ Tool results are truncated to 2000 characters during serialization. Content beyond that limit is replaced with a marker indicating how many characters were truncated. This keeps summarization requests within reasonable token budgets, since tool results (especially from `read` and `bash`) are typically the largest contributors to context size.
266
+
265
267
  ## Custom Summarization via Extensions
266
268
 
267
269
  Extensions can intercept and customize both compaction and branch summarization. See [`extensions/types.ts`](https://github.com/draht-dev/draht/blob/main/packages/coding-agent/src/core/extensions/types.ts) for event type definitions.
@@ -159,6 +159,7 @@ The `api` field determines which streaming implementation is used:
159
159
  | `openai-responses` | OpenAI Responses API |
160
160
  | `azure-openai-responses` | Azure OpenAI Responses API |
161
161
  | `openai-codex-responses` | OpenAI Codex Responses API |
162
+ | `mistral-conversations` | Mistral SDK Conversations/Chat streaming |
162
163
  | `google-generative-ai` | Google Generative AI API |
163
164
  | `google-gemini-cli` | Google Cloud Code Assist API |
164
165
  | `google-vertex` | Google Vertex AI API |
@@ -180,14 +181,17 @@ models: [{
180
181
  high: "default",
181
182
  xhigh: "default"
182
183
  },
183
- maxTokensField: "max_tokens", // instead of "max_completion_tokens"
184
- requiresToolResultName: true, // tool results need name field
185
- requiresMistralToolIds: true,
186
- thinkingFormat: "qwen" // uses enable_thinking: true
187
- }
188
- }]
184
+ maxTokensField: "max_tokens", // instead of "max_completion_tokens"
185
+ requiresToolResultName: true, // tool results need name field
186
+ thinkingFormat: "qwen" // uses enable_thinking: true
187
+ }
188
+ }]
189
189
  ```
190
190
 
191
+ > Migration note: Mistral moved from `openai-completions` to `mistral-conversations`.
192
+ > Use `mistral-conversations` for native Mistral models.
193
+ > If you intentionally route Mistral-compatible/custom endpoints through `openai-completions`, set `compat` flags explicitly as needed.
194
+
191
195
  ### Auth Header
192
196
 
193
197
  If your provider expects `Authorization: Bearer <key>` but doesn't use a standard API, set `authHeader: true`:
@@ -301,6 +305,7 @@ For providers with non-standard APIs, implement `streamSimple`. Study the existi
301
305
 
302
306
  **Reference implementations:**
303
307
  - [anthropic.ts](https://github.com/draht-dev/draht/blob/main/packages/ai/src/providers/anthropic.ts) - Anthropic Messages API
308
+ - [mistral.ts](https://github.com/draht-dev/draht/blob/main/packages/ai/src/providers/mistral.ts) - Mistral Conversations API
304
309
  - [openai-completions.ts](https://github.com/draht-dev/draht/blob/main/packages/ai/src/providers/openai-completions.ts) - OpenAI Chat Completions
305
310
  - [openai-responses.ts](https://github.com/draht-dev/draht/blob/main/packages/ai/src/providers/openai-responses.ts) - OpenAI Responses API
306
311
  - [google.ts](https://github.com/draht-dev/draht/blob/main/packages/ai/src/providers/google.ts) - Google Generative AI
@@ -581,7 +586,6 @@ interface ProviderModelConfig {
581
586
  requiresToolResultName?: boolean;
582
587
  requiresAssistantAfterToolResult?: boolean;
583
588
  requiresThinkingAsText?: boolean;
584
- requiresMistralToolIds?: boolean;
585
589
  thinkingFormat?: "openai" | "zai" | "qwen";
586
590
  };
587
591
  }
@@ -225,8 +225,9 @@ Run `npm install` in the extension directory, then imports from `node_modules/`
225
225
  ### Lifecycle Overview
226
226
 
227
227
  ```
228
- pi starts
228
+ pi starts (CLI only)
229
229
 
230
+ ├─► session_directory (CLI startup only, no ctx)
230
231
  └─► session_start
231
232
 
232
233
 
@@ -243,6 +244,7 @@ user sends prompt ────────────────────
243
244
  │ │ │ │
244
245
  │ ├─► turn_start │ │
245
246
  │ ├─► context (can modify messages) │ │
247
+ │ ├─► before_provider_request (can inspect or replace payload)
246
248
  │ │ │ │
247
249
  │ │ LLM responds, may call tools: │ │
248
250
  │ │ ├─► tool_call (can block) │ │
@@ -284,6 +286,26 @@ exit (Ctrl+C, Ctrl+D)
284
286
 
285
287
  See [session.md](session.md) for session storage internals and the SessionManager API.
286
288
 
289
+ #### session_directory
290
+
291
+ Fired by the `pi` CLI during startup session resolution, before the initial session manager is created.
292
+
293
+ This event is:
294
+ - CLI-only. It is not emitted in SDK mode.
295
+ - Startup-only. It is not emitted for later interactive `/new` or `/resume` actions.
296
+ - Bypassed when `--session-dir` is provided.
297
+ - Special-cased to receive no `ctx` argument.
298
+
299
+ If multiple extensions return `sessionDir`, the last one wins.
300
+
301
+ ```typescript
302
+ pi.on("session_directory", async (event) => {
303
+ return {
304
+ sessionDir: `/tmp/pi-sessions/${encodeURIComponent(event.cwd)}`,
305
+ };
306
+ });
307
+ ```
308
+
287
309
  #### session_start
288
310
 
289
311
  Fired on initial session load.
@@ -489,6 +511,21 @@ pi.on("context", async (event, ctx) => {
489
511
  });
490
512
  ```
491
513
 
514
+ #### before_provider_request
515
+
516
+ Fired after the provider-specific payload is built, right before the request is sent. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
517
+
518
+ ```typescript
519
+ pi.on("before_provider_request", (event, ctx) => {
520
+ console.log(JSON.stringify(event.payload, null, 2));
521
+
522
+ // Optional: replace payload
523
+ // return { ...event.payload, temperature: 0 };
524
+ });
525
+ ```
526
+
527
+ This is mainly useful for debugging provider serialization and cache behavior.
528
+
492
529
  ### Model Events
493
530
 
494
531
  #### model_select
@@ -658,7 +695,9 @@ Transforms chain across handlers. See [input-transform.ts](../examples/extension
658
695
 
659
696
  ## ExtensionContext
660
697
 
661
- Every handler receives `ctx: ExtensionContext`:
698
+ All handlers except `session_directory` receive `ctx: ExtensionContext`.
699
+
700
+ `session_directory` is a CLI startup hook and receives only the event.
662
701
 
663
702
  ### ctx.ui
664
703
 
@@ -1340,6 +1379,18 @@ pi.registerTool({
1340
1379
  });
1341
1380
  ```
1342
1381
 
1382
+ **Signaling errors:** To mark a tool execution as failed (sets `isError: true` on the result and reports it to the LLM), throw an error from `execute`. Returning a value never sets the error flag regardless of what properties you include in the return object.
1383
+
1384
+ ```typescript
1385
+ // Correct: throw to signal an error
1386
+ async execute(toolCallId, params) {
1387
+ if (!isValid(params.input)) {
1388
+ throw new Error(`Invalid input: ${params.input}`);
1389
+ }
1390
+ return { content: [{ type: "text", text: "OK" }], details: {} };
1391
+ }
1392
+ ```
1393
+
1343
1394
  **Important:** Use `StringEnum` from `@draht/ai` for string enums. `Type.Union`/`Type.Literal` doesn't work with Google's API.
1344
1395
 
1345
1396
  ### Overriding Built-in Tools
@@ -1880,7 +1931,7 @@ const highlighted = highlightCode(code, lang, theme);
1880
1931
 
1881
1932
  - Extension errors are logged, agent continues
1882
1933
  - `tool_call` errors block the tool (fail-safe)
1883
- - Tool `execute` errors are reported to the LLM with `isError: true`
1934
+ - Tool `execute` errors must be signaled by throwing; the thrown error is caught, reported to the LLM with `isError: true`, and execution continues
1884
1935
 
1885
1936
  ## Mode Behavior
1886
1937
 
@@ -1922,6 +1973,7 @@ All examples in [examples/extensions/](../examples/extensions/).
1922
1973
  | `dirty-repo-guard.ts` | Warn on dirty git repo | `on("session_before_*")`, `exec` |
1923
1974
  | `input-transform.ts` | Transform user input | `on("input")` |
1924
1975
  | `model-status.ts` | React to model changes | `on("model_select")`, `setStatus` |
1976
+ | `provider-payload.ts` | Inspect or patch provider payloads | `on("before_provider_request")` |
1925
1977
  | `system-prompt-header.ts` | Display system prompt info | `on("agent_start")`, `getSystemPrompt` |
1926
1978
  | `claude-rules.ts` | Load rules from files | `on("session_start")`, `on("before_agent_start")` |
1927
1979
  | `file-trigger.ts` | File watcher triggers messages | `sendMessage` |
@@ -7,11 +7,12 @@ All keyboard shortcuts can be customized via `~/.pi/agent/keybindings.json`. Eac
7
7
  `modifier+key` where modifiers are `ctrl`, `shift`, `alt` (combinable) and keys are:
8
8
 
9
9
  - **Letters:** `a-z`
10
+ - **Digits:** `0-9`
10
11
  - **Special:** `escape`, `esc`, `enter`, `return`, `tab`, `space`, `backspace`, `delete`, `insert`, `clear`, `home`, `end`, `pageUp`, `pageDown`, `up`, `down`, `left`, `right`
11
12
  - **Function:** `f1`-`f12`
12
13
  - **Symbols:** `` ` ``, `-`, `=`, `[`, `]`, `\`, `;`, `'`, `,`, `.`, `/`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `(`, `)`, `_`, `+`, `|`, `~`, `{`, `}`, `:`, `<`, `>`, `?`
13
14
 
14
- Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, etc.
15
+ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, `ctrl+1`, etc.
15
16
 
16
17
  ## All Actions
17
18
 
@@ -119,6 +120,13 @@ Modifier combinations: `ctrl+shift+x`, `alt+ctrl+x`, `ctrl+shift+alt+x`, etc.
119
120
  | `selectConfirm` | `enter` | Confirm selection |
120
121
  | `selectCancel` | `escape`, `ctrl+c` | Cancel selection |
121
122
 
123
+ ### Tree Navigation
124
+
125
+ | Action | Default | Description |
126
+ |--------|---------|-------------|
127
+ | `treeFoldOrUp` | `ctrl+left`, `alt+left` | Fold current branch segment, or jump to the previous segment start |
128
+ | `treeUnfoldOrDown` | `ctrl+right`, `alt+right` | Unfold current branch segment, or jump to the next segment start or branch end |
129
+
122
130
  ### Session Picker
123
131
 
124
132
  | Action | Default | Description |
package/docs/models.md CHANGED
@@ -129,7 +129,7 @@ The `apiKey` and `headers` fields support three formats:
129
129
  | Field | Required | Default | Description |
130
130
  |-------|----------|---------|-------------|
131
131
  | `id` | Yes | — | Model identifier (passed to the API) |
132
- | `name` | No | `id` | Display name in model selector |
132
+ | `name` | No | `id` | Human-readable model label. Used for matching (`--model` patterns) and shown in model details/status text. |
133
133
  | `api` | No | provider's `api` | Override provider's API for this model |
134
134
  | `reasoning` | No | `false` | Supports extended thinking |
135
135
  | `input` | No | `["text"]` | Input types: `["text"]` or `["text", "image"]` |
@@ -137,6 +137,10 @@ The `apiKey` and `headers` fields support three formats:
137
137
  | `maxTokens` | No | `16384` | Maximum output tokens |
138
138
  | `cost` | No | all zeros | `{"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0}` (per million tokens) |
139
139
 
140
+ Current behavior:
141
+ - `/model` and `--list-models` list entries by model `id`.
142
+ - The configured `name` is used for model matching and detail/status text.
143
+
140
144
  ## Overriding Built-in Providers
141
145
 
142
146
  Route a built-in provider through a proxy without redefining models:
package/docs/rpc.md CHANGED
@@ -24,6 +24,17 @@ Common options:
24
24
 
25
25
  All commands support an optional `id` field for request/response correlation. If provided, the corresponding response will include the same `id`.
26
26
 
27
+ ### Framing
28
+
29
+ RPC mode uses strict JSONL semantics with LF (`\n`) as the only record delimiter.
30
+
31
+ This matters for clients:
32
+ - Split records on `\n` only
33
+ - Accept optional `\r\n` input by stripping a trailing `\r`
34
+ - Do not use generic line readers that treat Unicode separators as newlines
35
+
36
+ In particular, Node `readline` is not protocol-compliant for RPC mode because it also splits on `U+2028` and `U+2029`, which are valid inside JSON strings.
37
+
27
38
  ## Commands
28
39
 
29
40
  ### Prompting
@@ -1292,13 +1303,39 @@ For a complete example of handling the extension UI protocol, see [`examples/rpc
1292
1303
 
1293
1304
  ```javascript
1294
1305
  const { spawn } = require("child_process");
1295
- const readline = require("readline");
1306
+ const { StringDecoder } = require("string_decoder");
1296
1307
 
1297
1308
  const agent = spawn("pi", ["--mode", "rpc", "--no-session"]);
1298
1309
 
1299
- readline.createInterface({ input: agent.stdout }).on("line", (line) => {
1310
+ function attachJsonlReader(stream, onLine) {
1311
+ const decoder = new StringDecoder("utf8");
1312
+ let buffer = "";
1313
+
1314
+ stream.on("data", (chunk) => {
1315
+ buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);
1316
+
1317
+ while (true) {
1318
+ const newlineIndex = buffer.indexOf("\n");
1319
+ if (newlineIndex === -1) break;
1320
+
1321
+ let line = buffer.slice(0, newlineIndex);
1322
+ buffer = buffer.slice(newlineIndex + 1);
1323
+ if (line.endsWith("\r")) line = line.slice(0, -1);
1324
+ onLine(line);
1325
+ }
1326
+ });
1327
+
1328
+ stream.on("end", () => {
1329
+ buffer += decoder.end();
1330
+ if (buffer.length > 0) {
1331
+ onLine(buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer);
1332
+ }
1333
+ });
1334
+ }
1335
+
1336
+ attachJsonlReader(agent.stdout, (line) => {
1300
1337
  const event = JSON.parse(line);
1301
-
1338
+
1302
1339
  if (event.type === "message_update") {
1303
1340
  const { assistantMessageEvent } = event;
1304
1341
  if (assistantMessageEvent.type === "text_delta") {
package/docs/session.md CHANGED
@@ -5,14 +5,14 @@ Sessions are stored as JSONL (JSON Lines) files. Each line is a JSON object with
5
5
  ## File Location
6
6
 
7
7
  ```
8
- ~/.pi/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl
8
+ ~/.draht/agent/sessions/--<path>--/<timestamp>_<uuid>.jsonl
9
9
  ```
10
10
 
11
11
  Where `<path>` is the working directory with `/` replaced by `-`.
12
12
 
13
13
  ## Deleting Sessions
14
14
 
15
- Sessions can be removed by deleting their `.jsonl` files under `~/.pi/agent/sessions/`.
15
+ Sessions can be removed by deleting their `.jsonl` files under `~/.draht/agent/sessions/`.
16
16
 
17
17
  Pi also supports deleting sessions interactively from `/resume` (select a session and press `Ctrl+D`, then confirm). When available, pi uses the `trash` CLI to avoid permanent deletion.
18
18
 
package/docs/settings.md CHANGED
@@ -42,6 +42,7 @@ Edit directly or use `/settings` for common options.
42
42
  | `quietStartup` | boolean | `false` | Hide startup header |
43
43
  | `collapseChangelog` | boolean | `false` | Show condensed changelog after updates |
44
44
  | `doubleEscapeAction` | string | `"tree"` | Action for double-escape: `"tree"`, `"fork"`, or `"none"` |
45
+ | `treeFilterMode` | string | `"default"` | Default filter for `/tree`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, `"all"` |
45
46
  | `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) |
46
47
  | `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
47
48
  | `showHardwareCursor` | boolean | `false` | Show terminal cursor |
@@ -8,13 +8,30 @@ Work out of the box.
8
8
 
9
9
  ## Ghostty
10
10
 
11
- Add to your Ghostty config (`~/.config/ghostty/config`):
11
+ Add to your Ghostty config (`~/Library/Application Support/com.mitchellh.ghostty/config` on macOS, `~/.config/ghostty/config` on Linux):
12
12
 
13
13
  ```
14
14
  keybind = alt+backspace=text:\x1b\x7f
15
+ ```
16
+
17
+ Older Claude Code versions may have added this Ghostty mapping:
18
+
19
+ ```
15
20
  keybind = shift+enter=text:\n
16
21
  ```
17
22
 
23
+ That mapping sends a raw linefeed byte. Inside pi, that is indistinguishable from `Ctrl+J`, so tmux and pi no longer see a real `shift+enter` key event.
24
+
25
+ If Claude Code 2.x or newer is the only reason you added that mapping, you can remove it, unless you want to use Claude Code in tmux, where it still requires that Ghostty mapping.
26
+
27
+ If you want `Shift+Enter` to keep working in tmux via that remap, add `ctrl+j` to your pi `newLine` keybinding in `~/.pi/agent/keybindings.json`:
28
+
29
+ ```json
30
+ {
31
+ "newLine": ["shift+enter", "ctrl+j"]
32
+ }
33
+ ```
34
+
18
35
  ## WezTerm
19
36
 
20
37
  Create `~/.wezterm.lua`:
@@ -46,7 +63,7 @@ Add to `keybindings.json` to enable `Shift+Enter` for multi-line input:
46
63
 
47
64
  ## Windows Terminal
48
65
 
49
- Add to `settings.json` (Ctrl+Shift+, or Settings → Open JSON file):
66
+ Add to `settings.json` (Ctrl+Shift+, or Settings → Open JSON file) to forward the modified Enter keys pi uses:
50
67
 
51
68
  ```json
52
69
  {
@@ -54,12 +71,20 @@ Add to `settings.json` (Ctrl+Shift+, or Settings → Open JSON file):
54
71
  {
55
72
  "command": { "action": "sendInput", "input": "\u001b[13;2u" },
56
73
  "keys": "shift+enter"
74
+ },
75
+ {
76
+ "command": { "action": "sendInput", "input": "\u001b[13;3u" },
77
+ "keys": "alt+enter"
57
78
  }
58
79
  ]
59
80
  }
60
81
  ```
61
82
 
62
- If you already have an `actions` array, add the object to it.
83
+ - `Shift+Enter` inserts a new line.
84
+ - Windows Terminal binds `Alt+Enter` to fullscreen by default. That prevents pi from receiving `Alt+Enter` for follow-up queueing.
85
+ - Remapping `Alt+Enter` to `sendInput` forwards the real key chord to pi instead.
86
+
87
+ If you already have an `actions` array, add the objects to it. If the old fullscreen behavior persists, fully close and reopen Windows Terminal.
63
88
 
64
89
  ## IntelliJ IDEA (Integrated Terminal)
65
90
 
package/docs/tmux.md ADDED
@@ -0,0 +1,61 @@
1
+ # tmux Setup
2
+
3
+ Pi works inside tmux, but tmux strips modifier information from certain keys by default. Without configuration, `Shift+Enter` and `Ctrl+Enter` are usually indistinguishable from plain `Enter`.
4
+
5
+ ## Recommended Configuration
6
+
7
+ Add to `~/.tmux.conf`:
8
+
9
+ ```tmux
10
+ set -g extended-keys on
11
+ set -g extended-keys-format csi-u
12
+ ```
13
+
14
+ Then restart tmux fully:
15
+
16
+ ```bash
17
+ tmux kill-server
18
+ tmux
19
+ ```
20
+
21
+ Pi requests extended key reporting automatically when Kitty keyboard protocol is not available. With `extended-keys-format csi-u`, tmux forwards modified keys in CSI-u format, which is the most reliable configuration.
22
+
23
+ ## Why `csi-u` Is Recommended
24
+
25
+ With only:
26
+
27
+ ```tmux
28
+ set -g extended-keys on
29
+ ```
30
+
31
+ tmux defaults to `extended-keys-format xterm`. When an application requests extended key reporting, modified keys are forwarded in xterm `modifyOtherKeys` format such as:
32
+
33
+ - `Ctrl+C` → `\x1b[27;5;99~`
34
+ - `Ctrl+D` → `\x1b[27;5;100~`
35
+ - `Ctrl+Enter` → `\x1b[27;5;13~`
36
+
37
+ With `extended-keys-format csi-u`, the same keys are forwarded as:
38
+
39
+ - `Ctrl+C` → `\x1b[99;5u`
40
+ - `Ctrl+D` → `\x1b[100;5u`
41
+ - `Ctrl+Enter` → `\x1b[13;5u`
42
+
43
+ Pi supports both formats, but `csi-u` is the recommended tmux setup.
44
+
45
+ ## What This Fixes
46
+
47
+ Without tmux extended keys, modified Enter keys collapse to legacy sequences:
48
+
49
+ | Key | Without extkeys | With `csi-u` |
50
+ |-----|-----------------|--------------|
51
+ | Enter | `\r` | `\r` |
52
+ | Shift+Enter | `\r` | `\x1b[13;2u` |
53
+ | Ctrl+Enter | `\r` | `\x1b[13;5u` |
54
+ | Alt/Option+Enter | `\x1b\r` | `\x1b[13;3u` |
55
+
56
+ This affects the default keybindings (`Enter` to submit, `Shift+Enter` for newline) and any custom keybindings using modified Enter.
57
+
58
+ ## Requirements
59
+
60
+ - tmux 3.2 or later (run `tmux -V` to check)
61
+ - A terminal emulator that supports extended keys (Ghostty, Kitty, iTerm2, WezTerm, Windows Terminal)
package/docs/tree.md CHANGED
@@ -33,16 +33,25 @@ Sessions are stored as trees where each entry has an `id` and `parentId`. The "l
33
33
  | Key | Action |
34
34
  |-----|--------|
35
35
  | ↑/↓ | Navigate (depth-first order) |
36
+ | ←/→ | Page up/down |
37
+ | Ctrl+←/Ctrl+→ or Alt+←/Alt+→ | Fold/unfold and jump between branch segments |
36
38
  | Enter | Select node |
37
39
  | Escape/Ctrl+C | Cancel |
38
40
  | Ctrl+U | Toggle: user messages only |
39
41
  | Ctrl+O | Toggle: show all (including custom/label entries) |
40
42
 
43
+ `Ctrl+←` or `Alt+←` folds the current node if it is foldable. Foldable nodes are roots and branch segment starts that have visible children. If the current node is not foldable, or is already folded, the selection jumps up to the previous visible branch segment start.
44
+
45
+ `Ctrl+→` or `Alt+→` unfolds the current node if it is folded. Otherwise, the selection jumps down to the next visible branch segment start, or to the branch end when there is no further branch point.
46
+
41
47
  ### Display
42
48
 
43
49
  - Height: half terminal height
44
50
  - Current leaf marked with `← active`
45
51
  - Labels shown inline: `[label-name]`
52
+ - Foldable branch starts show `⊟` in the connector. Folded branches show `⊞`
53
+ - Active path marker `•` appears after the fold indicator when applicable
54
+ - Search and filter changes reset all folds
46
55
  - Default filter hides `label` and `custom` entries (shown in Ctrl+O mode)
47
56
  - Children sorted by timestamp (oldest first)
48
57