diff --git a/get-shit-done/bin/lib/config.cjs b/get-shit-done/bin/lib/config.cjs index 8b04c256d..d7bc44df8 100644 --- a/get-shit-done/bin/lib/config.cjs +++ b/get-shit-done/bin/lib/config.cjs @@ -16,6 +16,7 @@ const VALID_CONFIG_KEYS = new Set([ 'search_gitignored', 'brave_search', 'workflow.research', 'workflow.plan_check', 'workflow.verifier', 'workflow.nyquist_validation', 'workflow.ui_phase', 'workflow.ui_safety_gate', + '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', diff --git a/get-shit-done/bin/lib/core.cjs b/get-shit-done/bin/lib/core.cjs index 6d4eaa115..f39ac5bd2 100644 --- a/get-shit-done/bin/lib/core.cjs +++ b/get-shit-done/bin/lib/core.cjs @@ -65,6 +65,7 @@ function loadConfig(cwd) { nyquist_validation: true, parallelization: true, brave_search: false, + text_mode: false, // when true, use plain-text numbered lists instead of AskUserQuestion menus resolve_model_ids: false, // when true, resolve aliases (opus/sonnet/haiku) to full model IDs context_window: 200000, // default 200k; set to 1000000 for Opus/Sonnet 4.6 1M models phase_naming: 'sequential', // 'sequential' (default, auto-increment) or 'custom' (arbitrary string IDs) @@ -111,6 +112,7 @@ function loadConfig(cwd) { nyquist_validation: get('nyquist_validation', { section: 'workflow', field: 'nyquist_validation' }) ?? defaults.nyquist_validation, parallelization, brave_search: get('brave_search') ?? defaults.brave_search, + text_mode: get('text_mode', { section: 'workflow', field: 'text_mode' }) ?? defaults.text_mode, resolve_model_ids: get('resolve_model_ids') ?? defaults.resolve_model_ids, context_window: get('context_window') ?? defaults.context_window, phase_naming: get('phase_naming') ?? defaults.phase_naming, diff --git a/get-shit-done/workflows/discuss-phase.md b/get-shit-done/workflows/discuss-phase.md index 5182afce8..b0da511c3 100644 --- a/get-shit-done/workflows/discuss-phase.md +++ b/get-shit-done/workflows/discuss-phase.md @@ -110,6 +110,18 @@ Phase: "API documentation" 1. Retry the question once with the same parameters 2. If still empty, present the options as a plain-text numbered list and ask the user to type their choice number Never proceed with an empty answer. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** +When text mode is active, **do not use AskUserQuestion at all**. Instead, present every +question as 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 the Claude App +cannot forward TUI menu selections back to the host. + +Enable text mode: +- Per-session: pass `--text` flag to any command (e.g., `/gsd:discuss-phase --text`) +- Per-project: `gsd-tools config-set workflow.text_mode true` + +Text mode applies to ALL workflows in the session, not just discuss-phase. @@ -464,6 +476,13 @@ With that context: How should users authenticate? When disabled (default), skip the research and present questions directly as before. +**Text mode support:** Parse optional `--text` from `$ARGUMENTS`. +- Accept `--text` flag OR read `workflow.text_mode` from config (from init context) +- When active, replace ALL `AskUserQuestion` calls with plain-text numbered lists +- User types a number to select, or types free text for "Other" +- This is required for Claude Code remote sessions (`/rc` mode) where TUI menus + don't work through the Claude App + **Batch mode support:** Parse optional `--batch` from `$ARGUMENTS`. - Accept `--batch`, `--batch=N`, or `--batch N` diff --git a/get-shit-done/workflows/settings.md b/get-shit-done/workflows/settings.md index 5f70afba6..5bf50e48c 100644 --- a/get-shit-done/workflows/settings.md +++ b/get-shit-done/workflows/settings.md @@ -173,6 +173,9 @@ Merge new settings into existing config.json: "context_warnings": true/false, "workflow_guard": true/false, "research_questions": true/false + }, + "workflow": { + "text_mode": true/false // Use plain-text questions instead of TUI menus (for /rc remote sessions) } } ``` diff --git a/tests/config.test.cjs b/tests/config.test.cjs index 789753153..33fb9d44a 100644 --- a/tests/config.test.cjs +++ b/tests/config.test.cjs @@ -287,6 +287,16 @@ describe('config-set command', () => { ); }); + test('sets workflow.text_mode for remote session support', () => { + writeConfig(tmpDir, {}); + + const result = runGsdTools('config-set workflow.text_mode true', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const config = readConfig(tmpDir); + assert.strictEqual(config.workflow.text_mode, true); + }); + test('errors when no key path provided', () => { const result = runGsdTools('config-set', tmpDir); assert.strictEqual(result.success, false); diff --git a/tests/core.test.cjs b/tests/core.test.cjs index 56d658a53..3a3861ea1 100644 --- a/tests/core.test.cjs +++ b/tests/core.test.cjs @@ -61,6 +61,7 @@ describe('loadConfig', () => { assert.strictEqual(config.brave_search, false); assert.strictEqual(config.parallelization, true); assert.strictEqual(config.nyquist_validation, true); + assert.strictEqual(config.text_mode, false); }); test('reads model_profile from config.json', () => {