Merge pull request #1181 from diegomarino/feat/materialize-new-project-config
feat: materialize full config on new-project initialization
This commit is contained in:
@@ -188,7 +188,7 @@ async function main() {
|
||||
const command = args[0];
|
||||
|
||||
if (!command) {
|
||||
error('Usage: gsd-tools <command> [args] [--raw] [--cwd <path>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, init');
|
||||
error('Usage: gsd-tools <command> [args] [--raw] [--cwd <path>]\nCommands: state, resolve-model, find-phase, commit, verify-summary, verify, frontmatter, template, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-new-project, init');
|
||||
}
|
||||
|
||||
// Multi-repo guard: resolve project root for commands that read/write .planning/.
|
||||
@@ -449,6 +449,11 @@ async function main() {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'config-new-project': {
|
||||
config.cmdConfigNewProject(cwd, args[1], raw);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'history-digest': {
|
||||
commands.cmdHistoryDigest(cwd, raw);
|
||||
break;
|
||||
|
||||
@@ -16,10 +16,12 @@ const VALID_CONFIG_KEYS = new Set([
|
||||
'search_gitignored', 'brave_search', 'firecrawl', 'exa_search',
|
||||
'workflow.research', 'workflow.plan_check', 'workflow.verifier',
|
||||
'workflow.nyquist_validation', 'workflow.ui_phase', 'workflow.ui_safety_gate',
|
||||
'workflow.auto_advance', 'workflow.node_repair', 'workflow.node_repair_budget',
|
||||
'workflow.text_mode',
|
||||
'workflow._auto_chain_active',
|
||||
'git.branching_strategy', 'git.phase_branch_template', 'git.milestone_branch_template', 'git.quick_branch_template',
|
||||
'planning.commit_docs', 'planning.search_gitignored',
|
||||
'hooks.context_warnings',
|
||||
]);
|
||||
|
||||
const CONFIG_KEY_SUGGESTIONS = {
|
||||
@@ -35,6 +37,154 @@ function validateKnownConfigKeyPath(keyPath) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a fully-materialized config object for a new project.
|
||||
*
|
||||
* Merges (increasing priority):
|
||||
* 1. Hardcoded defaults — every key that loadConfig() resolves, plus mode/granularity
|
||||
* 2. User-level defaults from ~/.gsd/defaults.json (if present)
|
||||
* 3. userChoices — the settings the user explicitly selected during /gsd:new-project
|
||||
*
|
||||
* Uses the canonical `git` namespace for branching keys (consistent with VALID_CONFIG_KEYS
|
||||
* and the settings workflow). loadConfig() handles both flat and nested formats, so this
|
||||
* is backward-compatible with existing projects that have flat keys.
|
||||
*
|
||||
* Returns a plain object — does NOT write any files.
|
||||
*/
|
||||
function buildNewProjectConfig(userChoices) {
|
||||
const choices = userChoices || {};
|
||||
const homedir = require('os').homedir();
|
||||
|
||||
// Detect API key availability
|
||||
const braveKeyFile = path.join(homedir, '.gsd', 'brave_api_key');
|
||||
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || fs.existsSync(braveKeyFile));
|
||||
const firecrawlKeyFile = path.join(homedir, '.gsd', 'firecrawl_api_key');
|
||||
const hasFirecrawl = !!(process.env.FIRECRAWL_API_KEY || fs.existsSync(firecrawlKeyFile));
|
||||
const exaKeyFile = path.join(homedir, '.gsd', 'exa_api_key');
|
||||
const hasExaSearch = !!(process.env.EXA_API_KEY || fs.existsSync(exaKeyFile));
|
||||
|
||||
// Load user-level defaults from ~/.gsd/defaults.json if available
|
||||
const globalDefaultsPath = path.join(homedir, '.gsd', 'defaults.json');
|
||||
let userDefaults = {};
|
||||
try {
|
||||
if (fs.existsSync(globalDefaultsPath)) {
|
||||
userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, 'utf-8'));
|
||||
// Migrate deprecated "depth" key to "granularity"
|
||||
if ('depth' in userDefaults && !('granularity' in userDefaults)) {
|
||||
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
||||
userDefaults.granularity = depthToGranularity[userDefaults.depth] || userDefaults.depth;
|
||||
delete userDefaults.depth;
|
||||
try {
|
||||
fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8');
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore malformed global defaults
|
||||
}
|
||||
|
||||
const hardcoded = {
|
||||
model_profile: 'balanced',
|
||||
commit_docs: true,
|
||||
parallelization: true,
|
||||
search_gitignored: false,
|
||||
brave_search: hasBraveSearch,
|
||||
firecrawl: hasFirecrawl,
|
||||
exa_search: hasExaSearch,
|
||||
git: {
|
||||
branching_strategy: 'none',
|
||||
phase_branch_template: 'gsd/phase-{phase}-{slug}',
|
||||
milestone_branch_template: 'gsd/{milestone}-{slug}',
|
||||
quick_branch_template: null,
|
||||
},
|
||||
workflow: {
|
||||
research: true,
|
||||
plan_check: true,
|
||||
verifier: true,
|
||||
nyquist_validation: true,
|
||||
auto_advance: false,
|
||||
node_repair: true,
|
||||
node_repair_budget: 2,
|
||||
ui_phase: true,
|
||||
ui_safety_gate: true,
|
||||
text_mode: false,
|
||||
},
|
||||
hooks: {
|
||||
context_warnings: true,
|
||||
},
|
||||
};
|
||||
|
||||
// Three-level deep merge: hardcoded <- userDefaults <- choices
|
||||
return {
|
||||
...hardcoded,
|
||||
...userDefaults,
|
||||
...choices,
|
||||
git: {
|
||||
...hardcoded.git,
|
||||
...(userDefaults.git || {}),
|
||||
...(choices.git || {}),
|
||||
},
|
||||
workflow: {
|
||||
...hardcoded.workflow,
|
||||
...(userDefaults.workflow || {}),
|
||||
...(choices.workflow || {}),
|
||||
},
|
||||
hooks: {
|
||||
...hardcoded.hooks,
|
||||
...(userDefaults.hooks || {}),
|
||||
...(choices.hooks || {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Command: create a fully-materialized .planning/config.json for a new project.
|
||||
*
|
||||
* Accepts user-chosen settings as a JSON string (the keys the user explicitly
|
||||
* configured during /gsd:new-project). All remaining keys are filled from
|
||||
* hardcoded defaults and optional ~/.gsd/defaults.json.
|
||||
*
|
||||
* Idempotent: if config.json already exists, returns { created: false }.
|
||||
*/
|
||||
function cmdConfigNewProject(cwd, choicesJson, raw) {
|
||||
const configPath = path.join(cwd, '.planning', 'config.json');
|
||||
const planningDir = path.join(cwd, '.planning');
|
||||
|
||||
// Idempotent: don't overwrite existing config
|
||||
if (fs.existsSync(configPath)) {
|
||||
output({ created: false, reason: 'already_exists' }, raw, 'exists');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse user choices
|
||||
let userChoices = {};
|
||||
if (choicesJson && choicesJson.trim() !== '') {
|
||||
try {
|
||||
userChoices = JSON.parse(choicesJson);
|
||||
} catch (err) {
|
||||
error('Invalid JSON for config-new-project: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure .planning directory exists
|
||||
try {
|
||||
if (!fs.existsSync(planningDir)) {
|
||||
fs.mkdirSync(planningDir, { recursive: true });
|
||||
}
|
||||
} catch (err) {
|
||||
error('Failed to create .planning directory: ' + err.message);
|
||||
}
|
||||
|
||||
const config = buildNewProjectConfig(userChoices);
|
||||
|
||||
try {
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
output({ created: true, path: '.planning/config.json' }, raw, 'created');
|
||||
} catch (err) {
|
||||
error('Failed to write config.json: ' + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the config file exists (creates it if needed).
|
||||
*
|
||||
@@ -59,67 +209,10 @@ function ensureConfigFile(cwd) {
|
||||
return { created: false, reason: 'already_exists' };
|
||||
}
|
||||
|
||||
// Detect Brave Search API key availability
|
||||
const homedir = require('os').homedir();
|
||||
const braveKeyFile = path.join(homedir, '.gsd', 'brave_api_key');
|
||||
const hasBraveSearch = !!(process.env.BRAVE_API_KEY || fs.existsSync(braveKeyFile));
|
||||
|
||||
// Detect Firecrawl API key availability
|
||||
const firecrawlKeyFile = path.join(homedir, '.gsd', 'firecrawl_api_key');
|
||||
const hasFirecrawl = !!(process.env.FIRECRAWL_API_KEY || fs.existsSync(firecrawlKeyFile));
|
||||
|
||||
// Detect Exa API key availability
|
||||
const exaKeyFile = path.join(homedir, '.gsd', 'exa_api_key');
|
||||
const hasExaSearch = !!(process.env.EXA_API_KEY || fs.existsSync(exaKeyFile));
|
||||
|
||||
// Load user-level defaults from ~/.gsd/defaults.json if available
|
||||
const globalDefaultsPath = path.join(homedir, '.gsd', 'defaults.json');
|
||||
let userDefaults = {};
|
||||
try {
|
||||
if (fs.existsSync(globalDefaultsPath)) {
|
||||
userDefaults = JSON.parse(fs.readFileSync(globalDefaultsPath, 'utf-8'));
|
||||
// Migrate deprecated "depth" key to "granularity"
|
||||
if ('depth' in userDefaults && !('granularity' in userDefaults)) {
|
||||
const depthToGranularity = { quick: 'coarse', standard: 'standard', comprehensive: 'fine' };
|
||||
userDefaults.granularity = depthToGranularity[userDefaults.depth] || userDefaults.depth;
|
||||
delete userDefaults.depth;
|
||||
try {
|
||||
fs.writeFileSync(globalDefaultsPath, JSON.stringify(userDefaults, null, 2), 'utf-8');
|
||||
} catch { /* intentionally empty */ }
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Ignore malformed global defaults, fall back to hardcoded
|
||||
}
|
||||
|
||||
// Create default config (user-level defaults override hardcoded defaults)
|
||||
const hardcoded = {
|
||||
model_profile: 'balanced',
|
||||
commit_docs: true,
|
||||
search_gitignored: false,
|
||||
branching_strategy: 'none',
|
||||
phase_branch_template: 'gsd/phase-{phase}-{slug}',
|
||||
milestone_branch_template: 'gsd/{milestone}-{slug}',
|
||||
quick_branch_template: null,
|
||||
workflow: {
|
||||
research: true,
|
||||
plan_check: true,
|
||||
verifier: true,
|
||||
nyquist_validation: true,
|
||||
},
|
||||
parallelization: true,
|
||||
brave_search: hasBraveSearch,
|
||||
firecrawl: hasFirecrawl,
|
||||
exa_search: hasExaSearch,
|
||||
};
|
||||
const defaults = {
|
||||
...hardcoded,
|
||||
...userDefaults,
|
||||
workflow: { ...hardcoded.workflow, ...(userDefaults.workflow || {}) },
|
||||
};
|
||||
const config = buildNewProjectConfig({});
|
||||
|
||||
try {
|
||||
fs.writeFileSync(configPath, JSON.stringify(defaults, null, 2), 'utf-8');
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
return { created: true, path: '.planning/config.json' };
|
||||
} catch (err) {
|
||||
error('Failed to create config.json: ' + err.message);
|
||||
@@ -316,4 +409,5 @@ module.exports = {
|
||||
cmdConfigSet,
|
||||
cmdConfigGet,
|
||||
cmdConfigSetModelProfile,
|
||||
cmdConfigNewProject,
|
||||
};
|
||||
|
||||
@@ -7,11 +7,13 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
</required_reading>
|
||||
|
||||
<auto_mode>
|
||||
|
||||
## Auto Mode Detection
|
||||
|
||||
Check if `--auto` flag is present in $ARGUMENTS.
|
||||
|
||||
**If auto mode:**
|
||||
|
||||
- Skip brownfield mapping offer (assume greenfield)
|
||||
- Skip deep questioning (extract context from provided document)
|
||||
- Config: YOLO mode is implicit (skip that question), but ask granularity/git/agents FIRST (Step 2a)
|
||||
@@ -23,6 +25,7 @@ Check if `--auto` flag is present in $ARGUMENTS.
|
||||
|
||||
**Document requirement:**
|
||||
Auto mode requires an idea document — either:
|
||||
|
||||
- File reference: `/gsd:new-project --auto @prd.md`
|
||||
- Pasted/written text in the prompt
|
||||
|
||||
@@ -37,6 +40,7 @@ Usage:
|
||||
|
||||
The document should describe what you want to build.
|
||||
```
|
||||
|
||||
</auto_mode>
|
||||
|
||||
<process>
|
||||
@@ -55,6 +59,7 @@ Parse JSON for: `researcher_model`, `synthesizer_model`, `roadmapper_model`, `co
|
||||
**If `project_exists` is true:** Error — project already initialized. Use `/gsd:progress`.
|
||||
|
||||
**If `has_git` is false:** Initialize git:
|
||||
|
||||
```bash
|
||||
git init
|
||||
```
|
||||
@@ -66,6 +71,7 @@ git init
|
||||
**If `needs_codebase_map` is true** (from init — existing code detected but no codebase map):
|
||||
|
||||
Use AskUserQuestion:
|
||||
|
||||
- header: "Codebase"
|
||||
- question: "I detected existing code in this directory. Would you like to map the codebase first?"
|
||||
- options:
|
||||
@@ -73,9 +79,11 @@ Use AskUserQuestion:
|
||||
- "Skip mapping" — Proceed with project initialization
|
||||
|
||||
**If "Map codebase first":**
|
||||
|
||||
```
|
||||
Run `/gsd:map-codebase` first, then return to `/gsd:new-project`
|
||||
```
|
||||
|
||||
Exit command.
|
||||
|
||||
**If "Skip mapping" OR `needs_codebase_map` is false:** Continue to Step 3.
|
||||
@@ -166,23 +174,11 @@ AskUserQuestion([
|
||||
])
|
||||
```
|
||||
|
||||
Create `.planning/config.json` with mode set to "yolo":
|
||||
Create `.planning/config.json` with all settings (CLI fills in remaining defaults automatically):
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "yolo",
|
||||
"granularity": "[selected]",
|
||||
"parallelization": true|false,
|
||||
"commit_docs": true|false,
|
||||
"model_profile": "quality|balanced|budget|inherit",
|
||||
"workflow": {
|
||||
"research": true|false,
|
||||
"plan_check": true|false,
|
||||
"verifier": true|false,
|
||||
"nyquist_validation": depth !== "quick",
|
||||
"auto_advance": true
|
||||
}
|
||||
}
|
||||
```bash
|
||||
mkdir -p .planning
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-new-project '{"mode":"yolo","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":true|false,"auto_advance":true}}'
|
||||
```
|
||||
|
||||
**If commit_docs = No:** Add `.planning/` to `.gitignore`.
|
||||
@@ -223,6 +219,7 @@ Ask inline (freeform, NOT AskUserQuestion):
|
||||
Wait for their response. This gives you the context needed to ask intelligent follow-up questions.
|
||||
|
||||
**Research-before-questions mode:** Check if `research_questions` is enabled in `.planning/config.json` (or the config from init context). When enabled, before asking follow-up questions about a topic area:
|
||||
|
||||
1. Do a brief web search for best practices related to what the user described
|
||||
2. Mention key findings naturally as you ask questions (e.g., "Most projects like this use X — is that what you're thinking, or something different?")
|
||||
3. This makes questions more informed without changing the conversational flow
|
||||
@@ -234,6 +231,7 @@ When disabled (default), ask questions directly as before.
|
||||
Based on what they said, ask follow-up questions that dig into their response. Use AskUserQuestion with options that probe what they mentioned — interpretations, clarifications, concrete examples.
|
||||
|
||||
Keep following threads. Each answer opens new threads to explore. Ask about:
|
||||
|
||||
- What excited them
|
||||
- What problem sparked this
|
||||
- What they mean by vague terms
|
||||
@@ -241,6 +239,7 @@ Keep following threads. Each answer opens new threads to explore. Ask about:
|
||||
- What's already decided
|
||||
|
||||
Consult `questioning.md` for techniques:
|
||||
|
||||
- Challenge vagueness
|
||||
- Make abstract concrete
|
||||
- Surface assumptions
|
||||
@@ -495,29 +494,22 @@ questions: [
|
||||
]
|
||||
```
|
||||
|
||||
Create `.planning/config.json` with all settings:
|
||||
Create `.planning/config.json` with all settings (CLI fills in remaining defaults automatically):
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "yolo|interactive",
|
||||
"granularity": "coarse|standard|fine",
|
||||
"parallelization": true|false,
|
||||
"commit_docs": true|false,
|
||||
"model_profile": "quality|balanced|budget|inherit",
|
||||
"workflow": {
|
||||
"research": true|false,
|
||||
"plan_check": true|false,
|
||||
"verifier": true|false,
|
||||
"nyquist_validation": depth !== "quick"
|
||||
}
|
||||
}
|
||||
```bash
|
||||
mkdir -p .planning
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-new-project '{"mode":"[yolo|interactive]","granularity":"[selected]","parallelization":true|false,"commit_docs":true|false,"model_profile":"quality|balanced|budget|inherit","workflow":{"research":true|false,"plan_check":true|false,"verifier":true|false,"nyquist_validation":[false if granularity=coarse, true otherwise]}}'
|
||||
```
|
||||
|
||||
**Note:** Run `/gsd:settings` anytime to update model profile, workflow agents, branching strategy, and other preferences.
|
||||
|
||||
**If commit_docs = No:**
|
||||
|
||||
- Set `commit_docs: false` in config.json
|
||||
- Add `.planning/` to `.gitignore` (create if needed)
|
||||
|
||||
**If commit_docs = Yes:**
|
||||
|
||||
- No additional gitignore entries needed
|
||||
|
||||
**Commit config.json:**
|
||||
@@ -526,8 +518,6 @@ Create `.planning/config.json` with all settings:
|
||||
node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "chore: add project config" --files .planning/config.json
|
||||
```
|
||||
|
||||
**Note:** Run `/gsd:settings` anytime to update these preferences.
|
||||
|
||||
## 5.1. Sub-Repo Detection
|
||||
|
||||
**Detect multi-repo workspace:**
|
||||
@@ -543,6 +533,7 @@ find . -maxdepth 1 -type d -not -name ".*" -not -name "node_modules" -exec test
|
||||
Strip the `./` prefix to get directory names (e.g., `./backend` → `backend`).
|
||||
|
||||
Use AskUserQuestion:
|
||||
|
||||
- header: "Multi-Repo Workspace"
|
||||
- question: "I detected separate git repos in this workspace. Which directories contain code that GSD should commit to?"
|
||||
- multiSelect: true
|
||||
@@ -550,6 +541,7 @@ Use AskUserQuestion:
|
||||
- "[directory name]" — Separate git repo
|
||||
|
||||
**If user selects one or more directories:**
|
||||
|
||||
- Set `planning.sub_repos` in config.json to the selected directory names array (e.g., `["backend", "frontend"]`)
|
||||
- Auto-set `planning.commit_docs` to `false` (planning docs stay local in multi-repo workspaces)
|
||||
- Add `.planning/` to `.gitignore` if not already present
|
||||
@@ -567,6 +559,7 @@ Use models from init: `researcher_model`, `synthesizer_model`, `roadmapper_model
|
||||
**If auto mode:** Default to "Research first" without asking.
|
||||
|
||||
Use AskUserQuestion:
|
||||
|
||||
- header: "Research"
|
||||
- question: "Research the domain ecosystem before defining requirements?"
|
||||
- options:
|
||||
@@ -576,6 +569,7 @@ Use AskUserQuestion:
|
||||
**If "Research first":**
|
||||
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCHING
|
||||
@@ -585,6 +579,7 @@ Researching [domain] ecosystem...
|
||||
```
|
||||
|
||||
Create research directory:
|
||||
|
||||
```bash
|
||||
mkdir -p .planning/research
|
||||
```
|
||||
@@ -592,10 +587,12 @@ mkdir -p .planning/research
|
||||
**Determine milestone context:**
|
||||
|
||||
Check if this is greenfield or subsequent milestone:
|
||||
|
||||
- If no "Validated" requirements in PROJECT.md → Greenfield (building from scratch)
|
||||
- If "Validated" requirements exist → Subsequent milestone (adding to existing app)
|
||||
|
||||
Display spawning indicator:
|
||||
|
||||
```
|
||||
◆ Spawning 4 researchers in parallel...
|
||||
→ Stack research
|
||||
@@ -784,6 +781,7 @@ Commit after writing.
|
||||
```
|
||||
|
||||
Display research complete banner and key findings:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCH COMPLETE ✓
|
||||
@@ -803,6 +801,7 @@ Files: `.planning/research/`
|
||||
## 7. Define Requirements
|
||||
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DEFINING REQUIREMENTS
|
||||
@@ -812,6 +811,7 @@ Display stage banner:
|
||||
**Load context:**
|
||||
|
||||
Read PROJECT.md and extract:
|
||||
|
||||
- Core value (the ONE thing that must work)
|
||||
- Stated constraints (budget, timeline, tech limitations)
|
||||
- Any explicit scope boundaries
|
||||
@@ -819,6 +819,7 @@ Read PROJECT.md and extract:
|
||||
**If research exists:** Read research/FEATURES.md and extract feature categories.
|
||||
|
||||
**If auto mode:**
|
||||
|
||||
- Auto-include all table stakes features (users expect these)
|
||||
- Include features explicitly mentioned in provided document
|
||||
- Auto-defer differentiators not mentioned in document
|
||||
@@ -857,6 +858,7 @@ Here are the features for [domain]:
|
||||
Ask: "What are the main things users need to be able to do?"
|
||||
|
||||
For each capability mentioned:
|
||||
|
||||
- Ask clarifying questions to make it specific
|
||||
- Probe for related capabilities
|
||||
- Group into categories
|
||||
@@ -875,6 +877,7 @@ For each category, use AskUserQuestion:
|
||||
- "None for v1" — Defer entire category
|
||||
|
||||
Track responses:
|
||||
|
||||
- Selected features → v1 requirements
|
||||
- Unselected table stakes → v2 (users expect these)
|
||||
- Unselected differentiators → out of scope
|
||||
@@ -882,6 +885,7 @@ Track responses:
|
||||
**Identify gaps:**
|
||||
|
||||
Use AskUserQuestion:
|
||||
|
||||
- header: "Additions"
|
||||
- question: "Any requirements research missed? (Features specific to your vision)"
|
||||
- options:
|
||||
@@ -895,6 +899,7 @@ Cross-check requirements against Core Value from PROJECT.md. If gaps detected, s
|
||||
**Generate REQUIREMENTS.md:**
|
||||
|
||||
Create `.planning/REQUIREMENTS.md` with:
|
||||
|
||||
- v1 Requirements grouped by category (checkboxes, REQ-IDs)
|
||||
- v2 Requirements (deferred)
|
||||
- Out of Scope (explicit exclusions with reasoning)
|
||||
@@ -905,12 +910,14 @@ Create `.planning/REQUIREMENTS.md` with:
|
||||
**Requirement quality criteria:**
|
||||
|
||||
Good requirements are:
|
||||
|
||||
- **Specific and testable:** "User can reset password via email link" (not "Handle password reset")
|
||||
- **User-centric:** "User can X" (not "System does Y")
|
||||
- **Atomic:** One capability per requirement (not "User can login and manage profile")
|
||||
- **Independent:** Minimal dependencies on other requirements
|
||||
|
||||
Reject vague requirements. Push for specificity:
|
||||
|
||||
- "Handle authentication" → "User can log in with email/password and stay logged in across sessions"
|
||||
- "Support sharing" → "User can share post via link that opens in recipient's browser"
|
||||
|
||||
@@ -948,6 +955,7 @@ node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: define v1 req
|
||||
## 8. Create Roadmap
|
||||
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CREATING ROADMAP
|
||||
@@ -988,6 +996,7 @@ Write files first, then return. This ensures artifacts persist even if context i
|
||||
**Handle roadmapper return:**
|
||||
|
||||
**If `## ROADMAP BLOCKED`:**
|
||||
|
||||
- Present blocker information
|
||||
- Work with user to resolve
|
||||
- Re-spawn when resolved
|
||||
@@ -1037,6 +1046,7 @@ Success criteria:
|
||||
**CRITICAL: Ask for approval before committing (interactive mode only):**
|
||||
|
||||
Use AskUserQuestion:
|
||||
|
||||
- header: "Roadmap"
|
||||
- question: "Does this roadmap structure work for you?"
|
||||
- options:
|
||||
@@ -1047,8 +1057,10 @@ Use AskUserQuestion:
|
||||
**If "Approve":** Continue to commit.
|
||||
|
||||
**If "Adjust phases":**
|
||||
|
||||
- Get user's adjustment notes
|
||||
- Re-spawn roadmapper with revision context:
|
||||
|
||||
```
|
||||
Task(prompt="
|
||||
<revision>
|
||||
@@ -1064,6 +1076,7 @@ Use AskUserQuestion:
|
||||
</revision>
|
||||
", subagent_type="gsd-roadmapper", model="{roadmapper_model}", description="Revise roadmap")
|
||||
```
|
||||
|
||||
- Present revised roadmap
|
||||
- Loop until user approves
|
||||
|
||||
|
||||
Reference in New Issue
Block a user