@oh-my-pi/pi-coding-agent 14.2.1 → 14.4.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 (137) hide show
  1. package/CHANGELOG.md +143 -1
  2. package/package.json +19 -19
  3. package/src/autoresearch/prompt.md +1 -1
  4. package/src/cli/args.ts +10 -1
  5. package/src/cli/shell-cli.ts +15 -3
  6. package/src/commit/agentic/prompts/analyze-file.md +1 -1
  7. package/src/config/model-registry.ts +67 -15
  8. package/src/config/prompt-templates.ts +5 -5
  9. package/src/config/settings-schema.ts +63 -4
  10. package/src/cursor.ts +3 -8
  11. package/src/debug/system-info.ts +6 -2
  12. package/src/discovery/claude.ts +58 -36
  13. package/src/discovery/helpers.ts +3 -3
  14. package/src/discovery/opencode.ts +20 -2
  15. package/src/edit/diff.ts +50 -47
  16. package/src/edit/index.ts +87 -57
  17. package/src/edit/line-hash.ts +735 -19
  18. package/src/edit/modes/apply-patch.ts +0 -9
  19. package/src/edit/modes/atom.ts +658 -0
  20. package/src/edit/modes/chunk.ts +144 -78
  21. package/src/edit/modes/hashline.ts +223 -146
  22. package/src/edit/modes/patch.ts +5 -9
  23. package/src/edit/modes/replace.ts +6 -11
  24. package/src/edit/renderer.ts +112 -143
  25. package/src/edit/streaming.ts +385 -0
  26. package/src/exec/bash-executor.ts +58 -5
  27. package/src/export/html/template.generated.ts +1 -1
  28. package/src/export/html/template.js +4 -12
  29. package/src/extensibility/custom-tools/types.ts +2 -0
  30. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  31. package/src/internal-urls/docs-index.generated.ts +7 -7
  32. package/src/internal-urls/pi-protocol.ts +0 -2
  33. package/src/lsp/client.ts +8 -1
  34. package/src/lsp/defaults.json +2 -1
  35. package/src/lsp/index.ts +1 -1
  36. package/src/mcp/render.ts +1 -8
  37. package/src/modes/acp/acp-agent.ts +76 -2
  38. package/src/modes/components/assistant-message.ts +5 -34
  39. package/src/modes/components/diff.ts +23 -14
  40. package/src/modes/components/footer.ts +21 -16
  41. package/src/modes/components/hook-editor.ts +1 -1
  42. package/src/modes/components/settings-defs.ts +6 -1
  43. package/src/modes/components/todo-reminder.ts +1 -8
  44. package/src/modes/components/tool-execution.ts +112 -105
  45. package/src/modes/controllers/input-controller.ts +1 -1
  46. package/src/modes/controllers/selector-controller.ts +1 -1
  47. package/src/modes/interactive-mode.ts +0 -2
  48. package/src/modes/print-mode.ts +8 -0
  49. package/src/modes/theme/mermaid-cache.ts +13 -52
  50. package/src/modes/theme/theme.ts +2 -2
  51. package/src/prompts/agents/librarian.md +1 -1
  52. package/src/prompts/agents/reviewer.md +4 -4
  53. package/src/prompts/ci-green-request.md +1 -1
  54. package/src/prompts/review-request.md +1 -1
  55. package/src/prompts/system/subagent-system-prompt.md +3 -3
  56. package/src/prompts/system/subagent-yield-reminder.md +11 -0
  57. package/src/prompts/system/system-prompt.md +4 -1
  58. package/src/prompts/tools/ask.md +3 -2
  59. package/src/prompts/tools/ast-edit.md +15 -19
  60. package/src/prompts/tools/ast-grep.md +18 -24
  61. package/src/prompts/tools/atom.md +96 -0
  62. package/src/prompts/tools/browser.md +1 -0
  63. package/src/prompts/tools/chunk-edit.md +58 -179
  64. package/src/prompts/tools/debug.md +4 -5
  65. package/src/prompts/tools/exit-plan-mode.md +4 -5
  66. package/src/prompts/tools/find.md +4 -8
  67. package/src/prompts/tools/github.md +18 -0
  68. package/src/prompts/tools/grep.md +8 -8
  69. package/src/prompts/tools/hashline.md +22 -89
  70. package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
  71. package/src/prompts/tools/inspect-image.md +6 -6
  72. package/src/prompts/tools/lsp.md +6 -0
  73. package/src/prompts/tools/patch.md +12 -19
  74. package/src/prompts/tools/python.md +3 -2
  75. package/src/prompts/tools/read-chunk.md +46 -8
  76. package/src/prompts/tools/read.md +9 -6
  77. package/src/prompts/tools/ssh.md +8 -17
  78. package/src/prompts/tools/todo-write.md +54 -41
  79. package/src/sdk.ts +22 -14
  80. package/src/session/agent-session.ts +61 -22
  81. package/src/session/session-manager.ts +228 -57
  82. package/src/session/streaming-output.ts +11 -0
  83. package/src/system-prompt.ts +7 -2
  84. package/src/task/executor.ts +44 -48
  85. package/src/task/render.ts +11 -13
  86. package/src/tools/ask.ts +7 -7
  87. package/src/tools/ast-edit.ts +45 -41
  88. package/src/tools/ast-grep.ts +77 -85
  89. package/src/tools/bash.ts +21 -9
  90. package/src/tools/browser.ts +32 -30
  91. package/src/tools/calculator.ts +4 -4
  92. package/src/tools/cancel-job.ts +1 -1
  93. package/src/tools/checkpoint.ts +2 -2
  94. package/src/tools/debug.ts +41 -37
  95. package/src/tools/exit-plan-mode.ts +1 -1
  96. package/src/tools/find.ts +4 -4
  97. package/src/tools/gh-renderer.ts +12 -4
  98. package/src/tools/gh.ts +514 -712
  99. package/src/tools/grep.ts +115 -130
  100. package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
  101. package/src/tools/index.ts +14 -32
  102. package/src/tools/inspect-image.ts +3 -3
  103. package/src/tools/json-tree.ts +114 -114
  104. package/src/tools/match-line-format.ts +9 -8
  105. package/src/tools/notebook.ts +8 -7
  106. package/src/tools/poll-tool.ts +2 -1
  107. package/src/tools/python.ts +9 -23
  108. package/src/tools/read.ts +32 -21
  109. package/src/tools/render-mermaid.ts +1 -1
  110. package/src/tools/render-utils.ts +18 -0
  111. package/src/tools/renderers.ts +2 -2
  112. package/src/tools/report-tool-issue.ts +3 -2
  113. package/src/tools/resolve.ts +1 -1
  114. package/src/tools/review.ts +12 -10
  115. package/src/tools/search-tool-bm25.ts +2 -4
  116. package/src/tools/sqlite-reader.ts +116 -3
  117. package/src/tools/ssh.ts +4 -4
  118. package/src/tools/todo-write.ts +172 -147
  119. package/src/tools/vim.ts +14 -15
  120. package/src/tools/write.ts +4 -4
  121. package/src/tools/{submit-result.ts → yield.ts} +11 -13
  122. package/src/utils/edit-mode.ts +2 -1
  123. package/src/utils/file-display-mode.ts +10 -5
  124. package/src/utils/git.ts +9 -5
  125. package/src/utils/shell-snapshot.ts +2 -3
  126. package/src/vim/render.ts +4 -4
  127. package/src/web/search/providers/codex.ts +129 -6
  128. package/src/prompts/system/subagent-submit-reminder.md +0 -11
  129. package/src/prompts/tools/gh-issue-view.md +0 -11
  130. package/src/prompts/tools/gh-pr-checkout.md +0 -12
  131. package/src/prompts/tools/gh-pr-diff.md +0 -12
  132. package/src/prompts/tools/gh-pr-push.md +0 -11
  133. package/src/prompts/tools/gh-pr-view.md +0 -11
  134. package/src/prompts/tools/gh-repo-view.md +0 -11
  135. package/src/prompts/tools/gh-run-watch.md +0 -12
  136. package/src/prompts/tools/gh-search-issues.md +0 -11
  137. package/src/prompts/tools/gh-search-prs.md +0 -11
package/CHANGELOG.md CHANGED
@@ -1,6 +1,148 @@
1
1
  # Changelog
2
2
 
3
3
  ## [Unreleased]
4
+ ### Breaking Changes
5
+
6
+ - Replaced the legacy `gh_repo_view`, `gh_issue_view`, `gh_pr_view`, `gh_pr_diff`, `gh_pr_checkout`, `gh_pr_push`, `gh_run_watch`, `gh_search_issues`, and `gh_search_prs` tool names with only `github`, which requires updating existing callers that invoked the old `gh_*` tools
7
+
8
+ ### Added
9
+
10
+ - Added the unified `github` tool with op-based dispatch for repository, issue, pull request, search, checkout, push, and Actions watch workflows
11
+ - Added `op` routing so callers can select `repo_view`, `issue_view`, `pr_view`, `pr_diff`, `pr_checkout`, `pr_push`, `search_issues`, `search_prs`, or `run_watch` through a single tool entry point
12
+
13
+ ### Changed
14
+
15
+ - Updated GitHub CLI render output to show `GitHub <op>` for tool calls dispatched through `github` operations
16
+
17
+ ## [14.4.0] - 2026-04-26
18
+
19
+ ### Breaking Changes
20
+
21
+ - Removed multi-pattern array input from `ast_grep` by changing `pat` to a single pattern string, so call sites using `pat: [...]` must be updated to send one query per invocation
22
+ - Removed `lang`, `glob`, and `sel` options from `ast_edit` and `ast_grep`, and moved those behaviors into the required `path` argument
23
+ - Required `path` for `ast_edit` and `ast_grep`, so invocations that relied on implicit repo-root searching are no longer valid
24
+ - Changed `todo_write` from multi-field verb payloads to an ordered array of flat operations, while retaining `replace` for harness bootstrap compatibility
25
+ - Renamed atom edit operations from `before` and `after` to `pre` and `post`, so existing `atom` payloads using the old operation keys must be updated
26
+ - Changed the hashline anchor format from `LINE#ID:content` to `LINEID:content` (no `#` separator, colon between anchor and content, no padding on line numbers); expanded the bigram alphabet from 40 hand-picked English bigrams to the full 647 single-token 2-letter bigrams — invalidates every previously captured `LINE#ID` reference
27
+ - Renamed the subagent completion contract from `submit_result` to `yield`, so subagent sessions must now finish with the `yield` tool and the `requireYieldTool` option; `submit_result`/`requireSubmitResultTool` and old completion calls are no longer recognized
28
+ - Changed the hashline and chunk anchor ID format from the prior hex-like tokens to two-letter BPE bigrams (for example `#th`), which invalidates previously captured `LINE#ID`/chunk selectors and requires re-reading to refresh anchors
29
+
30
+ ### Added
31
+
32
+ - Added inline file overrides in atom locators (`loc: "a.ts:160sr"`) so cross-file edits can be written without a separate per-entry `path` field
33
+ - Added `openai` to the `providers.image` options, allowing image generation to be explicitly routed through the active GPT Responses/Codex model
34
+ - Added `between` atom edit operation to replace only the lines between two surviving anchors while preserving the boundary anchors
35
+ - Added conflict detection for `between` atom edits to require non-overlapping regions and forbid edits targeting lines strictly inside those regions
36
+ - Added `atom` edit mode to `edit` with single-anchor operations (`set`, `before`, `after`, `del`, `sub`, `ins`) for hashline-anchored line edits
37
+ - Added support for request-level `path` defaults in patch, replace, and chunk edits so shared file paths no longer need to be repeated in every entry
38
+
39
+ ### Changed
40
+
41
+ - Updated `atom` and `hashline` edit anchor validation to auto-rebase a stale anchor within ±2 lines when the same hash matches a unique nearby line, continuing the edit with a warning instead of immediate failure
42
+ - Changed bash command output labels from `[full result: artifact://…]` to `[raw output: artifact://…]` for artifact references produced from large command output
43
+ - Changed `todo_write` `done`, `rm`, and `drop` operations to target all tasks when neither `task` nor `phase` is provided, and made `append` create the target phase automatically when missing
44
+ - Updated `ast_edit` and `ast_grep` to pass file-selection intent through `path` (including inline globs and comma/space-separated path lists) instead of separate `glob` filters
45
+ - Changed `ast_grep` pagination API from `offset` to `skip`
46
+ - Flattened `todo_write` operation arguments to `{ op, task?, phase?, items? }[]` and removed task details from the persisted todo shape
47
+ - Changed `grep` truncation output to report `Result limit reached; narrow path.` and label match/result caps as `first N`
48
+ - Changed JSON tree output to truncate inline argument pairs by available width and add an ellipsis when values no longer fit in the display
49
+ - Changed JSON tree rendering to hide harness-internal `intent` and `__partialJson` fields from top-level tool output
50
+ - Simplified the `grep` tool schema by requiring `path`, folding glob and type filtering into path globs, auto-detecting multiline patterns, removing model-controlled context and limit options, and renaming result skipping to `skip`.
51
+ - Changed atom edit request format to use a shared `loc` selector, including range (`"160sr-9ab"`) and boundary (`"^"`, `"$"`) forms instead of per-operation anchor fields
52
+ - Changed atom edit payload fields so `set`, `pre`, and `post` now require line-array values and `sub` now takes a `[find, replace]` tuple, with boundary deletion now expressed as `set: []`
53
+ - Changed edit diff wrapping to preserve the active line-prefix separator (`|` or `│`) while keeping continuation lines aligned by line-number width
54
+ - Changed Vim focus and viewport rendering to align cursor/selection markers and line numbers in a single gutter format
55
+ - Changed auto image provider selection for `providers.image=auto` to try active GPT image generation before Antigravity, OpenRouter, and Gemini
56
+ - Updated atom and hashline anchor validation to require the full `line+suffix` anchor format and report missing-line-number errors more clearly, including guidance when only a 2-letter suffix is provided
57
+ - Changed read, grep, and ast-edit line-prefixed output to drop fixed-width line number padding, so anchors render in natural width without leading spaces
58
+ - Updated terminal diff rendering to use a continuous `│` gutter and hide repeated line numbers on adjacent diff lines
59
+ - Updated subagent reminders, prompts, and rendered subagent output to reference `yield` completion and report missing/final results from `yield` tool data
60
+ - Updated the `edit` workflow to treat `atom` mode like hashline mode for read output, so hashline anchors are shown when `atom` is selected
61
+ - Adjusted patch/replace/chunk tooling to accept optional entry paths and to apply a top-level path default
62
+ - Updated hashline/chunk selector parsing to the new stable bigram token set used for checksums
63
+ - Renamed the image generation implementation module to `image-gen` and routed active GPT Responses/Codex models through OpenAI's hosted `image_generation` tool with WebP output
64
+
65
+ ### Removed
66
+
67
+ - Removed line-range support from `atom` mode selectors, including `loc` values like `160sr-170ab`, so edits must target a single anchor (`160sr`, `^`, or `$`) per entry
68
+ - Removed the atom `del` verb and now require anchored-line deletion to be requested with `set: []`
69
+ - Removed `todo_write` task details and the `add_notes` operation
70
+
71
+ ### Fixed
72
+
73
+ - Improved no-op edit diagnostics for `atom` and `hashline` operations so edits that leave content unchanged now fail with contextual details (edit index, locator, and reason), including guidance for `replace_range` no-op cases
74
+ - Wrapped `todo_write` operations in an `ops` object so Codex/OpenAI function schemas always use a JSON Schema object.
75
+ - Fixed JSON tree rendering for tool arguments by excluding injected internal keys from displayed root records
76
+ - Printed assistant `errorMessage` text in print mode output to stderr so message-level errors are visible during non-interactive runs
77
+ - Displayed assistant `errorMessage` text in the assistant message component for completed tool responses with non-terminal stop reasons
78
+ - Fixed atom input handling to ignore null optional verb fields so entries with `pre`, `set`, `post`, or `sub` set to `null` remain valid
79
+ - Fixed status-line Git branch rendering to degrade gracefully when the process hits `ENFILE`/`EMFILE` while reading optional Git refs
80
+ - Changed hashline mismatch failure output to show a clean numbered context block with numbered gutter and full-anchor alignment guidance when edits are rejected after the file changed
81
+ - Fixed `atom` mode to apply multiple edits on the same anchor line without index-shift artifacts, so mixed operations like `before`, `after`, `set`, `sub`, `ins`, and `del` now resolve consistently
82
+ - Fixed `atom` mode `append_file` insertion to preserve a file’s trailing newline sentinel when appending content
83
+ - Fixed `read` output for raw archive entries so hashline anchors, line numbers, and chunked formatting are not injected into raw content
84
+ - Fixed hashline parsing so lines like `# Note:` or `# TODO:` are no longer misinterpreted and stripped as hashline prefixes
85
+ - Adjusted patch and replace validation to report a clear missing-path error when neither an entry path nor a top-level path is provided
86
+
87
+ ## [14.3.0] - 2026-04-25
88
+
89
+ ### Added
90
+
91
+ - Added Markdown pipe-table `row_N` chunk selectors for row-level table edits.
92
+ - Added `resolveToolAlias` export so tool names in CLI and session setup are normalized to canonical names, including mapping legacy `read` references to `open`
93
+ - Added new `open` and `open-chunk` tool prompt documentation pages to describe canonical `open` usage for local files/directories, chunk reads, and URLs
94
+ - Added full-output retrieval metadata to minimized shell command output by appending an `artifact://<id>` footer with byte counts, allowing users to open the original unminimized command output
95
+ - Added streaming preview API exports from the package (`resolveEditMode`, `EDIT_MODE_STRATEGIES`, and chunk preview helpers) so editors can reuse mode-aware edit preview logic programmatically
96
+ - Added `shellMinimizer` configuration options (`enabled`, `settingsPath`, `only`, `except`, and `maxCaptureBytes`) so users can control shell output minimization behavior
97
+
98
+ ### Changed
99
+
100
+ - Changed the canonical file/URL reader tool from `read` to `open` across default tool lists and routing, including system prompts, plan mode, cursor handlers, and runtime tool registration
101
+ - Changed runtime and UI handling to render and track `open` tool calls as first-class (with `read` accepted as legacy alias), including ACP mapping, session observers, and streaming message groups
102
+ - Changed chunk edit guidance to document parser-specific region behavior, including TypeScript decorator/JSDoc sibling chunks, Python docstrings as body content, Python opaque nested chunks, Markdown whole-chunk fallbacks, ID volatility, and indentation display differences
103
+ - Changed chunk deletion in chunk edit mode to require explicit `delete: true`; `write: null` and bare `{ path }` entries now fail with guidance instead of deleting content.
104
+ - Changed chunk edit validation to reject entries with multiple operation fields instead of choosing one and ignoring the rest.
105
+ - Changed chunk edit validation to reject `write: ""` as an accidental destructive empty replacement; use the open tool for inspection or `delete: true` for deletion.
106
+ - Changed chunk edit responses to warn when appending or prepending to a container without `~`, since that inserts outside the container rather than inside its body.
107
+ - Changed fetch output logging so URL-fetch artifacts now use `.open.log` naming instead of `.read.log`
108
+ - Changed Bash interception guidance and errors to recommend `open` in place of `read` for cat/head/tail-style commands
109
+ - Changed exported SDK tool surface to expose `OpenTool` as canonical and keep `ReadTool` as a compatibility alias
110
+ - Changed session list loading to use parallel workers and fixed-size prefix reads per session file, reducing latency when loading many or large sessions
111
+ - Changed edit call rendering to use mode-aware streaming diff previews, including multi-file chunk edit previews grouped by file path while arguments are still streaming
112
+ - Changed shell execution in both interactive and non-interactive modes to route command output through the configured shell output minimizer
113
+ - Changed default behavior so shell output minimization can now be toggled from settings without code changes
114
+ - Changed shell output minimization to leave compound and piped commands unchanged; only a single eligible whole command is captured and minimized after it exits
115
+
116
+ ### Removed
117
+
118
+ - Removed the chunk edit `read: true` operation; use the open tool to inspect chunks without modifying files.
119
+ - Removed the `replace: { old, new }` chunk edit operation. Use `write` or `insert` for chunk edits instead.
120
+
121
+ ### Fixed
122
+
123
+ - Fixed startup crashes on Linux systems where Bun's `os.cpus()` fails on non-contiguous CPU numbering ([#779](https://github.com/can1357/oh-my-pi/issues/779))
124
+ - Fixed `gh_pr_push` so branches without `gh_pr_checkout` metadata fail instead of falling back to the tracked merge branch, and updated the GitHub tool setting copy to stop calling the tool group read-only ([#778](https://github.com/can1357/oh-my-pi/issues/778))
125
+ - Fixed session list metadata extraction to better populate session titles and first-user summaries from partial session data when full JSONL parsing is unavailable
126
+ - Fixed shell execution output to replace raw streamed bash output with the minimizer’s rewritten text before final output while still preserving the full original output as artifact metadata
127
+ - Fixed bash command minimization to save the full unminimized output as a `bash-original` artifact during AgentSession shell execution, enabling `artifact://` access to complete command output
128
+ - Fixed streaming chunk previews that could display an incomplete trailing edit as a deletion when partial JSON temporarily converted in-flight values to `null`
129
+ - Fixed edit streaming preview updates to cancel obsolete in-flight computations and avoid rendering stale previews as args change
130
+ - Fixed chunk edits to reject unsafe `^`/`~` writes on code leaf chunks instead of falling back to whole-chunk replacement and risking structural indentation corruption
131
+ - Fixed chunk `replace` operations to dedent multiline replacement snippets before reapplying the matched source indentation, preventing Python nested replacements from compounding indentation on repeated edits.
132
+ - Fixed Go chunk trees to classify `package` clauses separately from imports and to avoid duplicating method receivers in method summaries.
133
+ - Fixed chunk path-not-found guidance so it recommends `sel="?"` without claiming the already-shown listing must be re-read.
134
+ - Fixed Markdown chunk appends to preserve blank-line separators after line-oriented inserts such as table rows
135
+ - Fixed Markdown section region-fallback warnings to call out child chunks that will be replaced by whole-section edits.
136
+ - Fixed rejected chunk-edit errors to distinguish current file content from hypothetical post-edit parse-error previews and to state when a same-file batch was rolled back.
137
+ - Fixed unsafe Python head-region edits by rejecting decorated Python `^` writes and Python `^` deletes that can orphan indented bodies while still parsing.
138
+ - Fixed Markdown table-row appends so row-shaped content lands inside the table block instead of after the trailing blank-line separator.
139
+ - Fixed Markdown root writes to preserve fenced-code indentation verbatim.
140
+ - Fixed Rust enum-variant replacement matching so trailing commas are included consistently with whole-variant writes.
141
+ - Fixed streaming edit call headers to keep showing the target file path while the edit arguments are still arriving
142
+ - Fixed Mermaid fenced markdown rendering in assistant messages on terminals without image protocol support ([#650](https://github.com/can1357/oh-my-pi/issues/650))
143
+ - Fixed chunk edit path parsing so plan-mode edits to section-addressed `local://PLAN.md:<selector>` paths are classified as writes to the plan file
144
+ - Fixed SQLite `read` helper queries to reject `where=` clauses with SQL control syntax that could override the structured selector's pagination; raw SQL remains available through `q=SELECT ...`
145
+ - Fixed `models` provider transport overrides so `headers`-only entries apply without requiring `baseUrl`, including runtime `registerProvider()` overrides that now persist across `refresh()` / `refreshProvider()`, preserve existing `baseUrl` on subsequent headers-only updates, clear stale transport overrides when a provider is re-registered under a different extension source, and keep runtime transport headers authoritative when `modelOverrides` set overlapping header keys
4
146
 
5
147
  ## [14.2.0] - 2026-04-23
6
148
 
@@ -7127,4 +7269,4 @@ Initial public release.
7127
7269
  - Git branch display in footer
7128
7270
  - Message queueing during streaming responses
7129
7271
  - OAuth integration for Gmail and Google Calendar access
7130
- - HTML export with syntax highlighting and collapsible sections
7272
+ - HTML export with syntax highlighting and collapsible sections
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "14.2.1",
4
+ "version": "14.4.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -44,31 +44,31 @@
44
44
  "generate-template": "bun scripts/generate-template.ts"
45
45
  },
46
46
  "dependencies": {
47
- "@agentclientprotocol/sdk": "0.16.1",
48
- "@mozilla/readability": "^0.6",
49
- "@oh-my-pi/omp-stats": "14.2.1",
50
- "@oh-my-pi/pi-agent-core": "14.2.1",
51
- "@oh-my-pi/pi-ai": "14.2.1",
52
- "@oh-my-pi/pi-natives": "14.2.1",
53
- "@oh-my-pi/pi-tui": "14.2.1",
54
- "@oh-my-pi/pi-utils": "14.2.1",
55
- "@sinclair/typebox": "^0.34",
56
- "@xterm/headless": "^6.0",
57
- "ajv": "^8.18",
58
- "chalk": "^5.6",
59
- "diff": "^8.0",
47
+ "@agentclientprotocol/sdk": "0.20.0",
48
+ "@mozilla/readability": "^0.6.0",
49
+ "@oh-my-pi/omp-stats": "14.4.0",
50
+ "@oh-my-pi/pi-agent-core": "14.4.0",
51
+ "@oh-my-pi/pi-ai": "14.4.0",
52
+ "@oh-my-pi/pi-natives": "14.4.0",
53
+ "@oh-my-pi/pi-tui": "14.4.0",
54
+ "@oh-my-pi/pi-utils": "14.4.0",
55
+ "@sinclair/typebox": "^0.34.49",
56
+ "@xterm/headless": "^6.0.0",
57
+ "ajv": "^8.20.0",
58
+ "chalk": "^5.6.2",
59
+ "diff": "^9.0.0",
60
60
  "fflate": "0.8.2",
61
61
  "handlebars": "^4.7.9",
62
- "linkedom": "^0.18",
63
- "lru-cache": "11.3.1",
64
- "markit-ai": "0.5.0",
65
- "puppeteer": "^24.37",
62
+ "linkedom": "^0.18.12",
63
+ "lru-cache": "11.3.5",
64
+ "markit-ai": "0.5.3",
65
+ "puppeteer": "^24.42.0",
66
66
  "turndown": "7.2.4",
67
67
  "turndown-plugin-gfm": "1.0.2",
68
68
  "zod": "4.3.6"
69
69
  },
70
70
  "devDependencies": {
71
- "@types/bun": "^1.3",
71
+ "@types/bun": "^1.3.13",
72
72
  "@types/turndown": "5.0.6"
73
73
  },
74
74
  "engines": {
@@ -1,4 +1,4 @@
1
- {{{base_system_prompt}}}
1
+ {{base_system_prompt}}
2
2
 
3
3
  ## Autoresearch Mode
4
4
 
package/src/cli/args.ts CHANGED
@@ -60,7 +60,16 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
60
60
  };
61
61
 
62
62
  for (let i = 0; i < args.length; i++) {
63
- const arg = args[i];
63
+ let arg = args[i];
64
+
65
+ // Support --flag=value syntax (e.g. --tools=ask,read)
66
+ if (arg.startsWith("--") && arg.includes("=")) {
67
+ const eqIdx = arg.indexOf("=");
68
+ const value = arg.slice(eqIdx + 1);
69
+ arg = arg.slice(0, eqIdx);
70
+ // Insert the value so the existing "args[++i]" logic picks it up
71
+ args.splice(i + 1, 0, value);
72
+ }
64
73
 
65
74
  if (arg === "--help" || arg === "-h") {
66
75
  result.help = true;
@@ -5,10 +5,10 @@
5
5
  */
6
6
  import * as path from "node:path";
7
7
  import { createInterface } from "node:readline/promises";
8
- import { Shell } from "@oh-my-pi/pi-natives";
8
+ import { type MinimizerOptions, Shell } from "@oh-my-pi/pi-natives";
9
9
  import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils";
10
10
  import chalk from "chalk";
11
- import { Settings } from "../config/settings";
11
+ import { Settings, type ShellMinimizerSettings } from "../config/settings";
12
12
  import { getOrCreateSnapshot } from "../utils/shell-snapshot";
13
13
 
14
14
  export interface ShellCommandArgs {
@@ -41,6 +41,17 @@ export function parseShellArgs(args: string[]): ShellCommandArgs | undefined {
41
41
  return result;
42
42
  }
43
43
 
44
+ function buildMinimizerOptions(group: ShellMinimizerSettings): MinimizerOptions | undefined {
45
+ if (!group.enabled) return undefined;
46
+ return {
47
+ enabled: true,
48
+ settingsPath: group.settingsPath || undefined,
49
+ only: group.only.length > 0 ? group.only : undefined,
50
+ except: group.except.length > 0 ? group.except : undefined,
51
+ maxCaptureBytes: group.maxCaptureBytes,
52
+ };
53
+ }
54
+
44
55
  export async function runShellCommand(cmd: ShellCommandArgs): Promise<void> {
45
56
  if (!process.stdin.isTTY) {
46
57
  process.stderr.write("Error: shell console requires an interactive TTY.\n");
@@ -51,7 +62,8 @@ export async function runShellCommand(cmd: ShellCommandArgs): Promise<void> {
51
62
  const settings = await Settings.init({ cwd });
52
63
  const { shell, env: shellEnv } = settings.getShellConfig();
53
64
  const snapshotPath = cmd.noSnapshot || !shell.includes("bash") ? null : await getOrCreateSnapshot(shell, shellEnv);
54
- const shellSession = new Shell({ sessionEnv: shellEnv, snapshotPath: snapshotPath ?? undefined });
65
+ const minimizer = buildMinimizerOptions(settings.getGroup("shellMinimizer"));
66
+ const shellSession = new Shell({ sessionEnv: shellEnv, snapshotPath: snapshotPath ?? undefined, minimizer });
55
67
 
56
68
  let active = false;
57
69
  let lastChar: string | null = null;
@@ -19,4 +19,4 @@ Return concise JSON object with:
19
19
  Consider how file's changes relate to above files.
20
20
  {{/if}}
21
21
 
22
- Call submit_result tool with JSON payload.
22
+ Call yield tool with JSON payload.
@@ -300,6 +300,7 @@ interface ProviderValidationModel {
300
300
 
301
301
  interface ProviderValidationConfig {
302
302
  baseUrl?: string;
303
+ headers?: Record<string, string>;
303
304
  apiKey?: string;
304
305
  api?: Api;
305
306
  auth?: ProviderAuthMode;
@@ -321,9 +322,9 @@ function validateProviderConfiguration(
321
322
  if (models.length === 0) {
322
323
  if (mode === "models-config") {
323
324
  const hasModelOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;
324
- if (!config.baseUrl && !config.compat && !hasModelOverrides && !config.discovery) {
325
+ if (!config.baseUrl && !config.headers && !config.compat && !hasModelOverrides && !config.discovery) {
325
326
  throw new Error(
326
- `Provider ${providerName}: must specify "baseUrl", "compat", "modelOverrides", "discovery", or "models"`,
327
+ `Provider ${providerName}: must specify "baseUrl", "headers", "compat", "modelOverrides", "discovery", or "models"`,
327
328
  );
328
329
  }
329
330
  }
@@ -378,6 +379,7 @@ export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsCon
378
379
  providerName,
379
380
  {
380
381
  baseUrl: providerConfig.baseUrl,
382
+ headers: providerConfig.headers,
381
383
  apiKey: providerConfig.apiKey,
382
384
  api: providerConfig.api as Api | undefined,
383
385
  auth: (providerConfig.auth ?? "apiKey") as ProviderAuthMode,
@@ -770,6 +772,7 @@ export class ModelRegistry {
770
772
  // models registered by extensions survive the model selector's offline reload.
771
773
  #runtimeModelOverlays: CustomModelOverlay[] = [];
772
774
  #runtimeProviderApiKeys: Map<string, string> = new Map();
775
+ #runtimeProviderOverrides: Map<string, ProviderOverride> = new Map();
773
776
  #runtimeProvidersBySource: Map<string, Set<string>> = new Map();
774
777
  #runtimeProviderSourceByName: Map<string, string> = new Map();
775
778
 
@@ -883,8 +886,8 @@ export class ModelRegistry {
883
886
  const withConfigModels = this.#mergeCustomModels(resolvedDefaults, this.#customModelOverlays);
884
887
  // Merge runtime extension models so they survive refresh() cycles
885
888
  const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
886
-
887
- this.#models = this.#applyModelOverrides(combined, this.#modelOverrides);
889
+ const withModelOverrides = this.#applyModelOverrides(combined, this.#modelOverrides);
890
+ this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
888
891
  this.#rebuildCanonicalIndex();
889
892
  }
890
893
 
@@ -1175,7 +1178,8 @@ export class ModelRegistry {
1175
1178
  const withConfigModels = this.#mergeCustomModels(resolved, this.#customModelOverlays);
1176
1179
  // Merge runtime extension models so they survive online discovery completion
1177
1180
  const combined = this.#mergeCustomModels(withConfigModels, this.#runtimeModelOverlays);
1178
- this.#models = this.#applyModelOverrides(combined, this.#modelOverrides);
1181
+ const withModelOverrides = this.#applyModelOverrides(combined, this.#modelOverrides);
1182
+ this.#models = this.#applyRuntimeProviderOverrides(withModelOverrides);
1179
1183
  this.#rebuildCanonicalIndex();
1180
1184
  }
1181
1185
 
@@ -1657,6 +1661,32 @@ export class ModelRegistry {
1657
1661
  });
1658
1662
  }
1659
1663
 
1664
+ #mergeProviderOverride(baseOverride: ProviderOverride | undefined, override: ProviderOverride): ProviderOverride {
1665
+ return {
1666
+ baseUrl: override.baseUrl ?? baseOverride?.baseUrl,
1667
+ apiKey: override.apiKey ?? baseOverride?.apiKey,
1668
+ headers: override.headers ? { ...(baseOverride?.headers ?? {}), ...override.headers } : baseOverride?.headers,
1669
+ compat: override.compat ? mergeCompat(baseOverride?.compat, override.compat) : baseOverride?.compat,
1670
+ };
1671
+ }
1672
+ #applyProviderTransportOverride<T extends { baseUrl?: string; headers?: Record<string, string> }>(
1673
+ entry: T,
1674
+ override: Pick<ProviderOverride, "baseUrl" | "headers">,
1675
+ ): T {
1676
+ return {
1677
+ ...entry,
1678
+ baseUrl: override.baseUrl ?? entry.baseUrl,
1679
+ headers: override.headers ? { ...entry.headers, ...override.headers } : entry.headers,
1680
+ };
1681
+ }
1682
+ #applyRuntimeProviderOverrides(models: Model<Api>[]): Model<Api>[] {
1683
+ if (this.#runtimeProviderOverrides.size === 0) return models;
1684
+ return models.map(model => {
1685
+ const override = this.#runtimeProviderOverrides.get(model.provider);
1686
+ if (!override) return model;
1687
+ return this.#applyProviderTransportOverride(model, override);
1688
+ });
1689
+ }
1660
1690
  #applyModelOverrides(models: Model<Api>[], overrides: Map<string, Map<string, ModelOverride>>): Model<Api>[] {
1661
1691
  if (overrides.size === 0) return models;
1662
1692
  return models.map(model => {
@@ -1916,6 +1946,12 @@ export class ModelRegistry {
1916
1946
  return this.authStorage.hasOAuth(model.provider);
1917
1947
  }
1918
1948
 
1949
+ #clearRuntimeProviderState(providerName: string): void {
1950
+ this.#runtimeProviderApiKeys.delete(providerName);
1951
+ this.#runtimeProviderOverrides.delete(providerName);
1952
+ this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(overlay => overlay.provider !== providerName);
1953
+ }
1954
+
1919
1955
  /**
1920
1956
  * Remove custom API/OAuth registrations for a specific extension source.
1921
1957
  */
@@ -1932,8 +1968,7 @@ export class ModelRegistry {
1932
1968
  continue;
1933
1969
  }
1934
1970
  this.#runtimeProviderSourceByName.delete(providerName);
1935
- this.#runtimeProviderApiKeys.delete(providerName);
1936
- this.#runtimeModelOverlays = this.#runtimeModelOverlays.filter(overlay => overlay.provider !== providerName);
1971
+ this.#clearRuntimeProviderState(providerName);
1937
1972
  }
1938
1973
  this.#reloadStaticModels();
1939
1974
  this.#rebuildCanonicalIndex();
@@ -1970,6 +2005,7 @@ export class ModelRegistry {
1970
2005
  providerName,
1971
2006
  {
1972
2007
  baseUrl: config.baseUrl,
2008
+ headers: config.headers,
1973
2009
  apiKey: config.apiKey,
1974
2010
  api: config.api,
1975
2011
  oauthConfigured: Boolean(config.oauth),
@@ -1993,6 +2029,7 @@ export class ModelRegistry {
1993
2029
  });
1994
2030
  }
1995
2031
 
2032
+ let sourceHandoff = false;
1996
2033
  if (sourceId) {
1997
2034
  this.#registeredProviderSources.add(sourceId);
1998
2035
  const previousSourceId = this.#runtimeProviderSourceByName.get(providerName);
@@ -2002,12 +2039,18 @@ export class ModelRegistry {
2002
2039
  if (previousProviders && previousProviders.size === 0) {
2003
2040
  this.#runtimeProvidersBySource.delete(previousSourceId);
2004
2041
  }
2042
+ this.#clearRuntimeProviderState(providerName);
2043
+ sourceHandoff = true;
2005
2044
  }
2006
2045
  const sourceProviders = this.#runtimeProvidersBySource.get(sourceId) ?? new Set<string>();
2007
2046
  sourceProviders.add(providerName);
2008
2047
  this.#runtimeProvidersBySource.set(sourceId, sourceProviders);
2009
2048
  this.#runtimeProviderSourceByName.set(providerName, sourceId);
2010
2049
  }
2050
+ if (sourceHandoff) {
2051
+ this.#reloadStaticModels();
2052
+ }
2053
+
2011
2054
  if (config.apiKey) {
2012
2055
  this.#customProviderApiKeys.set(providerName, config.apiKey);
2013
2056
  // Persist runtime API keys so they survive #reloadStaticModels() cycles
@@ -2042,29 +2085,38 @@ export class ModelRegistry {
2042
2085
  for (const overlay of newOverlays) {
2043
2086
  nextModels.push(finalizeCustomModel(overlay, { useDefaults: true }));
2044
2087
  }
2088
+ const runtimeTransportOverride = this.#runtimeProviderOverrides.get(providerName);
2089
+ const withRuntimeTransportOverride = runtimeTransportOverride
2090
+ ? nextModels.map(model => {
2091
+ if (model.provider !== providerName) return model;
2092
+ return this.#applyProviderTransportOverride(model, runtimeTransportOverride);
2093
+ })
2094
+ : nextModels;
2045
2095
 
2046
2096
  if (config.oauth?.modifyModels) {
2047
2097
  const credential = this.authStorage.getOAuthCredential(providerName);
2048
2098
  if (credential) {
2049
- this.#models = config.oauth.modifyModels(nextModels, credential);
2099
+ this.#models = config.oauth.modifyModels(withRuntimeTransportOverride, credential);
2050
2100
  this.#rebuildCanonicalIndex();
2051
2101
  return;
2052
2102
  }
2053
2103
  }
2054
2104
 
2055
- this.#models = nextModels;
2105
+ this.#models = withRuntimeTransportOverride;
2056
2106
  this.#rebuildCanonicalIndex();
2057
2107
  return;
2058
2108
  }
2059
2109
 
2060
- if (config.baseUrl) {
2110
+ if (config.baseUrl || config.headers) {
2111
+ const transportOverride = { baseUrl: config.baseUrl, headers: config.headers };
2112
+ const nextRuntimeOverride = this.#mergeProviderOverride(
2113
+ this.#runtimeProviderOverrides.get(providerName),
2114
+ transportOverride,
2115
+ );
2116
+ this.#runtimeProviderOverrides.set(providerName, nextRuntimeOverride);
2061
2117
  this.#models = this.#models.map(m => {
2062
2118
  if (m.provider !== providerName) return m;
2063
- return {
2064
- ...m,
2065
- baseUrl: config.baseUrl ?? m.baseUrl,
2066
- headers: config.headers ? { ...m.headers, ...config.headers } : m.headers,
2067
- };
2119
+ return this.#applyProviderTransportOverride(m, transportOverride);
2068
2120
  });
2069
2121
  this.#rebuildCanonicalIndex();
2070
2122
  }
@@ -9,7 +9,7 @@ import {
9
9
  parseFrontmatter,
10
10
  prompt,
11
11
  } from "@oh-my-pi/pi-utils";
12
- import { computeLineHash } from "../edit/line-hash";
12
+ import { computeLineHash, HASHLINE_CONTENT_SEPARATOR } from "../edit/line-hash";
13
13
  import { jtdToTypeScript } from "../tools/jtd-to-typescript";
14
14
  import { parseCommandArgs, substituteArgs } from "../utils/command-args";
15
15
 
@@ -40,13 +40,13 @@ function formatHashlineRef(lineNum: unknown, content: unknown): { num: number; t
40
40
  const num = typeof lineNum === "number" ? lineNum : Number.parseInt(String(lineNum), 10);
41
41
  const raw = typeof content === "string" ? content : String(content ?? "");
42
42
  const text = raw.replace(/\\t/g, "\t").replace(/\\n/g, "\n").replace(/\\r/g, "\r");
43
- const ref = `${num}#${computeLineHash(num, text)}`;
43
+ const ref = `${num}${computeLineHash(num, text)}`;
44
44
  return { num, text, ref };
45
45
  }
46
46
 
47
47
  /**
48
48
  * {{href lineNum "content"}} — compute a real hashline ref for prompt examples.
49
- * Returns `"lineNum#hash"` using the actual hash algorithm.
49
+ * Returns `"lineNumBIGRAM"` (e.g., `"42nd"`) using the actual hash algorithm.
50
50
  */
51
51
  prompt.registerHelper("href", (lineNum: unknown, content: unknown): string => {
52
52
  const { ref } = formatHashlineRef(lineNum, content);
@@ -55,11 +55,11 @@ prompt.registerHelper("href", (lineNum: unknown, content: unknown): string => {
55
55
 
56
56
  /**
57
57
  * {{hline lineNum "content"}} — format a full read-style line with prefix.
58
- * Returns `"lineNum#hash:content"`.
58
+ * Returns `"lineNumBIGRAM:content"` (colon between anchor and content).
59
59
  */
60
60
  prompt.registerHelper("hline", (lineNum: unknown, content: unknown): string => {
61
61
  const { ref, text } = formatHashlineRef(lineNum, content);
62
- return `${ref}:${text}`;
62
+ return `${ref}${HASHLINE_CONTENT_SEPARATOR}${text}`;
63
63
  });
64
64
 
65
65
  /**
@@ -955,7 +955,7 @@ export const SETTINGS_SCHEMA = {
955
955
  // Edit tool
956
956
  "edit.mode": {
957
957
  type: "enum",
958
- values: ["replace", "patch", "hashline", "chunk", "vim", "apply_patch"] as const,
958
+ values: ["replace", "patch", "hashline", "chunk", "vim", "apply_patch", "atom"] as const,
959
959
  default: "hashline",
960
960
  ui: {
961
961
  tab: "editing",
@@ -1021,7 +1021,7 @@ export const SETTINGS_SCHEMA = {
1021
1021
  ui: {
1022
1022
  tab: "editing",
1023
1023
  label: "Hash Lines",
1024
- description: "Include line hashes in read output for hashline edit mode (LINE#ID:content)",
1024
+ description: "Include line hashes in read output for hashline edit mode (LINE+ID\\tcontent)",
1025
1025
  },
1026
1026
  },
1027
1027
 
@@ -1123,6 +1123,39 @@ export const SETTINGS_SCHEMA = {
1123
1123
  },
1124
1124
  "bashInterceptor.patterns": { type: "array", default: DEFAULT_BASH_INTERCEPTOR_RULES },
1125
1125
 
1126
+ // Shell output minimizer
1127
+ "shellMinimizer.enabled": {
1128
+ type: "boolean",
1129
+ default: true,
1130
+ ui: {
1131
+ tab: "editing",
1132
+ label: "Shell Minimizer",
1133
+ description: "Compress verbose shell output (git, npm, cargo, etc.) before returning it to the agent",
1134
+ },
1135
+ },
1136
+ "shellMinimizer.settingsPath": {
1137
+ type: "string",
1138
+ default: undefined,
1139
+ ui: {
1140
+ tab: "editing",
1141
+ label: "Minimizer Settings Path",
1142
+ description: "Optional TOML file with per-command minimizer overrides",
1143
+ submenu: true,
1144
+ },
1145
+ },
1146
+ "shellMinimizer.only": { type: "array", default: EMPTY_STRING_ARRAY },
1147
+ "shellMinimizer.except": { type: "array", default: EMPTY_STRING_ARRAY },
1148
+ "shellMinimizer.maxCaptureBytes": {
1149
+ type: "number",
1150
+ default: 4 * 1024 * 1024,
1151
+ ui: {
1152
+ tab: "editing",
1153
+ label: "Minimizer Capture Limit",
1154
+ description: "Maximum captured output bytes before falling back to raw streaming",
1155
+ submenu: true,
1156
+ },
1157
+ },
1158
+
1126
1159
  // Python
1127
1160
  "python.toolMode": {
1128
1161
  type: "enum",
@@ -1315,7 +1348,8 @@ export const SETTINGS_SCHEMA = {
1315
1348
  ui: {
1316
1349
  tab: "tools",
1317
1350
  label: "GitHub CLI",
1318
- description: "Enable read-only gh_* tools for GitHub repository, issue, pull request, diff, and search access",
1351
+ description:
1352
+ "Enable the github tool (op-based dispatch for repository, issue, pull request, diff, search, checkout, push, and Actions watch workflows)",
1319
1353
  },
1320
1354
  },
1321
1355
 
@@ -1612,6 +1646,22 @@ export const SETTINGS_SCHEMA = {
1612
1646
  ui: { tab: "tasks", label: "Claude Project Commands", description: "Load commands from .claude/commands/" },
1613
1647
  },
1614
1648
 
1649
+ "commands.enableOpencodeUser": {
1650
+ type: "boolean",
1651
+ default: true,
1652
+ ui: {
1653
+ tab: "tasks",
1654
+ label: "OpenCode User Commands",
1655
+ description: "Load commands from ~/.config/opencode/commands/",
1656
+ },
1657
+ },
1658
+
1659
+ "commands.enableOpencodeProject": {
1660
+ type: "boolean",
1661
+ default: true,
1662
+ ui: { tab: "tasks", label: "OpenCode Project Commands", description: "Load commands from .opencode/commands/" },
1663
+ },
1664
+
1615
1665
  // ────────────────────────────────────────────────────────────────────────
1616
1666
  // Providers
1617
1667
  // ────────────────────────────────────────────────────────────────────────
@@ -1652,7 +1702,7 @@ export const SETTINGS_SCHEMA = {
1652
1702
  },
1653
1703
  "providers.image": {
1654
1704
  type: "enum",
1655
- values: ["auto", "gemini", "openrouter"] as const,
1705
+ values: ["auto", "openai", "gemini", "openrouter"] as const,
1656
1706
  default: "auto",
1657
1707
  ui: {
1658
1708
  tab: "providers",
@@ -1956,6 +2006,14 @@ export interface BashInterceptorRule {
1956
2006
  allowSubcommands?: string[];
1957
2007
  }
1958
2008
 
2009
+ export interface ShellMinimizerSettings {
2010
+ enabled: boolean;
2011
+ settingsPath: string | undefined;
2012
+ only: string[];
2013
+ except: string[];
2014
+ maxCaptureBytes: number;
2015
+ }
2016
+
1959
2017
  /** Map group prefix -> typed settings interface */
1960
2018
  export interface GroupTypeMap {
1961
2019
  compaction: CompactionSettings;
@@ -1973,6 +2031,7 @@ export interface GroupTypeMap {
1973
2031
  modelRoles: Record<string, string>;
1974
2032
  modelTags: ModelTagsSettings;
1975
2033
  cycleOrder: string[];
2034
+ shellMinimizer: ShellMinimizerSettings;
1976
2035
  }
1977
2036
 
1978
2037
  export type GroupPrefix = keyof GroupTypeMap;