Skip to content

feat: Windows shell write/delete detection (#46 follow-up)#75

Merged
Cannon07 merged 6 commits into
mainfrom
feat/shell-detect-windows
Jun 5, 2026
Merged

feat: Windows shell write/delete detection (#46 follow-up)#75
Cannon07 merged 6 commits into
mainfrom
feat/shell-detect-windows

Conversation

@Cannon07
Copy link
Copy Markdown
Owner

@Cannon07 Cannon07 commented Jun 4, 2026

Follow-up to the merged claudecode Windows slice (#73). Fixes the reported bug: on Windows, deleting/writing a file via the shell did not mark neo-tree.

Root cause (STEP 0, gathered on a real Windows box)

Claude Code on Windows exposes a distinct PowerShell tool alongside Bash and routes shell file ops through it (the Haiku model deletes via Remove-Item, moves via Move-Item, writes via Set-Content/Out-File). tool_name is "PowerShell" with a Bash-shaped {command} payload. Two things were wrong, not one:

  1. The PreToolUse matcher (Edit|Write|MultiEdit|Bash) never matched PowerShell, so the hook never fired.
  2. Even for git-bash (Opus emits POSIX rm with Windows paths), the detector was Unix-path-only.

The fix — three layers

  • Matcher: claudecode Pre/PostToolUse matchers gain PowerShell (harmless on Unix; no such tool there).
  • Normaliser: fold tool_name="PowerShell" → canonical Bash, so the dispatcher/emitters/detector need no awareness of a separate tool.
  • Detector (bash_detect.luashell_detect.lua, behind the unchanged M.detect): split into two independent axes per ADR-0009
    • an OS-selected path-convention adapter (Unix byte-identical; Windows handles C:\/C:/, UNC, backslashes, relative-against-backslash-cwd, canonical backslash output);
    • a PowerShell command grammar (Remove-Item/Set-Content/Out-File/Add-Content/Move-Item/Copy-Item + aliases, -Path/-Destination/positional, comma-lists, here-strings). POSIX stays byte-identical — the PS tables exclude the bash aliases, so the vocabularies never collide.

Also: diff.lua git-root reveal switched to list-form systemlist (drops the POSIX-only 2>/dev/null that misbehaves under Windows cmd).

Testing

  • macOS/Linux: full suite green (shell_detect spec = 58, normaliser = 38, etc.). POSIX rows run on Unix.
  • Windows CI (windows-test): now runs the whole tests/plugin dir (the old bash_detect exclusion is removed); the spec's Windows rows (real Haiku samples — comma-lists, here-strings, UNC, aliases, Move/Copy) execute there. This PR's windows-test run is the first CI validation of those rows.
  • test_install.sh updated to assert the new matcher (stricter, not looser).

Docs

  • ADR-0009 records the axis split (and why per-OS / Windows-branch were rejected).

Known scope / minor follow-up

  • The POSIX matchers remain per-verb functions (not folded into one grammar table) — functionally meets "second grammar slots in as data"; full POSIX table refactor is out of scope (ADR-0009).
  • Minor: detect_ps() also runs on Unix. Harmless for known cases (PS tables exclude bash aliases), but a Unix command whose first word is del/move/copy/etc. could yield a spurious Tier-1 indicator. Vanishingly rare; could be gated behind is_windows() in a later pass.

🤖 Generated with Claude Code

Cannon07 and others added 6 commits June 3, 2026 16:36
Task handoff for the Windows session: extend bash_detect to PowerShell
commands (Remove-Item etc.) + Windows path resolution so neo-tree change
indicators fire for shell deletes/writes on Windows. Includes the STEP 0
debug-sample gathering (only doable on Windows), the grammar requirements,
the diff.lua git portability fix, and a note to coordinate the bash_detect
-> shell_detect structure decision with the improve-architecture pass.

Scratchpad — to be removed before this follow-up PR merges.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replaces the "structure decision is not yours" placeholder with the now-
decided shape from the improve-codebase-architecture pass: split the two
independent axes (path conventions vs command grammar — they don't align to
OS), rename bash_detect -> shell_detect behind the same M.detect interface,
extract a path-convention adapter, make command matchers table-shaped, and
add the PowerShell grammar only after STEP 0 confirms it. Registry/engine and
any per-OS file split are explicitly out of scope (deferred by the review).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…46)

Records the fork-(b) decision from the architecture review: the shell
write/delete detector splits path-convention from command-grammar (the two
do not align onto OS — git-bash on Windows is POSIX grammar + Windows paths),
renames bash_detect -> shell_detect behind the same M.detect interface,
extracts a path-convention adapter, table-shapes the matchers, and defers the
PowerShell grammar until an empirical finding confirms which shell the Windows
Bash tool emits. Backs the shell_detect handoff on this branch.

0008 is intentionally reserved for the hook-entry consolidation ADR, which
lands with its own PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On Windows, Claude Code routes shell file ops through a distinct `PowerShell`
tool (the Haiku model deletes via `Remove-Item`, moves via `Move-Item`, writes
via `Set-Content`/`Out-File`), and the detector understood only POSIX commands +
Unix paths — so shell deletes/writes never marked neo-tree. STEP 0 ground truth
captured on the Windows box corrected the original macOS-written premise: it is
a SEPARATE tool, not the Bash tool emitting PowerShell, and the hook matcher
never fired for it.

Three layers:
- matcher: add `PowerShell` to the claudecode Pre/PostToolUse matchers so the
  hook fires for those proposals at all.
- normaliser: fold tool_name="PowerShell" onto canonical Bash (shell proposal,
  Tier-1 indicator-only, bash_* origin prefix) — no dispatcher/emitter change,
  no permission gate.
- shell_detect (renamed from bash_detect): split the two axes the architecture
  review identified —
    * OS-selected path adapter: Unix (byte-identical) + Windows (C:\, C:/, UNC,
      backslash, relative-vs-backslash-cwd). Emits canonical backslash to match
      the registry keys Edit/Write use, since fnamemodify(":p") is a no-op on an
      already-absolute Windows path.
    * additive PowerShell grammar: Remove-Item/Set-Content/Out-File/Add-Content/
      Move-Item/Copy-Item (+ aliases), -Path/-Destination/positional, comma-list
      arrays (incl. spaces after commas) and here-strings. POSIX behaviour is
      unchanged (PS verbs never collide with POSIX-handled rm/cp/mv/tee).

Also: diff.lua git reveal -> list-form systemlist (drop POSIX-only 2>/dev/null).

Tests: spec renamed to pre_tool_shell_detect_spec.lua with OS-branched rows
(POSIX pending on Windows; Windows + PowerShell rows from real samples, incl.
multi-delete comma-list regression); pending bash_modified case re-enabled
portably; normaliser + install specs updated; CI windows-test exclusion dropped.
Verified end-to-end on Windows: single and multi-file PowerShell deletes mark
neo-tree with correct backslash paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion (#46)

The deferral in ADR-0009 is closed: STEP 0 landed and the PowerShell grammar
shipped in the same PR as the axis split. Append a resolution note correcting
the two premises the original text got wrong — it is a separate `PowerShell`
tool (not the Bash tool emitting PowerShell), so routing was NOT fine and a
matcher + normaliser layer were needed on top of the detection split — and
record what shipped for the detection layer, including the honest scope note
that the POSIX matchers were not refactored into a unified grammar table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Retitle ADR-0009 to "splits path-convention from command-grammar" (drop
  "defer grammar": the grammar shipped with the split, so the deferral framing
  never described a real shipped state). Rewrite as one coherent decision —
  fold in the STEP 0 premise corrections (distinct PowerShell tool; the
  matcher + normaliser layers) and the scope-honesty note. Fix stale
  bash_detect references to shell_detect.
- Remove HANDOFF-shell-detect.md: a cross-machine scratchpad, now consumed
  (findings live in the ADR, the spec's real-sample rows, and code comments).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Cannon07 Cannon07 merged commit e421443 into main Jun 5, 2026
3 checks passed
@Cannon07 Cannon07 deleted the feat/shell-detect-windows branch June 5, 2026 05:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant