Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion commands/gsd/plan-phase.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: gsd:plan-phase
description: Create detailed phase plan (PLAN.md) with verification loop
argument-hint: "[phase] [--auto] [--research] [--skip-research] [--gaps] [--skip-verify] [--prd <file>] [--reviews]"
argument-hint: "[phase] [--auto] [--research] [--skip-research] [--gaps] [--skip-verify] [--prd <file>] [--reviews] [--text]"
agent: gsd-planner
allowed-tools:
- Read
Expand Down Expand Up @@ -36,6 +36,7 @@ Phase number: $ARGUMENTS (optional — auto-detects next unplanned phase if omit
- `--skip-verify` — Skip verification loop
- `--prd <file>` — Use a PRD/acceptance criteria file instead of discuss-phase. Parses requirements into CONTEXT.md automatically. Skips discuss-phase entirely.
- `--reviews` — Replan incorporating cross-AI review feedback from REVIEWS.md (produced by `/gsd:review`)
- `--text` — Use plain-text numbered lists instead of TUI menus (required for `/rc` remote sessions)

Normalize phase input in step 2 before any directory lookups.
</context>
Expand Down
1 change: 1 addition & 0 deletions get-shit-done/bin/lib/init.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ function cmdInitPlanPhase(cwd, phase, raw) {
plan_checker_enabled: config.plan_checker,
nyquist_validation_enabled: config.nyquist_validation,
commit_docs: config.commit_docs,
text_mode: config.text_mode,

// Phase info
phase_found: !!phaseInfo,
Expand Down
47 changes: 42 additions & 5 deletions get-shit-done/workflows/plan-phase.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init plan-phase "$PH
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
```

Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `nyquist_validation_enabled`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_reviews`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`, `phase_req_ids`.
Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `nyquist_validation_enabled`, `commit_docs`, `text_mode`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_reviews`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`, `phase_req_ids`.

**File paths (for <files_to_read> blocks):** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`, `verification_path`, `uat_path`, `reviews_path`. These are null if files don't exist.

**If `planning_exists` is false:** Error — run `/gsd:new-project` first.

## 2. Parse and Normalize Arguments

Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--prd <filepath>`, `--reviews`).
Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--prd <filepath>`, `--reviews`, `--text`).

Set `TEXT_MODE=true` if `--text` is present in $ARGUMENTS OR `text_mode` from init JSON is `true`. When `TEXT_MODE` is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for Claude Code remote sessions (`/rc` mode) where TUI menus don't work through the Claude App.

Extract `--prd <filepath>` from $ARGUMENTS. If present, set PRD_FILE to the filepath.

Expand Down Expand Up @@ -192,7 +194,20 @@ Read discuss mode for context gate label:
DISCUSS_MODE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
```

Use AskUserQuestion:
If `TEXT_MODE` is true, present as a plain-text numbered list:
```
No CONTEXT.md found for Phase {X}. Plans will use research and requirements only — your design preferences won't be included.

1. Continue without context — Plan using research + requirements only
[If DISCUSS_MODE is "assumptions":]
2. Gather context (assumptions mode) — Analyze codebase and surface assumptions before planning
[If DISCUSS_MODE is "discuss" or unset:]
2. Run discuss-phase first — Capture design decisions before planning

Enter number:
```

Otherwise use AskUserQuestion:
- header: "No context"
- question: "No CONTEXT.md found for Phase {X}. Plans will use research and requirements only — your design preferences won't be included. Continue or capture context first?"
- options:
Expand Down Expand Up @@ -225,6 +240,17 @@ If "Run discuss-phase first":
**If no explicit flag (`--research` or `--skip-research`) and not `--auto`:**
Ask the user whether to research, with a contextual recommendation based on the phase:

If `TEXT_MODE` is true, present as a plain-text numbered list:
```
Research before planning Phase {X}: {phase_name}?

1. Research first (Recommended) — Investigate domain, patterns, and dependencies before planning. Best for new features, unfamiliar integrations, or architectural changes.
2. Skip research — Plan directly from context and requirements. Best for bug fixes, simple refactors, or well-understood tasks.

Enter number:
```

Otherwise use AskUserQuestion:
```
AskUserQuestion([
{
Expand Down Expand Up @@ -359,7 +385,18 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)

**If UI-SPEC.md missing AND `UI_GATE_CFG` is `true`:**

Use AskUserQuestion:
If `TEXT_MODE` is true, present as a plain-text numbered list:
```
Phase {N} has frontend indicators but no UI-SPEC.md. Generate a design contract before planning?

1. Generate UI-SPEC first — Run /gsd:ui-phase {N} then re-run /gsd:plan-phase {N}
2. Continue without UI-SPEC
3. Not a frontend phase

Enter number:
```

Otherwise use AskUserQuestion:
- header: "UI Design Contract"
- question: "Phase {N} has frontend indicators but no UI-SPEC.md. Generate a design contract before planning?"
- options:
Expand Down Expand Up @@ -667,7 +704,7 @@ Options:
3. Proceed anyway — accept coverage gaps
```

Use AskUserQuestion to present the options.
If `TEXT_MODE` is true, present as a plain-text numbered list (options already shown in the block above). Otherwise use AskUserQuestion to present the options.

## 14. Present Final Status

Expand Down
25 changes: 25 additions & 0 deletions tests/discuss-mode.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ describe('workflow.discuss_mode config', () => {
assert.ok(workflow.includes('--text'), 'should handle --text flag');
});

test('plan-phase workflow references text_mode', () => {
const planPhase = fs.readFileSync(
path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md'), 'utf8'
);
assert.ok(planPhase.includes('text_mode'), 'plan-phase workflow should reference text_mode');
assert.ok(planPhase.includes('TEXT_MODE'), 'plan-phase workflow should use TEXT_MODE variable');
assert.ok(planPhase.includes('--text'), 'plan-phase workflow should handle --text flag');
});

test('plan-phase command argument-hint includes --text', () => {
const command = fs.readFileSync(
path.join(__dirname, '..', 'commands', 'gsd', 'plan-phase.md'), 'utf8'
);
assert.ok(command.includes('--text'), 'argument-hint should include --text flag');
});

test('plan-phase init exposes text_mode in workflow flags', () => {
const initSrc = fs.readFileSync(
path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'init.cjs'), 'utf8'
);
// The cmdInitPlanPhase result object must include text_mode
const planPhaseBlock = initSrc.slice(initSrc.indexOf('function cmdInitPlanPhase'));
assert.ok(planPhaseBlock.includes('text_mode: config.text_mode'), 'init plan-phase must expose text_mode');
});

test('progress workflow references discuss_mode', () => {
const progress = fs.readFileSync(
path.join(__dirname, '..', 'get-shit-done', 'workflows', 'progress.md'), 'utf8'
Expand Down
19 changes: 19 additions & 0 deletions tests/init.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ describe('init commands', () => {
assert.strictEqual(output.uat_path, '.planning/phases/03-api/03-UAT.md');
});

test('init plan-phase exposes text_mode from config (defaults false)', () => {
const result = runGsdTools('init plan-phase 03', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.text_mode, false, 'text_mode should default to false');
});

test('init plan-phase exposes text_mode true when set in config', () => {
const configPath = path.join(tmpDir, '.planning', 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
config.workflow = { ...(config.workflow || {}), text_mode: true };
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));

const result = runGsdTools('init plan-phase 03', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.text_mode, true, 'text_mode should reflect config value');
});

test('init progress returns file paths', () => {
const result = runGsdTools('init progress', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
Expand Down