fix(from-plan): verbatim pass-through for external spec formats (release 0.5.1) - #23
Conversation
… section hints A plan/spec whose headings did not match the Claude plan-mode shape was reduced to a four-placeholder skeleton — the plan body was silently dropped and Cursor received an empty task, despite the module docs promising a pass-through fallback. (#16) - buildTaskContent: when no section hint matches, embed the source document verbatim under "Task specification" (leading H1 deduped, guardrail block still appended) instead of emitting placeholders - SECTION_HINTS: recognise common spec-driven headings (overview, problem statement, requirements, design, spec, tasks, steps, testing, validation, success criteria) - README: document that from-plan accepts any path, not just ~/.claude/plans/, and the verbatim fallback - tests for both the fallback and the new heading mappings
freema
left a comment
There was a problem hiding this comment.
CodeForge Review
Verdict: comment | Score: 7/10
Solid, well-tested fix for the plan-truncation bug (#16): the pass-through fallback and widened SECTION_HINTS are correct for the common case (H1 as the literal first line) and are covered by clear new tests, all of which pass, along with lint/typecheck. There's one real edge case in the new pass-through branch: the leading-H1-strip regex only matches when the H1 is the very first thing in the file, so any preamble (e.g. YAML frontmatter, a stray line) before the title heading causes the title to be duplicated in the output — a realistic scenario given the feature explicitly targets hand-written specs and other tooling's plan files, which often carry frontmatter.
Reviewed by CodeForge
| // Embed the document verbatim instead and let Cursor follow it as written. | ||
| if (!context && !approach && !files && !verification) { | ||
| const rawBody = String(plan.raw ?? '') | ||
| .replace(/^\s*#\s[^\n]*\n/, '') // drop the leading H1 — already emitted above |
There was a problem hiding this comment.
[MINOR] In the new pass-through fallback, the regex ^\s*#\s[^\n]*\n used to strip the leading H1 from plan.raw only matches when the H1 title line is literally the first thing in the file (after only whitespace). If the source document has any preamble before the H1 — most commonly a YAML frontmatter block (---\ntitle: ...\n---\n# Title), which is a very plausible shape for 'a hand-written doc' or another spec tool's output that this feature is explicitly designed to support — the regex fails to match, the H1 line is not stripped from rawBody, and the title ends up rendered twice in the generated task file (once from lines.push(\# ${plan.title...}`)and once inside the embedded verbatim body). Verified by reproduction: a raw doc with a---/frontmatter block before # Checkout flowplus an unrecognised section produces# Checkout flow` twice in the output.
Suggestion: Strip the title line by locating and removing the first line that actually matched as
titleduringsplitSections(e.g., track its line index, or replace the first occurrence of^#\s+<escaped title>\s*$scanned line-by-line) rather than anchoring the regex to the start of the whole string. Alternatively, reconstruct rawBody by re-joiningraw.split('\n')after removing the specific line index recorded when the title was parsed.
| 'files', | ||
| ], | ||
| verification: ['verification', 'how to verify', 'test plan', 'tests', 'acceptance criteria'], | ||
| verification: [ |
There was a problem hiding this comment.
[SUGGESTION] The new verification hint 'testing' matches via key.startsWith('testing '), so a heading like ## Testing infrastructure notes (developer commentary about test setup, not verification steps for the task) would be pulled into the '## How to verify' section. This is a broad generic hint added alongside more specific ones, and could occasionally pull in dev-notes rather than actual acceptance/verification steps.
Suggestion: Consider it acceptable as a heuristic, but if false positives show up in practice, narrow the hint to exact
'testing'(drop thekey.startsWith(hint + ' ')prefix match for this specific generic term) so it only fires on a bare## Testingheading.
freema
left a comment
There was a problem hiding this comment.
CodeForge Review
Verdict: approve | Score: 9/10
Well-scoped, well-tested bug fix: from-plan now embeds unrecognised plan/spec shapes verbatim instead of silently reducing them to a placeholder skeleton, and SECTION_HINTS gained common spec-tooling headings. Logic (fallback trigger condition, H1-strip regex, hint word-boundary matching) checked out correct; new tests for both the pass-through path and the new hint mappings pass. Docs (README/CHANGELOG) and version bumps are consistent with the change. No correctness, security, or performance issues found in the diff.
Reviewed by CodeForge
Summary
Addresses the technical half of #16 (integration with other spec/plan tooling).
buildTaskContent()promised a graceful pass-through (lib/plan.mjsmodule docs) but had none — a plan whose headings didn't matchSECTION_HINTSproduced a task file of four placeholders, silently discarding the whole plan body. Exactly the scenario hit when feeding files from other spec/plan plugins (Superpowers, GSD, hand-written specs).Task specificationheading (leading H1 deduped, default How-to-verify and the guardrail block still appended).SECTION_HINTS:overview,problem statement,requirements,design,spec,tasks,steps,testing,validation,success criterianow map onto the proper task sections, so common spec formats convert structurally instead of falling back./cursor:from-planaccepts any absolute/relative path (this always worked viaresolvePlanPath, but was undocumented), plus the/cursor:delegate "@spec.md"direct route.Test plan
Overview/Requirements/Testing).npm test: 125/125 green.npm run lint+npm run typecheck: clean.Closes the defect part of #16; the discussion reply follows on the issue.