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