From 47badff2ee20b61f4ae6a44f9bc0e81328db2efd Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Fri, 10 Apr 2026 12:30:46 -0400 Subject: [PATCH] fix(workflow): add plain-text fallback for AskUserQuestion on non-Claude runtimes (#2042) AskUserQuestion is a Claude Code-only tool. When running GSD on OpenAI Codex, Gemini CLI, or other non-Claude runtimes, the model renders the tool call as a markdown code block instead of executing it, so the interactive TUI never appears and the session stalls without collecting user input. The workflow.text_mode / --text flag mechanism already handles this in 5 of the 37 affected workflows. This commit adds the same TEXT_MODE fallback instruction to all remaining 32 workflows so that, when text_mode is enabled, every AskUserQuestion call is replaced with a plain-text numbered list that any runtime can handle. Fixes #2012 Co-authored-by: Claude Sonnet 4.6 --- get-shit-done/workflows/add-tests.md | 3 + get-shit-done/workflows/add-todo.md | 2 + .../workflows/ai-integration-phase.md | 2 + get-shit-done/workflows/autonomous.md | 2 + get-shit-done/workflows/check-todos.md | 2 + get-shit-done/workflows/cleanup.md | 2 + get-shit-done/workflows/complete-milestone.md | 2 + get-shit-done/workflows/discovery-phase.md | 2 + get-shit-done/workflows/do.md | 2 + get-shit-done/workflows/docs-update.md | 2 + get-shit-done/workflows/eval-review.md | 2 + get-shit-done/workflows/execute-plan.md | 2 + get-shit-done/workflows/import.md | 2 + get-shit-done/workflows/inbox.md | 3 + get-shit-done/workflows/manager.md | 2 + get-shit-done/workflows/new-milestone.md | 2 + get-shit-done/workflows/new-project.md | 2 + get-shit-done/workflows/new-workspace.md | 2 + get-shit-done/workflows/note.md | 2 + get-shit-done/workflows/plant-seed.md | 3 + get-shit-done/workflows/profile-user.md | 2 + get-shit-done/workflows/quick.md | 3 + get-shit-done/workflows/remove-workspace.md | 2 + get-shit-done/workflows/secure-phase.md | 2 + get-shit-done/workflows/settings.md | 2 + get-shit-done/workflows/ship.md | 3 + get-shit-done/workflows/ui-phase.md | 2 + get-shit-done/workflows/ui-review.md | 2 + get-shit-done/workflows/undo.md | 2 + get-shit-done/workflows/update.md | 2 + get-shit-done/workflows/validate-phase.md | 2 + get-shit-done/workflows/verify-work.md | 2 + tests/ask-user-questions-fallback.test.cjs | 83 +++++++++++++++++++ 33 files changed, 152 insertions(+) create mode 100644 tests/ask-user-questions-fallback.test.cjs diff --git a/get-shit-done/workflows/add-tests.md b/get-shit-done/workflows/add-tests.md index f3170552f..527450237 100644 --- a/get-shit-done/workflows/add-tests.md +++ b/get-shit-done/workflows/add-tests.md @@ -108,6 +108,9 @@ Read each file to verify classification. Don't classify based on filename alone. Present the classification to the user for confirmation before proceeding: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. + ``` AskUserQuestion( header: "Test Classification", diff --git a/get-shit-done/workflows/add-todo.md b/get-shit-done/workflows/add-todo.md index 65ce8ffea..002b1c8b8 100644 --- a/get-shit-done/workflows/add-todo.md +++ b/get-shit-done/workflows/add-todo.md @@ -70,6 +70,8 @@ If potential duplicate found: 1. Read the existing todo 2. Compare scope + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. If overlapping, use AskUserQuestion: - header: "Duplicate?" - question: "Similar todo exists: [title]. What would you like to do?" diff --git a/get-shit-done/workflows/ai-integration-phase.md b/get-shit-done/workflows/ai-integration-phase.md index bf412a03a..5820724a3 100644 --- a/get-shit-done/workflows/ai-integration-phase.md +++ b/get-shit-done/workflows/ai-integration-phase.md @@ -75,6 +75,8 @@ Continue (non-blocking). AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1) ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. **If exists:** Use AskUserQuestion: - header: "Existing AI-SPEC" - question: "AI-SPEC.md already exists for Phase {N}. What would you like to do?" diff --git a/get-shit-done/workflows/autonomous.md b/get-shit-done/workflows/autonomous.md index 7baaf97e8..6f318f93c 100644 --- a/get-shit-done/workflows/autonomous.md +++ b/get-shit-done/workflows/autonomous.md @@ -412,6 +412,8 @@ Proceed to iterate step. Read the human_verification section from VERIFICATION.md to get the count and items requiring manual testing. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Display the items, then ask user via AskUserQuestion: - **question:** "Phase ${PHASE_NUM} has items needing manual verification. Validate now or continue to next phase?" - **options:** "Validate now" / "Continue without validation" diff --git a/get-shit-done/workflows/check-todos.md b/get-shit-done/workflows/check-todos.md index 8ea6b476d..6d0a141ca 100644 --- a/get-shit-done/workflows/check-todos.md +++ b/get-shit-done/workflows/check-todos.md @@ -102,6 +102,8 @@ If `.planning/ROADMAP.md` exists: **If todo maps to a roadmap phase:** + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Action" - question: "This todo relates to Phase [N]: [name]. What would you like to do?" diff --git a/get-shit-done/workflows/cleanup.md b/get-shit-done/workflows/cleanup.md index ea9ee880f..41a201aca 100644 --- a/get-shit-done/workflows/cleanup.md +++ b/get-shit-done/workflows/cleanup.md @@ -93,6 +93,8 @@ No phase directories found to archive. Phases may have been removed or archived Stop here. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. AskUserQuestion: "Proceed with archiving?" with options: "Yes — archive listed phases" | "Cancel" If "Cancel": Stop. diff --git a/get-shit-done/workflows/complete-milestone.md b/get-shit-done/workflows/complete-milestone.md index d0d91e3b3..271f02fce 100644 --- a/get-shit-done/workflows/complete-milestone.md +++ b/get-shit-done/workflows/complete-milestone.md @@ -387,6 +387,8 @@ Verify: `✅ Milestone archived to .planning/milestones/` **Phase archival (optional):** After archival completes, ask the user: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. AskUserQuestion(header="Archive Phases", question="Archive phase directories to milestones/?", options: "Yes — move to milestones/v[X.Y]-phases/" | "Skip — keep phases in place") If "Yes": move phase directories to the milestone archive: diff --git a/get-shit-done/workflows/discovery-phase.md b/get-shit-done/workflows/discovery-phase.md index 81176bf5c..a45d56e87 100644 --- a/get-shit-done/workflows/discovery-phase.md +++ b/get-shit-done/workflows/discovery-phase.md @@ -214,6 +214,8 @@ Write `.planning/phases/XX-name/DISCOVERY.md`: After creating DISCOVERY.md, check confidence level. If confidence is LOW: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Low Conf." diff --git a/get-shit-done/workflows/do.md b/get-shit-done/workflows/do.md index 3c05a2b9f..9d78e210d 100644 --- a/get-shit-done/workflows/do.md +++ b/get-shit-done/workflows/do.md @@ -11,6 +11,8 @@ Read all files referenced by the invoking prompt's execution_context before star **Check for input.** + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. If `$ARGUMENTS` is empty, ask via AskUserQuestion: ``` diff --git a/get-shit-done/workflows/docs-update.md b/get-shit-done/workflows/docs-update.md index 34ee5fbdd..4a7032411 100644 --- a/get-shit-done/workflows/docs-update.md +++ b/get-shit-done/workflows/docs-update.md @@ -84,6 +84,8 @@ Assemble the complete doc queue from always-on docs plus conditional docs from c If CONTRIBUTING.md is in the conditional queue AND does NOT appear in the `existing_docs` array from init JSON: 1. If `--force` is present in `$ARGUMENTS`: skip this check, include CONTRIBUTING.md in the queue. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. 2. Otherwise, use AskUserQuestion to confirm: ``` diff --git a/get-shit-done/workflows/eval-review.md b/get-shit-done/workflows/eval-review.md index 25a6c5283..d78187a66 100644 --- a/get-shit-done/workflows/eval-review.md +++ b/get-shit-done/workflows/eval-review.md @@ -42,6 +42,8 @@ EVAL_REVIEW_FILE=$(ls "${PHASE_DIR}"/*-EVAL-REVIEW.md 2>/dev/null | head -1) **State B** — SUMMARY.md exists, no AI-SPEC.md: Audit against general best practices **State C** — No SUMMARY.md: Exit — "Phase {N} not executed. Run /gsd-execute-phase {N} first." + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. **If `EVAL_REVIEW_FILE` non-empty:** Use AskUserQuestion: - header: "Existing Eval Review" - question: "EVAL-REVIEW.md already exists for Phase {N}." diff --git a/get-shit-done/workflows/execute-plan.md b/get-shit-done/workflows/execute-plan.md index f4467feac..f9de1aebc 100644 --- a/get-shit-done/workflows/execute-plan.md +++ b/get-shit-done/workflows/execute-plan.md @@ -135,6 +135,8 @@ This IS the execution instructions. Follow exactly. If plan references CONTEXT.m node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" phases list --type summaries --raw # Extract the second-to-last summary from the JSON result ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. If previous SUMMARY has unresolved "Issues Encountered" or "Next Phase Readiness" blockers: AskUserQuestion(header="Previous Issues", options: "Proceed anyway" | "Address first" | "Review previous"). diff --git a/get-shit-done/workflows/import.md b/get-shit-done/workflows/import.md index f3fe0f807..a555c0edf 100644 --- a/get-shit-done/workflows/import.md +++ b/get-shit-done/workflows/import.md @@ -164,6 +164,8 @@ Exit WITHOUT writing any files. This is the safety gate — no PLAN.md is writte **If only WARNINGS and/or INFO (no blockers):** + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Ask via AskUserQuestion using the approve-revise-abort pattern: - question: "Review the warnings above. Proceed with import?" - header: "Approve?" diff --git a/get-shit-done/workflows/inbox.md b/get-shit-done/workflows/inbox.md index 4b450d835..7b6452ed8 100644 --- a/get-shit-done/workflows/inbox.md +++ b/get-shit-done/workflows/inbox.md @@ -326,6 +326,9 @@ gh pr close {number} --comment "Closed by GSD inbox triage: this PR does not mee ``` Always confirm with the user before closing anything: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. + ``` AskUserQuestion: question: "Found {N} items to close. Review the list above — proceed with closing?" diff --git a/get-shit-done/workflows/manager.md b/get-shit-done/workflows/manager.md index cb6a56a53..b92feab59 100644 --- a/get-shit-done/workflows/manager.md +++ b/get-shit-done/workflows/manager.md @@ -117,6 +117,8 @@ All {phase_count} phases done. Ready for final steps: → /gsd-complete-milestone — archive and wrap up ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Ask user via AskUserQuestion: - **question:** "All phases complete. What next?" - **options:** "Verify work" / "Complete milestone" / "Exit manager" diff --git a/get-shit-done/workflows/new-milestone.md b/get-shit-done/workflows/new-milestone.md index b3178a570..5ce94184a 100644 --- a/get-shit-done/workflows/new-milestone.md +++ b/get-shit-done/workflows/new-milestone.md @@ -40,6 +40,8 @@ If the flag is absent, keep the current behavior of continuing phase numbering f **If no context file:** - Present what shipped in last milestone + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. - Ask inline (freeform, NOT AskUserQuestion): "What do you want to build next?" - Wait for their response, then use AskUserQuestion to probe specifics - If user selects "Other" at any point to provide freeform input, ask follow-up as plain text — not another AskUserQuestion diff --git a/get-shit-done/workflows/new-project.md b/get-shit-done/workflows/new-project.md index aa25021bb..be37611f6 100644 --- a/get-shit-done/workflows/new-project.md +++ b/get-shit-done/workflows/new-project.md @@ -103,6 +103,8 @@ git init **If `needs_codebase_map` is true** (from init — existing code detected but no codebase map): + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Codebase" diff --git a/get-shit-done/workflows/new-workspace.md b/get-shit-done/workflows/new-workspace.md index b0dc14ea6..edbc2f9c5 100644 --- a/get-shit-done/workflows/new-workspace.md +++ b/get-shit-done/workflows/new-workspace.md @@ -31,6 +31,8 @@ Extract from $ARGUMENTS: **If `--name` is missing and not `--auto`:** + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Workspace Name" - question: "What should this workspace be called?" diff --git a/get-shit-done/workflows/note.md b/get-shit-done/workflows/note.md index 441c77078..48432417b 100644 --- a/get-shit-done/workflows/note.md +++ b/get-shit-done/workflows/note.md @@ -1,5 +1,7 @@ Zero-friction idea capture. One Write call, one confirmation line. No questions, no prompts. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Runs inline — no Task, no AskUserQuestion, no Bash. diff --git a/get-shit-done/workflows/plant-seed.md b/get-shit-done/workflows/plant-seed.md index d9c96ca5a..657f48082 100644 --- a/get-shit-done/workflows/plant-seed.md +++ b/get-shit-done/workflows/plant-seed.md @@ -32,6 +32,9 @@ mkdir -p .planning/seeds Ask focused questions to build a complete seed: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. + ``` AskUserQuestion( header: "Trigger", diff --git a/get-shit-done/workflows/profile-user.md b/get-shit-done/workflows/profile-user.md index dc143786b..b35f1db16 100644 --- a/get-shit-done/workflows/profile-user.md +++ b/get-shit-done/workflows/profile-user.md @@ -30,6 +30,8 @@ PROFILE_PATH="$HOME/.claude/get-shit-done/USER-PROFILE.md" **If profile exists AND --refresh NOT set AND --questionnaire NOT set:** + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Existing Profile" - question: "You already have a profile. What would you like to do?" diff --git a/get-shit-done/workflows/quick.md b/get-shit-done/workflows/quick.md index 15db625d9..7824a4187 100644 --- a/get-shit-done/workflows/quick.md +++ b/get-shit-done/workflows/quick.md @@ -38,6 +38,9 @@ Parse `$ARGUMENTS` for: If `$DESCRIPTION` is empty after parsing, prompt user interactively: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. + ``` AskUserQuestion( header: "Quick Task", diff --git a/get-shit-done/workflows/remove-workspace.md b/get-shit-done/workflows/remove-workspace.md index 634d0460c..267ba18a3 100644 --- a/get-shit-done/workflows/remove-workspace.md +++ b/get-shit-done/workflows/remove-workspace.md @@ -23,6 +23,8 @@ Parse JSON for: `workspace_name`, `workspace_path`, `has_manifest`, `strategy`, First run `/gsd-list-workspaces` to show available workspaces, then ask: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - header: "Remove Workspace" - question: "Which workspace do you want to remove?" diff --git a/get-shit-done/workflows/secure-phase.md b/get-shit-done/workflows/secure-phase.md index d4e4c406b..bb4726814 100644 --- a/get-shit-done/workflows/secure-phase.md +++ b/get-shit-done/workflows/secure-phase.md @@ -73,6 +73,8 @@ If `threats_open: 0` → skip to Step 6 directly. ## 4. Present Threat Plan + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Call AskUserQuestion with threat table and options: 1. "Verify all open threats" → Step 5 2. "Accept all open — document in accepted risks log" → add to SECURITY.md accepted risks, set all CLOSED, Step 6 diff --git a/get-shit-done/workflows/settings.md b/get-shit-done/workflows/settings.md index 75c160fa3..503460def 100644 --- a/get-shit-done/workflows/settings.md +++ b/get-shit-done/workflows/settings.md @@ -39,6 +39,8 @@ Parse current values (default to `true` if not present): + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion with current values pre-selected: ``` diff --git a/get-shit-done/workflows/ship.md b/get-shit-done/workflows/ship.md index 1a2b00f28..9a1c08375 100644 --- a/get-shit-done/workflows/ship.md +++ b/get-shit-done/workflows/ship.md @@ -161,6 +161,9 @@ Report: "PR #{number} created: {url}" Ask if user wants to trigger a code review: + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. + ``` AskUserQuestion: question: "PR created. Run a code review before merge?" diff --git a/get-shit-done/workflows/ui-phase.md b/get-shit-done/workflows/ui-phase.md index 31bd7eae2..c736474ef 100644 --- a/get-shit-done/workflows/ui-phase.md +++ b/get-shit-done/workflows/ui-phase.md @@ -83,6 +83,8 @@ Continue (non-blocking). UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1) ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. **If exists:** Use AskUserQuestion: - header: "Existing UI-SPEC" - question: "UI-SPEC.md already exists for Phase {N}. What would you like to do?" diff --git a/get-shit-done/workflows/ui-review.md b/get-shit-done/workflows/ui-review.md index 2b04289fb..f3991db42 100644 --- a/get-shit-done/workflows/ui-review.md +++ b/get-shit-done/workflows/ui-review.md @@ -44,6 +44,8 @@ UI_REVIEW_FILE=$(ls "${PHASE_DIR}"/*-UI-REVIEW.md 2>/dev/null | head -1) **If `SUMMARY_FILES` empty:** Exit — "Phase {N} not executed. Run /gsd-execute-phase {N} first." + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. **If `UI_REVIEW_FILE` non-empty:** Use AskUserQuestion: - header: "Existing UI Review" - question: "UI-REVIEW.md already exists for Phase {N}." diff --git a/get-shit-done/workflows/undo.md b/get-shit-done/workflows/undo.md index b78b01fee..5eb294f98 100644 --- a/get-shit-done/workflows/undo.md +++ b/get-shit-done/workflows/undo.md @@ -63,6 +63,8 @@ Recent GSD commits: 3. ghi9012 fix(02-03): correct validation logic ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion to ask: - question: "Which commits to revert? Enter numbers (e.g., 1,3) or 'all'" - header: "Select" diff --git a/get-shit-done/workflows/update.md b/get-shit-done/workflows/update.md index 9a28fc762..9c1544f49 100644 --- a/get-shit-done/workflows/update.md +++ b/get-shit-done/workflows/update.md @@ -341,6 +341,8 @@ Your custom files in other locations are preserved: If you've modified any GSD files directly, they'll be automatically backed up to `gsd-local-patches/` and can be reapplied with `/gsd-reapply-patches` after the update. ``` + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Use AskUserQuestion: - Question: "Proceed with update?" - Options: diff --git a/get-shit-done/workflows/validate-phase.md b/get-shit-done/workflows/validate-phase.md index 8da24a79b..e198773eb 100644 --- a/get-shit-done/workflows/validate-phase.md +++ b/get-shit-done/workflows/validate-phase.md @@ -83,6 +83,8 @@ No gaps → skip to Step 6, set `nyquist_compliant: true`. ## 4. Present Gap Plan + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Call AskUserQuestion with gap table and options: 1. "Fix all gaps" → Step 5 2. "Skip — mark manual-only" → add to Manual-Only, Step 6 diff --git a/get-shit-done/workflows/verify-work.md b/get-shit-done/workflows/verify-work.md index 3148aa18a..7556dc323 100644 --- a/get-shit-done/workflows/verify-work.md +++ b/get-shit-done/workflows/verify-work.md @@ -248,6 +248,8 @@ Display the returned checkpoint EXACTLY as-is: - Do NOT add commentary before or after the block. - If you notice protocol/meta markers such as `to=all:`, role-routing text, XML system tags, hidden instruction markers, ad copy, or any unrelated suffix, discard the draft and output `{CHECKPOINT}` only. + +**Text mode (`workflow.text_mode: true` in config or `--text` flag):** 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 non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available. Wait for user response (plain text, no AskUserQuestion). diff --git a/tests/ask-user-questions-fallback.test.cjs b/tests/ask-user-questions-fallback.test.cjs new file mode 100644 index 000000000..ffcfe64d2 --- /dev/null +++ b/tests/ask-user-questions-fallback.test.cjs @@ -0,0 +1,83 @@ +/** + * Regression guard for #2012: AskUserQuestion is Claude Code-only — non-Claude + * runtimes (OpenAI Codex, Gemini, etc.) render it as a markdown code block + * instead of triggering the interactive TUI, so the session stalls. + * + * Every workflow that calls AskUserQuestion MUST include a TEXT_MODE fallback + * instruction so that, when `workflow.text_mode` is true (or `--text` is + * passed), all AskUserQuestion calls are replaced with plain-text numbered + * lists that any runtime can handle. + * + * The canonical fallback phrase is: + * "TEXT_MODE" (or "text_mode") paired with "plain-text" (or "plain text") + * near the first AskUserQuestion reference in the file. + */ +'use strict'; + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const ROOT = path.join(__dirname, '..'); +const WORKFLOWS_DIR = path.join(ROOT, 'get-shit-done', 'workflows'); + +/** + * Return true if the file content contains a TEXT_MODE / text_mode fallback + * instruction for AskUserQuestion calls. + * + * Acceptable forms (case-insensitive on key terms): + * - "TEXT_MODE" + "plain-text" or "plain text" + * - "text_mode" + "plain-text" or "plain text" + * - "text mode" + "plain-text" or "plain text" + */ +function hasTextModeFallback(content) { + const lower = content.toLowerCase(); + const hasTextMode = + lower.includes('text_mode') || + lower.includes('text mode'); + const hasPlainText = + lower.includes('plain-text') || + lower.includes('plain text') || + lower.includes('numbered list'); + return hasTextMode && hasPlainText; +} + +describe('AskUserQuestion text-mode fallback (#2012)', () => { + test('every workflow that uses AskUserQuestion includes a TEXT_MODE plain-text fallback', () => { + const violations = []; + + const files = fs.readdirSync(WORKFLOWS_DIR).filter(f => f.endsWith('.md')); + + for (const fname of files) { + const fpath = path.join(WORKFLOWS_DIR, fname); + const content = fs.readFileSync(fpath, 'utf-8'); + + if (!content.includes('AskUserQuestion')) continue; + + if (!hasTextModeFallback(content)) { + violations.push(fname); + } + } + + assert.strictEqual( + violations.length, + 0, + [ + 'AskUserQuestion is Claude Code-only (issue #2012).', + 'Every workflow that uses AskUserQuestion must include a TEXT_MODE fallback', + 'so non-Claude runtimes (OpenAI Codex, Gemini, etc.) can present questions', + 'as plain-text numbered lists instead of stalling on an unexecuted tool call.', + '', + 'Add this near the argument-parsing section of each workflow:', + ' 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.', + '', + 'Workflows missing the fallback:', + ...violations.map(v => ' get-shit-done/workflows/' + v), + ].join('\n') + ); + }); +});