feat(links): Ctrl-click a bare name.ext after an agent-CLI file verb (plan 043) - #94
Merged
Merged
Conversation
An agent tool row such as `Update(015-terminal-watchdog-workflows-mockup.html)` got no Ctrl-click link / Copy Path: a bare filename with no directory separator matches none of PATH_RE's five alternatives, and that is deliberate — package.json, example.com and v5.91.0 all have the same shape in prose. Add a sixth alternative that admits a bare `name.ext` ONLY when an allowlisted file verb introduces it — `Update(x.html)`, `read(app.tsx)`, `Read foo.ts`, `ReadFile: a.md`, `Edited main.rs` — via a positive lookbehind so start/end cover just the filename. `\b` before the verb keeps `Unread foo.ts` out; the final extension must start with a letter so `Update v1.2.3` stays text (documented cost: digit-first extensions such as `.7z` are not linked after a verb). Branch 6 sits after branch 5 so `Update(src.v2/main.rs)` still links the full path. No resolver change: Claude Code prints cwd-relative paths, so a bare name means the file is in the terminal cwd and resolve_terminal_path's direct join finds it. Tests pin each hop — filename-only span, :line:col, precedence, soft-wrapped `Update(` row, linkAtIndex hit/miss — plus negative controls for every non-file lookalike. Plan: termflow-fabric docs/plan/043.
…or, drop prose verbs from the row form Review round 1 (Sol) on cd0c2c1 — all fixed at the class, not the site: - Branch 6 had no RIGHT boundary, so it linked a prefix of a longer token: `Read(archive.tar.7z)` → `archive.tar`, `Read foo.ts/bar` → `foo.ts`. A negative lookahead `(?![\w\/-]|\.[\w-])` now requires the token to end there; a sentence-final `.` (`read config.json.`) still links. - The separator `[ \t]+` sat inside a lookbehind that is evaluated at every scan position, so `Read` + N spaces cost O(N²) per hover. Bounded to `[ \t]{1,8}`; a timing test pins it (unbounded: ~385 ms, bounded: ~9 ms). - `Open example.com in your browser` / `Create Node.js project` linked. The space/colon row form now admits only row verbs; the paren form keeps Open/View/Create (`Create(e.rs)`). Residual, accepted: `Remove example.com` still links — the cost is the existing "File not found" toast. - Tests: `:line:col` after a row verb; linkAtIndex at every boundary (`(`, first, last, `)`); a bare name hit through the getLinkAt composition helper on both rows of a soft wrap. - Comment corrected in place: branches 1–5 are bounded by their char classes; branch 6 by its `{1,8}` separator and right-anchored tail. With the right boundary, alternation order (branch 5 before 6) is no longer load-bearing — `Update(src.v2/main.rs)` links the full path either way — so that case pins the behaviour, not the source order. The previous commit's "negative controls for every non-file lookalike" was an overclaim; the controls are the ones enumerated by name in findPathLinks.test.ts.
…b forms from one list
Review round 2 (Sol):
- `Read foo.ts..bar` still linked `foo.ts`: the round-1 lookahead forbade
`.`+word, and the char after the first dot was another dot. The rule is now
"the token ends here — not followed by a word char, a separator, or a
dot-run that leads to one" (`(?![\w\/-]|\.+[\w\/-])`), which closes the
prefix class without eating an ellipsis (`Edited main.rs...` still links).
- The paren and row verb allowlists were two hand-copied lists and the paren
copy had lost the past-tense verbs (`Edited(main.rs)` stopped linking).
Both forms now derive from ONE `FILE_VERBS` list; the row form is that list
minus `PROSE_STARTERS` (Open/View/Create). PATH_RE is composed with
`new RegExp`, branches 1-5 kept byte-identical via a literal's `.source`.
- The 200 ms wall-clock assertion was a CI flake risk and redundant: the
8-space / 9-space pair already pins the `{1,8}` separator bound (it is
what kills the unbounded mutant). Kept the 20k-space scan as a smoke.
- Comment rewritten in place to state the admitted forms and the boundary rule.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Ctrl-click / Copy Path now works on an agent-CLI tool row whose file has no directory separator:
A bare
name.extis deliberately not a path in general (package.json,example.com,v5.91.0all have that shape in prose), soPATH_REgains a sixth alternative that admits one only when an agent-CLI file verb introduces it —Update(x.html),read(app.tsx),Read foo.ts,ReadFile: a.md,Edited main.rs. The verb sits in a positive lookbehind, so the underline, hit-test and Copy Path cover only the filename.Rules (each pinned by a named test in
findPathLinks.test.ts)FILE_VERBSlist:Verb(admits every verb;Verb/Verb:drops the prose startersOpen/View/Create(Open example.com in your browserdoes not link).\bbefore the verb (Unread foo.tsdoes not link).Update v1.2.3does not link; documented cost:.7zafter a verb).Read(archive.tar.7z),Read foo.ts/bar,Read foo.ts..bardo not link a prefix;read config.json.andEdited main.rs...still link).[ \t]{1,8}— the lookbehind runs at every scan position, so an unbounded run was O(N²) per hover. Pinned by the 8-space / 9-space pair..source);:line:colstill parses.Not changed (decided)
find_descendantsstill rejects a bare filename for the BFS fallback: Claude Code prints cwd-relative paths, so a bare name means the file is in the terminal cwd and the direct join resolves it; a miss shows the existing "File not found" toast.Verification
bun run build:terminal-core,bunx tsc --noEmit,bun run buildgreen.\b, digit-first extension, consuming group instead of lookbehind, drop right boundary, unbound separator, empty prose-starter set, regress dot-run rule, paren form with the prose filter — each by the test that names its reason.8e49db1; r2 3 D / 1 C →0651775; r3 confirmation, zero findings, ready to merge.Plan: termflow-fabric
docs/plan/043-verb-prefixed-bare-file-links.md; reviewsdocs/review/222–224.