Skip to content

fix(cli): show clap error for flag-only invocations + rebrand fallback msg - #4

Merged
thehoff merged 1 commit into
developfrom
fix/run-fallback-flag-and-brand
May 14, 2026
Merged

thehoff merged 1 commit into
developfrom
fix/run-fallback-flag-and-brand

Conversation

@thehoff

@thehoff thehoff commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Found via Framework devel-test. `contextcrawler -v` failed with a confusing brand-mismatched OS error:

```
$ contextcrawler -v
[rtk: No such file or directory (os error 2)]
```

Root cause: `run_fallback` (`src/main.rs:1231`) catches clap parse failures and tries to exec `args[0]` as a passthrough binary. When `args[0]` is a flag like `-v`, it called `Command::new("-v")`, hit ENOENT, and wrapped the error in the stale `[rtk: …]` brand string.

Fixes

  1. Leading-dash guard in `run_fallback`: if `args[0].starts_with('-')`, call `parse_error.exit()` instead of attempting passthrough. Sibling to the existing `RTK_META_COMMANDS` guard.
  2. Rebrand the two `eprintln!("[rtk: {}]", e)` formats in `run_fallback` to `[contextcrawler: …]`. These still fire for legit passthrough failures (e.g. `contextcrawler nonexistent-tool`), and the brand prefix should match the binary name.

After

```
$ contextcrawler -v
error: 'contextcrawler' requires a subcommand but one was not provided
[subcommands: ls, tree, read, ...]
Usage: contextcrawler [OPTIONS]
For more information, try '--help'.
```

Test plan

  • `cargo build` clean
  • `contextcrawler -v` → clap subcommand-required error (not OS error 2)
  • `contextcrawler -nosuchflag` → clap unexpected-argument error
  • Devel-test on Framework

Independence

Branched off `develop`. Touches only `src/main.rs` (9 lines). No conflicts with PR #1/#2/#3.

🤖 Generated with Claude Code

…k msg

Reproduction (fresh v0.1.2 install, both macOS and Ubuntu):

  $ contextcrawler -v
  [rtk: No such file or directory (os error 2)]

`-v` is defined as a global verbosity-count flag, but parsing it without
a subcommand fails. The clap-error fallback path in `run_fallback`
(main.rs:1231) then treated `args[0]` as a binary to exec — so it
ran `Command::new("-v")`, hit ENOENT, and wrapped the error in the
stale `[rtk: ...]` brand string.

Two fixes:

1. Add a leading-dash guard in `run_fallback`: if `args[0].starts_with('-')`,
   call `parse_error.exit()` instead of trying to exec the flag.
   Sibling to the existing `RTK_META_COMMANDS` guard for the same reason —
   "this clearly isn't a passthrough target, show the parse error."

2. Rebrand the two `eprintln!("[rtk: {}]", e)` formats in `run_fallback`
   to `[contextcrawler: ...]`. These fire when a real passthrough
   command-not-found happens (e.g. `contextcrawler nonexistent-tool`),
   and the brand prefix should match the binary name.

After:

  $ contextcrawler -v
  error: 'contextcrawler' requires a subcommand but one was not provided
    [subcommands: ls, tree, read, ...]
  Usage: contextcrawler [OPTIONS] <COMMAND>
  For more information, try '--help'.

Verified: `cargo build` clean; smoke-tested `-v` and `-nosuchflag` —
both surface clap's proper parse error.

Independent of PR #1/#2/#3. Branched off develop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@thehoff
thehoff merged commit a2b7d63 into develop May 14, 2026
4 checks passed
@thehoff
thehoff deleted the fix/run-fallback-flag-and-brand branch May 14, 2026 13:04
@thehoff thehoff mentioned this pull request May 14, 2026
3 tasks done
thehoff added a commit that referenced this pull request May 24, 2026
…157)

The read filter is the #4 absolute leaker per the v0.1.10 audit (1838
runs, 2.5M leaked, 87.3% saved). The existing symmetric head/tail cap
works for medium files but degrades on large files where the
relevant content sits in the middle.

Add `contextcrawler read <file> --intent "<terms>"`. When provided AND
the file is > 5KB AND the splitter finds ≥ 3 sections AND at least one
section scores above zero, the output becomes:

  <metadata banner>
  <head bookend>  (omitted on files < 60 lines, byte-capped at 1KB)
  <top-scoring sections, in original document order>
  <omission count>
  <tail bookend>  (same gating as head)

Otherwise falls through to the existing render path — no regression on
small files, no regression when intent omitted.

Section splitters covered in v1:
  - Markdown (.md, .markdown)        split on heading lines, skip fenced code
  - Rust (Language::Rust)            top-level item starts (fn, impl, struct,
                                     enum, trait, mod, type, const, static),
                                     with vis/async/unsafe handled
  - TOML / Cargo.lock                split on lines starting with [
  - Unknown                          paragraph split on blank-line runs

Scoring is lexical: per-term occurrence count in body plus heading hits
weighted 5×. Stopword list is short and conservative; tokenisation is
case-insensitive, alphanumeric + underscore only. No stemming, no LLM
— preserves the zero-async, fast-startup posture.

Live result on Cargo.lock (this repo, 2316-line / 14K-token file):
  contextcrawler read Cargo.lock                            → 0% saved
  contextcrawler read Cargo.lock --intent "rusqlite chrono" → 96.1% saved

Tests added (13 new, total 2681 / 2681 pass):
  - tokenize_drops_stopwords_and_lowercases
  - tokenize_handles_punctuation
  - tokenize_only_stopwords_yields_empty
  - markdown_splitter_finds_sections
  - markdown_splitter_ignores_headings_in_fenced_code
  - rust_splitter_handles_visibility_and_qualifiers
  - rust_splitter_does_not_split_inner_fns
  - toml_splitter_array_of_tables
  - extract_returns_none_for_small_input
  - extract_returns_none_when_no_section_matches
  - extract_keeps_matching_section_and_drops_others
  - extract_preserves_head_and_tail_bookends
  - extract_achieves_meaningful_reduction

Closes #157.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
thehoff added a commit that referenced this pull request May 24, 2026
…157)

The read filter is the #4 absolute leaker per the v0.1.10 audit (1838
runs, 2.5M leaked, 87.3% saved). The existing symmetric head/tail cap
works for medium files but degrades on large files where the
relevant content sits in the middle.

Add `contextcrawler read <file> --intent "<terms>"`. When provided AND
the file is > 5KB AND the splitter finds ≥ 3 sections AND at least one
section scores above zero, the output becomes:

  <metadata banner>
  <head bookend>  (omitted on files < 60 lines, byte-capped at 1KB)
  <top-scoring sections, in original document order>
  <omission count>
  <tail bookend>  (same gating as head)

Otherwise falls through to the existing render path — no regression on
small files, no regression when intent omitted.

Section splitters covered in v1:
  - Markdown (.md, .markdown)        split on heading lines, skip fenced code
  - Rust (Language::Rust)            top-level item starts (fn, impl, struct,
                                     enum, trait, mod, type, const, static),
                                     with vis/async/unsafe handled
  - TOML / Cargo.lock                split on lines starting with [
  - Unknown                          paragraph split on blank-line runs

Scoring is lexical: per-term occurrence count in body plus heading hits
weighted 5×. Stopword list is short and conservative; tokenisation is
case-insensitive, alphanumeric + underscore only. No stemming, no LLM
— preserves the zero-async, fast-startup posture.

Live result on Cargo.lock (this repo, 2316-line / 14K-token file):
  contextcrawler read Cargo.lock                            → 0% saved
  contextcrawler read Cargo.lock --intent "rusqlite chrono" → 96.1% saved

Tests added (13 new, total 2681 / 2681 pass):
  - tokenize_drops_stopwords_and_lowercases
  - tokenize_handles_punctuation
  - tokenize_only_stopwords_yields_empty
  - markdown_splitter_finds_sections
  - markdown_splitter_ignores_headings_in_fenced_code
  - rust_splitter_handles_visibility_and_qualifiers
  - rust_splitter_does_not_split_inner_fns
  - toml_splitter_array_of_tables
  - extract_returns_none_for_small_input
  - extract_returns_none_when_no_section_matches
  - extract_keeps_matching_section_and_drops_others
  - extract_preserves_head_and_tail_bookends
  - extract_achieves_meaningful_reduction

Closes #157.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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