diff --git a/commands/gsd/import.md b/commands/gsd/import.md new file mode 100644 index 000000000..e8fe2d2c1 --- /dev/null +++ b/commands/gsd/import.md @@ -0,0 +1,36 @@ +--- +name: gsd:import +description: Ingest external plans with conflict detection against project decisions before writing anything. +argument-hint: "--from " +allowed-tools: + - Read + - Write + - Edit + - Bash + - Glob + - Grep + - AskUserQuestion + - Task +--- + + +Import external plan files into the GSD planning system with conflict detection against PROJECT.md decisions. + +- **--from**: Import an external plan file, detect conflicts, write as GSD PLAN.md, validate via gsd-plan-checker. + +Future: `--prd` mode for PRD extraction is planned for a follow-up PR. + + + +@~/.claude/get-shit-done/workflows/import.md +@~/.claude/get-shit-done/references/ui-brand.md +@~/.claude/get-shit-done/references/gate-prompts.md + + + +$ARGUMENTS + + + +Execute the import workflow end-to-end. + diff --git a/get-shit-done/workflows/import.md b/get-shit-done/workflows/import.md new file mode 100644 index 000000000..f3fe0f807 --- /dev/null +++ b/get-shit-done/workflows/import.md @@ -0,0 +1,274 @@ +# Import Workflow + +External plan ingestion with conflict detection and agent delegation. + +- **--from**: Import external plan → conflict detection → write PLAN.md → validate via gsd-plan-checker + +Future: `--prd` mode (PRD extraction into PROJECT.md + REQUIREMENTS.md + ROADMAP.md) is planned for a follow-up PR. + +--- + + + +Display the stage banner: + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► IMPORT +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + + + + + +Parse `$ARGUMENTS` to determine the execution mode: + +- If `--from` is present: extract FILEPATH (the next token after `--from`), set MODE=plan +- If `--prd` is present: display message that `--prd` is not yet implemented and exit: + ``` + GSD > --prd mode is planned for a future release. Use --from to import plan files. + ``` +- If neither flag is found: display usage and exit: + +``` +Usage: /gsd-import --from + + --from Import an external plan file into GSD format +``` + +**Validate the file path:** + +Verify the path does not contain traversal sequences and the file exists: + +```bash +case "{FILEPATH}" in + *..* ) echo "SECURITY_ERROR: path contains traversal sequence"; exit 1 ;; +esac +test -f "{FILEPATH}" || echo "FILE_NOT_FOUND" +``` + +If FILE_NOT_FOUND: display error and exit: + +``` +╔══════════════════════════════════════════════════════════════╗ +║ ERROR ║ +╚══════════════════════════════════════════════════════════════╝ + +File not found: {FILEPATH} + +**To fix:** Verify the file path and try again. +``` + + + +--- + +## Path A: MODE=plan (--from) + + + +Load project context for conflict detection: + +1. Read `.planning/ROADMAP.md` — extract phase structure, phase numbers, dependencies +2. Read `.planning/PROJECT.md` — extract project constraints, tech stack, scope boundaries. + **If PROJECT.md does not exist:** skip constraint checks that rely on it and display: + ``` + GSD > Note: No PROJECT.md found. Conflict checks against project constraints will be skipped. + ``` +3. Read `.planning/REQUIREMENTS.md` — extract existing requirements for overlap and contradiction checks. + **If REQUIREMENTS.md does not exist:** skip requirement conflict checks and continue. +4. Glob for all CONTEXT.md files across phase directories: + ```bash + find .planning/phases/ -name "*-CONTEXT.md" -o -name "CONTEXT.md" 2>/dev/null + ``` + Read each CONTEXT.md found — extract locked decisions (any decision in a `` block) + +Store loaded context for conflict detection in the next step. + + + + + +Read the imported file at FILEPATH. + +Determine the format: +- **GSD PLAN.md format**: Has YAML frontmatter with `phase:`, `plan:`, `type:` fields +- **Freeform document**: Any other format (markdown spec, design doc, task list, etc.) + +Extract from the imported content: +- **Phase target**: Which phase this plan belongs to (from frontmatter or inferred from content) +- **Plan objectives**: What the plan aims to accomplish +- **Tasks listed**: Individual work items described in the plan +- **Files modified**: Any files mentioned as targets +- **Dependencies**: Any referenced prerequisites + + + + + +Run conflict checks against the loaded project context. Output as a plain-text conflict report using [BLOCKER], [WARNING], and [INFO] labels. Do NOT use markdown tables (no `|---|` format). + +### BLOCKER checks (any one prevents import): + +- Plan targets a phase number that does not exist in ROADMAP.md → [BLOCKER] +- Plan specifies a tech stack that contradicts PROJECT.md constraints → [BLOCKER] +- Plan contradicts a locked decision in any CONTEXT.md `` block → [BLOCKER] +- Plan contradicts an existing requirement in REQUIREMENTS.md → [BLOCKER] + +### WARNING checks (user confirmation required): + +- Plan partially overlaps existing requirement coverage in REQUIREMENTS.md → [WARNING] +- Plan has `depends_on` referencing plans that are not yet complete → [WARNING] +- Plan modifies files that overlap with existing incomplete plans → [WARNING] +- Plan phase number conflicts with existing phase numbering in ROADMAP.md → [WARNING] + +### INFO checks (informational, no action needed): + +- Plan uses a library not currently in the project tech stack → [INFO] +- Plan adds a new phase to the ROADMAP.md structure → [INFO] + +Display the full Conflict Detection Report: + +``` +## Conflict Detection Report + +### BLOCKERS ({N}) + +[BLOCKER] {Short title} + Found: {what the imported plan says} + Expected: {what project context requires} + → {Specific action to resolve} + +### WARNINGS ({N}) + +[WARNING] {Short title} + Found: {what was detected} + Impact: {what could go wrong} + → {Suggested action} + +### INFO ({N}) + +[INFO] {Short title} + Note: {relevant information} +``` + +**If any [BLOCKER] exists:** + +Display: +``` +GSD > BLOCKED: {N} blockers must be resolved before import can proceed. +``` + +Exit WITHOUT writing any files. This is the safety gate — no PLAN.md is written when blockers exist. + +**If only WARNINGS and/or INFO (no blockers):** + +Ask via AskUserQuestion using the approve-revise-abort pattern: +- question: "Review the warnings above. Proceed with import?" +- header: "Approve?" +- options: Approve | Abort + +If user selects "Abort": exit cleanly with message "Import cancelled." + + + + + +Convert the imported content to GSD PLAN.md format. + +Ensure the PLAN.md has all required frontmatter fields: +```yaml +--- +phase: "{NN}-{slug}" +plan: "{NN}-{MM}" +type: "feature|refactor|config|test|docs" +wave: 1 +depends_on: [] +files_modified: [] +autonomous: true +must_haves: + truths: [] + artifacts: [] +--- +``` + +**Reject PBR naming conventions in source content:** +If the imported plan references PBR plan naming (e.g., `PLAN-01.md`, `plan-01.md`), rename all references to GSD `{NN}-{MM}-PLAN.md` convention during conversion. + +Apply GSD naming convention for the output filename: +- Format: `{NN}-{MM}-PLAN.md` (e.g., `04-01-PLAN.md`) +- NEVER use `PLAN-01.md`, `plan-01.md`, or any other format +- NN = phase number (zero-padded), MM = plan number within the phase (zero-padded) + +Determine the target directory: +``` +.planning/phases/{NN}-{slug}/ +``` + +If the directory does not exist, create it: +```bash +mkdir -p ".planning/phases/{NN}-{slug}/" +``` + +Write the PLAN.md file to the target directory. + + + + + +Delegate validation to gsd-plan-checker: + +``` +Task({ + subagent_type: "gsd-plan-checker", + prompt: "Validate: .planning/phases/{phase}/{plan}-PLAN.md — check frontmatter completeness, task structure, and GSD conventions. Report any issues." +}) +``` + +If the checker returns errors: +- Display the errors to the user +- Ask the user to resolve issues before the plan is considered imported +- Do not delete the written file — the user can fix and re-validate manually + +If the checker returns clean: +- Display: "Plan validation passed" + + + + + +Update `.planning/ROADMAP.md` to reflect the new plan: +- Add the plan to the Plans list under the correct phase section +- Include the plan name and description + +Update `.planning/STATE.md` if appropriate (e.g., increment total plan count). + +Commit the imported plan and updated files: +```bash +node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs({phase}): import plan from {basename FILEPATH}" --files .planning/phases/{phase}/{plan}-PLAN.md .planning/ROADMAP.md +``` + +Display completion: +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► IMPORT COMPLETE +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +Show: plan filename written, phase directory, validation result, next steps. + + + +--- + +## Anti-Patterns + +Do NOT: +- Use markdown tables (`|---|`) in the conflict detection report — use plain-text [BLOCKER]/[WARNING]/[INFO] labels +- Write PLAN.md files as `PLAN-01.md` or `plan-01.md` — always use `{NN}-{MM}-PLAN.md` +- Use `pbr:plan-checker` or `pbr:planner` — use `gsd-plan-checker` and `gsd-planner` +- Write `.planning/.active-skill` — this is a PBR pattern with no GSD equivalent +- Reference `pbr-tools`, `pbr:`, or `PLAN-BUILD-RUN` anywhere +- Write any PLAN.md file when blockers exist — the safety gate must hold +- Skip path validation on the --from file argument diff --git a/tests/import-command.test.cjs b/tests/import-command.test.cjs new file mode 100644 index 000000000..ea41e2d65 --- /dev/null +++ b/tests/import-command.test.cjs @@ -0,0 +1,121 @@ +/** + * Import Command Tests — import-command.test.cjs + * + * Structural assertions for the /gsd-import command and workflow files. + */ + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const CMD_PATH = path.join(__dirname, '..', 'commands', 'gsd', 'import.md'); +const WF_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'import.md'); + +// ─── File Existence ──────────────────────────────────────────────────────────── + +describe('import command file structure', () => { + test('command file exists', () => { + assert.ok(fs.existsSync(CMD_PATH), 'commands/gsd/import.md should exist'); + }); + + test('workflow file exists', () => { + assert.ok(fs.existsSync(WF_PATH), 'get-shit-done/workflows/import.md should exist'); + }); +}); + +// ─── Command Frontmatter ─────────────────────────────────────────────────────── + +describe('import command frontmatter', () => { + const content = fs.readFileSync(CMD_PATH, 'utf-8'); + + test('has name field', () => { + assert.match(content, /^name:\s*gsd:import$/m); + }); + + test('has description field', () => { + assert.match(content, /^description:\s*.+$/m); + }); + + test('has argument-hint with --from', () => { + assert.match(content, /^argument-hint:.*--from/m); + }); +}); + +// ─── Command References Workflow ─────────────────────────────────────────────── + +describe('import command references', () => { + const content = fs.readFileSync(CMD_PATH, 'utf-8'); + + test('references the import workflow', () => { + assert.ok( + content.includes('@~/.claude/get-shit-done/workflows/import.md'), + 'command should reference the workflow via @~/.claude/get-shit-done/workflows/import.md' + ); + }); +}); + +// ─── Workflow Content ────────────────────────────────────────────────────────── + +describe('import workflow content', () => { + const content = fs.readFileSync(WF_PATH, 'utf-8'); + + test('contains --from mode handling', () => { + assert.ok( + content.includes('--from'), + 'workflow should contain --from mode handling' + ); + }); + + test('does NOT contain --prd implementation', () => { + // --prd should be mentioned as deferred/future only, not implemented + assert.ok( + content.includes('--prd'), + 'workflow should mention --prd exists' + ); + assert.ok( + content.includes('not yet implemented') || content.includes('follow-up PR') || content.includes('future release'), + 'workflow should defer --prd to a future release' + ); + // Should not have a full "Path B: MODE=prd" implementation section + assert.ok( + !content.includes('## Path B: MODE=prd'), + 'workflow should NOT have a Path B implementation for --prd' + ); + }); + + test('references path validation for --from argument', () => { + // After fix: inline path check instead of security.cjs CLI invocation + assert.ok( + content.includes('traversal') || content.includes('validatePath') || content.includes('..'), + 'workflow should validate the file path' + ); + }); + + test('includes REQUIREMENTS.md in conflict detection context loading', () => { + assert.ok( + content.includes('REQUIREMENTS.md'), + 'workflow should load REQUIREMENTS.md for conflict detection' + ); + }); + + test('includes BLOCKER/WARNING/INFO conflict severity model', () => { + assert.ok(content.includes('[BLOCKER]'), 'workflow should include BLOCKER severity'); + assert.ok(content.includes('[WARNING]'), 'workflow should include WARNING severity'); + assert.ok(content.includes('[INFO]'), 'workflow should include INFO severity'); + }); + + test('includes plan-checker validation gate', () => { + assert.ok( + content.includes('gsd-plan-checker'), + 'workflow should delegate validation to gsd-plan-checker' + ); + }); + + test('no-args usage display is present', () => { + assert.ok( + content.includes('Usage: /gsd-import'), + 'workflow should display usage when no arguments provided' + ); + }); +});