Merge pull request #1223 from trek-e/fix/remote-session-menu-fallback-1214

fix(workflows): text_mode config for Claude Code remote session compatibility
This commit is contained in:
Tom Boucher
2026-03-19 12:13:10 -04:00
committed by GitHub
6 changed files with 36 additions and 0 deletions

View File

@@ -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',

View File

@@ -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,

View File

@@ -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.
</answer_validation>
<process>
@@ -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`

View File

@@ -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)
}
}
```

View File

@@ -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);

View File

@@ -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', () => {