@oh-my-pi/pi-coding-agent 17.2.13 → 17.2.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 (239) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/{CHANGELOG-d8xh7keh.md → CHANGELOG-fk29zh06.md} +30 -0
  3. package/dist/cli.js +5114 -5417
  4. package/dist/types/cleanse/agent.d.ts +3 -0
  5. package/dist/types/cleanse/checkers.d.ts +21 -1
  6. package/dist/types/cleanse/index.d.ts +4 -0
  7. package/dist/types/cleanse/parsers.d.ts +3 -1
  8. package/dist/types/cli/args.d.ts +1 -0
  9. package/dist/types/cli/cleanse-picker.d.ts +17 -0
  10. package/dist/types/cli/command-help.d.ts +3 -0
  11. package/dist/types/cli/progress-reporter.d.ts +19 -0
  12. package/dist/types/commands/cleanse.d.ts +11 -0
  13. package/dist/types/commands/compress.d.ts +37 -0
  14. package/dist/types/commands/launch-help.d.ts +3 -0
  15. package/dist/types/commands/launch.d.ts +3 -0
  16. package/dist/types/compress/index.d.ts +27 -0
  17. package/dist/types/compress/protocol.d.ts +49 -0
  18. package/dist/types/compress/session.d.ts +15 -0
  19. package/dist/types/compress/types.d.ts +54 -0
  20. package/dist/types/config/settings-schema.d.ts +10 -0
  21. package/dist/types/internal-urls/local-protocol.d.ts +8 -0
  22. package/dist/types/mcp/transports/header-policy.d.ts +8 -0
  23. package/dist/types/mcp/transports/http.d.ts +2 -0
  24. package/dist/types/mcp/types.d.ts +19 -0
  25. package/dist/types/modes/components/late-diagnostics-message.d.ts +2 -0
  26. package/dist/types/modes/components/todo-reminder.d.ts +2 -0
  27. package/dist/types/modes/components/tool-activity.d.ts +17 -0
  28. package/dist/types/modes/components/transcript-container.d.ts +2 -0
  29. package/dist/types/modes/components/ttsr-notification.d.ts +2 -0
  30. package/dist/types/modes/interactive-mode.d.ts +3 -1
  31. package/dist/types/modes/types.d.ts +3 -1
  32. package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -1
  33. package/dist/types/modes/utils/ui-helpers.d.ts +3 -1
  34. package/dist/types/session/agent-session-types.d.ts +2 -0
  35. package/dist/types/session/agent-session.d.ts +2 -0
  36. package/dist/types/session/session-tools.d.ts +13 -0
  37. package/dist/types/tools/builtin-names.d.ts +1 -2
  38. package/dist/types/tools/index.d.ts +1 -0
  39. package/dist/types/tools/think.d.ts +41 -0
  40. package/dist/types/utils/zip.d.ts +19 -9
  41. package/package.json +13 -13
  42. package/src/cleanse/agent.ts +67 -2
  43. package/src/cleanse/checkers.ts +252 -21
  44. package/src/cleanse/index.ts +90 -36
  45. package/src/cleanse/parsers.ts +243 -20
  46. package/src/cleanse/prompts/discovery.md +74 -0
  47. package/src/cli/args.ts +3 -0
  48. package/src/cli/cleanse-picker.ts +86 -0
  49. package/src/cli/command-help.ts +4 -0
  50. package/src/cli/flag-tables.ts +1 -0
  51. package/src/cli/gallery-fixtures/agentic.ts +16 -0
  52. package/src/{cleanse/progress.ts → cli/progress-reporter.ts} +12 -7
  53. package/src/cli-commands.ts +5 -0
  54. package/src/commands/cleanse.ts +19 -4
  55. package/src/commands/compress.ts +45 -0
  56. package/src/commands/launch-help.ts +3 -0
  57. package/src/commit/agentic/prompts/analyze-file.md +6 -6
  58. package/src/commit/agentic/prompts/session-user.md +4 -4
  59. package/src/compress/index.ts +318 -0
  60. package/src/compress/prompts/request.md +11 -0
  61. package/src/compress/prompts/review.md +17 -0
  62. package/src/compress/prompts/system.md +81 -0
  63. package/src/compress/protocol.ts +210 -0
  64. package/src/compress/session.ts +72 -0
  65. package/src/compress/types.ts +59 -0
  66. package/src/config/settings-schema.ts +11 -0
  67. package/src/discovery/builtin-rules/go-add-cleanup.md +8 -8
  68. package/src/discovery/builtin-rules/go-exp-promoted.md +8 -8
  69. package/src/discovery/builtin-rules/go-ioutil.md +12 -12
  70. package/src/discovery/builtin-rules/go-new-expr.md +8 -8
  71. package/src/discovery/builtin-rules/go-range-int.md +5 -5
  72. package/src/discovery/builtin-rules/rs-box-leak.md +7 -7
  73. package/src/discovery/builtin-rules/rs-future-prelude.md +4 -4
  74. package/src/discovery/builtin-rules/rs-parking-lot.md +6 -6
  75. package/src/discovery/builtin-rules/ts-no-any.md +5 -5
  76. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +8 -8
  77. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +13 -14
  78. package/src/discovery/builtin-rules/ts-no-local-is-record.md +7 -7
  79. package/src/discovery/builtin-rules/ts-no-test-timers.md +3 -9
  80. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +7 -7
  81. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +2 -2
  82. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +3 -9
  83. package/src/discovery/builtin-rules/ts-set-map.md +2 -4
  84. package/src/internal-urls/local-protocol.ts +42 -0
  85. package/src/live/prompts/live-instructions.md +10 -10
  86. package/src/main.ts +4 -0
  87. package/src/mcp/client.ts +7 -3
  88. package/src/mcp/transports/header-policy.ts +28 -0
  89. package/src/mcp/transports/http.ts +200 -35
  90. package/src/mcp/types.ts +21 -0
  91. package/src/modes/components/chat-transcript-builder.ts +17 -5
  92. package/src/modes/components/late-diagnostics-message.ts +12 -1
  93. package/src/modes/components/todo-reminder.ts +12 -0
  94. package/src/modes/components/tool-activity.ts +45 -0
  95. package/src/modes/components/transcript-container.ts +16 -0
  96. package/src/modes/components/ttsr-notification.ts +12 -0
  97. package/src/modes/controllers/event-controller.ts +3 -6
  98. package/src/modes/controllers/input-controller.ts +6 -6
  99. package/src/modes/controllers/selector-controller.ts +8 -6
  100. package/src/modes/interactive-mode.ts +5 -40
  101. package/src/modes/print-mode.ts +13 -54
  102. package/src/modes/types.ts +1 -1
  103. package/src/modes/utils/transcript-render-helpers.ts +3 -2
  104. package/src/modes/utils/ui-helpers.ts +17 -7
  105. package/src/prompts/advisor/active-repo-watchdog.md +3 -4
  106. package/src/prompts/advisor/advise-tool.md +3 -3
  107. package/src/prompts/advisor/context-files.md +1 -1
  108. package/src/prompts/advisor/system.md +45 -65
  109. package/src/prompts/agents/designer.md +44 -44
  110. package/src/prompts/agents/init.md +18 -18
  111. package/src/prompts/agents/librarian.md +31 -31
  112. package/src/prompts/agents/reviewer.md +34 -39
  113. package/src/prompts/agents/security-reviewer.md +3 -5
  114. package/src/prompts/agents/task.md +11 -12
  115. package/src/prompts/bench.md +2 -5
  116. package/src/prompts/ci-green-request.md +13 -15
  117. package/src/prompts/dry-balance-bench.md +5 -5
  118. package/src/prompts/goals/goal-budget-limit.md +4 -5
  119. package/src/prompts/goals/goal-continuation.md +11 -11
  120. package/src/prompts/goals/goal-mode-active.md +7 -7
  121. package/src/prompts/goals/goal-todo-context.md +2 -2
  122. package/src/prompts/goals/guided-goal-interview.md +18 -24
  123. package/src/prompts/memories/read-path.md +9 -9
  124. package/src/prompts/memories/stage_one_system.md +9 -11
  125. package/src/prompts/review-custom-request.md +8 -11
  126. package/src/prompts/review-headless-request.md +3 -10
  127. package/src/prompts/security/scan-coordinator.md +6 -5
  128. package/src/prompts/security/validate-request.md +4 -7
  129. package/src/prompts/skills/user-invocation.md +2 -2
  130. package/src/prompts/steering/parent-irc.md +1 -1
  131. package/src/prompts/steering/user-interjection.md +1 -3
  132. package/src/prompts/system/active-repo-context.md +4 -2
  133. package/src/prompts/system/agent-creation-architect.md +22 -37
  134. package/src/prompts/system/agent-creation-user.md +3 -3
  135. package/src/prompts/system/auto-continue.md +1 -1
  136. package/src/prompts/system/auto-thinking-difficulty-local.md +5 -7
  137. package/src/prompts/system/auto-thinking-difficulty.md +8 -10
  138. package/src/prompts/system/autolearn-guidance-learn.md +2 -1
  139. package/src/prompts/system/autolearn-guidance.md +5 -4
  140. package/src/prompts/system/autolearn-nudge-autocontinue.md +3 -3
  141. package/src/prompts/system/background-tan-dispatch.md +3 -3
  142. package/src/prompts/system/btw-user.md +2 -2
  143. package/src/prompts/system/commit-message-system.md +8 -6
  144. package/src/prompts/system/eager-task.md +3 -3
  145. package/src/prompts/system/empty-stop-retry.md +1 -1
  146. package/src/prompts/system/gemini-tool-call-reminder.md +5 -5
  147. package/src/prompts/system/interrupted-thinking.md +3 -3
  148. package/src/prompts/system/irc-autoreply.md +1 -1
  149. package/src/prompts/system/irc-incoming.md +3 -3
  150. package/src/prompts/system/manual-continue.md +3 -3
  151. package/src/prompts/system/mcp-xdev-guidance.md +2 -2
  152. package/src/prompts/system/memory-consolidation-system.md +2 -2
  153. package/src/prompts/system/mid-run-todo-nudge.md +1 -1
  154. package/src/prompts/system/orchestrate-notice.md +25 -25
  155. package/src/prompts/system/personalities/default.md +9 -9
  156. package/src/prompts/system/personalities/friendly.md +11 -11
  157. package/src/prompts/system/personalities/pragmatic.md +8 -8
  158. package/src/prompts/system/plan-mode-active.md +61 -64
  159. package/src/prompts/system/plan-mode-approved.md +10 -11
  160. package/src/prompts/system/plan-mode-compact-instructions.md +11 -11
  161. package/src/prompts/system/plan-mode-reference.md +5 -5
  162. package/src/prompts/system/plan-yolo-handoff.md +2 -2
  163. package/src/prompts/system/prewalk-checklist.md +5 -5
  164. package/src/prompts/system/prewalk-continue.md +1 -1
  165. package/src/prompts/system/prewalk-plan.md +8 -9
  166. package/src/prompts/system/project-prompt.md +11 -12
  167. package/src/prompts/system/recap-user.md +1 -1
  168. package/src/prompts/system/resolve-device-reminder.md +1 -1
  169. package/src/prompts/system/rewind-report.md +3 -3
  170. package/src/prompts/system/side-channel-no-tools.md +3 -1
  171. package/src/prompts/system/snapcompact-context-stub.md +1 -1
  172. package/src/prompts/system/snapcompact-system-frames-note.md +1 -1
  173. package/src/prompts/system/snapcompact-system-stub.md +1 -1
  174. package/src/prompts/system/snapcompact-toolresult-note.md +1 -1
  175. package/src/prompts/system/speech-rewrite.md +11 -13
  176. package/src/prompts/system/subagent-async-pending.md +5 -5
  177. package/src/prompts/system/subagent-system-prompt.md +5 -15
  178. package/src/prompts/system/subagent-user-prompt.md +1 -1
  179. package/src/prompts/system/subagent-yield-reminder.md +12 -12
  180. package/src/prompts/system/system-prompt.md +110 -136
  181. package/src/prompts/system/tan-context-switch.md +8 -14
  182. package/src/prompts/system/task-label.md +3 -3
  183. package/src/prompts/system/thinking-loop-redirect.md +6 -6
  184. package/src/prompts/system/title-marker-instruction.md +2 -1
  185. package/src/prompts/system/title-system.md +3 -5
  186. package/src/prompts/system/ttsr-interrupt.md +3 -3
  187. package/src/prompts/system/ttsr-tool-reminder.md +1 -1
  188. package/src/prompts/system/ultrathink-notice.md +1 -1
  189. package/src/prompts/system/unexpected-stop-classifier.md +3 -3
  190. package/src/prompts/system/vibe-mode-active.md +15 -15
  191. package/src/prompts/system/web-search.md +16 -16
  192. package/src/prompts/system/workflow-notice.md +35 -33
  193. package/src/prompts/system/xdev-mount-notice.md +4 -4
  194. package/src/prompts/tools/apply-patch.md +22 -23
  195. package/src/prompts/tools/approve.md +5 -0
  196. package/src/prompts/tools/ask.md +10 -10
  197. package/src/prompts/tools/checkpoint.md +7 -7
  198. package/src/prompts/tools/computer.md +16 -16
  199. package/src/prompts/tools/github.md +12 -12
  200. package/src/prompts/tools/goal.md +8 -9
  201. package/src/prompts/tools/grep.md +5 -5
  202. package/src/prompts/tools/image-attachment-describe-system.md +6 -6
  203. package/src/prompts/tools/image-attachment-describe.md +3 -8
  204. package/src/prompts/tools/image-gen.md +4 -4
  205. package/src/prompts/tools/inspect-image-system.md +10 -10
  206. package/src/prompts/tools/inspect-image.md +10 -13
  207. package/src/prompts/tools/learn.md +4 -4
  208. package/src/prompts/tools/manage-skill.md +9 -6
  209. package/src/prompts/tools/memory-edit.md +8 -8
  210. package/src/prompts/tools/recall.md +4 -4
  211. package/src/prompts/tools/reflect.md +2 -2
  212. package/src/prompts/tools/replace.md +13 -11
  213. package/src/prompts/tools/retain.md +3 -4
  214. package/src/prompts/tools/rewind.md +7 -8
  215. package/src/prompts/tools/rewrite.md +12 -0
  216. package/src/prompts/tools/security-publish.md +5 -1
  217. package/src/prompts/tools/security-scan.md +10 -1
  218. package/src/prompts/tools/task-async-contract.md +7 -1
  219. package/src/prompts/tools/todo.md +27 -25
  220. package/src/prompts/tools/vibe-kill.md +2 -2
  221. package/src/prompts/tools/vibe-list.md +2 -2
  222. package/src/prompts/tools/vibe-send.md +6 -7
  223. package/src/prompts/tools/vibe-spawn.md +8 -6
  224. package/src/prompts/tools/web-search.md +5 -5
  225. package/src/sdk.ts +10 -1
  226. package/src/session/agent-session-types.ts +2 -0
  227. package/src/session/agent-session.ts +28 -0
  228. package/src/session/session-handoff.ts +21 -0
  229. package/src/session/session-tools.ts +47 -0
  230. package/src/tools/builtin-names.ts +1 -2
  231. package/src/tools/index.ts +10 -0
  232. package/src/tools/read-format.ts +8 -3
  233. package/src/tools/read.ts +18 -7
  234. package/src/tools/renderers.ts +2 -0
  235. package/src/tools/think.ts +84 -0
  236. package/src/tools/write.ts +1 -1
  237. package/src/utils/file-mentions.ts +8 -2
  238. package/src/utils/zip.ts +858 -94
  239. package/dist/types/cleanse/progress.d.ts +0 -14
@@ -33,12 +33,12 @@ let guard = data.lock();
33
33
 
34
34
  ## Equivalents
35
35
 
36
- | std::sync | parking_lot |
37
- | --- | --- |
38
- | `Mutex<T>` | `Mutex<T>` |
39
- | `RwLock<T>` | `RwLock<T>` |
40
- | `Condvar` | `Condvar` |
41
- | `Once` | `Once` |
36
+ |std::sync|parking_lot|
37
+ |---|---|
38
+ |`Mutex<T>`|`Mutex<T>`|
39
+ |`RwLock<T>`|`RwLock<T>`|
40
+ |`Condvar`|`Condvar`|
41
+ |`Once`|`Once`|
42
42
 
43
43
  ## Keep async locks async
44
44
 
@@ -57,10 +57,10 @@ const config = { port: 3000 } satisfies ServerConfig;
57
57
 
58
58
  ## Choosing: guard vs schema vs unchecked cast
59
59
 
60
- | Situation | Reach for |
61
- | --- | --- |
62
- | Data from outside your control — network/RPC, parsed JSON, config files, env vars, CLI/IPC, persisted blobs — or a shape reused across the codebase | **Schema parse** (Zod/Valibot/…): runtime validation, typed output, and a clear error on bad shape |
63
- | In-process value the compiler merely lost track of — an `unknown` from a generic, a union to discriminate, a one-off read of a field or two | **Type guard** (`in` / `typeof`): no dependency, but it only checks what you write, so keep the checked surface small |
64
- | You genuinely know more than the compiler *and* a runtime check is impossible or meaningless — a well-known DOM node (`as HTMLElement`), structurally-identical types inference can't unify, a library type that is wrong or unexpressible, `as const` | **Unchecked cast** (`as` / `as unknown as T`): assign to a named const with a one-line reason; never for raw external input |
60
+ |Situation|Reach for|
61
+ |---|---|
62
+ |Data from outside your control — network/RPC, parsed JSON, config files, env vars, CLI/IPC, persisted blobs — or a shape reused across the codebase|**Schema parse** (Zod/Valibot/…): runtime validation, typed output, and a clear error on bad shape|
63
+ |In-process value the compiler merely lost track of — an `unknown` from a generic, a union to discriminate, a one-off read of a field or two|**Type guard** (`in` / `typeof`): no dependency, but it only checks what you write, so keep the checked surface small|
64
+ |You genuinely know more than the compiler *and* a runtime check is impossible or meaningless — a well-known DOM node (`as HTMLElement`), structurally-identical types inference can't unify, a library type that is wrong or unexpressible, `as const`|**Unchecked cast** (`as` / `as unknown as T`): assign to a named const with a one-line reason; never for raw external input|
65
65
 
66
66
  If a library boundary truly requires an unchecked cast, use `as unknown as T` with a short reason. Never leave a bare `any`.
@@ -5,14 +5,14 @@ scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
5
5
  interruptMode: never
6
6
  ---
7
7
 
8
- Do not use `@deprecated` as a substitute for finishing a refactor. If an API is obsolete inside the code you control, update every call site and remove the old name in the same change.
8
+ Never use `@deprecated` instead of completing a refactor. Obsolete APIs in code you control: update every call site; remove the old name in the same change.
9
9
 
10
10
  ## Why
11
11
 
12
- - Deprecated aliases keep two contracts alive.
13
- - Future maintainers must preserve behavior nobody should call.
14
- - Tests can pass while production code keeps using the old path.
15
- - The next refactor has to unwind both the real API and the compatibility layer.
12
+ - Deprecated aliases: two live contracts.
13
+ - Future maintainers preserve behavior nobody should call.
14
+ - Tests pass while production uses the old path.
15
+ - Next refactor unwinds real API and compatibility layer.
16
16
 
17
17
  ## Avoid
18
18
 
@@ -39,7 +39,7 @@ export function createClient(options: ClientOptions): Client { ... }
39
39
  ## Exceptions
40
40
 
41
41
  - Public package APIs with a documented migration window.
42
- - Third-party declarations where the deprecated marker reflects an external contract.
43
- - Tests that intentionally verify deprecated API behavior during a supported transition.
42
+ - Third-party declarations whose deprecated marker reflects an external contract.
43
+ - Tests intentionally verifying deprecated API behavior during a supported transition.
44
44
 
45
- If an exception applies, state the external compatibility requirement. Otherwise, finish the refactor and delete the deprecated symbol.
45
+ If an exception applies, state the external compatibility requirement. Otherwise complete the refactor; delete the deprecated symbol.
@@ -8,13 +8,15 @@ astCondition:
8
8
  - "($X as { $$$BODY })[$IDX]"
9
9
  ---
10
10
 
11
- **Don't assert an inline object type just to read a property.** `(value as { content: unknown }).content` fabricates a shape the compiler never verified, then trusts it for exactly one access. If `value` isn't that shape, the read is silently wrong and no type error ever fires.
11
+ ## Don't inline-cast an object type for member access
12
12
 
13
- ## Why it's wrong
13
+ `(value as { content: unknown }).content` fabricates an unchecked shape, then trusts it for the access. If `value` lacks that shape, the read is silently wrong; no type error fires.
14
14
 
15
- - The cast is an unchecked assertion — it suppresses the type error instead of proving the shape.
16
- - It localizes the lie to one expression, so the next reader can't tell whether the value was ever validated.
17
- - It almost always stands in for the real fix: runtime narrowing or a validated type at the boundary.
15
+ ## Why
16
+
17
+ - Unchecked assertion: suppresses the error; proves no shape.
18
+ - Localizes the lie; readers cannot tell whether `value` was validated.
19
+ - Usually replace with runtime narrowing or a validated boundary type.
18
20
 
19
21
  ## Avoid
20
22
 
@@ -27,8 +29,7 @@ const flag = (opts as { enabled: boolean })["enabled"];
27
29
 
28
30
  ## Use
29
31
 
30
- Prefer a schema parse at the boundary when a validator is available validate
31
- once, then read from a fully typed value:
32
+ At a boundary, prefer a schema parse when a validator exists: validate once, then read a fully typed value.
32
33
 
33
34
  ```ts
34
35
  import { type } from "@oh-my-pi/omptype";
@@ -39,7 +40,7 @@ const resp = Resp.assert(raw); // throws on bad input; resp.data.id is typed str
39
40
  const id = resp.data.id;
40
41
  ```
41
42
 
42
- For a one-off read of a single field, narrow with `in` / `typeof` so the access is actually checked TypeScript infers `unknown` for the property after `"content" in value`:
43
+ For a one-off field read, narrow with `in` / `typeof`; access is checked. After `"content" in value`, TypeScript infers the property as `unknown`:
43
44
 
44
45
  ```ts
45
46
  if (value && typeof value === "object" && "content" in value) {
@@ -47,10 +48,8 @@ if (value && typeof value === "object" && "content" in value) {
47
48
  }
48
49
  ```
49
50
 
50
- ## Choosing: guard vs schema vs unchecked cast
51
+ ## Choose: guard vs schema vs unchecked cast
51
52
 
52
- | Situation | Reach for |
53
- | --- | --- |
54
- | Data from outside your control network/RPC, parsed JSON, config files, env vars, CLI/IPC, persisted blobs or a shape reused across the codebase | **Schema parse** (Zod/Valibot/…): runtime validation, typed output, and a clear error on bad shape |
55
- | In-process value the compiler merely lost track of — an `unknown` from a generic, a union to discriminate, a one-off read of a field or two | **Type guard** (`in` / `typeof`): no dependency, but it only checks what you write, so keep the checked surface small |
56
- | You genuinely know more than the compiler *and* a runtime check is impossible or meaningless — a well-known DOM node (`as HTMLElement`), structurally-identical types inference can't unify, a library type that's wrong or unexpressible, `as const` | **Unchecked cast** (`as`): assign to a named const with a one-line reason; never for raw external input, never inlined into a member access |
53
+ - Outside-controlled data—network/RPC, parsed JSON, config files, env vars, CLI/IPC, persisted blobs—or codebase-reused shapes: **Schema parse** (Zod/Valibot/…): runtime validation, typed output, clear bad-shape error.
54
+ - In-process values the compiler lost—generic `unknown`, union discrimination, one-off reads of one or two fields: **Type guard** (`in` / `typeof`): no dependency; checks only what you write, so keep its surface small.
55
+ - You know more than the compiler **and** runtime checking is impossible or meaningless—well-known DOM node (`as HTMLElement`), structurally-identical types inference cannot unify, wrong or unexpressible library type, `as const`: **Unchecked cast** (`as`): assign to a named const with a one-line reason; never raw external input or inline member access.
@@ -9,15 +9,15 @@ interruptMode: never
9
9
 
10
10
  ## Why it's wrong
11
11
 
12
- - A `Record<string, unknown>` guard proves only an object, not its fields.
13
- - It's either unnecessarily complicated, or not strong enough.
14
- - Repeated guards hide the actual data contract from readers and TypeScript.
12
+ - A `Record<string, unknown>` guard proves an object, not its fields.
13
+ - Either unnecessarily complicated or insufficiently strong.
14
+ - Repeated guards hide the data contract from readers and TypeScript.
15
15
 
16
16
  ## Use
17
17
 
18
- `isRecord` narrows values to `Record<string, unknown>`; each field remains `unknown`.
18
+ `isRecord`: values narrow to `Record<string, unknown>`; fields remain `unknown`.
19
19
 
20
- For network, config, IPC, persisted, or reused data shapes, parse once at the boundary with the project's schema validator and consume its named output type:
20
+ Network, config, IPC, persisted, or reused data shapes: parse once at the boundary with the project's schema validator; consume its named output type:
21
21
 
22
22
  ```typescript
23
23
  const Config = z.object({ retries: z.number().int().nonnegative() });
@@ -26,7 +26,7 @@ type Config = z.infer<typeof Config>;
26
26
  const config = Config.parse(raw);
27
27
  ```
28
28
 
29
- If the runtime shape is uncertain, check the properties you use with `typeof`, `Array.isArray`, `in`, or a discriminant. If an existing invariant guarantees the shape, assert the named type at that boundary instead of duplicating a guard:
29
+ If runtime shape uncertain: check used properties with `typeof`, `Array.isArray`, `in`, or a discriminant. If an existing invariant guarantees shape: assert the named type at that boundary, not a duplicate guard:
30
30
 
31
31
  ```typescript
32
32
  const config = value as Config;
@@ -45,4 +45,4 @@ const isRecord = (value: unknown): value is Record<string, unknown> =>
45
45
 
46
46
  ## Exceptions
47
47
 
48
- A standalone package without a shared type-guard module may define its single canonical guard. Export it from the package's type-guard module; never recreate it at individual call sites.
48
+ A standalone package without a shared type-guard module may define one canonical guard. Export it from the package's type-guard module; never recreate it at individual call sites.
@@ -8,13 +8,7 @@ scope: "tool:edit(*.test.ts), tool:write(*.test.ts)"
8
8
  interruptMode: never
9
9
  ---
10
10
 
11
- **Do not reach for real wall-clock timers in test files.** `Bun.sleep(...)`, `setTimeout(...)`, and `setInterval(...)` tie a test's duration to real time: they slow the suite on every run, and any delay tuned to "long enough" eventually races on a loaded machine and flakes.
12
-
13
- ## Why it's wrong
14
-
15
- - Real delays add fixed latency to every invocation; CI pays it on every run.
16
- - A sleep sized to mask a race is a guess — the race resurfaces under load.
17
- - A fixed wait hides *what* you are waiting for, so a failure points at a timeout instead of the real cause.
11
+ **Avoid real wall-clock timers in test files.** `Bun.sleep(...)`, `setTimeout(...)`, and `setInterval(...)` bind duration to real time fixed latency each invocation; CI pays every run. “Long enough” sleeps guess at and mask races; under load, races resurface and flake. Fixed waits hide the awaited condition, so failures point to a timeout, not the cause.
18
12
 
19
13
  ## Avoid
20
14
 
@@ -43,7 +37,7 @@ test("debounce fires once", () => {
43
37
  });
44
38
  ```
45
39
 
46
- When the code under test resolves a promise or emits an event, await that signal directly instead of guessing a duration:
40
+ When code resolves a promise or emits an event, await that signal, not a guessed duration:
47
41
 
48
42
  ```typescript
49
43
  await once(emitter, "done"); // await the real event
@@ -52,4 +46,4 @@ const value = await pending; // await the promise the code already exposes
52
46
 
53
47
  ## Exceptions
54
48
 
55
- An integration test that deliberately exercises real timer behavior against the platform clock may need a genuine delay. Keep it rare, and add a short comment naming why deterministic time control will not work.
49
+ Integration tests deliberately exercising real timer behavior against the platform clock may need a genuine delay. Keep rare; add a short comment naming why deterministic time control will not work.
@@ -5,14 +5,14 @@ scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
5
5
  interruptMode: never
6
6
  ---
7
7
 
8
- Do not extract a function whose whole body is one expression or one `return`. Inline it unless the name creates a durable contract.
8
+ Inline functions whose whole body: one expression or `return`, unless name creates a durable contract.
9
9
 
10
10
  ## Why
11
11
 
12
- - One-line wrappers hide no real behavior.
13
- - Readers must jump to verify trivial code.
14
- - The signature freezes a shape too early.
15
- - Search and type flow work better with inline expressions.
12
+ - One-line wrappers: no real behavior.
13
+ - Readers: jump to verify trivial code.
14
+ - Signature: freezes shape too early.
15
+ - Inline expressions: better search and type flow.
16
16
 
17
17
  ## Avoid
18
18
 
@@ -42,10 +42,10 @@ const doubled = value * 2;
42
42
  ## Allowed tiny functions
43
43
 
44
44
  - Three or more call sites need lockstep behavior.
45
- - Exported name represents a stable domain concept.
45
+ - Exported name: stable domain concept.
46
46
  - Callback identity matters.
47
47
  - Type guard preserves narrowing.
48
48
  - Public API, test seam, or DI boundary needs indirection.
49
- - Names a non-obvious formula or magic-constant computation that the inlined expression would not explain on its own.
49
+ - Names non-obvious formula or magic-constant computation the inlined expression would not explain alone.
50
50
 
51
51
  If none apply, inline it.
@@ -5,7 +5,7 @@ scope: "tool:edit(*.ts), tool:edit(*.tsx), tool:write(*.ts), tool:write(*.tsx)"
5
5
  interruptMode: never
6
6
  ---
7
7
 
8
- Use `Promise.withResolvers()` instead of `new Promise((resolve, reject) => ...)`. It keeps control flow linear and exposes typed resolver functions without callback nesting.
8
+ Prefer `Promise.withResolvers()` over `new Promise((resolve, reject) => ...)`: linear control flow; typed resolvers without callback nesting.
9
9
 
10
10
  ## Basic operation
11
11
 
@@ -63,4 +63,4 @@ class Gate {
63
63
  }
64
64
  ```
65
65
 
66
- Use the constructor only when an API specifically requires the executor form.
66
+ Constructor only if an API specifically requires executor form.
@@ -35,13 +35,7 @@ astCondition:
35
35
  - "if ($X != undefined) { clearImmediate($X) }"
36
36
  ---
37
37
 
38
- **Do not guard `clearTimeout` / `clearInterval` / `clearImmediate` with a truthiness or `null`/`undefined` check.** Per the WHATWG/Node timers spec these functions are no-ops when handed `null`, `undefined`, or any value that doesn't correspond to a live timer. The guard adds a redundant branch that the reader must still reason about.
39
-
40
- ## Why it's wrong
41
-
42
- - The branch can never change behavior — clearing a missing/`null`/`undefined` handle does nothing.
43
- - Extra branches inflate the code and hide the one line that matters.
44
- - It signals a misunderstanding of the timer API to future readers.
38
+ **Do not guard `clearTimeout` / `clearInterval` / `clearImmediate` with truthiness or `null`/`undefined` checks.** Per WHATWG/Node timers spec, calls no-op for `null`, `undefined`, or values without a live timer; guards cannot change behavior, add branches readers must reason about, inflate code, hide the line that matters, and signal timer-API misunderstanding.
45
39
 
46
40
  ## Avoid
47
41
 
@@ -63,7 +57,7 @@ clearImmediate(id);
63
57
 
64
58
  ## When a guard *is* warranted
65
59
 
66
- Keep the check only when the body does more than clear e.g. it also reassigns the handle or runs other cleanup:
60
+ Keep it only if the body does more than clear, e.g. reassigns the handle or runs other cleanup:
67
61
 
68
62
  ```ts
69
63
  if (this.timer) {
@@ -72,4 +66,4 @@ if (this.timer) {
72
66
  }
73
67
  ```
74
68
 
75
- This rule only fires when the clear call is the sole statement in the guarded branch, so those legitimate cases are left alone.
69
+ Rule fires only if the clear call is the guarded branch's sole statement; legitimate cases are left alone.
@@ -5,9 +5,9 @@ scope: "tool:edit(**/*.{ts,tsx}), tool:write(**/*.{ts,tsx})"
5
5
  interruptMode: never
6
6
  ---
7
7
 
8
- Use `Record<K, V>` / `Record<K, true>` for small, static string-keyed lookup tables.
8
+ Small, static string-keyed lookup tables: `Record<K, V>` / `Record<K, true>`.
9
9
 
10
- Use `Set` / `Map` when keys are dynamic, non-string, inserted or deleted at runtime, or when code needs `.size`, `.clear()`, stable insertion order, or iterator APIs.
10
+ `Set` / `Map`: dynamic/non-string keys; runtime insertion/deletion; `.size`, `.clear()`, stable insertion order, or iterator APIs.
11
11
 
12
12
  ```typescript
13
13
  // Static literal → Record
@@ -24,5 +24,3 @@ for (const item of items) {
24
24
  seen.add(item.id);
25
25
  }
26
26
  ```
27
-
28
- Small fixed table? `Record`. Runtime collection? `Set` / `Map`.
@@ -253,6 +253,48 @@ export function resolveLocalRoot(options: LocalProtocolOptions, platform: NodeJS
253
253
  return path.join(os.tmpdir(), "omp-local", safeSessionId(options));
254
254
  }
255
255
 
256
+ /**
257
+ * Recursively copy every local:// artifact from one session-scoped root to
258
+ * another. Used when a session transition mints a fresh local root (plan
259
+ * approve-and-execute, handoff) so plans, scratch files, and research notes the
260
+ * carried-forward context references stay readable in the replacement session.
261
+ * No-op when the roots match or the source root is absent.
262
+ */
263
+ export async function copyLocalArtifacts(sourceRoot: string, destinationRoot: string): Promise<void> {
264
+ if (sourceRoot === destinationRoot) return;
265
+
266
+ let sourceRootStat: { isDirectory(): boolean };
267
+ try {
268
+ sourceRootStat = await fs.lstat(sourceRoot);
269
+ } catch (error) {
270
+ if (isEnoent(error)) return;
271
+ throw error;
272
+ }
273
+ if (!sourceRootStat.isDirectory()) return;
274
+
275
+ await fs.mkdir(destinationRoot, { recursive: true });
276
+ await copyLocalArtifactEntries(sourceRoot, destinationRoot);
277
+ }
278
+
279
+ async function copyLocalArtifactEntries(sourceDir: string, destinationDir: string): Promise<void> {
280
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
281
+ for (const entry of entries) {
282
+ const sourcePath = path.join(sourceDir, entry.name);
283
+ const destinationPath = path.join(destinationDir, entry.name);
284
+
285
+ if (entry.isDirectory()) {
286
+ await fs.mkdir(destinationPath, { recursive: true });
287
+ await copyLocalArtifactEntries(sourcePath, destinationPath);
288
+ continue;
289
+ }
290
+
291
+ if (entry.isFile()) {
292
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
293
+ await fs.copyFile(sourcePath, destinationPath);
294
+ }
295
+ }
296
+ }
297
+
256
298
  /** Resolve a local:// URL to an on-disk path under the active session's local root. */
257
299
  export function resolveLocalUrlToPath(
258
300
  input: string | InternalUrl,
@@ -1,23 +1,23 @@
1
- You are omp Live, the realtime voice surface of one unified coding assistant for {{firstName}} (OS account: {{username}}).
1
+ You: omp Live, realtime voice surface of one unified coding assistant for {{firstName}} (OS account: {{username}}).
2
2
 
3
3
  <system-conventions>
4
- RFC 2119 applies to MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, and OPTIONAL. `NEVER` means `MUST NOT`.
4
+ RFC 2119: MUST, REQUIRED, SHOULD, RECOMMENDED, MAY, OPTIONAL. `NEVER` = `MUST NOT`.
5
5
  </system-conventions>
6
6
 
7
7
  <critical>
8
- - You and the omp coding agent are one assistant, not separate agents.
9
- - You MUST delegate repository work, coding, tool use, and verification to the client backend.
10
- - You MUST keep conversation natural while the client backend works.
8
+ - You + omp coding agent: one assistant, not separate agents.
9
+ - MUST delegate repository work, coding, tool use, verification to client backend.
10
+ - MUST keep conversation natural while client backend works.
11
11
  </critical>
12
12
 
13
- The user is speaking to you. You MUST respond directly, briefly, and conversationally. You MUST use speech-friendly phrasing. NEVER use markdown, code blocks, or long lists. NEVER read implementation detail aloud unless requested.
13
+ User speaks to you. MUST respond directly, briefly, conversationally, with speech-friendly phrasing. NEVER use markdown, code blocks, long lists, or read implementation detail aloud unless requested.
14
14
 
15
- The client backend is the same assistant's execution surface. It has the repository context, normal omp AgentSession, coding model, and tools. Coding, investigation, repository changes, commands, or verification? You MUST create a client delegation containing the complete plain-language request and all relevant conversational context. You MUST delegate promptly instead of attempting tool work yourself. A new request during active work MUST create a new delegation so it steers the same backend session.
15
+ Client backend: same assistant's execution surface; repository context, normal omp AgentSession, coding model, tools. Coding, investigation, repository changes, commands, or verification MUST promptly create client delegation with complete plain-language request and all relevant conversational context; NEVER attempt tool work. New request during active work MUST create new delegation, steering same backend session.
16
16
 
17
- You MUST treat delegation context as your own internal progress and result. NEVER describe the backend as another assistant. You MAY briefly acknowledge active work, but NEVER claim changes, findings, or verification before the backend reports them. Commentary context is silent progress for conversational continuity; NEVER recite it. Context beginning with `"Agent Final Message":` is the backend's final visible answer. You MUST present its useful result naturally as your own without mentioning the label, protocol, delegation, or backend.
17
+ Treat delegation context as own internal progress/results. NEVER describe backend as another assistant. MAY briefly acknowledge active work; NEVER claim changes, findings, or verification before backend reports. Commentary context: silent progress for conversational continuity; NEVER recite. Context beginning with `"Agent Final Message":`: backend's final visible answer; MUST present useful result naturally as own, NEVER mention label, protocol, delegation, or backend.
18
18
 
19
- Greetings, clarification, or ordinary conversation requiring no repository or tools? You MUST answer directly without delegation. You MUST ask a concise clarifying question only when the execution request is genuinely underspecified.
19
+ Greetings, clarification, ordinary conversation needing no repository/tools: MUST answer directly without delegation. MUST ask concise clarifying question only when execution request genuinely underspecified.
20
20
 
21
21
  <critical>
22
- You MUST preserve one-assistant continuity: converse here, delegate execution, then communicate the returned result as your own.
22
+ MUST preserve one-assistant continuity: converse here, delegate execution, communicate returned result as own.
23
23
  </critical>
package/src/main.ts CHANGED
@@ -1333,6 +1333,10 @@ export async function runRootCommand(
1333
1333
  if (parsedArgs.advisor) {
1334
1334
  settingsInstance.override("advisor.enabled", true);
1335
1335
  }
1336
+ // Apply --external-thinking CLI flag (ephemeral, not persisted)
1337
+ if (parsedArgs.externalThinking) {
1338
+ settingsInstance.override("externalThinking", true);
1339
+ }
1336
1340
 
1337
1341
  await logger.time(
1338
1342
  "initTheme:final",
package/src/mcp/client.ts CHANGED
@@ -38,8 +38,7 @@ import type {
38
38
  MCPTransport,
39
39
  } from "./types";
40
40
 
41
- /** MCP protocol version we support */
42
- const PROTOCOL_VERSION = "2025-03-26";
41
+ import { MCP_PROTOCOL_VERSION } from "./types";
43
42
 
44
43
  /** Client info sent during initialization */
45
44
  const CLIENT_INFO = {
@@ -98,7 +97,7 @@ async function initializeConnection(
98
97
  },
99
98
  ): Promise<MCPInitializeResult> {
100
99
  const params: MCPInitializeParams = {
101
- protocolVersion: PROTOCOL_VERSION,
100
+ protocolVersion: MCP_PROTOCOL_VERSION,
102
101
  capabilities: {
103
102
  roots: { listChanged: false },
104
103
  },
@@ -115,6 +114,11 @@ async function initializeConnection(
115
114
  throw options.signal.reason instanceof Error ? options.signal.reason : new Error("Aborted");
116
115
  }
117
116
 
117
+ // Echo the negotiated protocol version on every subsequent request. The MCP
118
+ // Streamable HTTP spec requires the MCP-Protocol-Version header after
119
+ // initialize; transports that don't need it ignore this.
120
+ transport.setProtocolVersion?.(result.protocolVersion);
121
+
118
122
  // Hook point: the transport now has the session ID from the initialize response.
119
123
  // For HTTP, this is the moment to open the SSE stream so server-to-client requests
120
124
  // triggered by notifications/initialized (e.g. roots/list) can be delivered.
@@ -48,6 +48,34 @@ export function setGeneratedHeader(headers: Record<string, string>, name: string
48
48
  headers[name] = value;
49
49
  }
50
50
 
51
+ /**
52
+ * Return `headers` without any entry whose name case-insensitively matches
53
+ * `name`. Used to keep transport-reserved protocol headers (e.g.
54
+ * `MCP-Protocol-Version`) out of user-configured headers so config can never
55
+ * inject them. Returns the original reference when there is nothing to strip,
56
+ * so the common (no-match) path allocates nothing.
57
+ */
58
+ export function withoutHeader(
59
+ headers: Record<string, string> | undefined,
60
+ name: string,
61
+ ): Record<string, string> | undefined {
62
+ if (!headers) return headers;
63
+ const lower = name.toLowerCase();
64
+ let hasMatch = false;
65
+ for (const key in headers) {
66
+ if (key.toLowerCase() === lower) {
67
+ hasMatch = true;
68
+ break;
69
+ }
70
+ }
71
+ if (!hasMatch) return headers;
72
+ const result: Record<string, string> = {};
73
+ for (const key in headers) {
74
+ if (key.toLowerCase() !== lower) result[key] = headers[key];
75
+ }
76
+ return result;
77
+ }
78
+
51
79
  const REDIRECT_STATUSES: Record<number, true> = { 301: true, 302: true, 303: true, 307: true, 308: true };
52
80
  const MAX_REDIRECT_HOPS = 5;
53
81