Skip to content

fix: eliminate 0-byte ghost files from shell-redirect misinterpretation#8

Merged
drknowhow merged 1 commit into
mainfrom
fix/ghost-files-shell-redirect
Jun 9, 2026
Merged

fix: eliminate 0-byte ghost files from shell-redirect misinterpretation#8
drknowhow merged 1 commit into
mainfrom
fix/ghost-files-shell-redirect

Conversation

@drknowhow

Copy link
Copy Markdown
Owner

The bug

0-byte "ghost" files (e.g. 110tok, 69tok, main, str) kept appearing in the project root. Despite the suspicion, it is not caused by &.

Root cause: text containing -> word reaches a shell, which parses > word as a redirection and creates an empty file named word. The biggest source was c3's own output filter, which formats its savings header as raw->Ntok — the literal -> becomes > Ntok.

Reproduced live: a git log command (no -> in its own output) was filtered with header 818->69tok, and a 0-byte 69tok immediately appeared. Other instances of the same mechanism: git's ref -> refmain, Python -> Type hints in read files → str/dict, pip >=x=x.

The fix

  1. cli/tools/filter.py — the filter header now uses instead of ->, so c3 never emits a shell-redirect metacharacter in its own output (eliminates the Ntok family entirely).
  2. cli/hook_ghost_files.py — the cleanup hook previously only ran after Bash. It now also runs after mcp__c3__c3_shell, mcp__c3__c3_read, and Read, so ghosts from any tool's output (git refs, type hints, pip specifiers) are swept regardless of source.
  3. cli/c3.py (install-mcp) — registers hook_ghost_files on the Read and c3_read matchers and adds a new c3_shell matcher block.

Verified

  • The broadened hook deletes a synthetic ghost when fed a c3_shell PostToolUse payload (clean UTF-8 stdin): [c3:ghost-cleanup] Deleted 2 ghost file(s)….
  • The filter header no longer contains >.
  • ruff clean; test_ghost_files.py + test_cli_smoke.py pass (19).

Activation

Logged under [Unreleased]. After merge, re-run c3 install-mcp (to register the new hook matchers) and restart the MCP server (to pick up the filter change). Happy to cut a 2.32.3 release once merged.

Side note (not fixed here): the generated Windows hook commands use shlex.quote, which emits POSIX single quotes (cmd /c '…') that cmd.exe treats literally. Hooks still work, but it's worth a follow-up to use Windows-appropriate quoting.

🤖 Generated with Claude Code

Root cause: c3's output filter wrote its savings header as "raw->Ntok". The
literal "->" was re-read by a shell as "> Ntok", creating an empty file named
after the token count (e.g. 110tok). The same class produced "main" (git's
"ref -> ref"), "str"/"dict" (Python "-> Type" hints), and "=x.y.z" (pip ">=").
It was NOT caused by "&".

- cli/tools/filter.py: header now uses the Unicode arrow instead of "->", so c3
  never emits a shell-redirect metacharacter in its own output.
- cli/hook_ghost_files.py: cleanup now triggers after c3_shell / c3_read / Read,
  not just Bash, so ghosts from any tool's output get swept.
- cli/c3.py (install-mcp): register hook_ghost_files for those matchers and add a
  mcp__c3__c3_shell block.

Verified: the broadened hook deletes a synthetic ghost on a c3_shell payload, and
the filter header no longer contains ">". Re-run `c3 install-mcp` and restart the
MCP server to activate the hook + filter changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 9, 2026 13:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Eliminates creation of 0-byte “ghost” files in the project root caused by shell redirection metacharacters appearing in tool output and being misinterpreted downstream. This change reduces accidental filesystem pollution and broadens the existing cleanup hook so it runs after more tool types (not just native shell runs).

Changes:

  • Update the output-filter savings header to use instead of ->, avoiding > redirection metacharacters in emitted output.
  • Expand the ghost-file cleanup hook trigger set to also run after c3_shell, c3_read, and native read tools.
  • Register the ghost-file hook for additional matchers in install-mcp, including a new mcp__c3__c3_shell matcher, and document the fix in the changelog.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
cli/tools/filter.py Replaces raw->Ntok style header formatting with a glyph to prevent shell redirect misinterpretation.
cli/hook_ghost_files.py Adds a shared trigger-tool allowlist so ghost cleanup runs after more tool invocations.
cli/c3.py Extends install-mcp hook registration to run ghost cleanup after Read, c3_read, and c3_shell.
CHANGELOG.md Records the ghost-file fix and notes the need to re-run c3 install-mcp.

Comment thread cli/hook_ghost_files.py
Comment on lines +215 to +218
# Tools whose output can carry shell-meta text that leaks into 0-byte files:
# native shells, c3_shell (its `N->Mtok` filter header), and file reads whose
# content has `-> Type` hints. A downstream shell sees `> word` and creates an
# empty file named `word`.
@drknowhow drknowhow merged commit ad608a8 into main Jun 9, 2026
12 checks passed
@drknowhow

Copy link
Copy Markdown
Owner Author

Added commit b345f80 — a more impactful fix uncovered while investigating: no c3 hook was launching on Windows at all. The generated hook commands used a bare `cmd /c` prefix, but Git Bash (Claude Code's Windows hook executor) doesn't resolve bare `cmd` on PATH, so enforcement / c3-signal / output-filter / ghost-cleanup all silently failed to start (that's also why `.c3/last_c3_call.json` was never written). Fix: `cmd.exe /c`. Verified under bash — the hook then runs and writes its signal file. NB: this is what made the ghost cleanup dormant; the filter `->`→`→` change in e861345 is the part that stops c3 creating ghosts regardless. Re-run `c3 install-mcp` + restart to activate.

drknowhow added a commit that referenced this pull request Jun 9, 2026
fix(windows): c3 hooks never launched (cmd to cmd.exe) — completes PR #8
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.

2 participants