Help us improve
Share bugs, ideas, or general feedback.
From prd2impl
Heterogeneous document ingestion — parse multiple human-authored Markdown files (gap analyses, design specs, plans, PRDs) into the same YAML artifacts as skill-1/skill-2, plus a task-hints.yaml that preserves human design decisions. Use when the user says 'ingest docs', 'read my markdown files', 'import gap analysis', or provides multiple MD files as starting point instead of a formal PRD.
npx claudepluginhub ezagent42/prd2impl --plugin prd2implHow this skill is triggered — by the user, by Claude, or both
Slash command
/prd2impl:skill-0-ingestThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<SUBAGENT-STOP>
lib/cross-validator.mdlib/gap-extractor.mdlib/plan-parser.mdlib/prd-extractor.mdlib/role-detector.mdlib/spec-extractor.mdschemas/gap-analysis.example.yamlschemas/task-hints.example.yamlschemas/task-hints.schema.yamltemplates/role-confirmation.mdtests/expected/admin-v2-p1.task-hints.yamltests/expected/clear-gap.gap-analysis.yamltests/expected/clear-plan.prd-structure.yamltests/expected/clear-prd.prd-structure.yamltests/expected/clear-spec.prd-structure.yamltests/expected/clear-spec.task-hints.yamltests/expected/design-spec-extraction/deps-bullets.prd-structure.yamltests/expected/design-spec-extraction/deps-table.prd-structure.yamltests/expected/design-spec-extraction/sparse-opt-in.prd-structure.yamltests/expected/design-spec-full.prd-structure.yamlGuides technical evaluation of code review feedback: read fully, restate for understanding, verify against codebase, respond with reasoning or pushback before implementing.
Share bugs, ideas, or general feedback.
Convert multiple human-authored Markdown files into the YAML artifacts that skill-3-task-gen and the rest of the prd2impl pipeline expect. This is Entry B — an alternative to running /prd-analyze (Entry A) when the project already has hand-written gap analyses, design specs, or plans.
/ingest-docs [file1.md] [file2.md] ...--tag role=path overrides alongside file paths--synthesize-user-stories flag (opt-in LLM synthesis of user_stories for design-spec role — see §Inputs)Required: One or more MD file paths
Optional: --tag <role>=<path> overrides (force a role instead of auto-detecting)
gap, design-spec, plan, prd, user-stories--tag design-spec=a.md --tag gap=b.mdOptional: --synthesize-user-stories — opt-in flag. When passed and any input file is classified as role=design-spec, run the LLM synthesis pass described in lib/prd-extractor.md §user_stories LLM synthesis. Default: off, which preserves existing user_stories: [] behavior for design-spec.
Three YAML files written to {plans_dir}/{date}-*.yaml:
| File | Produced when | Consumed by |
|---|---|---|
{date}-prd-structure.yaml | Any prd, plan, user-stories, or design-spec MD found | skill-3-task-gen |
{date}-gap-analysis.yaml | Any gap MD found | skill-3-task-gen |
{date}-task-hints.yaml | Any design-spec or plan MD found | skill-3-task-gen (optional) |
All files carry source_type: "ingested" to distinguish them from skill-1/skill-2 outputs.
Read: lib/role-detector.md — follow it exactly for this phase.
Steps:
--tag overrides (force confidence=100 for those files).templates/role-confirmation.md.[HUMAN REVIEW CHECKPOINT 1]
Present the table and wait. Accept commands:
ok → proceed to Phase 2override N <role> → update role for row N; re-display table; ask againdrop N → remove file from processing list; re-display; ask againIf all remaining files are unknown → abort:
ERROR: All input files classified as 'unknown'. Re-run with --tag role=path to specify roles.
Important: Run extractors in the order: gap → spec → prd. This order ensures gap_analysis is populated before spec-extractor's cross-references need it.
If any files have detected_role: gap:
lib/gap-extractor.md — follow it exactly.gap_analysis dict in memory. Extracted {N} gaps (P0={n0}, P1={n1}, P2={n2}) from {files}If any files have detected_role: design-spec:
lib/spec-extractor.md — follow it exactly for task-hints extraction.lib/prd-extractor.md §Extraction: role=design-spec — follow it for modules/nfrs/constraints extraction.task_hints dict in memory (from spec-extractor).prd_structure dict in memory (from prd-extractor, partial: modules/nfrs/constraints only). Extracted {F} file_changes, {S} steps, {G} non_goals (spec) + {M} modules, {N} nfrs, {C} constraints (design) from {files}If any files have detected_role: plan:
lib/spec-extractor.md — Phase 0 detects writing-plans format and delegates to lib/plan-parser.md. For writing-plans-format md (role-detector Signal 0 hit), the output is task_hints.tasks[] (Task → Files → Steps hierarchy preserved verbatim). For legacy plans (no writing-plans header), the legacy step-extraction flow (Steps 1-7) runs.lib/prd-extractor.md §Extraction: role=plan for module extraction.task_hints.tasks[] (when present), populate source_plan_path with the input file's repo-relative path. The parser leaves this null; only the caller knows the path.task_hints; merge module data into prd_structure. Extracted {T} tasks ({S} total steps) (plan-passthrough) + {M} modules from {files} Extracted {S} steps (plan-legacy) + {M} modules from {files}If any files have detected_role: prd or detected_role: user-stories:
lib/prd-extractor.md — follow the appropriate role routing.prd_structure dict in memory. Extracted {M} modules, {U} user stories, {N} NFRs from {files}Read: lib/cross-validator.md — follow it exactly for this phase.
Steps:
[HUMAN REVIEW CHECKPOINT 2] (only if warnings exist)
Cross-validation complete. {F} fatal errors, {W} warnings.
{warning_table}
Proceed despite warnings? (y/n):
n → abort (no files written)y → proceed to Phase 4If no warnings (and no fatals): skip checkpoint, proceed automatically.
Path resolution: Before constructing any output path, resolve
{plans_dir}perlib/plans-dir-resolver.md. All paths below use{plans_dir}as the base directory.
Before writing output files, verify extraction completeness against source documents:
Collect source item counts — prefer using the per-role extraction counts already reported by Phase 2:
Extracted {N} gaps (P0={n0}, P1={n1}, P2={n2}) from {files}. Use {N} as the extracted gap count.Compare extraction counts against what ended up in the in-memory YAML dicts:
task_hints.file_changes count vs Phase 2b reported file_changes counttask_hints.tasks is non-empty, use len(tasks) vs Phase 2b reported task count; skip the file_changes comparisongap_analysis.gaps count vs Phase 2a reported gap countprd_structure.modules count vs Phase 2c reported module countIf any ratio is < 70% (and source count ≥ 3 to avoid false positives on tiny docs), print a WARNING:
⚠️ Extraction completeness warning:
file_changes: 6 extracted from 16 detected in source (37%) — design-spec may have non-standard formatting
modules: 4 extracted from 5 detected in source (80%)
Consider manual review of the output YAMLs.
If all ratios ≥ 70%: print nothing (clean extraction, no noise).
This check is INFORMATIONAL only — never blocks the pipeline.
{plans_dir}/{date}-gap-analysis.yaml (if gap_analysis populated)
{plans_dir}/{date}-prd-structure.yaml (if prd_structure populated)
{plans_dir}/{date}-task-hints.yaml (if task_hints populated)
Where {date} = today's date in YYYY-MM-DD format.
If a file already exists at the target path: print a diff-summary line (informational, non-blocking) and overwrite.
Required diff-summary format (enforced — tests match on this):
Overwriting {path} ({old_count} → {new_count} {entity}[, {old_N} → {new_N} {entity2}]...)
Overwriting followed by the absolute/relative path.{old_count} → {new_count} {entity} delta inside parentheses, using the → arrow character (U+2192).gap-analysis.yaml → gaps (primary), P0 / P1 / P2 (secondary)prd-structure.yaml → modules (primary), user_stories / constraints (secondary)task-hints.yaml → file_changes or tasks (primary, whichever is non-empty), steps / non_goals (secondary). When source role is plan AND writing-plans format (plan-passthrough path), tasks count is primary; the secondary delta is the sum of len(t.steps) for t in tasks[].old_count is 0 (file did not exist before overwrite — shouldn't reach this branch, but defensive), skip the diff and print Writing {path} ({new_count} {entity}) instead.Required downstream-invalidation hint (print verbatim after the overwrite):
Note: tasks.yaml for this plans_dir already exists and may now be inconsistent. Re-run /task-gen to regenerate.
Only print the hint if {plans_dir}/tasks.yaml exists at overwrite time; otherwise skip it (no downstream to invalidate yet).
Users who want to keep both versions should run with a different --plans-dir.
Worked example — re-running ingest on an updated PRD:
Overwriting docs/plans/m2/2026-04-20-prd-structure.yaml (12 → 13 modules, 47 → 49 user_stories)
Overwriting docs/plans/m2/2026-04-20-gap-analysis.yaml (17 → 18 gaps, 6 → 7 P0)
Note: tasks.yaml for this plans_dir already exists and may now be inconsistent. Re-run /task-gen to regenerate.
Before writing prd-structure.yaml, if prd_structure.source_role == "design-spec", add an extraction key recording which fields came from which code path:
extraction:
regex_fields: [modules, nfrs, constraints, external_deps]
llm_fields: [] # default
If the --synthesize-user-stories flag was passed AND the LLM synthesis actually produced ≥1 valid user story: append user_stories to llm_fields:
extraction:
regex_fields: [modules, nfrs, constraints, external_deps]
llm_fields: [user_stories]
If the flag was passed but the LLM pass produced 0 valid stories (trigger fired but personas all failed verbatim check, or LLM call failed), keep llm_fields: [] — downstream treats the output uniformly whether the flag was passed or not.
For source_role in {prd, plan, user-stories}: do NOT add the extraction key — all their fields come from regex, no provenance tracking needed.
Downstream skills (skill-3-task-gen, skill-12-contract-check) read prd_structure.extraction to decide where to apply fuzzy matching vs. strict validation.
Before writing each YAML file, compute and add a source_hash field:
For each source file that contributed to a YAML output, run:
git hash-object "{source_file_path}"
Add to each YAML dict before writing:
source_hash:
path/to/source.md: "<git-hash-object-hex>"
another/source.md: "<git-hash-object-hex>"
This applies to gap-analysis.yaml, prd-structure.yaml, and task-hints.yaml (any YAML produced from source files).
Write each populated YAML dict to its target path. Validate that:
gap_analysis.yaml begins with gap_analysis: keyprd-structure.yaml begins with prd_structure: keytask-hints.yaml begins with task_hints: key─────────────────────────────────────────────────────
skill-0-ingest complete
─────────────────────────────────────────────────────
Input files: {N} processed, {D} dropped, {U} unknown/skipped
Outputs written:
{plans_dir}/{date}-gap-analysis.yaml {N_gaps} gaps (P0={n0}, P1={n1}, P2={n2})
{plans_dir}/{date}-prd-structure.yaml {N_mods} modules, {N_us} user stories
{plans_dir}/{date}-task-hints.yaml {N_fc} file_changes, {N_steps} steps
Cross-validation: {W} warnings (see above), 0 fatals
─────────────────────────────────────────────────────
[HUMAN REVIEW CHECKPOINT 3]
Review the output YAMLs above. See the footer at the end of this output for next steps.
Ready to proceed? (y/n — or adjust the YAML files first)
| Scenario | Response |
|---|---|
| Input file not found | ERROR: file not found: {path} → abort before Phase 1 |
| All files unknown after Phase 1 | ERROR: all unknown → abort with --tag suggestion |
| One file fails extraction | Skip file + warning; continue with others |
| Fatal cross-validation conflict | Abort Phase 4; print conflict pairs |
| Same-day re-run of /ingest-docs | Overwrite with diff-summary log (no suffix; use different --plans-dir for coexistence) |
All extraction logic lives in lib/. Do not duplicate logic here — read the lib file and follow it.
| Phase | Read this lib file |
|---|---|
| 1 | lib/role-detector.md (Signal 0 = writing-plans header override → role=plan / confidence=100) |
| 2a | lib/gap-extractor.md |
| 2b | lib/spec-extractor.md (Phase 0 delegates writing-plans md to lib/plan-parser.md) |
| 2b plan-passthrough | lib/plan-parser.md (used when role=plan AND writing-plans header present) |
| 2c | lib/prd-extractor.md |
| 3 | lib/cross-validator.md |
───────────────────────────────────────────────────── ⬆ /ingest-docs complete ─────────────────────────────────────────────────────
📋 Next: /task-gen — Generate tasks from ingested YAML artifacts /gap-scan — Verify codebase coverage against PRD (optional, for non-greenfield) ─────────────────────────────────────────────────────