All other .gsd/ state files use uppercase naming (DECISIONS.md, REQUIREMENTS.md, PROJECT.md, etc). This renames the canonical preferences file to PREFERENCES.md while keeping a migration fallback — the loader checks PREFERENCES.md first, then falls back to lowercase preferences.md for existing installations. Closes #2700 Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
Plan: Onboarding, Detection & Init Wizard
Status: Phase 1 (Detection), Phase 3 (Init Wizard), Phase 4 (Integration), Phase 5 (Tests) — IMPLEMENTED Branch:
feature/onboarding-detection-wizardPhase 2 (Global Setup refactor): Deferred —/gsd setupprovides routing to existing commands for now
Problem Statement
GSD currently has two disconnected onboarding paths:
- App onboarding (
onboarding.ts) — pre-TUI, only handles LLM/tool auth. Runs once ever, no project awareness. - Project bootstrap (
showSmartEntry()) — silently creates.gsd/and drops you into the discuss prompt with zero explanation.
Neither detects v1 .planning/ directories, explains what GSD is, offers project-level configuration, or helps returning users entering a new folder.
Design Decisions
Q1: Should bare /gsd show a menu or auto-start?
Answer: Contextual behavior based on detection.
| Detected State | Behavior |
|---|---|
No .gsd/, no .planning/ |
Init Wizard (new project onboarding) |
No .gsd/, has .planning/ |
Migration offer → /gsd migrate or skip |
Has .gsd/, no milestones |
Current flow (discuss prompt) |
Has .gsd/, has milestones |
Current flow (smart entry / auto resume) |
First-ever GSD launch (no ~/.gsd/) |
Global setup first, then project init |
Q2: Should there be an onboarding wizard?
Answer: Yes — two-tier wizard.
- Global wizard (
/gsd setup) — runs once per machine. Handles: LLM auth (absorb currentonboarding.ts), global preferences (default model, mode, timeout defaults), tool keys, remote questions. - Project wizard (
/gsd init) — runs once per project folder. Handles: project-type detection, preferences template, git init,.gitignore, optional CONTEXT.md seeding.
Both wizards should be:
- Skippable at every step
- Re-runnable (
/gsd setupand/gsd initwork any time) - Non-blocking (sensible defaults if skipped entirely)
Architecture
New File: src/resources/extensions/gsd/init-wizard.ts
Project-level init wizard. Responsible for the interactive experience when entering a new folder.
New File: src/resources/extensions/gsd/detection.ts
Pure detection functions. No UI, no side effects.
Modified: src/resources/extensions/gsd/guided-flow.ts
showSmartEntry() gains a detection preamble before the current logic.
Modified: src/resources/extensions/gsd/commands.ts
New subcommands: /gsd init, /gsd setup. Existing /gsd migrate stays.
Modified: src/onboarding.ts
Refactored to be callable from both pre-TUI boot and /gsd setup.
Phase 1: Detection Engine (detection.ts)
Pure functions, zero UI dependencies.
Task 1.1: detectProjectState(basePath: string): ProjectDetection
interface ProjectDetection {
/** What kind of GSD state exists */
state: 'none' | 'v1-planning' | 'v2-gsd' | 'v2-gsd-empty';
/** Is this the first time GSD has been used on this machine? */
isFirstEverLaunch: boolean;
/** Does ~/.gsd/ exist with preferences? */
hasGlobalSetup: boolean;
/** v1 details (if state === 'v1-planning') */
v1?: {
path: string;
hasPhasesDir: boolean;
hasRoadmap: boolean;
phaseCount: number;
};
/** v2 details (if state === 'v2-gsd' or 'v2-gsd-empty') */
v2?: {
milestoneCount: number;
hasPreferences: boolean;
hasContext: boolean;
};
/** Detected project ecosystem signals */
projectSignals: ProjectSignals;
}
interface ProjectSignals {
/** Detected package managers / project files */
detectedFiles: string[]; // e.g. ['package.json', 'Cargo.toml', 'go.mod']
/** Is this a git repo already? */
isGitRepo: boolean;
/** Is this a monorepo? (workspaces, lerna, nx, turborepo) */
isMonorepo: boolean;
/** Primary language hint */
primaryLanguage?: string;
/** Has existing CI? */
hasCI: boolean;
/** Has existing tests? */
hasTests: boolean;
}
Task 1.2: detectV1Planning(basePath: string): V1Detection | null
Checks for .planning/ directory with v1 markers:
ROADMAP.md,PROJECT.md,REQUIREMENTS.md,STATE.mdphases/directory with numbered phases- Returns null if no
.planning/found
Task 1.3: detectProjectSignals(basePath: string): ProjectSignals
Quick filesystem scan (no heavy reads):
- Check for
package.json,Cargo.toml,go.mod,pyproject.toml,Gemfile, etc. - Check for monorepo markers (
workspacesin package.json,lerna.json,nx.json,turbo.json,pnpm-workspace.yaml) - Check for
.github/workflows/,.gitlab-ci.yml,Jenkinsfile - Check for test directories (
__tests__,tests/,test/,spec/)
Task 1.4: isFirstEverLaunch(): boolean
Returns true if ~/.gsd/ doesn't exist or has no PREFERENCES.md.
Phase 2: Global Setup Wizard (/gsd setup)
Absorbs and extends current onboarding.ts functionality.
Task 2.1: Refactor onboarding.ts into composable steps
Extract each step into a standalone async function that can be called from:
- Pre-TUI boot (current behavior)
/gsd setupcommand (new)
Steps become:
runLlmSetupStep()— already exists, just needs exportrunWebSearchStep()— already existsrunRemoteQuestionsStep()— already existsrunToolKeysStep()— already exists- NEW
runGlobalPreferencesStep()— default mode (solo/team), default model routing, timeout defaults
Task 2.2: /gsd setup command handler
/gsd setup → full wizard (all steps)
/gsd setup llm → just LLM auth
/gsd setup search → just web search
/gsd setup remote → just remote questions
/gsd setup keys → just tool keys
/gsd setup prefs → just global preferences
Shows a summary dashboard at the end:
┌ Global Setup ─────────────────────────────────┐
│ │
│ ✓ LLM: Anthropic (Claude) │
│ ✓ Web search: Brave Search │
│ ✓ Remote questions: Discord #gsd-bot │
│ ✓ Tool keys: 2/3 configured │
│ ✓ Preferences: solo mode, Sonnet default │
│ │
└───────────────────────────────────────────────┘
Task 2.3: Pre-TUI boot integration
Modify shouldRunOnboarding() to also check isFirstEverLaunch().
When it runs, use the refactored steps so the experience is identical.
Phase 3: Project Init Wizard (/gsd init)
Per-Project Preferences Strategy
Not all preferences belong in the init wizard. The filter: "What would you regret not setting before your first milestone?"
Tier 1: Ask in init wizard (high impact, easy to answer)
| Pref | Why at init time | Default |
|---|---|---|
mode (solo / team) |
Changes git strategy, merge behavior, everything downstream. Wrong default = friction on every milestone. | solo |
git.commit_docs |
Whether .gsd/ plans get committed to git. Team projects usually want true, throwaway prototypes want false. Affects the very first commit. |
true |
git.isolation (worktree / branch / none) |
Worktree isolation fails in some setups (submodules, shallow clones). Better to detect + ask upfront than crash during first auto run. | worktree |
verification_commands |
"How do we verify code works?" — e.g. npm test, cargo test, make check. Auto-detected from project signals (package.json scripts, Makefile, etc.) and confirmed. Critical for auto-mode to actually validate work. |
[] (auto-detect) |
custom_instructions |
Project-specific rules the LLM should follow. E.g. "use Tailwind, not CSS modules", "always write tests", "this is a monorepo, only touch packages/api". First milestone benefits hugely from these. | [] |
Tier 2: Show but default-skip (power users, "Advanced" expandable section)
| Pref | Why offer but not push | Default |
|---|---|---|
token_profile (budget / balanced / quality) |
Cost-conscious users want to set this early, but balanced works fine for most. |
balanced |
phases.skip_research |
Small projects don't need a research phase. Detectable from project signals (tiny repo = suggest skipping). | false |
git.main_branch |
Usually main or master — auto-detected from git, confirm only if ambiguous. |
auto-detect |
git.auto_push |
Whether auto-mode pushes after merging. Solo users usually want this; team users may want PR review first. | true (solo) / false (team) |
Tier 3: Don't ask at init (defer to /gsd prefs project)
| Pref | Why defer |
|---|---|
models (per-phase model config) |
Complex, per-phase config. Global default is fine to start. |
auto_supervisor (timeouts) |
Needs experience with the tool to calibrate. |
budget_ceiling / budget_enforcement |
Users don't know their budget on a new project. |
notifications |
Defaults work fine. |
skill_rules / always_use_skills / avoid_skills |
Too advanced for init — needs milestone experience first. |
post_unit_hooks / pre_dispatch_hooks |
Power-user territory. |
dynamic_routing |
Requires understanding model routing. |
parallel (workers, merge strategy) |
Needs milestones defined first. |
unique_milestone_ids |
Niche preference. |
uat_dispatch |
Niche. |
remote_questions |
Already handled in global setup. |
context_pause_threshold |
Internal tuning, not user-facing at init. |
skill_discovery / skill_staleness_days |
Defaults are sensible. |
auto_visualize / auto_report |
Nice-to-have, defaults fine. |
Verification Command Auto-Detection
The wizard auto-populates verification_commands from project signals:
| Signal | Suggested command |
|---|---|
package.json with scripts.test |
npm test (or pnpm test / yarn test if lockfile detected) |
package.json with scripts.build |
npm run build |
package.json with scripts.lint |
npm run lint |
package.json with scripts.typecheck or scripts.tsc |
npm run typecheck |
Cargo.toml |
cargo test, cargo clippy |
go.mod |
go test ./..., go vet ./... |
pyproject.toml or setup.py |
pytest or python -m pytest |
Makefile with test target |
make test |
Gemfile |
bundle exec rspec or bundle exec rake test |
.github/workflows/*.yml |
Parse for test commands (informational, not auto-added) |
User sees: "I detected these verification commands — confirm, edit, or add more."
Task 3.1: showProjectInit() — the main wizard
Flow:
Step 1: Detection scan (automatic, instant, no prompt)
├─ v1 .planning/ found? → Offer migration (Task 3.2)
├─ .gsd/ already exists? → Re-init safety (Task 3.3)
└─ Clean folder → Continue to step 2
Step 2: Project Recognition (informational, no prompt needed)
"Detected: Node.js monorepo, 3 packages, Jest tests, GitHub Actions CI"
→ Displayed as context, saved to CONTEXT.md seed
Step 3: Git Setup
├─ Already a git repo? → Auto-detect main branch, skip init
└─ Not a git repo → "Initialize git?" (default: yes)
Step 4: Mode Selection
"How are you working on this project?"
> Solo (just me) ← default
Team (multiple contributors)
Step 5: Verification Commands (auto-populated from detection)
"How should GSD verify code changes?"
> npm test ← auto-detected from package.json
npm run build ← auto-detected
Add more commands...
Skip verification
Step 6: Git Preferences
"Git settings for this project:"
Commit .gsd/ plans to git? [Y/n] ← default yes
Isolation strategy: [worktree] ← default, warn if submodules
Main branch: [main] ← auto-detected, confirm if ambiguous
Step 7: Project Instructions (optional, skippable)
"Any rules GSD should follow for this project?"
> (text input, multi-line or one-liner)
e.g. "Use TypeScript strict mode", "Follow existing patterns in src/"
Hint: "These become custom_instructions in your project preferences"
Step 8: Advanced (collapsed by default, expandable)
"Advanced settings (press Enter to skip):"
Token profile: [balanced] / budget / quality
Skip research? [no] / yes
Auto-push on merge? [yes] / no
Step 9: Bootstrap .gsd/ structure
- Creates .gsd/milestones/
- Creates .gsd/PREFERENCES.md (from wizard answers)
- Creates .gitignore entries
- Seeds CONTEXT.md with detected project signals
- Commits "chore: init gsd" (if commit_docs enabled)
Step 10: Transition
→ Auto-transition to discuss prompt for first milestone
(Fluid experience — wizard flows directly into "tell me about your project")
Task 3.2: v1 migration detection + offer
When .planning/ is detected in showSmartEntry():
┌ GSD — Legacy Project Detected ────────────────┐
│ │
│ Found .planning/ directory (GSD v1 format) │
│ 3 phases, 12 tasks detected │
│ │
│ > Migrate to GSD v2 (recommended) │
│ Start fresh │
│ Cancel │
│ │
└───────────────────────────────────────────────┘
"Migrate" → delegates to existing handleMigrate() pipeline.
"Start fresh" → runs the normal init wizard, ignoring .planning/.
Task 3.3: Re-init safety
If .gsd/ already exists when /gsd init is run:
- Show current state (X milestones, Y slices)
- Offer: "Reset preferences" / "Re-run project detection" / "Cancel"
- Never destructively delete milestones via init
Phase 4: Smart Entry Integration
Task 4.1: Update showSmartEntry() detection preamble
Before the current logic, add:
const detection = detectProjectState(basePath);
// First-ever launch — run global setup first
if (detection.isFirstEverLaunch) {
await showGlobalSetupWizard(ctx);
}
// v1 detected, no v2 — offer migration
if (detection.state === 'v1-planning') {
const choice = await offerMigration(ctx, detection.v1!);
if (choice === 'migrate') {
await handleMigrate('', ctx, pi);
return;
}
// 'fresh' falls through to normal init
}
// No .gsd/ — run project init wizard
if (detection.state === 'none') {
await showProjectInit(ctx, pi, basePath, detection);
return;
}
// Existing .gsd/ — current logic continues unchanged
Task 4.2: Preserve zero-friction for returning users
The detection + init wizard only triggers when .gsd/ doesn't exist.
Once .gsd/ exists, the flow is identical to today — no regressions.
Phase 5: Tests
Task 5.1: Detection engine tests
detectProjectState()with various folder layoutsdetectV1Planning()with real and fake.planning/dirsdetectProjectSignals()with different project typesisFirstEverLaunch()with/without~/.gsd/
Task 5.2: Init wizard integration tests
- New folder → wizard triggers →
.gsd/created correctly - v1 folder → migration offer shown
- Existing
.gsd/→ wizard skipped, normal flow - Re-run
/gsd initon existing project → safe behavior
Task 5.3: Global setup tests
/gsd setupfrom command handler- Individual sub-steps (
/gsd setup llm, etc.) - Pre-TUI boot still works with refactored steps
File Inventory
| File | Action | Purpose |
|---|---|---|
src/resources/extensions/gsd/detection.ts |
NEW | Pure detection functions |
src/resources/extensions/gsd/init-wizard.ts |
NEW | Project init wizard UI |
src/resources/extensions/gsd/global-setup.ts |
NEW | Global setup wizard (refactored from onboarding.ts) |
src/onboarding.ts |
MODIFY | Delegate to global-setup.ts, keep boot integration |
src/resources/extensions/gsd/guided-flow.ts |
MODIFY | Add detection preamble to showSmartEntry() |
src/resources/extensions/gsd/commands.ts |
MODIFY | Add /gsd init and /gsd setup subcommands |
| Tests (TBD paths) | NEW | Detection, init, setup tests |
Open Questions for Discussion
-
Should
/gsd initauto-transition to the discuss prompt? Or end with "Run /gsd to start"? Auto-transition is more fluid but might feel jarring after a wizard. -
Should project signals (detected language, CI, etc.) be persisted? They're useful context for the discuss prompt but could go stale. Option: seed into CONTEXT.md as a starting point the user can edit.
-
Should
/gsd setupbe accessible outside the TUI? e.g.gsd setupfrom the shell before launching. Currentlyonboarding.tshandles this but it's limited. -
Migration: should we auto-detect
.planning/in parent directories? Some users might run GSD from a subdirectory while.planning/is at the repo root.
Estimated Scope
- 3 new files, ~400-600 lines total
- 3 modified files, ~50-80 lines of changes
- Test files, ~200-300 lines
- No breaking changes to existing behavior