fix(plan): open docs linked beside the plan file (#1620)
* fix(plan): open documents linked beside the plan file Claude Code's ExitPlanMode input carries planFilePath under ~/.claude/plans/, outside the review root, so a relative link to a sibling document returned 404. The plan server now trusts that path when the file holds the plan under review, and /api/doc serves the documents the plan names from its directory. The rest of the directory stays unreachable. Closes #1443 * fix(plan): serve only exact plan link targets beside the plan planNamesPath matched the requested path as a substring of the plan text, so a link to evidence.md unlocked e.md and [[notes-extended]] unlocked notes.md, reaching other files in ~/.claude/plans/. The plan directory now serves only paths equal to one of the plan's link targets (markdown links, wiki links, HTML href), normalized the same way on both sides (no fragment or query, percent-decoded, ./ collapsed). * fix(plan): keep the project-root fallback when the base is the plan directory The client sends the plan directory as base on every open. When that directory sits inside the project it was trusted for root resolution, so HTML links resolved only against it and lost the project-root fallback. The plan-directory base now feeds only the plan-linked lookup; root resolution runs without a base, as before. Documents that a linked file beside the plan shadows a project doc of the same name. * fix(plan): serve the normalized link target, never the raw request resolvePlanLinkedDoc matched the normalized key but joined the raw request onto the plan directory, so a ?/.. or #/.. suffix after a linked name (linked.md?/../other-plan.md) collapsed to a sibling the plan never linked. Every check and the path join now read the normalized key, which also makes a percent-encoded link (my%20doc.md) open the decoded file. Links written inside fenced code or code spans no longer count as targets. * perf(plan): compute plan link targets once, in linear time The fence-stripping regex backtracked quadratically on a long run of backticks or tildes (50k chars took ~0.9s), as did the wiki-link pattern on a run of [[ openers, and targets were recomputed on every plan-directory /api/doc request, blocking the event loop. Code is now stripped by a line scan, the wiki and href patterns stop at a newline or the next opener, and readPlanFile stores the targets on PlanFile. --------- Co-authored-by: Michael Ramos <mdramos8@gmail.com>
B
Ben Drucker committed
3723ce25b41904c55b1ed4e03f7f3655d31a18f3
Parent: 31a0078
Committed by GitHub <noreply@github.com>
on 9/27/2026, 6:19:25 PM