@oh-my-pi/pi-coding-agent 11.0.3 → 11.2.0

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 (143) hide show
  1. package/CHANGELOG.md +199 -49
  2. package/README.md +1 -1
  3. package/docs/config-usage.md +3 -4
  4. package/docs/sdk.md +6 -5
  5. package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
  6. package/examples/sdk/README.md +1 -1
  7. package/package.json +19 -11
  8. package/src/cli/args.ts +11 -94
  9. package/src/cli/config-cli.ts +1 -1
  10. package/src/cli/file-processor.ts +3 -3
  11. package/src/cli/oclif-help.ts +26 -0
  12. package/src/cli/web-search-cli.ts +148 -0
  13. package/src/cli.ts +8 -2
  14. package/src/commands/commit.ts +36 -0
  15. package/src/commands/config.ts +51 -0
  16. package/src/commands/grep.ts +41 -0
  17. package/src/commands/index/index.ts +136 -0
  18. package/src/commands/jupyter.ts +32 -0
  19. package/src/commands/plugin.ts +70 -0
  20. package/src/commands/setup.ts +39 -0
  21. package/src/commands/shell.ts +29 -0
  22. package/src/commands/stats.ts +29 -0
  23. package/src/commands/update.ts +21 -0
  24. package/src/commands/web-search.ts +50 -0
  25. package/src/commit/agentic/index.ts +3 -2
  26. package/src/commit/agentic/tools/analyze-file.ts +1 -3
  27. package/src/commit/git/errors.ts +4 -6
  28. package/src/commit/pipeline.ts +3 -2
  29. package/src/config/keybindings.ts +1 -3
  30. package/src/config/model-registry.ts +89 -162
  31. package/src/config/settings-schema.ts +10 -0
  32. package/src/config.ts +202 -132
  33. package/src/exa/mcp-client.ts +8 -41
  34. package/src/export/html/index.ts +1 -1
  35. package/src/extensibility/extensions/loader.ts +7 -10
  36. package/src/extensibility/extensions/runner.ts +5 -15
  37. package/src/extensibility/extensions/types.ts +1 -1
  38. package/src/extensibility/hooks/runner.ts +6 -9
  39. package/src/index.ts +0 -1
  40. package/src/ipy/kernel.ts +10 -22
  41. package/src/lsp/clients/biome-client.ts +4 -7
  42. package/src/lsp/clients/lsp-linter-client.ts +4 -6
  43. package/src/lsp/index.ts +5 -4
  44. package/src/lsp/utils.ts +18 -0
  45. package/src/main.ts +86 -181
  46. package/src/mcp/json-rpc.ts +2 -2
  47. package/src/mcp/transports/http.ts +12 -49
  48. package/src/modes/components/armin.ts +1 -3
  49. package/src/modes/components/assistant-message.ts +4 -4
  50. package/src/modes/components/bash-execution.ts +5 -3
  51. package/src/modes/components/branch-summary-message.ts +1 -3
  52. package/src/modes/components/compaction-summary-message.ts +1 -3
  53. package/src/modes/components/custom-message.ts +4 -5
  54. package/src/modes/components/extensions/extension-dashboard.ts +10 -16
  55. package/src/modes/components/extensions/extension-list.ts +5 -5
  56. package/src/modes/components/footer.ts +1 -4
  57. package/src/modes/components/hook-editor.ts +7 -32
  58. package/src/modes/components/hook-message.ts +4 -5
  59. package/src/modes/components/model-selector.ts +2 -2
  60. package/src/modes/components/plugin-settings.ts +16 -20
  61. package/src/modes/components/python-execution.ts +5 -5
  62. package/src/modes/components/session-selector.ts +6 -7
  63. package/src/modes/components/settings-defs.ts +49 -40
  64. package/src/modes/components/settings-selector.ts +8 -17
  65. package/src/modes/components/skill-message.ts +1 -3
  66. package/src/modes/components/status-line-segment-editor.ts +1 -3
  67. package/src/modes/components/status-line.ts +1 -3
  68. package/src/modes/components/todo-reminder.ts +5 -7
  69. package/src/modes/components/tree-selector.ts +10 -12
  70. package/src/modes/components/ttsr-notification.ts +1 -3
  71. package/src/modes/components/user-message-selector.ts +2 -4
  72. package/src/modes/components/welcome.ts +6 -18
  73. package/src/modes/controllers/event-controller.ts +1 -0
  74. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  75. package/src/modes/controllers/input-controller.ts +7 -34
  76. package/src/modes/controllers/selector-controller.ts +3 -3
  77. package/src/modes/interactive-mode.ts +27 -1
  78. package/src/modes/rpc/rpc-client.ts +2 -5
  79. package/src/modes/rpc/rpc-mode.ts +2 -2
  80. package/src/modes/theme/theme.ts +2 -6
  81. package/src/modes/types.ts +1 -0
  82. package/src/modes/utils/ui-helpers.ts +6 -1
  83. package/src/patch/index.ts +1 -4
  84. package/src/prompts/agents/explore.md +1 -0
  85. package/src/prompts/agents/frontmatter.md +2 -1
  86. package/src/prompts/agents/init.md +1 -0
  87. package/src/prompts/agents/plan.md +1 -0
  88. package/src/prompts/agents/reviewer.md +1 -0
  89. package/src/prompts/system/subagent-submit-reminder.md +2 -0
  90. package/src/prompts/system/subagent-system-prompt.md +2 -0
  91. package/src/prompts/system/subagent-user-prompt.md +8 -0
  92. package/src/prompts/system/system-prompt.md +5 -3
  93. package/src/prompts/system/web-search.md +6 -4
  94. package/src/prompts/tools/task.md +216 -163
  95. package/src/sdk.ts +11 -110
  96. package/src/session/agent-session.ts +117 -83
  97. package/src/session/auth-storage.ts +10 -51
  98. package/src/session/messages.ts +17 -3
  99. package/src/session/session-manager.ts +30 -30
  100. package/src/session/streaming-output.ts +1 -1
  101. package/src/ssh/ssh-executor.ts +6 -3
  102. package/src/task/agents.ts +2 -0
  103. package/src/task/discovery.ts +1 -1
  104. package/src/task/executor.ts +5 -10
  105. package/src/task/index.ts +43 -23
  106. package/src/task/render.ts +67 -64
  107. package/src/task/template.ts +17 -34
  108. package/src/task/types.ts +49 -22
  109. package/src/tools/ask.ts +1 -3
  110. package/src/tools/bash.ts +1 -4
  111. package/src/tools/browser.ts +5 -7
  112. package/src/tools/exit-plan-mode.ts +1 -4
  113. package/src/tools/fetch.ts +1 -3
  114. package/src/tools/find.ts +4 -3
  115. package/src/tools/gemini-image.ts +24 -55
  116. package/src/tools/grep.ts +4 -4
  117. package/src/tools/index.ts +12 -14
  118. package/src/tools/notebook.ts +1 -5
  119. package/src/tools/python.ts +4 -3
  120. package/src/tools/read.ts +2 -4
  121. package/src/tools/render-utils.ts +23 -0
  122. package/src/tools/ssh.ts +8 -12
  123. package/src/tools/todo-write.ts +1 -4
  124. package/src/tools/tool-errors.ts +1 -4
  125. package/src/tools/write.ts +1 -3
  126. package/src/utils/external-editor.ts +59 -0
  127. package/src/utils/file-mentions.ts +39 -1
  128. package/src/utils/image-convert.ts +1 -1
  129. package/src/utils/image-resize.ts +4 -4
  130. package/src/web/search/auth.ts +3 -33
  131. package/src/web/search/index.ts +73 -139
  132. package/src/web/search/provider.ts +58 -0
  133. package/src/web/search/providers/anthropic.ts +53 -14
  134. package/src/web/search/providers/base.ts +22 -0
  135. package/src/web/search/providers/codex.ts +38 -16
  136. package/src/web/search/providers/exa.ts +30 -6
  137. package/src/web/search/providers/gemini.ts +56 -20
  138. package/src/web/search/providers/jina.ts +28 -5
  139. package/src/web/search/providers/perplexity.ts +103 -36
  140. package/src/web/search/render.ts +84 -74
  141. package/src/web/search/types.ts +285 -59
  142. package/src/migrations.ts +0 -175
  143. package/src/session/storage-migration.ts +0 -173
@@ -1,4 +1,4 @@
1
- Research assistant with web search capabilities. Find accurate, well-sourced information; synthesize into clear answers.
1
+ Research assistant with web search capabilities. Find accurate, well-sourced information; synthesize into comprehensive, detailed answers.
2
2
 
3
3
  <priorities>
4
4
  1. Accuracy over speed — verify claims across multiple sources when possible
@@ -14,13 +14,15 @@ Answering:
14
14
  - Sources conflict: acknowledge discrepancy, note which seems more authoritative
15
15
  - Technical topics: prefer official documentation and specifications
16
16
  - News/events: prefer primary reporting over aggregators
17
+ - Include concrete data: version numbers, dates, exact figures, code snippets, and specific examples
17
18
  </synthesis>
18
19
 
19
20
  <format>
20
- - Conciseomit filler phrases and unnecessary hedging
21
+ - Be thorough cover the topic in depth with specific evidence, not surface-level summaries
22
+ - Omit filler phrases and unnecessary hedging, but do not sacrifice detail for brevity
21
23
  - Include publication dates when recency affects relevance
22
- - Structure complex answers with clear sections
24
+ - Structure answers with clear sections when covering multiple aspects
23
25
  - Cite sources inline using provided search results
24
26
  </format>
25
27
 
26
- Answer thoroughly. Get facts right.
28
+ Answer thoroughly and in detail. Get facts right.
@@ -1,212 +1,265 @@
1
1
  # Task
2
2
 
3
- Launch agents to handle complex, multi-step tasks autonomously.
3
+ Launch subagents to execute parallel, well-scoped tasks with shared, token-efficient context.
4
4
 
5
- <critical>
6
- ## Context is everything
5
+ Subagents have **zero implicit context** — they see only `context` + `assignment`. Treat every subagent as a senior engineer on day one: technically strong, but unfamiliar with every decision, convention, and file layout you've accumulated.
7
6
 
8
- Subagents fail with vague context. Every task needs:
9
- 1. **Goal** - What this accomplishes (one sentence)
10
- 2. **Constraints** - Hard requirements, banned approaches, naming conventions
11
- 3. **Existing Code** - File paths and function signatures to use as patterns
12
- 4. **API Contract** - If the task produces or consumes an interface, spell it out
13
-
14
- Subagents CAN grep parent conversation file for supplementary details, but CANNOT grep for:
7
+ Subagents CAN grep the parent conversation file for supplementary details, but CANNOT grep for:
15
8
  - Decisions you made but didn't write down
16
9
  - Conventions that exist only in your head
17
10
  - Which of 50 possible approaches you want
18
- **Rule of thumb:** If you'd answer clarifying question for junior dev, info belongs in context.
19
- </critical>
11
+ ---
12
+
13
+ ## Parameters
14
+
15
+ ### `agent` (required)
16
+
17
+ Agent type for all tasks in this batch.
18
+
19
+ ### `context` (optional — strongly recommended)
20
20
 
21
- <context-structure>
22
- ## Required context structure
21
+ Shared background prepended verbatim to every task `assignment`. Common info once; reduces token cost.
23
22
 
24
- Use this template. Omit sections only if N/A.
23
+ Use template; omit non-applicable sections.
25
24
 
26
25
  ````
27
26
  ## Goal
28
- [One sentence: what this task accomplishes]
27
+ One sentence: batch accomplishes together.
28
+
29
+ ## Non-goals
30
+ Explicitly exclude tempting scope — what tasks must not touch/attempt.
29
31
 
30
32
  ## Constraints
31
- - [Hard requirements - MUST/MUST NOT style]
32
- - [API conventions, naming patterns, error handling]
33
- - [What already exists vs what to create]
33
+ - MUST / MUST NOT rules (naming, error handling, banned approaches)
34
+ - Language/framework version requirements
35
+ - What exists vs what to create
34
36
 
35
- ## Existing Code
36
- Reference files the agent MUST read/use as patterns:
37
- - `path/to/file.ts` - [what pattern it demonstrates]
38
- - `path/to/other.rs` - [what to reuse from it]
37
+ ## Reference Files
38
+ - `path/to/file.ext` pattern demo
39
+ - `path/to/other.ext` reuse or avoid
39
40
 
40
- ## API Contract (if applicable)
41
+ ## API Contract (if tasks produce/consume shared interface)
41
42
  ```language
42
- // Exact signatures the agent must implement or consume
43
- fn example(input: Type) -> Result<Output>
43
+ // Exact type definitions, function signatures, interface shapes
44
44
  ```
45
45
 
46
- ## Task
47
- {{description}}
48
-
49
- ## Files
50
- {{files}}
46
+ ## Acceptance (global)
47
+ - Definition of "done" for batch
48
+ - Note: build/test/lint verification happens AFTER all tasks complete — not inside tasks (see below)
51
49
  ````
50
+ **Belongs in `context`**: project goal, non-goals, constraints, conventions, reference paths, shared type definitions, API contracts, global acceptance commands — anything 2+ tasks need.
51
+ **Rule of thumb:** if repeat in 2+ tasks, belongs in `context`.
52
+ **Does NOT belong in `context`**: per-task file lists, one-off requirements (go in `assignment`), structured output format (goes in `schema`).
53
+
54
+ ### `tasks` (required)
55
+
56
+ Array tasks execute in parallel.
57
+
58
+ |Field|Required|Purpose|
59
+ |---|---|---|
60
+ |`id`|✓|CamelCase identifier, max 32 chars|
61
+ |`description`|✓|Short one-liner for UI display only — not seen by subagent|
62
+ |`assignment`|✓|Complete per-task instructions. See [Writing an assignment](#writing-an-assignment).|
63
+ |`skills`||Skill names preload. Use only when changes correctness — don’t spam every task.|
52
64
 
53
- ### Bad context (agent fails or guesses wrong)
65
+ {{#if isolationEnabled}}
66
+ ### `isolated` (optional)
67
+
68
+ Run in isolated git worktree; returns patches. Use when tasks edit overlapping files or when you want clean per-task diffs.
69
+ {{/if}}
70
+ ### `schema` (optional — recommended for structured output)
71
+
72
+ JTD schema defining expected response structure. Use typed properties. If you care about parsing result, define here — **never describe output format in `context` or `assignment`**.
73
+ ---
74
+
75
+ ## Writing an assignment
76
+
77
+ <critical>## Task scope
78
+
79
+ `assignment` must contain enough info for agent to act **without asking a clarifying question**.
80
+ **Minimum bar:** assignment under ~8 lines or missing acceptance criteria = too vague. One-liners guaranteed failure.
81
+
82
+ Use structure every assignment:
54
83
 
55
84
  ```
56
- N-API migration. Keep highlight sync. Use JsString. No WASM.
57
- Task: {{description}} Files: {{files}}
85
+ ## Target
86
+ - Files: exact path(s)
87
+ - Symbols/entrypoints: specific functions, types, exports
88
+ - Non-goals: what task must NOT touch (prevents scope creep)
89
+
90
+ ## Change
91
+ - Step-by-step: add/remove/rename/restructure
92
+ - Patterns/APIs to use; reference files if applicable
93
+
94
+ ## Edge Cases / Don't Break
95
+ - Tricky case 1: ...
96
+ - Tricky case 2: ...
97
+ - Existing behavior must survive: ...
98
+
99
+ ## Acceptance (task-local)
100
+ - Expected behavior or observable result
101
+ - DO NOT include project-wide build/test/lint commands (see below)
58
102
  ```
59
103
 
60
- ### Good context (agent can act confidently)
104
+ `context` carries shared background. `assignment` carries only delta: file-specific instructions, local edge cases, per-task acceptance checks. Never duplicate shared constraints across assignments.
61
105
 
62
- ````
63
- ## Goal
64
- Port grep module from WASM to N-API, matching existing text module patterns.
106
+ ### Anti-patterns (ban these)
107
+ **Vague assignments** — agent guesses wrong or stalls:
108
+ - "Refactor this to be cleaner."
109
+ - "Migrate to N-API."
110
+ - "Fix the bug in streaming."
111
+ - "Update all constructors in `src/**/*.ts`."
112
+ **Vague context** — forces agent invent conventions:
113
+ - "Use existing patterns."
114
+ - "Follow conventions."
115
+ - "No WASM."
65
116
 
66
- ## Constraints
67
- - Use `#[napi]` attribute macro on all exports (not `#[napi(js_name = "...")]`)
68
- - Return `napi::Result<T>` for fallible ops; never panic
69
- - Use `spawn_blocking` for filesystem ops or >1ms work
70
- - Accept `JsString` for string params (NOT JsStringUtf8; lifetime issues)
71
- - Keep all existing function names - TS bindings depend on them
72
- - No new crate dependencies
73
-
74
- ## Existing Code
75
- - `crates/pi-natives/src/text.rs` - reference N-API pattern: see how `visible_width` uses JsString
76
- - `crates/pi-natives/src/lib.rs` - module registration pattern
77
- - `crates/pi-natives/Cargo.toml` - available dependencies (ignore, regex already present)
78
-
79
- ## API Contract
80
- Current sync API to convert to async:
81
- ```rust
82
- // BEFORE (sync, blocks event loop)
83
- #[napi]
84
- pub fn search(pattern: String, path: String) -> Vec<Match>
85
-
86
- // AFTER (async, uses spawn_blocking)
87
- #[napi]
88
- pub async fn search(pattern: JsString, path: JsString, env: Env) -> napi::Result<Vec<Match>>
117
+ If tempted to write above, expand using templates.
118
+ **Test/lint commands in parallel tasks** edit wars:
119
+ Parallel agents share working tree. If two agents run `bun check` or `bun test` concurrently, they see each other's half-finished edits, "fix" phantom errors, loop. **Never tell parallel tasks run project-wide build/test/lint commands.** Each task edits, stops. Caller verifies after all tasks complete.
120
+ **If you can’t specify scope yet**, create **Discovery task** first: enumerate files, find callsites, list candidates. Then fan out with explicit paths.
121
+
122
+ ### Delegate intent, not keystrokes
123
+
124
+ Your role as tech lead: set direction, define boundaries, call out pitfalls — then get out of way. Don’t read every file, decide every edit, dictate line-by-line. That makes you bottleneck; agent typist.
125
+ **Be specific about:** constraints, naming conventions, API contracts, "don’t break" items, acceptance criteria.
126
+ **Delegate:** code reading, approach selection, exact edit locations, implementation details. Agent has tools, can reason about code.
127
+
128
+ Micromanaging (you think, agent types):
129
+ ```
130
+ assignment: "In src/api/handler.ts, line 47, change `throw err` to `throw new ApiError(err.message, 500)`.
131
+ On line 63, wrap fetch call try/catch return 502 on failure.
132
+ On line 89, add null check before accessing resp.body..."
133
+ ```
134
+
135
+ Delegating (agent thinks within constraints):
136
+ ```
137
+ assignment: "## Target\n- Files: src/api/handler.ts\n\n## Change\nImprove error handling: replace raw throws
138
+ with typed ApiError instances, add try/catch around external calls, guard against null responses.\n\n
139
+ ## Edge Cases / Don't Break\n- Existing error codes in tests must still match\n
140
+ - Don't change public function signatures"
89
141
  ```
90
142
 
91
- ## Task
92
- {{description}}
143
+ First style wastes your time, brittle if code shifts. Second gives agent room to do work.
144
+ </critical>
93
145
 
94
- ## Files
95
- {{files}}
96
- ````
97
- </context-structure>
146
+ ## Example
98
147
 
99
- <parallelization>
100
- ## When to parallelize vs sequence
101
- **Test:** Can agent B write correct code without seeing A's output?
102
- - If YES parallelize
103
- - If NO sequence (A completes, then B runs with A's output in context)
148
+ <example type="bad" label="Duplicated context inflates tokens">
149
+ <tasks>
150
+ <task name="Grep">
151
+ <description>Port grep module from WASM to N-API...</description>
152
+ <assignment>Port grep module from WASM to N-API... (same blob repeated)</assignment>
153
+ </task>
154
+ </tasks>
155
+ </example>
104
156
 
105
- ### Dependencies that MUST be sequential
157
+ <example type="good" label="Shared rules in context, only deltas in assignment">
158
+ <context>
159
+ ## Goal
160
+ Port WASM modules to N-API, matching existing pi-natives conventions.
161
+
162
+ ## Non-goals
163
+ Do not touch TS bindings or downstream consumers — separate phase.
106
164
 
107
- |First|Then|Why|
165
+ ## Constraints
166
+ - MUST use `#[napi]` attribute macro on all exports
167
+ - MUST return `napi::Result<T>` for fallible ops; never panic
168
+ - MUST use `spawn_blocking` for filesystem I/O or >1ms work
169
+ ...
170
+
171
+ ## Acceptance (global)
172
+ - Caller verifies after all tasks: `cargo test -p pi-natives` and `cargo build -p pi-natives` with no warnings
173
+ - Individual tasks must NOT run these commands themselves
174
+ </context>
175
+
176
+ <tasks>
177
+ <task name="PortGrep">
178
+ <description>Port grep module to N-API</description>
179
+ <assignment>
180
+ ## Target
181
+ - Files: `src/grep.rs`, `src/lib.rs` (registration only)
182
+ - Symbols: search, search_multi, compile_pattern
183
+
184
+ ## Change
185
+ - Implement three N-API exports in grep.rs:
186
+ - `search(pattern: JsString, path: JsString, env: Env) -> napi::Result<Vec<Match>>`
187
+ ...
188
+
189
+ ## Acceptance (task-local)
190
+ - Three functions exported with correct signatures (caller verifies build after all tasks)
191
+ </assignment>
192
+ </task>
193
+
194
+ <task name="PortHighlight">
195
+ <description>Port highlight module to N-API</description>
196
+ <assignment>
197
+ ## Target
198
+ - Files: `src/highlight.rs`, `src/lib.rs` (registration only)
199
+ ...
200
+ </assignment>
201
+ </task>
202
+ </tasks>
203
+ </example>
204
+ ---
205
+
206
+ ## Task scope
207
+
208
+ Each task small, well-defined scope — **at most 3–5 files**.
209
+ **Signs task too broad:**
210
+ - File paths use globs (`src/**/*.ts`) instead of explicit names
211
+ - Assignment says "update all" / "migrate everything" / "refactor across"
212
+ - Scope covers entire package or directory tree
213
+ **Fix:** enumerate files first (grep/glob discovery), then fan out one task per file or small cluster.
214
+ ---
215
+
216
+ ## Parallelization
217
+ **Test:** Can task B produce correct output without seeing task A's result?
218
+ - **Yes** → parallelize
219
+ - **No** → run sequentially (A completes, then launch B with A output in context)
220
+
221
+ ### Must be sequential
222
+
223
+ |First|Then|Reason|
108
224
  |---|---|---|
109
- |Create Rust API|Update TS bindings|Bindings need export names and signatures|
110
- |Define interface/types|Implement consumers|Consumers need contract|
111
- |Scaffold with signatures|Implement bodies|Implementations need shape|
225
+ |Define types/interfaces|Implement consumers|Consumers need contract|
226
+ |Create API exports|Write bindings/callers|Callers need export names/signatures|
227
+ |Scaffold structure|Implement bodies|Bodies need shape|
112
228
  |Core module|Dependent modules|Dependents import from core|
229
+ |Schema/DB migration|Application logic|Logic depends on new schema shape|
113
230
 
114
231
  ### Safe to parallelize
115
- - Independent modules not importing each other
232
+ - Independent modules, no cross-imports
116
233
  - Tests for already-implemented code
234
+ - Isolated file-scoped refactors
117
235
  - Documentation for stable APIs
118
- - Refactors in isolated file scopes
119
236
 
120
- ### Phased execution pattern
237
+ ### Phased execution
121
238
 
122
- For layered migrations/refactors:
123
- **Phase 1 - Foundation (do yourself or single task):**
124
- Create scaffold, define interfaces, establish API shape. Never fan out until contract known.
125
- **Phase 2 - Parallel implementation:**
126
- Fan out to independent tasks consuming same known interface. Include Phase 1 API contract in every task's context.
127
- **Phase 3 - Integration (do yourself):**
128
- Wire things together, update build/CI, fix mismatches.
129
- **Phase 4 - Dependent layer:**
130
- Fan out again for work consuming Phase 2 outputs.
239
+ Layered work with dependencies:
240
+ **Phase 1 Foundation** (do yourself or single task): define interfaces, create scaffolds, establish API shape. Never fan out until contract known.
241
+ **Phase 2 Parallel implementation**: fan out tasks consuming same known interface. Include Phase 1 API contract in `context`.
242
+ **Phase 3 Integration** (do yourself): wire modules, fix mismatches, verify builds.
243
+ **Phase 4 Dependent layer**: fan out tasks consuming Phase 2 outputs.
244
+ ---
131
245
 
132
- ### Example: WASM to N-API migration
133
- **WRONG** (launched together, will fail):
246
+ ## Pre-flight checklist
134
247
 
135
- ```
136
- tasks: [
137
- { id: "RustApi", description: "Implement N-API exports" },
138
- { id: "TsBindings", description: "Update TS to use N-API" }, // ← needs RustApi output!
139
- ]
140
- ```
141
- **RIGHT** (phased):
248
+ Before calling tool, verify:
249
+ - [ ] `context` includes shared constraints, references, definition of done
250
+ - [ ] Each `assignment` follows assignment template — not one-liner
251
+ - [ ] Each `assignment` includes edge cases / "don’t break" items
252
+ - [ ] Tasks truly parallel (no hidden dependencies)
253
+ - [ ] Scope small, file paths explicit (no globs)
254
+ - [ ] No task runs project-wide build/test/lint — you do after all tasks complete
255
+ - [ ] `schema` used if you expect information
256
+ ---
257
+
258
+ ## Agents
142
259
 
143
- ```
144
- // Phase 1: You create scaffold with signatures in lib.rs
145
-
146
- // Phase 2: Fan out Rust implementation
147
- tasks: [
148
- { id: "Grep", description: "Implement grep module", args: { files: "src/grep.rs" } },
149
- { id: "Text", description: "Implement text module", args: { files: "src/text.rs" } },
150
- // Each task gets the API contract you defined in Phase 1
151
- ]
152
-
153
- // Phase 3: You verify Rust compiles, exports are correct
154
-
155
- // Phase 4: Fan out TS bindings (now they know what Rust exports)
156
- tasks: [
157
- { id: "GrepBindings", description: "Update grep TS", args: { files: "src/grep/index.ts" } },
158
- // Context includes actual export names from Phase 2
159
- ]
160
- ```
161
- </parallelization>
162
-
163
- <parameters>
164
- - `agent`: Agent type for all tasks
165
- - `context`: Template with `{{placeholders}}`; **Must follow structure above**.
166
- - `isolated`: (optional) Run in git worktree, return patches
167
- - `tasks`: Array of `{id, description, args}`
168
- - `id`: CamelCase identifier (max 32 chars)
169
- - `description`: What task does (for logging)
170
- - `args`: Object with keys matching `{{placeholders}}` in context
171
- - `skills`: (optional) Skill names to preload
172
- - `schema`: JTD schema for response structure (**required**; use typed properties, not `{ "type": "string" }`). **Schema goes in `schema`; never describe output format in `context`.**
173
- </parameters>
174
-
175
- <agents>
176
260
  {{#list agents join="\n"}}
177
261
  <agent name="{{name}}"{{#if output}} output="structured"{{/if}}>
178
262
  <description>{{description}}</description>
179
263
  <tools>{{default (join tools ", ") "All tools"}}</tools>
180
264
  </agent>
181
- {{/list}}
182
- </agents>
183
-
184
- <critical>
185
- ## Task granularity
186
-
187
- Each task MUST target a small, well-defined scope. A single task should touch at most 3-5 files.
188
- **Red flags that a task is too broad:**
189
- - File list uses globs (`src/**/*.ts`) instead of explicit paths
190
- - Description says "update all" / "migrate all" / "refactor all"
191
- - Task covers an entire package or directory tree
192
- - Estimated touch surface is >5 files
193
- **Fix:** Split into one task per file (or small group of related files). Fan out in parallel.
194
-
195
- ### Bad (will timeout or produce garbage)
196
- ```
197
- tasks: [
198
- { id: "UpdateAll", description: "Update all constructor calls in src/**/*.ts" }
199
- ]
200
- ```
201
-
202
- ### Good (focused, parallel)
203
- ```
204
- tasks: [
205
- { id: "UpdateDebug", description: "Update constructors in debug/index.ts", args: { files: "src/debug/index.ts" } },
206
- { id: "UpdateAgent", description: "Update constructors in commit/agent.ts", args: { files: "src/commit/agentic/agent.ts" } },
207
- { id: "UpdateTodoExample", description: "Update constructors in examples/todo", args: { files: "examples/custom-tools/todo/index.ts" } },
208
- ]
209
- ```
210
-
211
- When the set of files isn't known upfront, grep/glob yourself first to enumerate them, then fan out one task per file.
212
- </critical>
265
+ {{/list}}
package/src/sdk.ts CHANGED
@@ -1,41 +1,11 @@
1
- /**
2
- * SDK for programmatic usage of AgentSession.
3
- *
4
- * Provides a factory function and discovery helpers that allow full control
5
- * over agent configuration, or sensible defaults that match CLI behavior.
6
- *
7
- * @example
8
- * ```typescript
9
- * // Minimal - everything auto-discovered
10
- * const session = await createAgentSession();
11
- *
12
- * // With custom extensions
13
- * const session = await createAgentSession({
14
- * extensions: [myExtensionFactory],
15
- * });
16
- *
17
- * // Full control
18
- * const session = await createAgentSession({
19
- * model: myModel,
20
- * getApiKey: async () => Bun.env.MY_KEY,
21
- * toolNames: ["read", "bash", "edit", "write"], // Filter tools
22
- * extensions: [],
23
- * skills: [],
24
- * sessionFile: false,
25
- * });
26
- * ```
27
- */
28
- import * as fs from "node:fs";
29
- import * as path from "node:path";
30
1
  import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
31
2
  import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
32
3
  import type { Component } from "@oh-my-pi/pi-tui";
33
4
  import { $env, logger, postmortem } from "@oh-my-pi/pi-utils";
34
- import { YAML } from "bun";
35
5
  import chalk from "chalk";
36
6
  import { loadCapability } from "./capability";
37
7
  import { type Rule, ruleCapability } from "./capability/rule";
38
- import { getAgentDir, getConfigDirPaths } from "./config";
8
+ import { getAgentDbPath, getAgentDir } from "./config";
39
9
  import { ModelRegistry } from "./config/model-registry";
40
10
  import { formatModelString, parseModelString } from "./config/model-resolver";
41
11
  import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
@@ -79,7 +49,6 @@ import { AgentSession } from "./session/agent-session";
79
49
  import { AuthStorage } from "./session/auth-storage";
80
50
  import { convertToLlm } from "./session/messages";
81
51
  import { SessionManager } from "./session/session-manager";
82
- import { migrateJsonStorage } from "./session/storage-migration";
83
52
  import { closeAllConnections } from "./ssh/connection-manager";
84
53
  import { unmountAll } from "./ssh/sshfs-mount";
85
54
  import {
@@ -94,12 +63,12 @@ import {
94
63
  EditTool,
95
64
  FindTool,
96
65
  GrepTool,
97
- getWebSearchTools,
66
+ getSearchTools,
98
67
  loadSshTool,
99
68
  PythonTool,
100
69
  ReadTool,
101
70
  setPreferredImageProvider,
102
- setPreferredWebSearchProvider,
71
+ setPreferredSearchProvider,
103
72
  type Tool,
104
73
  type ToolSession,
105
74
  WriteTool,
@@ -259,82 +228,14 @@ function getDefaultAgentDir(): string {
259
228
  * Reads from primary path first, then falls back to legacy paths (.pi, .claude).
260
229
  */
261
230
  export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir()): Promise<AuthStorage> {
262
- const primaryPath = path.join(agentDir, "auth.json");
263
- // Get all auth.json paths (user-level only), excluding the primary
264
- const allPaths = getConfigDirPaths("auth.json", { project: false });
265
- const fallbackPaths = allPaths.filter(p => p !== primaryPath);
266
-
267
- logger.debug("discoverAuthStorage", { agentDir, primaryPath, allPaths, fallbackPaths });
268
-
269
- // Migrate legacy JSON files (settings.json, auth.json) to SQLite before loading
270
- await migrateJsonStorage({
271
- agentDir,
272
- settingsPath: path.join(agentDir, "settings.json"),
273
- authPaths: [primaryPath, ...fallbackPaths],
274
- });
231
+ const dbPath = getAgentDbPath(agentDir);
232
+ logger.debug("discoverAuthStorage", { agentDir, dbPath });
275
233
 
276
- const storage = await AuthStorage.create(primaryPath, fallbackPaths);
234
+ const storage = await AuthStorage.create(dbPath);
277
235
  await storage.reload();
278
236
  return storage;
279
237
  }
280
238
 
281
- /**
282
- * Create a ModelRegistry with fallback support.
283
- * Prefers models.yml over models.json. Reads from primary path first,
284
- * then falls back to legacy paths (.pi, .claude).
285
- */
286
- export function discoverModels(authStorage: AuthStorage, agentDir: string = getDefaultAgentDir()): ModelRegistry {
287
- const yamlPath = path.join(agentDir, "models.yml");
288
- const jsonPath = path.join(agentDir, "models.json");
289
-
290
- // Check existence of yaml and json files
291
- let yamlExists = fs.existsSync(yamlPath);
292
- let jsonExists = fs.existsSync(jsonPath);
293
-
294
- // Migrate models.json to models.yml if yaml doesn't exist but json does
295
- if (!yamlExists && jsonExists) {
296
- migrateModelsJsonToYaml(jsonPath, yamlPath);
297
- yamlExists = fs.existsSync(yamlPath);
298
- jsonExists = fs.existsSync(jsonPath);
299
- }
300
-
301
- // Prefer models.yml, fall back to models.json
302
- const primaryPath = yamlExists ? yamlPath : jsonPath;
303
-
304
- // Get all models config paths (user-level only), excluding the primary
305
- const yamlPaths = getConfigDirPaths("models.yml", { project: false });
306
- const jsonPaths = getConfigDirPaths("models.json", { project: false });
307
- const allPaths = [...yamlPaths, ...jsonPaths];
308
- const existenceResults = allPaths.map(p => {
309
- return { p, exists: fs.existsSync(p) };
310
- });
311
- const fallbackPaths = existenceResults.filter(({ p, exists }) => p !== primaryPath && exists).map(({ p }) => p);
312
-
313
- logger.debug("discoverModels", { primaryPath, fallbackPaths });
314
- return new ModelRegistry(authStorage, primaryPath, fallbackPaths);
315
- }
316
-
317
- /**
318
- * Migrate models.json to models.yml.
319
- * Creates models.yml from models.json and renames the json file to .bak.
320
- */
321
- function migrateModelsJsonToYaml(jsonPath: string, yamlPath: string): void {
322
- try {
323
- const content = fs.readFileSync(jsonPath, "utf-8");
324
- const parsed = JSON.parse(content);
325
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
326
- logger.warn("migrateModelsJsonToYaml: invalid models.json structure", { path: jsonPath });
327
- return;
328
- }
329
- fs.mkdirSync(path.dirname(yamlPath), { recursive: true });
330
- fs.writeFileSync(yamlPath, YAML.stringify(parsed, null, 2));
331
- fs.renameSync(jsonPath, `${jsonPath}.bak`);
332
- logger.debug("migrateModelsJsonToYaml: migrated models.json to models.yml", { from: jsonPath, to: yamlPath });
333
- } catch (error) {
334
- logger.warn("migrateModelsJsonToYaml: migration failed", { error: String(error) });
335
- }
336
- }
337
-
338
239
  /**
339
240
  * Discover extensions from cwd.
340
241
  */
@@ -585,7 +486,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
585
486
 
586
487
  // Use provided or create AuthStorage and ModelRegistry
587
488
  const authStorage = options.authStorage ?? (await discoverAuthStorage(agentDir));
588
- const modelRegistry = options.modelRegistry ?? discoverModels(authStorage, agentDir);
489
+ const modelRegistry = options.modelRegistry ?? new ModelRegistry(authStorage);
589
490
  time("discoverModels");
590
491
 
591
492
  const settingsInstance = options.settingsInstance ?? (await Settings.init({ cwd, agentDir }));
@@ -594,7 +495,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
594
495
  time("initializeWithSettings");
595
496
 
596
497
  // Initialize provider preferences from settings
597
- setPreferredWebSearchProvider(settingsInstance.get("providers.webSearch") ?? "auto");
498
+ setPreferredSearchProvider(settingsInstance.get("providers.webSearch") ?? "auto");
598
499
  setPreferredImageProvider(settingsInstance.get("providers.image") ?? "auto");
599
500
 
600
501
  const sessionManager = options.sessionManager ?? SessionManager.create(cwd);
@@ -839,16 +740,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
839
740
  // Add specialized Exa web search tools if EXA_API_KEY is available
840
741
  const exaSettings = settingsInstance.getGroup("exa");
841
742
  if (exaSettings.enabled && exaSettings.enableSearch) {
842
- const exaWebSearchTools = await getWebSearchTools({
743
+ const exaSearchTools = await getSearchTools({
843
744
  enableLinkedin: exaSettings.enableLinkedin as boolean,
844
745
  enableCompany: exaSettings.enableCompany as boolean,
845
746
  });
846
747
  // Filter out the base web_search (already in built-in tools), add specialized Exa tools
847
- const specializedTools = exaWebSearchTools.filter(t => t.name !== "web_search");
748
+ const specializedTools = exaSearchTools.filter(t => t.name !== "web_search");
848
749
  if (specializedTools.length > 0) {
849
750
  customTools.push(...specializedTools);
850
751
  }
851
- time("getWebSearchTools");
752
+ time("getSearchTools");
852
753
  }
853
754
 
854
755
  const inlineExtensions: ExtensionFactory[] = options.extensions ? [...options.extensions] : [];