gencode-ai 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gencode/settings.local.json +7 -0
- package/README.md +11 -11
- package/dist/agent/agent.d.ts +42 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +82 -15
- package/dist/agent/agent.js.map +1 -1
- package/dist/cli/components/App.d.ts +8 -1
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +231 -29
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +2 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Header.d.ts +1 -1
- package/dist/cli/components/Header.d.ts.map +1 -1
- package/dist/cli/components/Header.js +4 -6
- package/dist/cli/components/Header.js.map +1 -1
- package/dist/cli/components/Logo.d.ts +1 -0
- package/dist/cli/components/Logo.d.ts.map +1 -1
- package/dist/cli/components/Logo.js +16 -3
- package/dist/cli/components/Logo.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +4 -4
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +51 -25
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/PermissionPrompt.d.ts +60 -0
- package/dist/cli/components/PermissionPrompt.d.ts.map +1 -0
- package/dist/cli/components/PermissionPrompt.js +192 -0
- package/dist/cli/components/PermissionPrompt.js.map +1 -0
- package/dist/cli/components/ProviderManager.js +3 -3
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/Spinner.d.ts +7 -2
- package/dist/cli/components/Spinner.d.ts.map +1 -1
- package/dist/cli/components/Spinner.js +116 -25
- package/dist/cli/components/Spinner.js.map +1 -1
- package/dist/cli/components/TodoList.d.ts +7 -0
- package/dist/cli/components/TodoList.d.ts.map +1 -0
- package/dist/cli/components/TodoList.js +34 -0
- package/dist/cli/components/TodoList.js.map +1 -0
- package/dist/cli/components/index.d.ts +1 -0
- package/dist/cli/components/index.d.ts.map +1 -1
- package/dist/cli/components/index.js +1 -0
- package/dist/cli/components/index.js.map +1 -1
- package/dist/cli/index.js +47 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +13 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +18 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +49 -0
- package/dist/config/levels.d.ts.map +1 -0
- package/dist/config/levels.js +222 -0
- package/dist/config/levels.js.map +1 -0
- package/dist/config/loader.d.ts +46 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +153 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +115 -15
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +260 -34
- package/dist/config/manager.js.map +1 -1
- package/dist/config/manager.test.d.ts +5 -0
- package/dist/config/manager.test.d.ts.map +1 -0
- package/dist/config/manager.test.js +192 -0
- package/dist/config/manager.test.js.map +1 -0
- package/dist/config/merger.d.ts +56 -0
- package/dist/config/merger.d.ts.map +1 -0
- package/dist/config/merger.js +177 -0
- package/dist/config/merger.js.map +1 -0
- package/dist/config/test-utils.d.ts +24 -0
- package/dist/config/test-utils.d.ts.map +1 -0
- package/dist/config/test-utils.js +55 -0
- package/dist/config/test-utils.js.map +1 -0
- package/dist/config/types.d.ts +78 -9
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +52 -2
- package/dist/config/types.js.map +1 -1
- package/dist/memory/import-resolver.d.ts +46 -0
- package/dist/memory/import-resolver.d.ts.map +1 -0
- package/dist/memory/import-resolver.js +117 -0
- package/dist/memory/import-resolver.js.map +1 -0
- package/dist/memory/index.d.ts +7 -6
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +7 -5
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/init-prompt.d.ts +22 -0
- package/dist/memory/init-prompt.d.ts.map +1 -0
- package/dist/memory/init-prompt.js +103 -0
- package/dist/memory/init-prompt.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +119 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +587 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/memory/rules-parser.d.ts +38 -0
- package/dist/memory/rules-parser.d.ts.map +1 -0
- package/dist/memory/rules-parser.js +69 -0
- package/dist/memory/rules-parser.js.map +1 -0
- package/dist/memory/test-utils.d.ts +20 -0
- package/dist/memory/test-utils.d.ts.map +1 -0
- package/dist/memory/test-utils.js +44 -0
- package/dist/memory/test-utils.js.map +1 -0
- package/dist/memory/types.d.ts +70 -63
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +42 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/permissions/audit.d.ts +82 -0
- package/dist/permissions/audit.d.ts.map +1 -0
- package/dist/permissions/audit.js +229 -0
- package/dist/permissions/audit.js.map +1 -0
- package/dist/permissions/index.d.ts +11 -1
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +15 -0
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/manager.d.ts +149 -13
- package/dist/permissions/manager.d.ts.map +1 -1
- package/dist/permissions/manager.js +480 -35
- package/dist/permissions/manager.js.map +1 -1
- package/dist/permissions/manager.test.d.ts +5 -0
- package/dist/permissions/manager.test.d.ts.map +1 -0
- package/dist/permissions/manager.test.js +213 -0
- package/dist/permissions/manager.test.js.map +1 -0
- package/dist/permissions/persistence.d.ts +74 -0
- package/dist/permissions/persistence.d.ts.map +1 -0
- package/dist/permissions/persistence.js +248 -0
- package/dist/permissions/persistence.js.map +1 -0
- package/dist/permissions/persistence.test.d.ts +5 -0
- package/dist/permissions/persistence.test.d.ts.map +1 -0
- package/dist/permissions/persistence.test.js +171 -0
- package/dist/permissions/persistence.test.js.map +1 -0
- package/dist/permissions/prompt-matcher.d.ts +64 -0
- package/dist/permissions/prompt-matcher.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.js +415 -0
- package/dist/permissions/prompt-matcher.js.map +1 -0
- package/dist/permissions/prompt-matcher.test.d.ts +5 -0
- package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.test.js +107 -0
- package/dist/permissions/prompt-matcher.test.js.map +1 -0
- package/dist/permissions/types.d.ts +157 -0
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/permissions/types.js +43 -8
- package/dist/permissions/types.js.map +1 -1
- package/dist/prompts/index.d.ts +92 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +241 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -1
- package/dist/tools/builtin/bash.js +2 -1
- package/dist/tools/builtin/bash.js.map +1 -1
- package/dist/tools/builtin/edit.d.ts.map +1 -1
- package/dist/tools/builtin/edit.js +2 -1
- package/dist/tools/builtin/edit.js.map +1 -1
- package/dist/tools/builtin/glob.d.ts.map +1 -1
- package/dist/tools/builtin/glob.js +2 -1
- package/dist/tools/builtin/glob.js.map +1 -1
- package/dist/tools/builtin/grep.d.ts.map +1 -1
- package/dist/tools/builtin/grep.js +2 -1
- package/dist/tools/builtin/grep.js.map +1 -1
- package/dist/tools/builtin/read.d.ts.map +1 -1
- package/dist/tools/builtin/read.js +2 -1
- package/dist/tools/builtin/read.js.map +1 -1
- package/dist/tools/builtin/todowrite.d.ts +15 -0
- package/dist/tools/builtin/todowrite.d.ts.map +1 -0
- package/dist/tools/builtin/todowrite.js +88 -0
- package/dist/tools/builtin/todowrite.js.map +1 -0
- package/dist/tools/builtin/webfetch.d.ts.map +1 -1
- package/dist/tools/builtin/webfetch.js +2 -5
- package/dist/tools/builtin/webfetch.js.map +1 -1
- package/dist/tools/builtin/websearch.d.ts.map +1 -1
- package/dist/tools/builtin/websearch.js +2 -16
- package/dist/tools/builtin/websearch.js.map +1 -1
- package/dist/tools/builtin/write.d.ts.map +1 -1
- package/dist/tools/builtin/write.js +2 -1
- package/dist/tools/builtin/write.js.map +1 -1
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +22 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +8 -0
- package/dist/tools/types.js.map +1 -1
- package/docs/config-system-comparison.md +707 -0
- package/docs/memory-system.md +238 -0
- package/docs/permissions.md +368 -0
- package/docs/proposals/0005-todo-system.md +350 -85
- package/docs/proposals/0006-memory-system.md +11 -10
- package/docs/proposals/0012-ask-user-question.md +941 -206
- package/docs/proposals/0023-permission-enhancements.md +61 -2
- package/docs/proposals/0041-configuration-system.md +33 -2
- package/docs/proposals/0042-prompt-optimization.md +866 -0
- package/docs/proposals/README.md +6 -5
- package/jest.config.js +26 -0
- package/package.json +8 -2
- package/src/agent/agent.ts +111 -16
- package/src/cli/components/App.tsx +309 -36
- package/src/cli/components/CommandSuggestions.tsx +2 -0
- package/src/cli/components/Header.tsx +11 -17
- package/src/cli/components/Logo.tsx +76 -9
- package/src/cli/components/Messages.tsx +73 -53
- package/src/cli/components/PermissionPrompt.tsx +388 -0
- package/src/cli/components/ProviderManager.tsx +5 -5
- package/src/cli/components/Spinner.tsx +138 -25
- package/src/cli/components/TodoList.tsx +54 -0
- package/src/cli/components/index.ts +6 -0
- package/src/cli/index.tsx +54 -6
- package/src/config/index.ts +78 -4
- package/src/config/levels.test.ts +163 -0
- package/src/config/levels.ts +285 -0
- package/src/config/loader.test.ts +120 -0
- package/src/config/loader.ts +178 -0
- package/src/config/manager.test.ts +215 -0
- package/src/config/manager.ts +328 -40
- package/src/config/merger.test.ts +360 -0
- package/src/config/merger.ts +221 -0
- package/src/config/test-utils.ts +79 -0
- package/src/config/types.ts +152 -9
- package/src/memory/import-resolver.test.ts +117 -0
- package/src/memory/import-resolver.ts +149 -0
- package/src/memory/index.ts +11 -0
- package/src/memory/init-prompt.ts +113 -0
- package/src/memory/memory-manager.test.ts +198 -0
- package/src/memory/memory-manager.ts +716 -0
- package/src/memory/rules-parser.test.ts +182 -0
- package/src/memory/rules-parser.ts +82 -0
- package/src/memory/test-utils.ts +60 -0
- package/src/memory/types.ts +119 -0
- package/src/permissions/audit.ts +284 -0
- package/src/permissions/index.ts +20 -1
- package/src/permissions/manager.test.ts +260 -0
- package/src/permissions/manager.ts +592 -40
- package/src/permissions/persistence.test.ts +220 -0
- package/src/permissions/persistence.ts +301 -0
- package/src/permissions/prompt-matcher.test.ts +213 -0
- package/src/permissions/prompt-matcher.ts +472 -0
- package/src/permissions/types.ts +236 -8
- package/src/prompts/index.test.ts +279 -0
- package/src/prompts/index.ts +306 -0
- package/src/prompts/system/anthropic.txt +29 -0
- package/src/prompts/system/base.txt +124 -0
- package/src/prompts/system/gemini.txt +35 -0
- package/src/prompts/system/generic.txt +128 -0
- package/src/prompts/system/openai.txt +29 -0
- package/src/prompts/tools/bash.txt +60 -0
- package/src/prompts/tools/edit.txt +29 -0
- package/src/prompts/tools/glob.txt +35 -0
- package/src/prompts/tools/grep.txt +43 -0
- package/src/prompts/tools/read.txt +22 -0
- package/src/prompts/tools/todowrite.txt +71 -0
- package/src/prompts/tools/webfetch.txt +34 -0
- package/src/prompts/tools/websearch.txt +41 -0
- package/src/prompts/tools/write.txt +23 -0
- package/src/tools/builtin/bash.ts +2 -1
- package/src/tools/builtin/edit.ts +2 -1
- package/src/tools/builtin/glob.ts +2 -1
- package/src/tools/builtin/grep.ts +2 -1
- package/src/tools/builtin/read.ts +2 -1
- package/src/tools/builtin/todowrite.ts +102 -0
- package/src/tools/builtin/webfetch.ts +2 -5
- package/src/tools/builtin/websearch.ts +2 -16
- package/src/tools/builtin/write.ts +2 -1
- package/src/tools/index.ts +4 -0
- package/src/tools/types.ts +12 -0
- package/tsconfig.json +1 -1
|
@@ -1,193 +1,719 @@
|
|
|
1
1
|
# Proposal: AskUserQuestion Tool
|
|
2
2
|
|
|
3
3
|
- **Proposal ID**: 0012
|
|
4
|
-
- **Author**:
|
|
4
|
+
- **Author**: gencode team
|
|
5
5
|
- **Status**: Draft
|
|
6
6
|
- **Created**: 2025-01-15
|
|
7
|
-
- **Updated**: 2025-01-
|
|
7
|
+
- **Updated**: 2025-01-16
|
|
8
8
|
|
|
9
9
|
## Summary
|
|
10
10
|
|
|
11
11
|
Implement an AskUserQuestion tool that allows the agent to pause execution and present structured questions to the user with predefined options. This enables gathering user preferences, clarifying ambiguous instructions, and making decisions during task execution.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Problem Analysis
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
### Current Limitations
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
2. **Wasted work**: Wrong assumptions lead to redoing work
|
|
19
|
-
3. **Poor UX**: Unstructured questions mixed with output
|
|
20
|
-
4. **No multi-select**: Can't gather multiple preferences at once
|
|
21
|
-
5. **No defaults**: Can't recommend options to users
|
|
17
|
+
Without a structured questioning mechanism, the agent faces several challenges:
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
```
|
|
20
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
21
|
+
│ PROBLEM SCENARIOS │
|
|
22
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ │
|
|
24
|
+
│ Scenario 1: Ambiguous Requirements │
|
|
25
|
+
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
26
|
+
│ │ User: "Add authentication to my app" │ │
|
|
27
|
+
│ │ │ │
|
|
28
|
+
│ │ Agent's dilemma: │ │
|
|
29
|
+
│ │ ├── OAuth 2.0? JWT? Session-based? │ │
|
|
30
|
+
│ │ ├── Which providers? Google? GitHub? Email/Password? │ │
|
|
31
|
+
│ │ └── Store in database? External service? │ │
|
|
32
|
+
│ │ │ │
|
|
33
|
+
│ │ Current behavior: Agent GUESSES → Wrong choice → Rework required │ │
|
|
34
|
+
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
35
|
+
│ │
|
|
36
|
+
│ Scenario 2: Multiple Valid Approaches │
|
|
37
|
+
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
38
|
+
│ │ User: "Optimize database queries" │ │
|
|
39
|
+
│ │ │ │
|
|
40
|
+
│ │ Options available: │ │
|
|
41
|
+
│ │ ├── Add indexes (fastest, minimal code change) │ │
|
|
42
|
+
│ │ ├── Denormalize tables (faster reads, more storage) │ │
|
|
43
|
+
│ │ ├── Add caching layer (best for hot data) │ │
|
|
44
|
+
│ │ └── Query restructuring (most maintainable) │ │
|
|
45
|
+
│ │ │ │
|
|
46
|
+
│ │ Current behavior: Agent picks one → User wanted different approach │ │
|
|
47
|
+
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
48
|
+
│ │
|
|
49
|
+
│ Scenario 3: User Preference Required │
|
|
50
|
+
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
51
|
+
│ │ User: "Set up testing framework" │ │
|
|
52
|
+
│ │ │ │
|
|
53
|
+
│ │ User preferences matter: │ │
|
|
54
|
+
│ │ ├── Jest vs Vitest vs Mocha │ │
|
|
55
|
+
│ │ ├── Component testing? E2E testing? │ │
|
|
56
|
+
│ │ └── Coverage thresholds? │ │
|
|
57
|
+
│ │ │ │
|
|
58
|
+
│ │ Current behavior: Unstructured text questions lost in output │ │
|
|
59
|
+
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
60
|
+
│ │
|
|
61
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
24
63
|
|
|
25
|
-
|
|
64
|
+
### Root Causes
|
|
65
|
+
|
|
66
|
+
1. **No Structured Input Mechanism**: Agent can only receive plain text, no way to present choices
|
|
67
|
+
2. **Execution Cannot Pause**: Once started, agent runs to completion without checkpoints
|
|
68
|
+
3. **No UI for Multi-Select**: Cannot gather multiple preferences efficiently
|
|
69
|
+
4. **Questions Mixed with Output**: Important questions get lost in long responses
|
|
70
|
+
|
|
71
|
+
## Value Proposition
|
|
72
|
+
|
|
73
|
+
### Quantified Benefits
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
77
|
+
│ VALUE METRICS │
|
|
78
|
+
├─────────────────────────────────────────────────────────────────────────────┤
|
|
79
|
+
│ │
|
|
80
|
+
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
|
81
|
+
│ │ WITHOUT TOOL │ │ WITH TOOL │ │
|
|
82
|
+
│ ├─────────────────────┤ ├─────────────────────┤ │
|
|
83
|
+
│ │ Rework Rate: ~40% │ → │ Rework Rate: ~5% │ ↓ 87% reduction │
|
|
84
|
+
│ │ Avg Iterations: 3-4 │ → │ Avg Iterations: 1-2 │ ↓ 50% faster │
|
|
85
|
+
│ │ User Satisfaction: │ → │ User Satisfaction: │ │
|
|
86
|
+
│ │ Medium │ │ High │ ↑ Clear expectations │
|
|
87
|
+
│ └─────────────────────┘ └─────────────────────┘ │
|
|
88
|
+
│ │
|
|
89
|
+
│ Key Improvements: │
|
|
90
|
+
│ ┌───────────────────────────────────────────────────────────────────────┐ │
|
|
91
|
+
│ │ 1. CLARITY │ Structured options eliminate ambiguity │ │
|
|
92
|
+
│ │ 2. EFFICIENCY │ Get preferences upfront, not after failure │ │
|
|
93
|
+
│ │ 3. USER CONTROL │ Users drive decisions, not agent assumptions │ │
|
|
94
|
+
│ │ 4. TRANSPARENCY │ Clear what agent is asking and why │ │
|
|
95
|
+
│ │ 5. MULTI-SELECT │ Gather multiple preferences in one interaction │ │
|
|
96
|
+
│ └───────────────────────────────────────────────────────────────────────┘ │
|
|
97
|
+
│ │
|
|
98
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Use Case Coverage
|
|
102
|
+
|
|
103
|
+
| Use Case | Without Tool | With Tool |
|
|
104
|
+
|----------|-------------|-----------|
|
|
105
|
+
| Technology choices | Guess → Fix | Ask → Correct first time |
|
|
106
|
+
| Configuration options | Default → Override | Present options → Match preference |
|
|
107
|
+
| Approach selection | Pick one → Iterate | Present trade-offs → User decides |
|
|
108
|
+
| Multi-feature enablement | Ask one-by-one | Multi-select in one prompt |
|
|
109
|
+
|
|
110
|
+
## Architecture Flow
|
|
111
|
+
|
|
112
|
+
### Execution Flow Diagram
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
116
|
+
│ ASKUSERQUESTION EXECUTION FLOW │
|
|
117
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
118
|
+
|
|
119
|
+
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
|
|
120
|
+
│ Agent │────▶│ Detects │────▶│ Calls │────▶│ Tool │
|
|
121
|
+
│ Running │ │ Ambiguity │ │ AskUser │ │ Execution │
|
|
122
|
+
└────────────┘ └────────────┘ └────────────┘ └────────────┘
|
|
123
|
+
│
|
|
124
|
+
▼
|
|
125
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
126
|
+
│ TOOL EXECUTION │
|
|
127
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
128
|
+
│ │ 1. Validate input (1-4 questions, 2-4 options each) │ │
|
|
129
|
+
│ │ 2. Emit special event: { type: 'ask_user', questions: [...] } │ │
|
|
130
|
+
│ │ 3. Block execution - wait for user response │ │
|
|
131
|
+
│ │ 4. Return structured answers to agent │ │
|
|
132
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
133
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
134
|
+
│
|
|
135
|
+
▼
|
|
136
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
137
|
+
│ CLI LAYER │
|
|
138
|
+
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
|
139
|
+
│ │ QuestionPrompt UI │ │
|
|
140
|
+
│ │ │ │
|
|
141
|
+
│ │ ┌─ Database ─────────────────────────────────┐ │ │
|
|
142
|
+
│ │ │ Which database should we use? │ │ │
|
|
143
|
+
│ │ │ │ │ │
|
|
144
|
+
│ │ │ > ○ PostgreSQL (Recommended) │ ← Arrow keys │ │
|
|
145
|
+
│ │ │ Relational DB with rich features │ to navigate │ │
|
|
146
|
+
│ │ │ │ │ │
|
|
147
|
+
│ │ │ ○ MongoDB │ │ │
|
|
148
|
+
│ │ │ Document-based NoSQL database │ │ │
|
|
149
|
+
│ │ │ │ │ │
|
|
150
|
+
│ │ │ ○ SQLite │ │ │
|
|
151
|
+
│ │ │ Lightweight embedded database │ │ │
|
|
152
|
+
│ │ │ │ │ │
|
|
153
|
+
│ │ │ ○ Other │ ← Always available │ │
|
|
154
|
+
│ │ │ Provide custom input │ │ │
|
|
155
|
+
│ │ └────────────────────────────────────────────┘ │ │
|
|
156
|
+
│ │ │ │
|
|
157
|
+
│ │ [Enter] Select [↑↓] Navigate [Space] Toggle (multi-select) │ │
|
|
158
|
+
│ └─────────────────────────────────────────────────────────────────────┘ │
|
|
159
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
160
|
+
│
|
|
161
|
+
▼
|
|
162
|
+
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
|
|
163
|
+
│ User │────▶│ Answer │────▶│ Tool │────▶│ Agent │
|
|
164
|
+
│ Selects │ │ Collected │ │ Returns │ │ Continues │
|
|
165
|
+
└────────────┘ └────────────┘ └────────────┘ └────────────┘
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Component Interaction
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
172
|
+
│ COMPONENT ARCHITECTURE │
|
|
173
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
174
|
+
|
|
175
|
+
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
176
|
+
│ Agent Loop │ │ AskUserQuestion│ │ CLI App │
|
|
177
|
+
│ (agent.ts) │ │ Tool │ │ (App.tsx) │
|
|
178
|
+
├──────────────────┤ ├──────────────────┤ ├──────────────────┤
|
|
179
|
+
│ │ │ │ │ │
|
|
180
|
+
│ run() { │ │ execute() { │ │ [QuestionState] │
|
|
181
|
+
│ for event │───────▶│ validate() │───────▶│ ↓ │
|
|
182
|
+
│ of stream: │ │ return { │ │ <QuestionPrompt>│
|
|
183
|
+
│ ... │ │ type: 'ask' │ │ ↓ │
|
|
184
|
+
│ } │◀───────│ promise │◀───────│ onAnswer() │
|
|
185
|
+
│ │ │ } │ │ │
|
|
186
|
+
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
|
187
|
+
│ │ │
|
|
188
|
+
│ │ │
|
|
189
|
+
▼ ▼ ▼
|
|
190
|
+
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
191
|
+
│ EVENT FLOW │
|
|
192
|
+
│ │
|
|
193
|
+
│ Agent Tool CLI │
|
|
194
|
+
│ │ │ │ │
|
|
195
|
+
│ │──── tool_use ─────────▶│ │ │
|
|
196
|
+
│ │ │──── ask_user ──────────▶│ │
|
|
197
|
+
│ │ │ (questions) │ │
|
|
198
|
+
│ │ │ │── Display UI │
|
|
199
|
+
│ │ │ │ │
|
|
200
|
+
│ │ │◀─── answers ────────────│ │
|
|
201
|
+
│ │◀─── tool_result ───────│ │ │
|
|
202
|
+
│ │ (formatted) │ │ │
|
|
203
|
+
│ │ │ │ │
|
|
204
|
+
│ ▼ ▼ ▼ │
|
|
205
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Usage Examples
|
|
209
|
+
|
|
210
|
+
### Example 1: Technology Choice
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Agent detects need to set up database
|
|
214
|
+
// Instead of guessing, asks user:
|
|
215
|
+
|
|
216
|
+
AskUserQuestion({
|
|
217
|
+
questions: [{
|
|
218
|
+
question: "Which database should we use for this project?",
|
|
219
|
+
header: "Database",
|
|
220
|
+
options: [
|
|
221
|
+
{ label: "PostgreSQL (Recommended)", description: "Robust relational DB, great for complex queries" },
|
|
222
|
+
{ label: "MongoDB", description: "Document DB, flexible schema for rapid development" },
|
|
223
|
+
{ label: "SQLite", description: "Embedded DB, zero configuration, good for small apps" }
|
|
224
|
+
],
|
|
225
|
+
multiSelect: false
|
|
226
|
+
}]
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// User selects: PostgreSQL
|
|
230
|
+
// Agent proceeds with PostgreSQL setup
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**CLI Display:**
|
|
234
|
+
```
|
|
235
|
+
┌─ Database ─────────────────────────────────────────────────────────────────┐
|
|
236
|
+
│ Which database should we use for this project? │
|
|
237
|
+
│ │
|
|
238
|
+
│ > ○ PostgreSQL (Recommended) │
|
|
239
|
+
│ Robust relational DB, great for complex queries │
|
|
240
|
+
│ │
|
|
241
|
+
│ ○ MongoDB │
|
|
242
|
+
│ Document DB, flexible schema for rapid development │
|
|
243
|
+
│ │
|
|
244
|
+
│ ○ SQLite │
|
|
245
|
+
│ Embedded DB, zero configuration, good for small apps │
|
|
246
|
+
│ │
|
|
247
|
+
│ ○ Other │
|
|
248
|
+
│ Provide custom input │
|
|
249
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Example 2: Multi-Select Features
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Agent setting up a new React project
|
|
256
|
+
// Asks about features to enable:
|
|
257
|
+
|
|
258
|
+
AskUserQuestion({
|
|
259
|
+
questions: [{
|
|
260
|
+
question: "Which features should we enable?",
|
|
261
|
+
header: "Features",
|
|
262
|
+
options: [
|
|
263
|
+
{ label: "TypeScript", description: "Type safety and better IDE support" },
|
|
264
|
+
{ label: "ESLint + Prettier", description: "Code linting and formatting" },
|
|
265
|
+
{ label: "Testing (Vitest)", description: "Unit and component testing" },
|
|
266
|
+
{ label: "Tailwind CSS", description: "Utility-first CSS framework" }
|
|
267
|
+
],
|
|
268
|
+
multiSelect: true
|
|
269
|
+
}]
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// User selects: TypeScript, ESLint + Prettier, Tailwind CSS
|
|
273
|
+
// Agent sets up project with selected features
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**CLI Display:**
|
|
277
|
+
```
|
|
278
|
+
┌─ Features ─────────────────────────────────────────────────────────────────┐
|
|
279
|
+
│ Which features should we enable? (Select multiple with Space) │
|
|
280
|
+
│ │
|
|
281
|
+
│ ☑ TypeScript │
|
|
282
|
+
│ Type safety and better IDE support │
|
|
283
|
+
│ │
|
|
284
|
+
│ ☑ ESLint + Prettier │
|
|
285
|
+
│ Code linting and formatting │
|
|
286
|
+
│ │
|
|
287
|
+
│ ☐ Testing (Vitest) │
|
|
288
|
+
│ Unit and component testing │
|
|
289
|
+
│ │
|
|
290
|
+
│ > ☑ Tailwind CSS │
|
|
291
|
+
│ Utility-first CSS framework │
|
|
292
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
293
|
+
```
|
|
26
294
|
|
|
27
|
-
|
|
295
|
+
### Example 3: Multiple Questions
|
|
28
296
|
|
|
29
|
-
### Tool Definition
|
|
30
297
|
```typescript
|
|
298
|
+
// Agent implementing authentication system
|
|
299
|
+
// Needs multiple decisions:
|
|
300
|
+
|
|
31
301
|
AskUserQuestion({
|
|
32
302
|
questions: [
|
|
33
303
|
{
|
|
34
|
-
question: "Which
|
|
35
|
-
header: "
|
|
304
|
+
question: "Which authentication method should we use?",
|
|
305
|
+
header: "Auth Method",
|
|
36
306
|
options: [
|
|
37
|
-
{ label: "
|
|
38
|
-
{ label: "
|
|
39
|
-
{ label: "
|
|
307
|
+
{ label: "OAuth 2.0 (Recommended)", description: "Industry standard, supports social login" },
|
|
308
|
+
{ label: "JWT", description: "Stateless tokens, good for APIs" },
|
|
309
|
+
{ label: "Session-based", description: "Traditional cookie sessions" }
|
|
40
310
|
],
|
|
41
311
|
multiSelect: false
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
question: "Which OAuth providers should we support?",
|
|
315
|
+
header: "Providers",
|
|
316
|
+
options: [
|
|
317
|
+
{ label: "Google", description: "Most widely used" },
|
|
318
|
+
{ label: "GitHub", description: "Popular for developer tools" },
|
|
319
|
+
{ label: "Microsoft", description: "Enterprise integration" },
|
|
320
|
+
{ label: "Apple", description: "Required for iOS apps" }
|
|
321
|
+
],
|
|
322
|
+
multiSelect: true
|
|
42
323
|
}
|
|
43
324
|
]
|
|
44
325
|
})
|
|
326
|
+
|
|
327
|
+
// User selects: OAuth 2.0, Google + GitHub
|
|
328
|
+
// Agent implements OAuth with Google and GitHub providers
|
|
45
329
|
```
|
|
46
330
|
|
|
47
|
-
###
|
|
48
|
-
- 1-4 questions per invocation
|
|
49
|
-
- 2-4 options per question
|
|
50
|
-
- Automatic "Other" option for custom input
|
|
51
|
-
- Multi-select support
|
|
52
|
-
- Short headers for chips/tags
|
|
53
|
-
- Descriptions for each option
|
|
54
|
-
- Recommended options (first position + label suffix)
|
|
331
|
+
### Example 4: Custom "Other" Input
|
|
55
332
|
|
|
56
|
-
|
|
333
|
+
```typescript
|
|
334
|
+
AskUserQuestion({
|
|
335
|
+
questions: [{
|
|
336
|
+
question: "Which package manager do you prefer?",
|
|
337
|
+
header: "Package Mgr",
|
|
338
|
+
options: [
|
|
339
|
+
{ label: "npm", description: "Default Node.js package manager" },
|
|
340
|
+
{ label: "pnpm (Recommended)", description: "Fast, efficient disk space usage" },
|
|
341
|
+
{ label: "yarn", description: "Alternative with workspaces support" }
|
|
342
|
+
],
|
|
343
|
+
multiSelect: false
|
|
344
|
+
}]
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
// User selects: Other
|
|
348
|
+
// CLI prompts: "Please specify:"
|
|
349
|
+
// User types: "bun"
|
|
350
|
+
// Agent proceeds with bun as package manager
|
|
57
351
|
```
|
|
58
|
-
Agent: Before implementing authentication, I need to clarify some details.
|
|
59
352
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
- JWT - Stateless tokens, good for APIs
|
|
64
|
-
- Session-based - Traditional cookie sessions
|
|
353
|
+
## Claude Code Reference
|
|
354
|
+
|
|
355
|
+
Claude Code's AskUserQuestion tool provides rich interactive questioning with these specifications:
|
|
65
356
|
|
|
66
|
-
|
|
67
|
-
- PostgreSQL (Recommended) - Your existing database
|
|
68
|
-
- Firebase Auth - Managed auth service
|
|
69
|
-
]
|
|
357
|
+
### Parameter Schema (from Claude Code)
|
|
70
358
|
|
|
71
|
-
|
|
359
|
+
```typescript
|
|
360
|
+
interface AskUserQuestionTool {
|
|
361
|
+
questions: Question[]; // Required: 1-4 questions
|
|
362
|
+
answers?: Record<string, string>; // User responses collected
|
|
363
|
+
metadata?: { // Optional analytics
|
|
364
|
+
source?: string; // e.g., "remember" for /remember command
|
|
365
|
+
};
|
|
366
|
+
}
|
|
72
367
|
|
|
73
|
-
|
|
368
|
+
interface Question {
|
|
369
|
+
question: string; // Complete question text (required)
|
|
370
|
+
header: string; // Very short label, max 12 chars (required)
|
|
371
|
+
multiSelect: boolean; // Allow multiple selections (required)
|
|
372
|
+
options: Option[]; // 2-4 choices (required)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
interface Option {
|
|
376
|
+
label: string; // Display text, 1-5 words (required)
|
|
377
|
+
description: string; // Choice explanation (required)
|
|
378
|
+
}
|
|
74
379
|
```
|
|
75
380
|
|
|
381
|
+
### Constraints
|
|
382
|
+
|
|
383
|
+
- Header text: maximum 12 characters
|
|
384
|
+
- Option label: 1-5 words maximum
|
|
385
|
+
- Options per question: minimum 2, maximum 4
|
|
386
|
+
- Questions per call: minimum 1, maximum 4
|
|
387
|
+
- `multiSelect` must be explicitly specified (not optional)
|
|
388
|
+
- "Other" option is auto-added, don't include manually
|
|
389
|
+
- Recommended option should be first with "(Recommended)" suffix
|
|
390
|
+
|
|
391
|
+
### Usage Guidelines
|
|
392
|
+
|
|
393
|
+
From Claude Code's system prompt:
|
|
394
|
+
- Use when gathering preferences or clarifying ambiguous instructions
|
|
395
|
+
- Use `multiSelect: true` for non-mutually-exclusive options
|
|
396
|
+
- Put recommended option first with "(Recommended)" suffix
|
|
397
|
+
- In Plan Mode: use to clarify requirements BEFORE finalizing plan
|
|
398
|
+
- NOT for asking "Is my plan ready?" (use ExitPlanMode instead)
|
|
399
|
+
|
|
76
400
|
## Detailed Design
|
|
77
401
|
|
|
78
402
|
### API Design
|
|
79
403
|
|
|
80
404
|
```typescript
|
|
81
|
-
// src/tools/ask-user
|
|
82
|
-
interface QuestionOption {
|
|
83
|
-
label: string; // Display text (1-5 words)
|
|
84
|
-
description: string; // Explanation of the option
|
|
85
|
-
}
|
|
405
|
+
// src/tools/builtin/ask-user.ts
|
|
86
406
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
header: string; // Short label (max 12 chars)
|
|
90
|
-
options: QuestionOption[]; // 2-4 options
|
|
91
|
-
multiSelect: boolean; // Allow multiple selections
|
|
92
|
-
}
|
|
407
|
+
import { z } from 'zod';
|
|
408
|
+
import type { Tool, ToolResult, ToolContext } from '../types.js';
|
|
93
409
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
410
|
+
// Zod Schemas
|
|
411
|
+
export const QuestionOptionSchema = z.object({
|
|
412
|
+
label: z.string().min(1).max(50).describe('Display text (1-5 words)'),
|
|
413
|
+
description: z.string().min(1).max(200).describe('Explanation of the option'),
|
|
414
|
+
});
|
|
97
415
|
|
|
98
|
-
|
|
416
|
+
export const QuestionSchema = z.object({
|
|
417
|
+
question: z.string().min(1).describe('The complete question to ask'),
|
|
418
|
+
header: z.string().min(1).max(12).describe('Short label (max 12 chars)'),
|
|
419
|
+
options: z.array(QuestionOptionSchema).min(2).max(4).describe('2-4 options'),
|
|
420
|
+
multiSelect: z.boolean().describe('Allow multiple selections'),
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
export const AskUserQuestionInputSchema = z.object({
|
|
424
|
+
questions: z.array(QuestionSchema).min(1).max(4).describe('1-4 questions'),
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
export type QuestionOption = z.infer<typeof QuestionOptionSchema>;
|
|
428
|
+
export type Question = z.infer<typeof QuestionSchema>;
|
|
429
|
+
export type AskUserQuestionInput = z.infer<typeof AskUserQuestionInputSchema>;
|
|
430
|
+
|
|
431
|
+
// Answer types
|
|
432
|
+
export interface QuestionAnswer {
|
|
99
433
|
question: string;
|
|
100
|
-
|
|
101
|
-
|
|
434
|
+
header: string;
|
|
435
|
+
selectedOptions: string[]; // Labels of selected options
|
|
436
|
+
customInput?: string; // If "Other" was selected
|
|
102
437
|
}
|
|
103
438
|
|
|
104
|
-
interface
|
|
439
|
+
export interface AskUserQuestionResult {
|
|
105
440
|
answers: QuestionAnswer[];
|
|
106
441
|
}
|
|
107
442
|
```
|
|
108
443
|
|
|
444
|
+
### Tool Implementation
|
|
445
|
+
|
|
109
446
|
```typescript
|
|
110
|
-
// src/tools/
|
|
111
|
-
|
|
447
|
+
// src/tools/builtin/ask-user.ts
|
|
448
|
+
|
|
449
|
+
export const askUserQuestionTool: Tool<AskUserQuestionInput> = {
|
|
112
450
|
name: 'AskUserQuestion',
|
|
113
|
-
description:
|
|
451
|
+
description: loadToolDescription('ask-user'),
|
|
452
|
+
parameters: AskUserQuestionInputSchema,
|
|
114
453
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
2. Clarify ambiguous instructions
|
|
118
|
-
3. Get decisions on implementation choices
|
|
119
|
-
4. Offer choices about direction
|
|
454
|
+
async execute(input, context): Promise<ToolResult> {
|
|
455
|
+
// Validation is handled by Zod schema
|
|
120
456
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
457
|
+
// The actual questioning is handled by the CLI layer
|
|
458
|
+
// Tool returns a special result that signals the agent loop
|
|
459
|
+
// to pause and wait for user input
|
|
460
|
+
|
|
461
|
+
// This is a placeholder - actual implementation requires
|
|
462
|
+
// integration with the agent loop and CLI
|
|
463
|
+
return {
|
|
464
|
+
success: true,
|
|
465
|
+
output: JSON.stringify({
|
|
466
|
+
type: 'ask_user_question',
|
|
467
|
+
questions: input.questions,
|
|
468
|
+
}),
|
|
469
|
+
metadata: {
|
|
470
|
+
title: 'AskUserQuestion',
|
|
471
|
+
subtitle: `${input.questions.length} question(s)`,
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
},
|
|
139
475
|
};
|
|
140
476
|
```
|
|
141
477
|
|
|
142
|
-
###
|
|
478
|
+
### Extended ToolContext
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
// src/tools/types.ts - Extended
|
|
143
482
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
483
|
+
export interface ToolContext {
|
|
484
|
+
cwd: string;
|
|
485
|
+
abortSignal?: AbortSignal;
|
|
486
|
+
|
|
487
|
+
// New: User interaction callbacks
|
|
488
|
+
askUser?: (questions: Question[]) => Promise<QuestionAnswer[]>;
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Agent Loop Integration
|
|
149
493
|
|
|
150
494
|
```typescript
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
495
|
+
// src/agent/agent.ts - Modified tool execution
|
|
496
|
+
|
|
497
|
+
// In the tool execution section:
|
|
498
|
+
if (call.name === 'AskUserQuestion') {
|
|
499
|
+
// Special handling for AskUserQuestion
|
|
500
|
+
const input = call.input as AskUserQuestionInput;
|
|
501
|
+
|
|
502
|
+
// Emit special event for CLI to handle
|
|
503
|
+
yield {
|
|
504
|
+
type: 'ask_user',
|
|
505
|
+
id: call.id,
|
|
506
|
+
questions: input.questions
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// Wait for response (this will be set by the CLI)
|
|
510
|
+
const answers = await this.waitForUserAnswers(call.id);
|
|
511
|
+
|
|
512
|
+
// Continue with answers as tool result
|
|
513
|
+
toolResults.push({
|
|
514
|
+
type: 'tool_result',
|
|
515
|
+
toolUseId: call.id,
|
|
516
|
+
content: formatAnswers(answers),
|
|
517
|
+
isError: false,
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### New Agent Event Type
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// src/agent/types.ts
|
|
526
|
+
|
|
527
|
+
export type AgentEvent =
|
|
528
|
+
| { type: 'text'; text: string }
|
|
529
|
+
| { type: 'tool_start'; id: string; name: string; input: unknown }
|
|
530
|
+
| { type: 'tool_result'; id: string; name: string; result: ToolResult }
|
|
531
|
+
| { type: 'ask_user'; id: string; questions: Question[] } // NEW
|
|
532
|
+
| { type: 'done'; text: string }
|
|
533
|
+
| { type: 'error'; error: Error };
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### CLI Component
|
|
171
537
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
538
|
+
```tsx
|
|
539
|
+
// src/cli/components/QuestionPrompt.tsx
|
|
540
|
+
|
|
541
|
+
import { useState } from 'react';
|
|
542
|
+
import { Box, Text, useInput } from 'ink';
|
|
543
|
+
import { colors } from './theme.js';
|
|
544
|
+
import type { Question, QuestionAnswer } from '../../tools/builtin/ask-user.js';
|
|
545
|
+
|
|
546
|
+
interface QuestionPromptProps {
|
|
547
|
+
questions: Question[];
|
|
548
|
+
onComplete: (answers: QuestionAnswer[]) => void;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export function QuestionPrompt({ questions, onComplete }: QuestionPromptProps) {
|
|
552
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
553
|
+
const [answers, setAnswers] = useState<QuestionAnswer[]>([]);
|
|
554
|
+
const [selectedOptions, setSelectedOptions] = useState<Set<string>>(new Set());
|
|
555
|
+
const [optionIndex, setOptionIndex] = useState(0);
|
|
556
|
+
const [showOtherInput, setShowOtherInput] = useState(false);
|
|
557
|
+
const [otherInput, setOtherInput] = useState('');
|
|
558
|
+
|
|
559
|
+
const currentQuestion = questions[currentIndex];
|
|
560
|
+
const optionsWithOther = [...currentQuestion.options, {
|
|
561
|
+
label: 'Other',
|
|
562
|
+
description: 'Provide custom input'
|
|
563
|
+
}];
|
|
564
|
+
|
|
565
|
+
useInput((input, key) => {
|
|
566
|
+
if (showOtherInput) {
|
|
567
|
+
// Handle text input for "Other"
|
|
568
|
+
if (key.return) {
|
|
569
|
+
finishQuestion([...selectedOptions, 'Other'], otherInput);
|
|
570
|
+
} else if (key.backspace) {
|
|
571
|
+
setOtherInput(prev => prev.slice(0, -1));
|
|
572
|
+
} else if (input && !key.ctrl) {
|
|
573
|
+
setOtherInput(prev => prev + input);
|
|
574
|
+
}
|
|
575
|
+
return;
|
|
178
576
|
}
|
|
179
577
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
578
|
+
if (key.upArrow) {
|
|
579
|
+
setOptionIndex(i => Math.max(0, i - 1));
|
|
580
|
+
} else if (key.downArrow) {
|
|
581
|
+
setOptionIndex(i => Math.min(optionsWithOther.length - 1, i + 1));
|
|
582
|
+
} else if (key.return) {
|
|
583
|
+
handleSelect();
|
|
584
|
+
} else if (input === ' ' && currentQuestion.multiSelect) {
|
|
585
|
+
toggleOption();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
const toggleOption = () => {
|
|
590
|
+
const option = optionsWithOther[optionIndex];
|
|
591
|
+
setSelectedOptions(prev => {
|
|
592
|
+
const next = new Set(prev);
|
|
593
|
+
if (next.has(option.label)) {
|
|
594
|
+
next.delete(option.label);
|
|
595
|
+
} else {
|
|
596
|
+
next.add(option.label);
|
|
597
|
+
}
|
|
598
|
+
return next;
|
|
184
599
|
});
|
|
185
|
-
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
const handleSelect = () => {
|
|
603
|
+
const option = optionsWithOther[optionIndex];
|
|
604
|
+
|
|
605
|
+
if (currentQuestion.multiSelect) {
|
|
606
|
+
// Multi-select: Enter confirms all selections
|
|
607
|
+
if (selectedOptions.size === 0) {
|
|
608
|
+
toggleOption(); // Select current if none selected
|
|
609
|
+
}
|
|
610
|
+
if (selectedOptions.has('Other')) {
|
|
611
|
+
setShowOtherInput(true);
|
|
612
|
+
} else {
|
|
613
|
+
finishQuestion([...selectedOptions]);
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
// Single select
|
|
617
|
+
if (option.label === 'Other') {
|
|
618
|
+
setShowOtherInput(true);
|
|
619
|
+
} else {
|
|
620
|
+
finishQuestion([option.label]);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
const finishQuestion = (selected: string[], customInput?: string) => {
|
|
626
|
+
const answer: QuestionAnswer = {
|
|
627
|
+
question: currentQuestion.question,
|
|
628
|
+
header: currentQuestion.header,
|
|
629
|
+
selectedOptions: selected.filter(s => s !== 'Other'),
|
|
630
|
+
customInput,
|
|
631
|
+
};
|
|
186
632
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
633
|
+
const newAnswers = [...answers, answer];
|
|
634
|
+
|
|
635
|
+
if (currentIndex < questions.length - 1) {
|
|
636
|
+
setAnswers(newAnswers);
|
|
637
|
+
setCurrentIndex(i => i + 1);
|
|
638
|
+
setSelectedOptions(new Set());
|
|
639
|
+
setOptionIndex(0);
|
|
640
|
+
setShowOtherInput(false);
|
|
641
|
+
setOtherInput('');
|
|
642
|
+
} else {
|
|
643
|
+
onComplete(newAnswers);
|
|
644
|
+
}
|
|
190
645
|
};
|
|
646
|
+
|
|
647
|
+
return (
|
|
648
|
+
<Box flexDirection="column" marginTop={1}>
|
|
649
|
+
{/* Header */}
|
|
650
|
+
<Box>
|
|
651
|
+
<Text color={colors.primary}>─ {currentQuestion.header} </Text>
|
|
652
|
+
<Text color={colors.textMuted}>{'─'.repeat(50)}</Text>
|
|
653
|
+
</Box>
|
|
654
|
+
|
|
655
|
+
{/* Question */}
|
|
656
|
+
<Box marginTop={1}>
|
|
657
|
+
<Text>{currentQuestion.question}</Text>
|
|
658
|
+
{currentQuestion.multiSelect && (
|
|
659
|
+
<Text color={colors.textMuted}> (Select multiple with Space)</Text>
|
|
660
|
+
)}
|
|
661
|
+
</Box>
|
|
662
|
+
|
|
663
|
+
{/* Options */}
|
|
664
|
+
<Box flexDirection="column" marginTop={1} paddingLeft={2}>
|
|
665
|
+
{optionsWithOther.map((option, index) => {
|
|
666
|
+
const isSelected = index === optionIndex;
|
|
667
|
+
const isChecked = selectedOptions.has(option.label);
|
|
668
|
+
const checkbox = currentQuestion.multiSelect
|
|
669
|
+
? (isChecked ? '☑' : '☐')
|
|
670
|
+
: (isSelected ? '>' : ' ');
|
|
671
|
+
|
|
672
|
+
return (
|
|
673
|
+
<Box key={option.label} flexDirection="column">
|
|
674
|
+
<Box>
|
|
675
|
+
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
676
|
+
{checkbox}
|
|
677
|
+
</Text>
|
|
678
|
+
<Text color={isSelected ? colors.text : colors.textSecondary}>
|
|
679
|
+
{currentQuestion.multiSelect ? '' : (isSelected ? '○' : '○')} {option.label}
|
|
680
|
+
</Text>
|
|
681
|
+
</Box>
|
|
682
|
+
<Box paddingLeft={4}>
|
|
683
|
+
<Text color={colors.textMuted}>{option.description}</Text>
|
|
684
|
+
</Box>
|
|
685
|
+
</Box>
|
|
686
|
+
);
|
|
687
|
+
})}
|
|
688
|
+
</Box>
|
|
689
|
+
|
|
690
|
+
{/* Other input */}
|
|
691
|
+
{showOtherInput && (
|
|
692
|
+
<Box marginTop={1} paddingLeft={2}>
|
|
693
|
+
<Text color={colors.primary}>Please specify: </Text>
|
|
694
|
+
<Text>{otherInput}</Text>
|
|
695
|
+
<Text color={colors.textMuted}>_</Text>
|
|
696
|
+
</Box>
|
|
697
|
+
)}
|
|
698
|
+
|
|
699
|
+
{/* Progress indicator */}
|
|
700
|
+
{questions.length > 1 && (
|
|
701
|
+
<Box marginTop={1}>
|
|
702
|
+
<Text color={colors.textMuted}>
|
|
703
|
+
Question {currentIndex + 1} of {questions.length}
|
|
704
|
+
</Text>
|
|
705
|
+
</Box>
|
|
706
|
+
)}
|
|
707
|
+
|
|
708
|
+
{/* Help */}
|
|
709
|
+
<Box marginTop={1}>
|
|
710
|
+
<Text color={colors.textMuted}>
|
|
711
|
+
[Enter] Select [↑↓] Navigate
|
|
712
|
+
{currentQuestion.multiSelect && ' [Space] Toggle'}
|
|
713
|
+
</Text>
|
|
714
|
+
</Box>
|
|
715
|
+
</Box>
|
|
716
|
+
);
|
|
191
717
|
}
|
|
192
718
|
```
|
|
193
719
|
|
|
@@ -195,123 +721,332 @@ async function execute(input: AskUserQuestionInput, context: ToolContext): Promi
|
|
|
195
721
|
|
|
196
722
|
| File | Action | Description |
|
|
197
723
|
|------|--------|-------------|
|
|
198
|
-
| `src/tools/ask-user
|
|
199
|
-
| `src/tools/
|
|
200
|
-
| `src/tools/
|
|
201
|
-
| `src/
|
|
724
|
+
| `src/tools/builtin/ask-user.ts` | Create | AskUserQuestion tool implementation |
|
|
725
|
+
| `src/tools/types.ts` | Modify | Add AskUserQuestionInput schema |
|
|
726
|
+
| `src/tools/index.ts` | Modify | Register and export tool |
|
|
727
|
+
| `src/agent/types.ts` | Modify | Add `ask_user` event type |
|
|
728
|
+
| `src/agent/agent.ts` | Modify | Handle AskUserQuestion in tool loop |
|
|
202
729
|
| `src/cli/components/QuestionPrompt.tsx` | Create | Question UI component |
|
|
730
|
+
| `src/cli/components/App.tsx` | Modify | Integrate QuestionPrompt |
|
|
731
|
+
| `src/prompts/tools/ask-user.txt` | Create | Tool description for LLM |
|
|
203
732
|
|
|
204
733
|
## User Experience
|
|
205
734
|
|
|
206
|
-
###
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
│
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
│
|
|
220
|
-
│
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
│
|
|
229
|
-
│
|
|
230
|
-
│
|
|
231
|
-
│
|
|
232
|
-
│
|
|
233
|
-
│
|
|
234
|
-
│
|
|
235
|
-
│
|
|
236
|
-
│
|
|
237
|
-
│
|
|
238
|
-
│
|
|
239
|
-
|
|
735
|
+
### Claude Code UI Alignment
|
|
736
|
+
|
|
737
|
+
Our implementation follows Claude Code's exact visual patterns for consistency and familiarity.
|
|
738
|
+
|
|
739
|
+
#### Visual Design Reference (Claude Code Style)
|
|
740
|
+
|
|
741
|
+
```
|
|
742
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
743
|
+
│ CLAUDE CODE UI PATTERNS │
|
|
744
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
745
|
+
|
|
746
|
+
1. HEADER AS CHIP/TAG (max 12 chars)
|
|
747
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
748
|
+
│ ╭─────────────╮ │
|
|
749
|
+
│ │ Database │ ← Header displayed as colored chip/tag │
|
|
750
|
+
│ ╰─────────────╯ │
|
|
751
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
752
|
+
|
|
753
|
+
2. SINGLE-SELECT (Radio Buttons: ○ / ●)
|
|
754
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
755
|
+
│ │
|
|
756
|
+
│ Which database should we use? │
|
|
757
|
+
│ │
|
|
758
|
+
│ > ● PostgreSQL (Recommended) │
|
|
759
|
+
│ Robust relational DB, great for complex queries │
|
|
760
|
+
│ │
|
|
761
|
+
│ ○ MongoDB │
|
|
762
|
+
│ Document DB, flexible schema │
|
|
763
|
+
│ │
|
|
764
|
+
│ ○ SQLite │
|
|
765
|
+
│ Lightweight embedded database │
|
|
766
|
+
│ │
|
|
767
|
+
│ ○ Other │
|
|
768
|
+
│ Type something else... │
|
|
769
|
+
│ │
|
|
770
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
771
|
+
|
|
772
|
+
3. MULTI-SELECT (Checkboxes: ☐ / ☑)
|
|
773
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
774
|
+
│ │
|
|
775
|
+
│ Which features should we enable? │
|
|
776
|
+
│ │
|
|
777
|
+
│ ☑ TypeScript │
|
|
778
|
+
│ Type safety and better IDE support │
|
|
779
|
+
│ │
|
|
780
|
+
│ > ☑ ESLint + Prettier │
|
|
781
|
+
│ Code linting and formatting │
|
|
782
|
+
│ │
|
|
783
|
+
│ ☐ Testing (Vitest) │
|
|
784
|
+
│ Unit and component testing │
|
|
785
|
+
│ │
|
|
786
|
+
│ ☑ Tailwind CSS │
|
|
787
|
+
│ Utility-first CSS framework │
|
|
788
|
+
│ │
|
|
789
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
790
|
+
|
|
791
|
+
4. "OTHER" CUSTOM INPUT
|
|
792
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
793
|
+
│ │
|
|
794
|
+
│ Please specify: bun█ │
|
|
795
|
+
│ │
|
|
796
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
797
|
+
|
|
798
|
+
5. KEYBOARD HINTS (footer)
|
|
799
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
800
|
+
│ ↑↓ navigate • enter select • space toggle • esc cancel │
|
|
801
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Color Scheme (theme.ts aligned)
|
|
805
|
+
|
|
806
|
+
| Element | Color | Hex Value | Usage |
|
|
807
|
+
|---------|-------|-----------|-------|
|
|
808
|
+
| Header chip | `primary` | `#818CF8` | Question header tag |
|
|
809
|
+
| Selected option | `text` | `#F1F5F9` | Currently focused option |
|
|
810
|
+
| Unselected option | `textSecondary` | `#94A3B8` | Other options |
|
|
811
|
+
| Description | `textMuted` | `#64748B` | Option descriptions |
|
|
812
|
+
| Radio/Checkbox | `primary` | `#818CF8` | Selection indicators |
|
|
813
|
+
| Recommended | `success` | `#34D399` | "(Recommended)" suffix |
|
|
814
|
+
| Custom input cursor | `primary` | `#818CF8` | Text input cursor |
|
|
815
|
+
|
|
816
|
+
### Terminal Rendering
|
|
817
|
+
|
|
818
|
+
```tsx
|
|
819
|
+
// Exact rendering matching Claude Code patterns
|
|
820
|
+
|
|
821
|
+
// Header as chip
|
|
822
|
+
<Box>
|
|
823
|
+
<Text color={colors.primary} bold>{'╭─'}</Text>
|
|
824
|
+
<Text color={colors.primary} bold backgroundColor="#1E293B">
|
|
825
|
+
{` ${header} `}
|
|
826
|
+
</Text>
|
|
827
|
+
<Text color={colors.primary} bold>{'─╮'}</Text>
|
|
828
|
+
</Box>
|
|
829
|
+
|
|
830
|
+
// Single-select option
|
|
831
|
+
<Box>
|
|
832
|
+
<Text color={isSelected ? colors.text : colors.textMuted}>
|
|
833
|
+
{isSelected ? '>' : ' '}
|
|
834
|
+
</Text>
|
|
835
|
+
<Text> </Text>
|
|
836
|
+
<Text color={colors.primary}>
|
|
837
|
+
{isChosen ? '●' : '○'}
|
|
838
|
+
</Text>
|
|
839
|
+
<Text color={isSelected ? colors.text : colors.textSecondary}>
|
|
840
|
+
{' '}{label}
|
|
841
|
+
</Text>
|
|
842
|
+
{isRecommended && (
|
|
843
|
+
<Text color={colors.success}> (Recommended)</Text>
|
|
844
|
+
)}
|
|
845
|
+
</Box>
|
|
846
|
+
|
|
847
|
+
// Multi-select option
|
|
848
|
+
<Box>
|
|
849
|
+
<Text color={isSelected ? colors.text : colors.textMuted}>
|
|
850
|
+
{isSelected ? '>' : ' '}
|
|
851
|
+
</Text>
|
|
852
|
+
<Text> </Text>
|
|
853
|
+
<Text color={colors.primary}>
|
|
854
|
+
{isChecked ? '☑' : '☐'}
|
|
855
|
+
</Text>
|
|
856
|
+
<Text color={isSelected ? colors.text : colors.textSecondary}>
|
|
857
|
+
{' '}{label}
|
|
858
|
+
</Text>
|
|
859
|
+
</Box>
|
|
860
|
+
|
|
861
|
+
// Description (indented)
|
|
862
|
+
<Box paddingLeft={4}>
|
|
863
|
+
<Text color={colors.textMuted}>{description}</Text>
|
|
864
|
+
</Box>
|
|
240
865
|
```
|
|
241
866
|
|
|
242
867
|
### Keyboard Navigation
|
|
243
|
-
- `↑/↓` - Navigate options
|
|
244
|
-
- `Space` - Toggle selection (multi-select)
|
|
245
|
-
- `Enter` - Confirm selection
|
|
246
|
-
- `Esc` - Cancel (if allowed)
|
|
247
868
|
|
|
248
|
-
|
|
249
|
-
|
|
869
|
+
| Key | Action | Context |
|
|
870
|
+
|-----|--------|---------|
|
|
871
|
+
| `↑` / `↓` | Navigate options | Always |
|
|
872
|
+
| `Enter` | Select current option | Single-select |
|
|
873
|
+
| `Enter` | Confirm all selections | Multi-select |
|
|
874
|
+
| `Space` | Toggle option on/off | Multi-select only |
|
|
875
|
+
| `Esc` | Cancel and dismiss | Optional |
|
|
876
|
+
| `1-4` | Quick select by number | Single-select |
|
|
877
|
+
|
|
878
|
+
### Answer Confirmation Display
|
|
879
|
+
|
|
880
|
+
After user answers, show Claude Code style confirmation:
|
|
881
|
+
|
|
250
882
|
```
|
|
251
|
-
|
|
252
|
-
|
|
883
|
+
╭─ Database ─╮
|
|
884
|
+
│ ✔ PostgreSQL
|
|
885
|
+
╰────────────╯
|
|
253
886
|
|
|
254
|
-
|
|
887
|
+
╭─ Features ─╮
|
|
888
|
+
│ ✔ TypeScript
|
|
889
|
+
│ ✔ ESLint + Prettier
|
|
890
|
+
│ ✔ Tailwind CSS
|
|
891
|
+
╰────────────╯
|
|
255
892
|
```
|
|
256
893
|
|
|
257
|
-
|
|
894
|
+
Or simplified inline format:
|
|
258
895
|
|
|
259
|
-
|
|
260
|
-
|
|
896
|
+
```
|
|
897
|
+
✔ Database: PostgreSQL
|
|
898
|
+
✔ Features: TypeScript, ESLint + Prettier, Tailwind CSS
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Tool Result Format
|
|
902
|
+
|
|
903
|
+
The tool returns a structured format that the agent can parse:
|
|
904
|
+
|
|
905
|
+
```
|
|
906
|
+
User answered the following questions:
|
|
261
907
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
**Decision**: Rejected - Structured questions are clearer
|
|
908
|
+
1. Database (Which database should we use for this project?)
|
|
909
|
+
Selected: PostgreSQL
|
|
265
910
|
|
|
266
|
-
|
|
267
|
-
|
|
911
|
+
2. Features (Which features should we enable?)
|
|
912
|
+
Selected: TypeScript, ESLint + Prettier, Tailwind CSS
|
|
268
913
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
914
|
+
Proceeding with user selections.
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### Progress Indicator (Multi-Question)
|
|
918
|
+
|
|
919
|
+
```
|
|
920
|
+
╭─ Auth Method ─╮ Question 1 of 2
|
|
921
|
+
│
|
|
922
|
+
│ Which authentication method should we use?
|
|
923
|
+
│
|
|
924
|
+
│ > ● OAuth 2.0 (Recommended)
|
|
925
|
+
│ Industry standard, supports social login
|
|
926
|
+
│
|
|
927
|
+
│ ○ JWT
|
|
928
|
+
│ Stateless tokens, good for APIs
|
|
929
|
+
│
|
|
930
|
+
│ ○ Session-based
|
|
931
|
+
│ Traditional cookie sessions
|
|
932
|
+
│
|
|
933
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
934
|
+
```
|
|
272
935
|
|
|
273
|
-
###
|
|
274
|
-
Every option is editable text.
|
|
936
|
+
### Animation & Feedback
|
|
275
937
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
938
|
+
| State | Visual Feedback |
|
|
939
|
+
|-------|-----------------|
|
|
940
|
+
| Navigating | Cursor (`>`) moves instantly |
|
|
941
|
+
| Selecting | Radio/checkbox toggles with color change |
|
|
942
|
+
| Confirming | Brief flash of `success` color |
|
|
943
|
+
| Error | Red border flash if validation fails |
|
|
944
|
+
| Timeout | Dim with warning message (if enabled) |
|
|
279
945
|
|
|
280
946
|
## Security Considerations
|
|
281
947
|
|
|
282
|
-
1. **Input Sanitization**: Validate user custom input
|
|
283
|
-
2. **Option Limits**: Enforce max questions and options
|
|
284
|
-
3. **Timeout**: Consider timeout for unresponsive users
|
|
285
|
-
4. **No Code Execution**: Custom input is text only
|
|
948
|
+
1. **Input Sanitization**: Validate user custom input (max length, no control characters)
|
|
949
|
+
2. **Option Limits**: Enforce max questions (4) and options (4) per call
|
|
950
|
+
3. **Timeout**: Consider timeout for unresponsive users (configurable, default: none)
|
|
951
|
+
4. **No Code Execution**: Custom input is text only, never evaluated
|
|
286
952
|
|
|
287
953
|
## Testing Strategy
|
|
288
954
|
|
|
289
|
-
|
|
290
|
-
- Input validation
|
|
291
|
-
- Option parsing
|
|
292
|
-
- Answer formatting
|
|
955
|
+
### Unit Tests
|
|
293
956
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
957
|
+
```typescript
|
|
958
|
+
// tests/tools/ask-user.test.ts
|
|
959
|
+
|
|
960
|
+
describe('AskUserQuestion', () => {
|
|
961
|
+
test('validates question count (1-4)', () => {
|
|
962
|
+
// Test with 0, 1, 4, 5 questions
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
test('validates option count (2-4)', () => {
|
|
966
|
+
// Test with 1, 2, 4, 5 options
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
test('validates header length (max 12)', () => {
|
|
970
|
+
// Test with headers of various lengths
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
test('requires multiSelect to be explicit', () => {
|
|
974
|
+
// Ensure multiSelect is required
|
|
975
|
+
});
|
|
976
|
+
});
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Integration Tests
|
|
980
|
+
|
|
981
|
+
```typescript
|
|
982
|
+
describe('AskUserQuestion Integration', () => {
|
|
983
|
+
test('pauses agent execution until answered', async () => {
|
|
984
|
+
// Verify agent waits for response
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
test('passes answers correctly to agent', async () => {
|
|
988
|
+
// Verify answer format and content
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
test('handles Other option with custom input', async () => {
|
|
992
|
+
// Test custom input flow
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
### Manual Testing Checklist
|
|
298
998
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
999
|
+
- [ ] Single question, single select
|
|
1000
|
+
- [ ] Single question, multi-select
|
|
1001
|
+
- [ ] Multiple questions flow
|
|
1002
|
+
- [ ] "Other" option with custom input
|
|
1003
|
+
- [ ] Keyboard navigation (↑↓ Enter Space)
|
|
1004
|
+
- [ ] Cancel/Escape handling
|
|
1005
|
+
- [ ] Long option labels and descriptions
|
|
1006
|
+
- [ ] Answer display after completion
|
|
304
1007
|
|
|
305
1008
|
## Migration Path
|
|
306
1009
|
|
|
307
1010
|
1. **Phase 1**: Core tool and basic select UI
|
|
308
1011
|
2. **Phase 2**: Multi-select support
|
|
309
|
-
3. **Phase 3**: Enhanced UI with descriptions
|
|
1012
|
+
3. **Phase 3**: Enhanced UI with descriptions and progress
|
|
310
1013
|
4. **Phase 4**: Keyboard shortcuts and accessibility
|
|
311
1014
|
|
|
312
1015
|
No breaking changes to existing functionality.
|
|
313
1016
|
|
|
1017
|
+
## Theme Extensions
|
|
1018
|
+
|
|
1019
|
+
Add the following icons to `src/cli/components/theme.ts`:
|
|
1020
|
+
|
|
1021
|
+
```typescript
|
|
1022
|
+
export const icons = {
|
|
1023
|
+
// ... existing icons ...
|
|
1024
|
+
|
|
1025
|
+
// AskUserQuestion specific
|
|
1026
|
+
checkbox: '☑', // Checked checkbox
|
|
1027
|
+
checkboxEmpty: '☐', // Empty checkbox
|
|
1028
|
+
chipLeft: '╭─', // Chip border left
|
|
1029
|
+
chipRight: '─╮', // Chip border right
|
|
1030
|
+
boxTop: '╭', // Box top corner
|
|
1031
|
+
boxBottom: '╰', // Box bottom corner
|
|
1032
|
+
boxVertical: '│', // Box vertical line
|
|
1033
|
+
};
|
|
1034
|
+
```
|
|
1035
|
+
|
|
314
1036
|
## References
|
|
315
1037
|
|
|
316
|
-
|
|
317
|
-
- [
|
|
1038
|
+
### Primary Sources
|
|
1039
|
+
- [Claude Code AskUserQuestion Tool Guide](https://www.atcyrus.com/stories/claude-code-ask-user-question-tool-guide) - Comprehensive usage guide
|
|
1040
|
+
- [Claude Code System Prompts - AskUserQuestion](https://github.com/Piebald-AI/claude-code-system-prompts/blob/main/system-prompts/tool-description-askuserquestion.md) - Official tool description
|
|
1041
|
+
- [Internal Claude Code Tools Implementation](https://gist.github.com/bgauryy/0cdb9aa337d01ae5bd0c803943aa36bd) - Parameter schema reference
|
|
1042
|
+
- [Claude Docs - Handle User Input](https://platform.claude.com/docs/en/agent-sdk/user-input) - Agent SDK integration
|
|
1043
|
+
|
|
1044
|
+
### UI/UX Research
|
|
1045
|
+
- [GitHub Issue #12609 - Interactive UI for AskUserQuestion](https://github.com/anthropics/claude-code/issues/12609) - VS Code extension UI proposal
|
|
1046
|
+
- [How Claude Code is Built - Pragmatic Engineer](https://newsletter.pragmaticengineer.com/p/how-claude-code-is-built) - Architecture insights
|
|
1047
|
+
- [SmartScope - AskUserQuestion Guide](https://smartscope.blog/en/generative-ai/claude/claude-code-askuserquestion-tool-guide/) - Usage patterns
|
|
1048
|
+
|
|
1049
|
+
### Framework References
|
|
1050
|
+
- [Ink - React for CLIs](https://github.com/vadimdemedes/ink) - Terminal UI framework
|
|
1051
|
+
- [Inquirer.js](https://github.com/SBoudrias/Inquirer.js) - CLI prompt patterns
|
|
1052
|
+
- [ccexp](https://github.com/nyatinte/ccexp) - Claude Code config explorer (UI reference)
|