one-code-extension 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +214 -147
  2. package/docs/guide/README.md +1 -0
  3. package/docs/guide/configuration.md +2 -2
  4. package/docs/guide/differences-from-claude-code.md +13 -4
  5. package/docs/guide/hooks.md +14 -2
  6. package/docs/guide/installation.md +54 -6
  7. package/docs/guide/permissions-modes-and-auto-mode.md +23 -7
  8. package/docs/guide/reference.md +5 -2
  9. package/docs/guide/sessions-and-context.md +18 -0
  10. package/docs/guide/terminal-interface.md +1 -1
  11. package/docs/guide/tools.md +24 -2
  12. package/docs/guide/troubleshooting.md +14 -4
  13. package/docs/guide/windows.md +189 -0
  14. package/extensions/auto-mode/actions.ts +3 -1
  15. package/extensions/auto-mode/paths.ts +21 -23
  16. package/extensions/auto-mode/safety-floor.ts +44 -6
  17. package/extensions/auto-mode/shell-analysis.ts +25 -10
  18. package/extensions/auto-mode/transcript.ts +15 -5
  19. package/extensions/background/index.ts +83 -34
  20. package/extensions/background/monitor-batch.ts +7 -5
  21. package/extensions/background/wakeup.ts +18 -24
  22. package/extensions/bash/background.ts +19 -10
  23. package/extensions/bash/guards.ts +36 -16
  24. package/extensions/bash/index.ts +36 -166
  25. package/extensions/branding/index.ts +35 -4
  26. package/extensions/btw/index.ts +207 -0
  27. package/extensions/btw/panel.ts +179 -0
  28. package/extensions/btw/prompt.ts +45 -0
  29. package/extensions/clear/index.ts +15 -0
  30. package/extensions/compaction/fit.ts +9 -6
  31. package/extensions/doctor/build.ts +22 -2
  32. package/extensions/doctor/dependencies.ts +63 -31
  33. package/extensions/doctor/fix-prompt.ts +12 -4
  34. package/extensions/doctor/index.ts +1 -0
  35. package/extensions/doctor/models.ts +2 -1
  36. package/extensions/doctor/report.ts +2 -3
  37. package/extensions/doctor/update-lookup.ts +19 -11
  38. package/extensions/effort/index.ts +2 -1
  39. package/extensions/footer/footer-line.ts +19 -0
  40. package/extensions/footer/index.ts +25 -5
  41. package/extensions/hooks/executor.ts +84 -18
  42. package/extensions/hooks/index.ts +1 -0
  43. package/extensions/hooks/matcher.ts +23 -9
  44. package/extensions/hooks/settings.ts +15 -0
  45. package/extensions/lib/branch-restore.ts +12 -0
  46. package/extensions/lib/claude-context.ts +2 -3
  47. package/extensions/lib/claude-settings.ts +19 -0
  48. package/extensions/lib/command-launch.ts +71 -0
  49. package/extensions/lib/deferred.ts +173 -33
  50. package/extensions/lib/git.ts +16 -0
  51. package/extensions/lib/keys.ts +38 -0
  52. package/extensions/lib/local-command.ts +112 -0
  53. package/extensions/lib/model-facts.generated.json +263 -68
  54. package/extensions/lib/model-policy.ts +13 -0
  55. package/extensions/lib/model-tier.ts +16 -5
  56. package/extensions/lib/notifications.ts +547 -53
  57. package/extensions/lib/paths.ts +130 -2
  58. package/extensions/lib/pi-ai-estimate.ts +57 -26
  59. package/extensions/lib/pi-version.ts +1 -1
  60. package/extensions/lib/process-tree.ts +131 -5
  61. package/extensions/lib/reminders.ts +80 -27
  62. package/extensions/lib/scratchpad.ts +11 -10
  63. package/extensions/lib/shell-spawn.ts +298 -0
  64. package/extensions/lib/shell-tool.ts +252 -0
  65. package/extensions/lib/shell-tools.ts +13 -0
  66. package/extensions/lib/side-call.ts +76 -0
  67. package/extensions/lib/tui-render.ts +62 -11
  68. package/extensions/lib/update-check.mjs +155 -0
  69. package/extensions/lib/usage-bus.ts +1 -1
  70. package/extensions/lib/which.ts +62 -0
  71. package/extensions/lib/worktree-channel.ts +18 -0
  72. package/extensions/lib/worktree-isolation.ts +2 -1
  73. package/extensions/lsp/client.ts +14 -3
  74. package/extensions/lsp/index.ts +12 -5
  75. package/extensions/lsp/install-hints.ts +33 -3
  76. package/extensions/lsp/plugin-servers.ts +2 -2
  77. package/extensions/lsp/servers.ts +29 -7
  78. package/extensions/mcp/index.ts +3 -2
  79. package/extensions/memory/entries.ts +9 -6
  80. package/extensions/memory/index.ts +3 -2
  81. package/extensions/memory/open-external.ts +4 -1
  82. package/extensions/model-default/index.ts +21 -2
  83. package/extensions/permissions/index.ts +57 -28
  84. package/extensions/permissions/matcher.ts +120 -33
  85. package/extensions/permissions/modes.ts +14 -6
  86. package/extensions/permissions/powershell-rules.ts +549 -0
  87. package/extensions/permissions/session-grant.ts +9 -9
  88. package/extensions/plugins/index.ts +3 -2
  89. package/extensions/plugins/install/install.ts +1 -1
  90. package/extensions/powershell/description.ts +139 -0
  91. package/extensions/powershell/guards.ts +91 -0
  92. package/extensions/powershell/index.ts +89 -0
  93. package/extensions/powershell/policy.ts +91 -0
  94. package/extensions/recap/index.ts +10 -24
  95. package/extensions/recap/prompt.ts +3 -3
  96. package/extensions/session-title/index.ts +120 -0
  97. package/extensions/session-title/title.ts +111 -0
  98. package/extensions/skill/index.ts +40 -18
  99. package/extensions/skill/invoke.ts +1 -0
  100. package/extensions/subagents/hand-back-review.ts +10 -12
  101. package/extensions/subagents/index.ts +221 -78
  102. package/extensions/subagents/model-select.ts +75 -7
  103. package/extensions/system-prompt/environment.ts +14 -1
  104. package/extensions/system-reminder/index.ts +9 -0
  105. package/extensions/tasks/index.ts +2 -1
  106. package/extensions/tool-search/index.ts +41 -20
  107. package/extensions/workflow/agent-session.ts +6 -4
  108. package/extensions/workflow/index.ts +28 -5
  109. package/extensions/worktree/guards.ts +5 -4
  110. package/extensions/worktree/index.ts +15 -2
  111. package/extensions/worktree/rewrite.ts +17 -0
  112. package/package.json +9 -5
  113. package/types/app.d.ts +18 -2
@@ -8,14 +8,17 @@ fits how you already work.
8
8
  | You | Install | What you get |
9
9
  |---|---|---|
10
10
  | New to this, or you want the simplest setup | `npm install -g @one-ai/one-code` | The bundled app: its own `onecode` command, a pinned version of the pi coding agent inside it, and its state kept in `~/.onecode`. It coexists with any `pi` you already have. |
11
+ | On Windows without Node.js | `powershell -c "irm https://raw.githubusercontent.com/IsuruMaduranga/one-code/master/install.ps1 \| iex"` | The bundled app, with Node.js fetched for you when the machine has none. See [Install on Windows](#install-on-windows). |
11
12
  | Already running [pi](https://github.com/earendil-works/pi) | `pi install npm:one-code-extension` | The extensions only. They run on your existing pi. One Code's own state still goes to `~/.onecode`; pi's own files stay under your pi agent directory (`~/.pi/agent`). |
12
13
 
13
14
  ## Requirements
14
15
 
15
16
  - Node.js 22.19 or later. The app refuses to start on older versions,
16
17
  because pi crashes at import time there.
17
- - macOS or Linux. Windows Subsystem for Linux (WSL) works the same way.
18
- Native Windows is untested and unsupported.
18
+ - macOS, Linux, or Windows. Windows Subsystem for Linux (WSL) works like
19
+ Linux. On native Windows, One Code takes Claude Code's shape: Git for
20
+ Windows is recommended but optional, and PowerShell is the primary shell
21
+ tool. See [Windows](windows.md) for what is verified and what isn't.
19
22
  - `git`. Optional: `rg` (ripgrep) for faster search, the GitHub CLI for the
20
23
  pull-request marker in the footer, and a language server for your
21
24
  language (see
@@ -38,6 +41,46 @@ The first run also seeds pi's settings under `~/.onecode/agent/` with the
38
41
  `onecode` theme, quiet startup, and full-screen mode. It never overrides a
39
42
  value you change later.
40
43
 
44
+ ## Install on Windows
45
+
46
+ Open Windows Terminal or PowerShell and run:
47
+
48
+ ```powershell
49
+ powershell -c "irm https://raw.githubusercontent.com/IsuruMaduranga/one-code/master/install.ps1 | iex"
50
+ ```
51
+
52
+ The script looks for Node.js 22.19 or later on your PATH. When there is
53
+ none, it downloads the current Node LTS for your CPU from nodejs.org as a
54
+ zip, checks it against nodejs.org's published checksums, unpacks it under
55
+ `%LOCALAPPDATA%\onecode\node`, and adds that folder to your user PATH.
56
+ Nothing is installed system-wide and no Windows installer runs. It then
57
+ runs `npm install -g @one-ai/one-code` and makes sure the `onecode` command
58
+ is on your PATH. Open a new terminal afterwards so it sees the PATH change.
59
+
60
+ The script also tells you when `git` is missing. One Code runs without it,
61
+ but Git for Windows gives you the branch in the footer, `/init`, worktrees,
62
+ and a bash tool for the model next to PowerShell. Install it with
63
+ `winget install --id Git.Git -e`, or set `ONECODE_INSTALL_GIT=1` before
64
+ running the script to have it do that for you.
65
+
66
+ If you already have Node.js 22.19 or later, the plain npm command works the
67
+ same on Windows as anywhere else:
68
+
69
+ ```powershell
70
+ npm install -g @one-ai/one-code
71
+ ```
72
+
73
+ The script's knobs are environment variables, because a piped script
74
+ cannot take parameters: `ONECODE_INSTALL_NODE=1` downloads the standalone
75
+ Node even when one exists, `ONECODE_NODE_VERSION` pins the version it
76
+ downloads, and `ONECODE_NO_PATH_UPDATE=1` leaves your PATH alone and prints
77
+ what to add. Read the script before running it if you like; it is the
78
+ `install.ps1` at the root of the repository.
79
+
80
+ Windows Subsystem for Linux (WSL) is Linux: use the npm or Homebrew route
81
+ inside the distribution. For what works on native Windows, see
82
+ [Windows](windows.md).
83
+
41
84
  ## Install with Homebrew
42
85
 
43
86
  Homebrew installs Node for you, so this route has no separate Node step:
@@ -59,7 +102,7 @@ pi install npm:one-code-extension
59
102
  ```
60
103
 
61
104
  Use `pi` in place of `onecode` for every command in this guide. The
62
- extension is tested against pi 0.83 through 0.85 and warns at startup when
105
+ extension is tested against pi 0.83 through 0.86 and warns at startup when
63
106
  your pi is outside that range. Full-screen mode is opt-in on your own pi;
64
107
  see [Full-screen mode and themes](configuration.md#full-screen-mode-and-themes).
65
108
 
@@ -102,7 +145,10 @@ npm install -g @one-ai/one-code # npm installs
102
145
  brew upgrade onecode # Homebrew installs
103
146
  ```
104
147
 
105
- Set `ONECODE_NO_UPDATE_CHECK=1` to skip the check; `--offline` skips it too.
148
+ A Homebrew install sees the notice a day after the npm release, because
149
+ Homebrew refuses packages younger than that and `brew upgrade` would find
150
+ nothing sooner. Set `ONECODE_NO_UPDATE_CHECK=1` to skip the check;
151
+ `--offline` skips it too.
106
152
  On your own pi, update with `pi update --extensions`.
107
153
 
108
154
  Both packages are released together with the same version number.
@@ -114,8 +160,10 @@ npm uninstall -g @one-ai/one-code # or: brew uninstall onecode
114
160
  rm -rf ~/.onecode # One Code's state, including pi's under the app
115
161
  ```
116
162
 
117
- On your own pi, run `pi remove one-code-extension` (or remove the package
118
- entry from pi's settings). Removing `~/.onecode` deletes One Code's
163
+ If the Windows script fetched Node.js for you, delete `%LOCALAPPDATA%\onecode`
164
+ too and remove that folder from your user PATH. On your own pi, run
165
+ `pi remove one-code-extension` (or remove the package entry from pi's
166
+ settings). Removing `~/.onecode` deletes One Code's
119
167
  settings, approvals, plan files, and, under the bundled app, pi's sessions
120
168
  and credentials. Memory files stay in `~/.claude/projects/`, because they
121
169
  are shared with Claude Code.
@@ -83,6 +83,18 @@ contains command substitution (`$(…)` or backticks), `eval`, `exec`, a pipe
83
83
  into an interpreter, or a base64 decode is never covered by a prefix or
84
84
  wildcard `allow` rule.
85
85
 
86
+ ### PowerShell rule patterns
87
+
88
+ `PowerShell(...)` rules have the same shape and follow Claude Code's
89
+ PowerShell matching: aliases are canonicalized on both sides
90
+ (`PowerShell(Remove-Item:*)` catches `rm`, `del`, and `ri`), cmdlet names
91
+ are case-insensitive, a line is split on `|`, `;`, `&&`, `||`, and newlines
92
+ outside quotes and here-strings, an `allow` rule must cover every statement,
93
+ and a `deny` or `ask` rule fires on any statement. A `$(…)` subexpression, a
94
+ backtick escape, a script block, the `&`/`.` call operators,
95
+ `Invoke-Expression`, or an encoded command keeps a wildcard `allow` from
96
+ applying. Details in [Windows](windows.md#permission-rules).
97
+
86
98
  ### Add a rule during a session
87
99
 
88
100
  To add an allow rule for the current repository, run:
@@ -120,10 +132,10 @@ stops risky ones, so you're not prompted for every step.
120
132
  The current badge appears in the banner and is announced when the mode
121
133
  changes.
122
134
 
123
- ### Cycle modes with ctrl+q
135
+ ### Cycle modes with ctrl+q (alt+m on Windows and WSL)
124
136
 
125
- Press **ctrl+q** to cycle through manual, accept edits, plan, and auto.
126
- Two modes join the cycle only under certain conditions:
137
+ Press **ctrl+q** to cycle through manual, accept edits, plan, and auto. On
138
+ Windows and WSL the key is **alt+m**. Two modes join the cycle only under certain conditions:
127
139
 
128
140
  - **Auto** appears only when a model that can run the classifier is
129
141
  available. With no provider connected, the cycle skips it.
@@ -134,8 +146,9 @@ flag or a settings file.
134
146
 
135
147
  Claude Code cycles modes with shift+tab. One Code can't use that key
136
148
  because pi reserves it for the reasoning-effort dial. On Windows and WSL,
137
- pi's own default keybindings also use ctrl+q to queue a follow-up message.
138
- If the two conflict there, change pi's binding in its keybindings file.
149
+ pi's own default keybindings use ctrl+q to queue a follow-up message, so
150
+ One Code cycles modes with alt+m there instead. The footer badge and the
151
+ startup hints name whichever key applies on your machine.
139
152
 
140
153
  ### Choose the starting mode
141
154
 
@@ -216,7 +229,10 @@ Reads and edits are confined to the working directory by default:
216
229
  mode, and is denied in don't-ask mode.
217
230
  - Accept-edits mode approves edits only inside the working directory.
218
231
  - The harness's own session directories count as inside: the auto-memory
219
- folder, the session scratchpad, persisted tool output, and the plan file.
232
+ folder, the session scratchpad, persisted tool output, the plan file, and
233
+ this project's own session transcripts (with auto mode's decision log next
234
+ to them), which is what `/doctor` reads. Other projects' transcripts are
235
+ outside, as they are for Claude Code.
220
236
 
221
237
  Paths are compared after resolving symlinks, so a symlink inside the project
222
238
  that points outside it counts as outside.
@@ -287,7 +303,7 @@ code. Only read-only tools and edits to one plan file are available.
287
303
  `plan` agent, and then write the plan.
288
304
  - You can read and edit the plan file yourself while the model works.
289
305
 
290
- Enter plan mode by cycling to it with **ctrl+q**, by starting with
306
+ Enter plan mode by cycling to it with **ctrl+q** (**alt+m** on Windows and WSL), by starting with
291
307
  `--permission-mode plan`, or by asking the model to plan first (it can call
292
308
  `enter_plan_mode` itself).
293
309
 
@@ -49,7 +49,7 @@ that explains it.
49
49
  | `/loop status`, `/loop stop` | Report or end the loop. | |
50
50
  | `/tools-deferred` | List deferred tools and whether each is loaded. | [Tools](tools.md#eager-and-deferred-tools) |
51
51
  | `/init` | Have the model write a `CLAUDE.md` for the project. | [CLAUDE.md](bring-your-claude-code-setup.md#project-instructions-claudemd) |
52
- | `/clear` | Start a new session (pi's `/new`). | [Sessions](sessions-and-context.md#start-over) |
52
+ | `/clear` | Start a new session (pi's `/new`); the model is told. | [Sessions](sessions-and-context.md#start-over) |
53
53
  | `/exit` | Quit (pi's `/quit`). | |
54
54
 
55
55
  ### pi commands
@@ -85,7 +85,7 @@ These come from pi and are available in every One Code session.
85
85
 
86
86
  | Shortcut | What it does |
87
87
  |---|---|
88
- | **ctrl+q** | Cycle the permission mode: manual, accept edits, plan, auto. |
88
+ | **ctrl+q** (**alt+m** on Windows and WSL) | Cycle the permission mode: manual, accept edits, plan, auto. |
89
89
  | **↓** (empty editor) | Focus the panel below the editor: background shells, then subagents, then workflows. |
90
90
  | **ctrl+x** then **ctrl+k** | Stop every running agent (in the subagent panel). |
91
91
  | **x** | Stop the selected agent, shell, or workflow (in a panel). |
@@ -175,6 +175,7 @@ The `CC_` prefix is historical; `ONECODE_`-prefixed aliases are planned.
175
175
  | `CC_FOOTER=0` | Keep pi's footer instead of One Code's status line. |
176
176
  | `CC_TURN_DURATION=0` | Remove the post-turn timing line. |
177
177
  | `CC_RECAP=0` | Turn off the "while you were away" recap. |
178
+ | `CC_SESSION_TITLE=0` | Turn off the automatic session title. |
178
179
  | `CC_RECAP_IDLE_MS` | Idle time before a recap, in milliseconds (default 300000). |
179
180
  | `CC_COMPACTION=0` | Use pi's compaction summary instead of Claude Code's. |
180
181
  | `CC_CLEAR_THINKING=0`, `=1` | Force the thinking-preservation request off or on for Anthropic models. Default: on for `api.anthropic.com` only. |
@@ -194,6 +195,8 @@ The `CC_` prefix is historical; `ONECODE_`-prefixed aliases are planned.
194
195
  | `PI_CACHE_RETENTION` | pi's prompt-cache lifetime. One Code sets `long` for interactive sessions unless you set it yourself. |
195
196
  | `PI_OFFLINE=1` | Same as `--offline`. |
196
197
  | `CLAUDE_CONFIG_DIR` | Where Claude Code's user configuration is read from. |
198
+ | `CLAUDE_CODE_USE_POWERSHELL_TOOL` | `1` turns the `powershell` tool on (the default on Windows), `0` turns it off. Off Windows it needs a `pwsh` on PATH. See [Windows](windows.md). |
199
+ | `CLAUDE_CODE_GIT_BASH_PATH` | The bash that drives the `bash` tool, hooks, and background shells. Also read from the `env` block of `~/.claude/settings.json`; a non-bash binary is ignored with a warning. |
197
200
  | `CLAUDE_CODE_SUBAGENT_MODEL` | Default subagent model on a Claude session. |
198
201
  | `EDITOR`, `VISUAL` | Editor used by `/memory` (and pi's **ctrl+g**). |
199
202
  | `SHELL` | Shell used for commands (default `/bin/sh`). |
@@ -184,6 +184,24 @@ Not restored:
184
184
  `/new`. Running background tasks are stopped and listed. The permission
185
185
  mode you chose carries over.
186
186
 
187
+ ### What the model learns about your commands
188
+
189
+ As in Claude Code, a slash command you run is recorded for the model: the
190
+ next message you send carries a short note that the command ran, wrapped in a
191
+ caveat telling the model not to act on it. `/clear` opens the new session
192
+ with that note, and a model switch records "Set model to …" the way Claude
193
+ Code does. Commands that One Code cannot observe (pi's own `/compact`,
194
+ `/settings`, `/name`) leave no note.
195
+
196
+ ### The session title
197
+
198
+ After your first real message, One Code names the session the way Claude
199
+ Code does: a cheap model on your provider turns the message into a short
200
+ title (Claude Code's own naming prompt), which shows in the terminal tab as
201
+ `One Code - <title> - <folder>` and in the session picker. A name you set
202
+ with `/name` is never overwritten. The call's cost is included in the
203
+ footer's total. Set `CC_SESSION_TITLE=0` to turn it off.
204
+
187
205
  ## Turn timing
188
206
 
189
207
  After each turn, a dim line such as `✻ Cooked for 5m 12s` reports how long
@@ -16,7 +16,7 @@ hint line lists the keys worth knowing:
16
16
  |---|---|
17
17
  | `/hotkeys` | pi's full key list. |
18
18
  | `shift+tab` | Cycle reasoning effort. |
19
- | `ctrl+q` | Cycle the permission mode. |
19
+ | `ctrl+q` (`alt+m` on Windows and WSL) | Cycle the permission mode. |
20
20
  | `ctrl+t` | Show or hide thinking blocks. |
21
21
  | `ctrl+o` | Expand a collapsed tool output. |
22
22
  | `/` | List commands. |
@@ -53,6 +53,15 @@ notification. A background shell shows up in the shells section of the
53
53
  panel below the editor. See
54
54
  [Tasks and background work](tasks-and-background-work.md).
55
55
 
56
+ `powershell` is Claude Code's PowerShell tool: on by default on Windows,
57
+ where it becomes the primary shell and `bash` (Git Bash) stays alongside
58
+ when Git for Windows is installed; off elsewhere unless
59
+ `CLAUDE_CODE_USE_POWERSHELL_TOOL=1` and a `pwsh` is on your PATH. It has the
60
+ same `run_in_background`, the same background panel, and its description is
61
+ Claude Code's own, with the edition section (PowerShell 7 or Windows
62
+ PowerShell 5.1) picked from the executable found. Permission rules use
63
+ Claude Code's `PowerShell(...)` form. See [Windows](windows.md).
64
+
56
65
  Some command shapes are refused before they run, with a message that steers
57
66
  the model to a better route:
58
67
 
@@ -140,10 +149,22 @@ and the model is steered to `tool_search`.
140
149
 
141
150
  In `-p` and `--mode json` runs the process exits when the turn settles, so
142
151
  nothing can run in the background. Tools that would detach work run to
143
- completion instead and return the output in their result: `bash` with
144
- `run_in_background`, `Agent`, `monitor`, and `workflow`. Timers such as
152
+ completion instead and return the output in their result: `bash` and
153
+ `powershell` with `run_in_background`, `Agent`, `monitor`, and `workflow`. Timers such as
145
154
  `schedule_wakeup` and `/loop` never fire in these modes.
146
155
 
156
+ Give a scripted run its own deadline. One Code has no idle timeout on a
157
+ provider response, so a stream that stalls mid-reply keeps the process
158
+ waiting indefinitely. Wrap the call in your script:
159
+
160
+ ```bash
161
+ timeout 180 onecode --mode json -p "…" </dev/null
162
+ ```
163
+
164
+ The `</dev/null` matters too: in `-p` mode piped standard input is read as
165
+ part of the prompt, so a run inside a shell script that reads its own body
166
+ from stdin would hand the rest of the script to the model.
167
+
147
168
  ## Claude Code tool names
148
169
 
149
170
  Claude Code's tool names are accepted in permission rules and hook
@@ -152,6 +173,7 @@ matchers, and mapped to One Code's names. Case is ignored.
152
173
  | Claude Code | One Code |
153
174
  |---|---|
154
175
  | `Bash` | `bash` |
176
+ | `PowerShell` | `powershell` |
155
177
  | `Read`, `Write`, `Edit` | `read`, `write`, `edit` |
156
178
  | `Grep`, `Glob`, `LS` | `grep`, `find`, `ls` |
157
179
  | `NotebookEdit` | `notebook_edit` |
@@ -46,12 +46,12 @@ on first run, so there's nothing to ask.
46
46
 
47
47
  ## Permissions
48
48
 
49
- **Auto mode is missing from the ctrl+q cycle.** Auto mode needs a model to
49
+ **Auto mode is missing from the mode cycle.** Auto mode needs a model to
50
50
  run its classifier. Connect a provider.
51
51
 
52
- **ctrl+q does something else.** On Windows and WSL, pi's default
53
- keybindings also bind ctrl+q, to queue a follow-up message. Rebind one of
54
- them in pi's keybindings file.
52
+ **ctrl+q queues a follow-up instead of changing the mode.** On Windows and
53
+ WSL that is pi's own binding for ctrl+q. The mode key there is alt+m; the
54
+ footer badge names it.
55
55
 
56
56
  **Everything prompts, even in auto mode.** Auto mode pauses after several
57
57
  blocked actions in a row and prompts you once; approving resumes it. Also
@@ -171,6 +171,16 @@ for its scrollback. Use the keys the panel's footer names (**Space** and
171
171
  `CC_FOOTER=0`, or the other toggles listed in
172
172
  [Environment variables](reference.md#environment-variables).
173
173
 
174
+ **On Windows the model has no shell tool, or the wrong one.** With Git for
175
+ Windows installed you get `bash` plus `powershell`; without it, `powershell`
176
+ alone. Set `CLAUDE_CODE_USE_POWERSHELL_TOOL=0` to hide the PowerShell tool
177
+ or `=1` to force it on (it also works on macOS and Linux with a `pwsh` on
178
+ PATH). A startup warning names a `CLAUDE_CODE_GIT_BASH_PATH` that was
179
+ ignored and why. If you report a Windows problem, include the shells on your
180
+ PATH (`where bash`, `where pwsh`, `where powershell`), the value of those two
181
+ variables, and whether the command came through `bash` or `powershell`.
182
+ See [Windows](windows.md).
183
+
174
184
  ## Getting help
175
185
 
176
186
  Report problems at the project's GitHub repository. Include the output of
@@ -0,0 +1,189 @@
1
+ # Windows
2
+
3
+ [← One Code user guide](README.md)
4
+
5
+ One Code on native Windows works the way Claude Code does today: Git for
6
+ Windows is optional, PowerShell is the primary shell tool when it is
7
+ enabled, and the Windows-specific pieces of your Claude Code configuration
8
+ keep working. This page says what that means in practice and what has and
9
+ hasn't been verified.
10
+
11
+ ## Install
12
+
13
+ ```powershell
14
+ powershell -c "irm https://raw.githubusercontent.com/IsuruMaduranga/one-code/master/install.ps1 | iex"
15
+ ```
16
+
17
+ This fetches Node.js when the machine has none and installs the app; with
18
+ Node.js 22.19 or later already present, `npm install -g @one-ai/one-code`
19
+ does the same. Details, and the script's knobs, are in
20
+ [Install on Windows](installation.md#install-on-windows). Git for Windows is
21
+ optional; the section below says what it adds.
22
+
23
+ ## Two shell tools
24
+
25
+ | Tool | When it is active | What runs it |
26
+ |---|---|---|
27
+ | `powershell` | On by default on Windows. Off elsewhere unless `CLAUDE_CODE_USE_POWERSHELL_TOOL=1`. | `pwsh.exe`, else `powershell.exe`, from your PATH. On macOS and Linux, a `pwsh` on PATH. |
28
+ | `bash` | Whenever a bash exists. | Git Bash (`Program Files\Git\bin\bash.exe`, then `bash.exe` on PATH), or the bash named by `CLAUDE_CODE_GIT_BASH_PATH`. |
29
+
30
+ When both are active, PowerShell is the primary shell: the system prompt says
31
+ so and the model reaches for it first, with bash alongside for POSIX scripts.
32
+ `CLAUDE_CODE_USE_POWERSHELL_TOOL=0` turns the PowerShell tool off and makes
33
+ bash primary again, which is the shape Claude Code had before 2.1.126.
34
+
35
+ A Windows machine with neither Git for Windows nor PowerShell has no shell
36
+ tool. One Code says so at startup, in Claude Code's words, and the fix is to
37
+ install one of them.
38
+
39
+ `CLAUDE_CODE_GIT_BASH_PATH` is read from your environment or from the `env`
40
+ block of `~/.claude/settings.json`. A value that isn't a `bash` or `sh`
41
+ binary is ignored with a warning, as Claude Code does since 2.1.219.
42
+
43
+ ## The PowerShell tool
44
+
45
+ The tool the model sees is Claude Code's PowerShell tool: same parameters
46
+ (`command`, `timeout` in milliseconds, `description`, `run_in_background`)
47
+ and Claude Code's own description text, including the edition section. One
48
+ Code picks that section from the executable it found: PowerShell 7 guidance
49
+ for `pwsh`, Windows PowerShell 5.1 guidance for `powershell.exe`. Under the
50
+ hood it is pi's `powershell` tool, run with `-NoProfile -NonInteractive
51
+ -ExecutionPolicy Bypass -Command`, output forced to UTF-8.
52
+
53
+ `run_in_background` works like bash's: a task id comes back at once, output
54
+ spools to the session directory, completion arrives as a notification, and
55
+ the shell shows up in the panel below the editor. A foreground command that
56
+ starts with `Start-Sleep` is refused with a pointer to background runs and
57
+ the monitor tool, and `Read-Host`, `Get-Credential`, `Out-GridView`, `pause`
58
+ and interactive git are refused because the shell has no console to prompt
59
+ on.
60
+
61
+ Inside a worktree session, every PowerShell command is prefixed with a
62
+ `Set-Location` into the worktree, the way bash commands get a `cd`.
63
+
64
+ ## Permission rules
65
+
66
+ `PowerShell(...)` rules use the same shape as `Bash(...)` rules and follow
67
+ Claude Code's PowerShell matching:
68
+
69
+ - Aliases are canonicalized on both sides, so `PowerShell(Remove-Item:*)`
70
+ catches `rm -Recurse build`, `del x`, and `ri y`, and
71
+ `PowerShell(Get-ChildItem:*)` allows `ls` and `dir`. Cmdlet names are
72
+ case-insensitive.
73
+ - A command line is split on `|`, `;`, `&&`, `||`, and newlines, outside
74
+ quotes and here-strings. An `allow` rule has to cover every statement; a
75
+ `deny` or `ask` rule fires on any statement, including inside a nested
76
+ `pwsh -Command '…'`.
77
+ - A line with a `$(…)` subexpression, a backtick escape, a script block, the
78
+ `&` or `.` call operators, `Invoke-Expression`, or an encoded command is
79
+ never covered by a wildcard or prefix allow rule. An exact rule for the
80
+ literal line still is.
81
+ - Unbalanced quoting is a parse failure: no allow rule applies and you are
82
+ asked.
83
+
84
+ Blanket rules (`PowerShell`, `PowerShell(*)`, `PowerShell(iex *)`,
85
+ `PowerShell(Start-Process *)`) are suspended in auto mode like their bash
86
+ counterparts. Hook matchers accept `PowerShell`, `Bash|PowerShell`, and
87
+ `Bash,PowerShell`.
88
+
89
+ ## Auto mode and plan mode
90
+
91
+ Claude Code's read-only cmdlets run without a prompt or a classifier call:
92
+ `Get-ChildItem`, `Get-Content`, `Get-Item`, `Test-Path`, `Resolve-Path`,
93
+ `Select-String`, `Get-Location`, `Get-Process`, `Get-Service`,
94
+ `Get-FileHash`, `Get-Acl`, `Format-Hex`, `findstr`, `where.exe`, plus
95
+ `Write-Output` and `Write-Host`. One Code adds the pure pipeline cmdlets
96
+ those are usually piped through, `Select-Object`, `Sort-Object`,
97
+ `Measure-Object`, `Group-Object`, `ConvertFrom-Json`, `Out-String` and the
98
+ `Format-*` family, so `Select-String … | Measure-Object` counts as read-only
99
+ too; `Where-Object` and `ForEach-Object` do not, because they take script
100
+ blocks. The check is textual and strict: no
101
+ redirection, no variables, no script blocks, and every path inside the
102
+ project or one of the harness's own session directories (auto-memory, the
103
+ scratchpad, persisted tool output, this project's transcripts). A relative
104
+ path is inside by construction; an absolute or drive-lettered path is
105
+ resolved and checked, so `Select-String -Path C:\src\app\notes.md` runs
106
+ unprompted while `Get-Content C:\Users\you\.ssh\id_rsa` does not. A UNC
107
+ path, `~`, a registry or environment drive, or `..` always takes the
108
+ ordinary route.
109
+
110
+ Everything else goes to the auto-mode classifier. One Code has no PowerShell
111
+ command parser yet, so unlike bash there is no deterministic shortcut for
112
+ in-project deletes: a `Remove-Item` inside the repository is classified, not
113
+ auto-approved. The safety floor still applies. Any PowerShell line that
114
+ names a permission settings file (`.claude/settings.json`,
115
+ `~/.onecode/settings.json`, `.claude.json`, managed settings) stops for you,
116
+ whether it reads or writes.
117
+
118
+ Plan mode allows the read-only cmdlets above and refuses the rest, as it
119
+ does for bash.
120
+
121
+ ## Hooks
122
+
123
+ Hooks run in bash where one exists and in PowerShell on a Windows machine
124
+ without Git for Windows. A hook can pick its interpreter with Claude Code's
125
+ `shell` field: `"shell": "powershell"` or `"shell": "bash"`. Timeouts kill
126
+ the whole process tree through `taskkill /T` on Windows. See
127
+ [Hooks](hooks.md#which-shell-runs-a-hook).
128
+
129
+ ## Keys
130
+
131
+ pi binds a few of its own shortcuts differently on Windows and WSL, where
132
+ alt+enter and ctrl+shift+letter do not reach the terminal reliably: queue a
133
+ follow-up is ctrl+q instead of alt+enter, fork is ctrl+f, paste is alt+v,
134
+ model selection is alt+p. Because ctrl+q is taken there, One Code cycles
135
+ permission modes with **alt+m** on Windows and WSL (ctrl+q everywhere
136
+ else). The footer badge, the startup hints, and `/doctor` name the key that
137
+ applies on your machine. Everything else in the [terminal reference](reference.md)
138
+ is the same.
139
+
140
+ ## Paths and state
141
+
142
+ - One Code's state lives in `%USERPROFILE%\.onecode`; Claude Code's
143
+ configuration is read from `%USERPROFILE%\.claude` (or
144
+ `CLAUDE_CONFIG_DIR`).
145
+ - The scratchpad directory is under `%TEMP%`.
146
+ - The `Shell:` line in the system prompt is the basename of `SHELL`, else
147
+ `COMSPEC`, with `.exe` stripped, the value Claude Code prints.
148
+ - Language servers installed with npm are `.cmd` shims on Windows
149
+ (`typescript-language-server.cmd`). One Code finds them on `PATH` and
150
+ starts them through `cmd.exe`, so a plugin's `.lsp.json` can name the
151
+ command the way it does elsewhere.
152
+ - Auto mode's shell pre-gate and the worktree guards understand Git Bash's
153
+ path spellings inside a command line: `/c/Users/…` is `C:\Users\…`, and
154
+ `/tmp/…` is your temp directory, which Git for Windows mounts there. A
155
+ path rule with a wildcard is spelled with forward slashes
156
+ (`Read(C:/notes/**)` or `Read(//c/notes/**)`), because a backslash before
157
+ `*` is the escape character.
158
+
159
+ ## What is verified
160
+
161
+ - The unit suite, including live PowerShell runs, passes on the
162
+ `windows-latest`, `macos-latest`, and `ubuntu-latest` GitHub runners.
163
+ - A real model drives both shell tools on the `windows-latest` runner: the
164
+ bash tool under Git Bash and the PowerShell tool under PowerShell 7, each
165
+ running a command whose output has to come back in the tool result. This
166
+ runs as its own workflow whenever the shell-facing code changes.
167
+ - Drive-letter, backslash, `C:/` and `~` paths in `Read(...)`/`Edit(...)`
168
+ rules, Windows PowerShell 5.1 (edition text, UTF-8 output, no `&&`),
169
+ stopping background PowerShell commands and timed-out hooks, and
170
+ npm-installed language servers all have Windows-only unit tests that run
171
+ on that runner.
172
+ - The PowerShell tool has also been driven through a real model with a
173
+ `pwsh` on macOS, the same way Claude Code's own PowerShell tool was
174
+ captured for reference.
175
+ - An interactive session on a real Windows machine (a Windows Server 2025
176
+ VM, the same kernel as the CI runner): the interface in Windows Terminal,
177
+ `alt+m` mode cycling, a permission prompt, plan mode, the PowerShell tool's
178
+ rendering, a background PowerShell shell with its panel and notification,
179
+ diagnostics through an npm-installed language server, and both `/doctor`
180
+ surfaces, with Git for Windows present and with PowerShell alone. Windows
181
+ 10 and 11 desktops have not had the same pass; if you hit something there,
182
+ [Troubleshooting](troubleshooting.md) says what to collect.
183
+
184
+ ## Not provided
185
+
186
+ - No PowerShell command parser or pre-gate beyond the read-only list.
187
+ - No sandbox on Windows. Claude Code has none there either.
188
+ - `defaultShell` for the user's own `!` prefix is pi's to handle; One Code
189
+ does not read it.
@@ -17,6 +17,8 @@
17
17
  * path auto mode exists to close.
18
18
  */
19
19
 
20
+ import type { HandBackVerdict } from "../lib/notifications.ts";
21
+
20
22
  export const SUBAGENT_ACTIONS_CHANNEL = "one-code:subagent-actions";
21
23
 
22
24
  export interface ChildAction {
@@ -45,7 +47,7 @@ export interface SubagentActionsPayload {
45
47
  * synchronously when no review will run, so the emitter never waits for
46
48
  * nothing.
47
49
  */
48
- onReview?: (flag: string | undefined) => void;
50
+ onReview?: (verdict: HandBackVerdict | undefined) => void;
49
51
  }
50
52
 
51
53
  /** Per-action and total caps: a long-running child must not blow up the prompt. */
@@ -15,9 +15,9 @@
15
15
  * and every caller treats that as "not provably contained".
16
16
  */
17
17
 
18
- import { lstatSync, readlinkSync, realpathSync } from "node:fs";
18
+ import { lstatSync, readlinkSync } from "node:fs";
19
19
  import { basename, dirname, isAbsolute, join, resolve } from "node:path";
20
- import { expandTilde } from "../lib/paths.ts";
20
+ import { comparablePath, expandTilde, gitBashPathToNative, isPathAtOrUnder, tryRealpath } from "../lib/paths.ts";
21
21
 
22
22
  /**
23
23
  * Tools whose calls write to a path. Lives here — the lowest shared layer both
@@ -41,23 +41,8 @@ export function pathArgument(input: Record<string, unknown> | undefined): string
41
41
  return typeof path === "string" ? path : undefined;
42
42
  }
43
43
 
44
- /** Case-folded on darwin/win32, where the filesystem is case-insensitive. */
45
- function normalize(target: string): string {
46
- const normalized = resolve(target).replace(/\\/g, "/").replace(/\/+$/, "");
47
- return process.platform === "linux" ? normalized : normalized.toLowerCase();
48
- }
49
-
50
- function tryRealpath(target: string): string | undefined {
51
- try {
52
- return realpathSync.native(target);
53
- } catch {
54
- try {
55
- return realpathSync(target);
56
- } catch {
57
- return undefined;
58
- }
59
- }
60
- }
44
+ /** The shared comparison form (lib/paths.ts): `/` separators, case-folded on darwin/win32. */
45
+ const normalize = comparablePath;
61
46
 
62
47
  /**
63
48
  * Resolve a path for containment checks, following a symlink leaf that does not
@@ -99,11 +84,9 @@ export function resolveForContainment(target: string): string | undefined {
99
84
  }
100
85
  }
101
86
 
102
- /** True only when `target` is provably at or under `base`. */
87
+ /** True only when `target` is provably at or under `base` (lib/paths.ts `isPathAtOrUnder`, argument order kept for the callers here). */
103
88
  export function isWithin(base: string, target: string): boolean {
104
- const normalizedBase = normalize(base);
105
- const normalizedTarget = normalize(target);
106
- return normalizedTarget === normalizedBase || normalizedTarget.startsWith(`${normalizedBase}/`);
89
+ return isPathAtOrUnder(target, base);
107
90
  }
108
91
 
109
92
  /**
@@ -119,3 +102,18 @@ export function toAbsolute(cwd: string, token: string, home: string): string {
119
102
  if (isAbsolute(token)) return resolve(token);
120
103
  return resolve(cwd, token);
121
104
  }
105
+
106
+ /**
107
+ * {@link toAbsolute} for a token taken from a bash command line. On Windows the
108
+ * bash tool runs Git Bash, whose absolute paths are MSYS spellings — `/c/proj/f`
109
+ * is `C:\proj\f`, `/tmp/f` sits under the user's temp dir — so the token is
110
+ * converted first (lib/paths.ts gitBashPathToNative). `path.resolve` alone reads
111
+ * `/c/proj/f` as `<cwd drive>:\c\proj\f`, which no gate recognises as the
112
+ * project: the error fell towards escalate/prompt, never towards allow, but every
113
+ * in-project write spelled that way went to the classifier. File-tool paths keep
114
+ * `toAbsolute`: pi's read/edit/write resolve them natively, and a gate must
115
+ * judge the path the tool acts on, not the one Git Bash would reach.
116
+ */
117
+ export function toAbsoluteBash(cwd: string, token: string, home: string): string {
118
+ return toAbsolute(cwd, gitBashPathToNative(token), home);
119
+ }