dexto 1.5.1 → 1.5.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.
Files changed (55) hide show
  1. package/dist/agents/agent-registry.json +9 -0
  2. package/dist/agents/coding-agent/coding-agent.yml +37 -6
  3. package/dist/agents/explore-agent/explore-agent.yml +124 -0
  4. package/dist/cli/approval/cli-approval-handler.d.ts +28 -0
  5. package/dist/cli/approval/cli-approval-handler.d.ts.map +1 -0
  6. package/dist/cli/approval/cli-approval-handler.js +146 -0
  7. package/dist/cli/approval/index.d.ts +7 -0
  8. package/dist/cli/approval/index.d.ts.map +1 -0
  9. package/dist/cli/approval/index.js +6 -0
  10. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  11. package/dist/cli/commands/interactive-commands/general-commands.js +5 -2
  12. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  13. package/dist/cli/ink-cli/components/ApprovalPrompt.js +10 -2
  14. package/dist/cli/ink-cli/components/Footer.d.ts +3 -1
  15. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  16. package/dist/cli/ink-cli/components/Footer.js +4 -2
  17. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  18. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +8 -2
  19. package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
  20. package/dist/cli/ink-cli/components/TextBufferInput.js +20 -5
  21. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts +3 -1
  22. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  23. package/dist/cli/ink-cli/components/chat/MessageItem.js +40 -9
  24. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  25. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +4 -4
  26. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  27. package/dist/cli/ink-cli/components/modes/StaticCLI.js +10 -3
  28. package/dist/cli/ink-cli/components/renderers/GenericRenderer.js +1 -1
  29. package/dist/cli/ink-cli/components/renderers/SearchRenderer.d.ts.map +1 -1
  30. package/dist/cli/ink-cli/components/renderers/SearchRenderer.js +15 -1
  31. package/dist/cli/ink-cli/components/shared/MarkdownText.d.ts.map +1 -1
  32. package/dist/cli/ink-cli/components/shared/MarkdownText.js +4 -4
  33. package/dist/cli/ink-cli/constants/processingPhrases.d.ts.map +1 -1
  34. package/dist/cli/ink-cli/constants/processingPhrases.js +58 -48
  35. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  36. package/dist/cli/ink-cli/constants/tips.js +34 -32
  37. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  38. package/dist/cli/ink-cli/containers/InputContainer.js +18 -11
  39. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  40. package/dist/cli/ink-cli/containers/OverlayContainer.js +24 -1
  41. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  42. package/dist/cli/ink-cli/hooks/useCLIState.js +14 -1
  43. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  44. package/dist/cli/ink-cli/services/processStream.js +77 -13
  45. package/dist/cli/ink-cli/state/types.d.ts +17 -0
  46. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  47. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +24 -0
  48. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  49. package/dist/cli/ink-cli/utils/messageFormatting.js +131 -5
  50. package/dist/index.js +5 -15
  51. package/dist/webui/assets/index-CUVc7IDL.css +1 -0
  52. package/dist/webui/assets/{index-8j-KMkX1.js → index-SGm5dxhp.js} +208 -208
  53. package/dist/webui/index.html +2 -2
  54. package/package.json +7 -7
  55. package/dist/webui/assets/index-c_AX24V4.css +0 -1
@@ -134,6 +134,15 @@
134
134
  "tags": ["gaming", "gameboy", "pokemon", "emulator", "mcp"],
135
135
  "source": "gaming-agent/",
136
136
  "main": "gaming-agent.yml"
137
+ },
138
+ "explore-agent": {
139
+ "id": "explore-agent",
140
+ "name": "Explore Agent",
141
+ "description": "Fast, read-only agent for codebase exploration. Use for: 'explore the codebase', 'what's in this folder', 'how does X work', 'find where Y is handled', 'understand the architecture'. Optimized for speed with Haiku.",
142
+ "author": "Truffle AI",
143
+ "tags": ["explore", "search", "understand", "find", "research"],
144
+ "source": "explore-agent/",
145
+ "main": "explore-agent.yml"
137
146
  }
138
147
  }
139
148
  }
@@ -24,8 +24,27 @@ systemPrompt:
24
24
  - Debug issues by examining error messages and code structure
25
25
  - Refactor code following best practices and design patterns
26
26
  - Explain complex code concepts clearly
27
+ - Delegate exploration tasks to specialized sub-agents
27
28
 
28
- Guidelines:
29
+ ## Task Delegation
30
+
31
+ You have access to spawn_agent for delegating tasks to specialized sub-agents.
32
+
33
+ **When to delegate to explore-agent:**
34
+ - Open-ended exploration: "explore the codebase", "what's in this folder", "how does X work"
35
+ - Understanding architecture: "explain the project structure", "how are components organized"
36
+ - Finding patterns: "where is authentication handled", "find all API endpoints"
37
+ - Research tasks: "what testing framework is used", "how is state managed"
38
+
39
+ **When to use your own tools directly:**
40
+ - Specific file operations: "read src/index.ts", "edit the config file"
41
+ - Targeted searches: "find the User class", "grep for 'TODO'"
42
+ - Writing/editing code: any task that requires modifications
43
+ - Running commands: build, test, install dependencies
44
+
45
+ **Rule of thumb:** If the task requires understanding or exploring before you know what to do, delegate to explore-agent first. If you know exactly what file/function to target, use your tools directly.
46
+
47
+ ## Guidelines
29
48
  - Always read relevant code before making changes to understand context
30
49
  - Use glob_files to find files and grep_content to search within files
31
50
  - Test changes when possible using bash_exec
@@ -70,13 +89,14 @@ toolConfirmation:
70
89
  # Tool policies optimized for coding workflows
71
90
  toolPolicies:
72
91
  # Tools that never require approval (safe, read-only operations)
92
+ # Use qualified names: custom--{tool_id} for custom tools, internal--{tool_id} for internal tools
73
93
  alwaysAllow:
74
94
  - internal--ask_user
75
- - custom--filesystem-tools--read_file # Read files without approval
76
- - custom--filesystem-tools--glob_files # Search for files without approval
77
- - custom--filesystem-tools--grep_content # Search within files without approval
78
- - custom--process-tools--bash_output # Check background process output
79
- - custom--process-tools--kill_process # Kill processes without approval
95
+ - custom--read_file # Read files without approval
96
+ - custom--glob_files # Search for files without approval
97
+ - custom--grep_content # Search within files without approval
98
+ - custom--bash_output # Check background process output
99
+ - custom--kill_process # Kill processes without approval (only processes started by agent)
80
100
 
81
101
  # Tools that are always denied (dangerous operations)
82
102
  # Uncomment to restrict certain operations
@@ -101,6 +121,17 @@ customTools:
101
121
  enableBackups: false
102
122
  - type: process-tools
103
123
  securityLevel: moderate
124
+ - type: agent-spawner
125
+ maxConcurrentAgents: 5
126
+ defaultTimeout: 300000
127
+ allowSpawning: true
128
+ # List of agent IDs from the registry that can be spawned.
129
+ # Agent metadata (name, description) is pulled from the registry at runtime.
130
+ allowedAgents:
131
+ - explore-agent
132
+ # Agents with read-only tools that should have auto-approved tool calls
133
+ autoApproveAgents:
134
+ - explore-agent
104
135
 
105
136
  # Internal resources configuration - expanded for coding projects
106
137
  internalResources:
@@ -0,0 +1,124 @@
1
+ # Explore Agent Configuration
2
+ # Lightweight, read-only agent optimized for codebase exploration
3
+ # Designed to be spawned by other agents for research tasks
4
+
5
+ # No image bundle - uses only explicitly defined tools below
6
+
7
+ # System prompt optimized for exploration tasks
8
+ systemPrompt:
9
+ contributors:
10
+ - id: primary
11
+ type: static
12
+ priority: 0
13
+ content: |
14
+ You are a fast, focused exploration agent specialized in understanding codebases and finding information.
15
+
16
+ ## Your Mission
17
+ Quickly and thoroughly explore codebases to answer questions, find patterns, locate files, and understand architecture. You are optimized for speed and accuracy.
18
+
19
+ ## Available Tools
20
+ You have access to read-only tools:
21
+ - `glob_files` - Find files matching patterns (e.g., "src/**/*.ts", "*.config.js")
22
+ - `grep_content` - Search for text/patterns within files
23
+ - `read_file` - Read file contents
24
+
25
+ ## Exploration Strategy
26
+
27
+ ### For "quick" searches:
28
+ - Single glob or grep to find the target
29
+ - Read 1-2 most relevant files
30
+ - Return focused answer
31
+
32
+ ### For "medium" exploration:
33
+ - Multiple search patterns to find related files
34
+ - Read key files to understand connections
35
+ - Summarize findings with file references
36
+
37
+ ### For "very thorough" analysis:
38
+ - Comprehensive search across multiple naming conventions
39
+ - Trace imports, exports, and dependencies
40
+ - Map relationships between components
41
+ - Provide detailed analysis with evidence
42
+
43
+ ## Guidelines
44
+ - Start with broad searches, then narrow down
45
+ - Use glob for file discovery, grep for content search
46
+ - Try multiple naming conventions (camelCase, snake_case, kebab-case, PascalCase)
47
+ - Check common locations: src/, lib/, packages/, tests/, config/
48
+ - Report what you found AND what you didn't find
49
+ - Include file paths and line numbers in your response
50
+ - Be concise but complete
51
+
52
+ ## Response Format
53
+ Structure your response as:
54
+ 1. **Summary** - Brief answer to the question
55
+ 2. **Key Files** - List of relevant files found
56
+ 3. **Details** - Specific findings with code references
57
+ 4. **Notes** - Any caveats or areas that need further exploration
58
+
59
+ # LLM configuration - Haiku for speed and cost efficiency
60
+ llm:
61
+ provider: anthropic
62
+ model: claude-haiku-4-5-20251001
63
+ apiKey: $ANTHROPIC_API_KEY
64
+
65
+ # Minimal storage - in-memory only for ephemeral use
66
+ storage:
67
+ cache:
68
+ type: in-memory
69
+ database:
70
+ type: in-memory
71
+ blob:
72
+ type: in-memory
73
+
74
+ # Auto-approve all tools since they're read-only
75
+ toolConfirmation:
76
+ mode: auto-approve
77
+ allowedToolsStorage: memory
78
+
79
+ # No internal tools needed for exploration
80
+ internalTools: []
81
+
82
+ # Read-only filesystem tools only - explicitly enable only read operations
83
+ customTools:
84
+ - type: filesystem-tools
85
+ enabledTools: ["read_file", "glob_files", "grep_content"] # Read-only tools only
86
+ allowedPaths: ["."]
87
+ blockedPaths:
88
+ # Version control
89
+ - ".git"
90
+ # Binaries
91
+ - "node_modules/.bin"
92
+ # Environment files with secrets
93
+ - ".env"
94
+ - ".env.local"
95
+ - ".env.production"
96
+ - ".env.development"
97
+ - ".env.test"
98
+ - ".env.staging"
99
+ # Package manager credentials
100
+ - ".npmrc"
101
+ - ".yarnrc"
102
+ - ".pypirc"
103
+ # Git credentials
104
+ - ".git-credentials"
105
+ - ".gitconfig"
106
+ # SSH keys
107
+ - ".ssh"
108
+ # Cloud provider credentials
109
+ - ".aws"
110
+ - ".gcp"
111
+ - ".azure"
112
+ # Kubernetes config
113
+ - ".kube"
114
+ # Docker credentials
115
+ - ".docker"
116
+ blockedExtensions: [".exe", ".dll", ".so", ".dylib"]
117
+ maxFileSize: 10485760 # 10MB
118
+ enableBackups: false
119
+
120
+ # Note: This agent intentionally excludes:
121
+ # - write_file, edit_file (write operations) - not in enabledTools
122
+ # - process-tools (bash execution) - provider not included
123
+ # - agent-spawner (no sub-agent spawning) - provider not included
124
+ # This ensures it remains a safe, read-only exploration tool
@@ -0,0 +1,28 @@
1
+ /**
2
+ * CLI-specific Approval Handler
3
+ *
4
+ * Creates a manual approval handler that works directly with AgentEventBus
5
+ * for the CLI/TUI mode. Unlike the server's ManualApprovalHandler which uses
6
+ * ApprovalCoordinator for HTTP-based flows, this handler emits events directly
7
+ * to the event bus that the TUI listens to.
8
+ *
9
+ * Flow:
10
+ * 1. Handler emits 'approval:request' → EventBus → TUI shows prompt
11
+ * 2. User responds in TUI → EventBus emits 'approval:response' → Handler resolves
12
+ * 3. For auto-approvals (parallel tools), handler emits 'approval:response' → TUI dismisses
13
+ */
14
+ import type { ApprovalHandler, AgentEventBus } from '@dexto/core';
15
+ /**
16
+ * Creates a manual approval handler for CLI mode that uses AgentEventBus directly.
17
+ *
18
+ * @param eventBus The agent event bus for request/response communication
19
+ * @returns ApprovalHandler with cancellation and auto-approve support
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const handler = createCLIApprovalHandler(agent.agentEventBus);
24
+ * agent.setApprovalHandler(handler);
25
+ * ```
26
+ */
27
+ export declare function createCLIApprovalHandler(eventBus: AgentEventBus): ApprovalHandler;
28
+ //# sourceMappingURL=cli-approval-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-approval-handler.d.ts","sourceRoot":"","sources":["../../../src/cli/approval/cli-approval-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EACR,eAAe,EAGf,aAAa,EAChB,MAAM,aAAa,CAAC;AAGrB;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,aAAa,GAAG,eAAe,CAyJjF"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * CLI-specific Approval Handler
3
+ *
4
+ * Creates a manual approval handler that works directly with AgentEventBus
5
+ * for the CLI/TUI mode. Unlike the server's ManualApprovalHandler which uses
6
+ * ApprovalCoordinator for HTTP-based flows, this handler emits events directly
7
+ * to the event bus that the TUI listens to.
8
+ *
9
+ * Flow:
10
+ * 1. Handler emits 'approval:request' → EventBus → TUI shows prompt
11
+ * 2. User responds in TUI → EventBus emits 'approval:response' → Handler resolves
12
+ * 3. For auto-approvals (parallel tools), handler emits 'approval:response' → TUI dismisses
13
+ */
14
+ import { ApprovalStatus, DenialReason } from '@dexto/core';
15
+ /**
16
+ * Creates a manual approval handler for CLI mode that uses AgentEventBus directly.
17
+ *
18
+ * @param eventBus The agent event bus for request/response communication
19
+ * @returns ApprovalHandler with cancellation and auto-approve support
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const handler = createCLIApprovalHandler(agent.agentEventBus);
24
+ * agent.setApprovalHandler(handler);
25
+ * ```
26
+ */
27
+ export function createCLIApprovalHandler(eventBus) {
28
+ // Track pending approvals for cancellation support
29
+ const pendingApprovals = new Map();
30
+ const handleApproval = (request) => {
31
+ return new Promise((resolve) => {
32
+ // Use per-request timeout (optional - undefined means no timeout)
33
+ const effectiveTimeout = request.timeout;
34
+ // Set timeout timer ONLY if timeout is specified
35
+ let timer;
36
+ if (effectiveTimeout !== undefined) {
37
+ timer = setTimeout(() => {
38
+ cleanup();
39
+ pendingApprovals.delete(request.approvalId);
40
+ // Create timeout response
41
+ const timeoutResponse = {
42
+ approvalId: request.approvalId,
43
+ status: ApprovalStatus.CANCELLED,
44
+ sessionId: request.sessionId,
45
+ reason: DenialReason.TIMEOUT,
46
+ message: `Approval request timed out after ${effectiveTimeout}ms`,
47
+ timeoutMs: effectiveTimeout,
48
+ };
49
+ // Emit timeout response so TUI can dismiss the prompt
50
+ eventBus.emit('approval:response', timeoutResponse);
51
+ resolve(timeoutResponse);
52
+ }, effectiveTimeout);
53
+ }
54
+ // Cleanup function to remove listener and clear timeout
55
+ const controller = new AbortController();
56
+ const cleanup = () => {
57
+ if (timer !== undefined) {
58
+ clearTimeout(timer);
59
+ }
60
+ controller.abort();
61
+ };
62
+ // Listen for approval:response events
63
+ const listener = (res) => {
64
+ // Only handle responses for this specific approval
65
+ if (res.approvalId === request.approvalId) {
66
+ cleanup();
67
+ pendingApprovals.delete(request.approvalId);
68
+ resolve(res);
69
+ }
70
+ };
71
+ // Register listener with abort signal for cleanup
72
+ eventBus.on('approval:response', listener, { signal: controller.signal });
73
+ // Store for cancellation support
74
+ pendingApprovals.set(request.approvalId, {
75
+ cleanup,
76
+ resolve,
77
+ request,
78
+ });
79
+ // Emit the approval:request event for TUI to receive
80
+ eventBus.emit('approval:request', request);
81
+ });
82
+ };
83
+ const handler = Object.assign(handleApproval, {
84
+ cancel: (approvalId) => {
85
+ const pending = pendingApprovals.get(approvalId);
86
+ if (pending) {
87
+ pending.cleanup();
88
+ pendingApprovals.delete(approvalId);
89
+ // Create cancellation response
90
+ const cancelResponse = {
91
+ approvalId,
92
+ status: ApprovalStatus.CANCELLED,
93
+ sessionId: pending.request.sessionId,
94
+ reason: DenialReason.SYSTEM_CANCELLED,
95
+ message: 'Approval request was cancelled',
96
+ };
97
+ // Emit cancellation event so TUI can dismiss the prompt
98
+ eventBus.emit('approval:response', cancelResponse);
99
+ // Resolve with CANCELLED response
100
+ pending.resolve(cancelResponse);
101
+ }
102
+ },
103
+ cancelAll: () => {
104
+ for (const [approvalId] of pendingApprovals) {
105
+ handler.cancel?.(approvalId);
106
+ }
107
+ },
108
+ getPending: () => {
109
+ return Array.from(pendingApprovals.keys());
110
+ },
111
+ getPendingRequests: () => {
112
+ return Array.from(pendingApprovals.values()).map((p) => p.request);
113
+ },
114
+ /**
115
+ * Auto-approve pending requests that match a predicate.
116
+ * Used when a pattern is remembered to auto-approve other parallel requests
117
+ * that would now match the same pattern.
118
+ */
119
+ autoApprovePending: (predicate, responseData) => {
120
+ let count = 0;
121
+ // Find all pending approvals that match the predicate
122
+ for (const [approvalId, pending] of pendingApprovals) {
123
+ if (predicate(pending.request)) {
124
+ // Clean up the pending state
125
+ pending.cleanup();
126
+ pendingApprovals.delete(approvalId);
127
+ // Create auto-approval response
128
+ const autoApproveResponse = {
129
+ approvalId,
130
+ status: ApprovalStatus.APPROVED,
131
+ sessionId: pending.request.sessionId,
132
+ message: 'Auto-approved due to matching remembered pattern',
133
+ data: responseData,
134
+ };
135
+ // Emit response so TUI can dismiss the prompt
136
+ eventBus.emit('approval:response', autoApproveResponse);
137
+ // Resolve the pending promise
138
+ pending.resolve(autoApproveResponse);
139
+ count++;
140
+ }
141
+ }
142
+ return count;
143
+ },
144
+ });
145
+ return handler;
146
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLI Approval Module
3
+ *
4
+ * Provides CLI-specific approval handling that works directly with AgentEventBus.
5
+ */
6
+ export { createCLIApprovalHandler } from './cli-approval-handler.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/approval/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * CLI Approval Module
3
+ *
4
+ * Provides CLI-specific approval handling that works directly with AgentEventBus.
5
+ */
6
+ export { createCLIApprovalHandler } from './cli-approval-handler.js';
@@ -1 +1 @@
1
- {"version":3,"file":"general-commands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/interactive-commands/general-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAwC,MAAM,qBAAqB,CAAC;AAiDnG;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,iBAAiB,EAAE,GAAG,iBAAiB,CAgC9F;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,iBAAiB,EAyO9C,CAAC"}
1
+ {"version":3,"file":"general-commands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/interactive-commands/general-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAwC,MAAM,qBAAqB,CAAC;AAoDnG;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,iBAAiB,EAAE,GAAG,iBAAiB,CAgC9F;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,iBAAiB,EAyO9C,CAAC"}
@@ -19,10 +19,13 @@ import { writeToClipboard } from '../../ink-cli/utils/clipboardUtils.js';
19
19
  */
20
20
  async function executeShellCommand(command, cwd, timeoutMs = 30000) {
21
21
  return new Promise((resolve) => {
22
- const child = spawn(command, [], {
22
+ // Use user's default shell from SHELL env var, fallback to /bin/sh
23
+ // Use -i flag for interactive mode to load aliases from shell config
24
+ const userShell = process.env.SHELL || '/bin/sh';
25
+ const child = spawn(userShell, ['-ic', command], {
23
26
  cwd,
24
- shell: true,
25
27
  stdio: ['ignore', 'pipe', 'pipe'],
28
+ env: { ...process.env },
26
29
  });
27
30
  let stdout = '';
28
31
  let stderr = '';
@@ -1 +1 @@
1
- {"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuE,MAAM,OAAO,CAAC;AAG5F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAK5D,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,mBAAmB;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,kGA2R1B,CAAC"}
1
+ {"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAM5D,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,mBAAmB;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,kGAkS1B,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { forwardRef, useState, useImperativeHandle, useRef, useEffect } from 'react';
2
+ import { forwardRef, useState, useImperativeHandle, useRef, useEffect, useMemo, } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { ElicitationForm } from './ElicitationForm.js';
5
5
  import { DiffPreview, CreateFilePreview } from './renderers/index.js';
6
6
  import { isEditWriteTool } from '../utils/toolUtils.js';
7
+ import { formatToolHeader } from '../utils/messageFormatting.js';
7
8
  /**
8
9
  * Compact approval prompt component that displays above the input area
9
10
  * Shows options based on approval type:
@@ -20,7 +21,14 @@ export const ApprovalPrompt = forwardRef(({ approval, onApprove, onDeny, onCance
20
21
  const hasBashPatterns = suggestedPatterns.length > 0;
21
22
  // Check if this is an edit/write file tool
22
23
  const toolName = approval.metadata.toolName;
24
+ const toolArgs = approval.metadata.args || {};
23
25
  const isEditOrWriteTool = isEditWriteTool(toolName);
26
+ // Format tool header using shared utility (same format as tool messages)
27
+ const formattedTool = useMemo(() => {
28
+ if (!toolName)
29
+ return null;
30
+ return formatToolHeader(toolName, toolArgs);
31
+ }, [toolName, toolArgs]);
24
32
  const [selectedIndex, setSelectedIndex] = useState(0);
25
33
  // Ref for elicitation form
26
34
  const elicitationFormRef = useRef(null);
@@ -183,7 +191,7 @@ export const ApprovalPrompt = forwardRef(({ approval, onApprove, onDeny, onCance
183
191
  const directoryPath = approval.metadata.path;
184
192
  const parentDir = approval.metadata.parentDir;
185
193
  const operation = approval.metadata.operation;
186
- return (_jsxs(Box, { paddingX: 0, paddingY: 0, flexDirection: "column", children: [_jsx(Box, { flexDirection: "column", marginBottom: 0, children: isDirectoryAccess ? (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Directory Access:", ' '] }), _jsx(Text, { color: "cyan", children: parentDir || directoryPath })] }), _jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' ' }), _jsxs(Text, { color: "gray", children: [toolName ? `"${toolName}"` : 'Tool', " wants to", ' ', operation || 'access', " files outside working directory"] })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Approval:", ' '] }), toolName && _jsx(Text, { color: "cyan", children: toolName })] }), isCommandConfirmation && command && (_jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' Command: ' }), _jsx(Text, { color: "red", children: command })] }))] })) }), renderPreview(), _jsx(Box, { flexDirection: "column", marginTop: 0, children: options.map((option, index) => {
194
+ return (_jsxs(Box, { paddingX: 0, paddingY: 0, flexDirection: "column", children: [_jsx(Box, { flexDirection: "column", marginBottom: 0, children: isDirectoryAccess ? (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Directory Access:", ' '] }), _jsx(Text, { color: "cyan", children: parentDir || directoryPath })] }), _jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' ' }), _jsxs(Text, { color: "gray", children: [formattedTool ? `"${formattedTool.displayName}"` : 'Tool', ' ', "wants to ", operation || 'access', " files outside working directory"] })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Approval:", ' '] }), formattedTool && _jsx(Text, { color: "cyan", children: formattedTool.header })] }), isCommandConfirmation && command && (_jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' Command: ' }), _jsx(Text, { color: "red", children: command })] }))] })) }), renderPreview(), _jsx(Box, { flexDirection: "column", marginTop: 0, children: options.map((option, index) => {
187
195
  const isSelected = index === selectedIndex;
188
196
  const isNo = option.id === 'no';
189
197
  return (_jsx(Box, { children: isSelected ? (_jsxs(Text, { color: isNo ? 'red' : 'green', bold: true, children: [' ▶ ', option.label] })) : (_jsxs(Text, { color: "gray", children: [' ', option.label] })) }, option.id));
@@ -7,10 +7,12 @@ interface FooterProps {
7
7
  cwd?: string;
8
8
  branchName?: string;
9
9
  autoApproveEdits?: boolean;
10
+ /** Whether user is in shell command mode (input starts with !) */
11
+ isShellMode?: boolean;
10
12
  }
11
13
  /**
12
14
  * Pure presentational component for footer status line
13
15
  */
14
- export declare function Footer({ modelName, cwd, branchName, autoApproveEdits }: FooterProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function Footer({ modelName, cwd, branchName, autoApproveEdits, isShellMode }: FooterProps): import("react/jsx-runtime").JSX.Element;
15
17
  export {};
16
18
  //# sourceMappingURL=Footer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAoBD;;GAEG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,WAAW,2CAwBnF"}
1
+ {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAoBD;;GAEG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,WAAW,2CAmChG"}
@@ -20,8 +20,10 @@ function shortenPath(path, maxLength = 40) {
20
20
  /**
21
21
  * Pure presentational component for footer status line
22
22
  */
23
- export function Footer({ modelName, cwd, branchName, autoApproveEdits }) {
23
+ export function Footer({ modelName, cwd, branchName, autoApproveEdits, isShellMode }) {
24
24
  const displayPath = cwd ? shortenPath(cwd) : '';
25
25
  const displayModelName = getModelDisplayName(modelName);
26
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: "blue", children: displayPath }), branchName && _jsxs(Text, { color: "gray", children: [" (", branchName, ")"] })] }), _jsx(Text, { color: "cyan", children: displayModelName })] }), autoApproveEdits && (_jsxs(Box, { children: [_jsx(Text, { color: "yellowBright", children: "accept edits" }), _jsx(Text, { color: "gray", children: " (shift + tab to toggle)" })] }))] }));
26
+ // Shell mode changes the path color to yellow as indicator
27
+ const pathColor = isShellMode ? 'yellow' : 'blue';
28
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: pathColor, children: displayPath }), branchName && _jsxs(Text, { color: "gray", children: [" (", branchName, ")"] })] }), _jsx(Text, { color: "cyan", children: displayModelName })] }), isShellMode && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", bold: true, children: "!" }), _jsx(Text, { color: "gray", children: " for shell mode" })] })), autoApproveEdits && !isShellMode && (_jsxs(Box, { children: [_jsx(Text, { color: "yellowBright", children: "accept edits" }), _jsx(Text, { color: "gray", children: " (shift + tab to toggle)" })] }))] }));
27
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AA6ED;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GAgR9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AA6ED;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GAoQ9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
@@ -74,7 +74,7 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
74
74
  // Combined state to guarantee single render on navigation
75
75
  const [selection, setSelection] = useState({ index: 0, offset: 0 });
76
76
  const selectedIndexRef = useRef(0);
77
- const MAX_VISIBLE_ITEMS = 8;
77
+ const MAX_VISIBLE_ITEMS = 5;
78
78
  // Update selection AND scroll offset in a single state update
79
79
  // This guarantees exactly one render per navigation action
80
80
  const updateSelection = useCallback((indexUpdater) => {
@@ -122,6 +122,10 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
122
122
  cancelled = true;
123
123
  };
124
124
  }, [isVisible, agent]);
125
+ // NOTE: Auto-close logic is handled synchronously in TextBufferInput.tsx
126
+ // (on backspace deleting @ and on space after @). We don't use useEffect here
127
+ // because React batches state updates, causing race conditions where isVisible
128
+ // and searchQuery update at different times.
125
129
  // Extract query from @mention (everything after @)
126
130
  const mentionQuery = useMemo(() => {
127
131
  // Find the last @ that's at start or after space
@@ -250,7 +254,9 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
250
254
  const uriParts = resource.uri.split('/');
251
255
  const displayName = resource.name || uriParts[uriParts.length - 1] || resource.uri;
252
256
  const isImage = (resource.mimeType || '').startsWith('image/');
253
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [isImage && (_jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: "\uD83D\uDDBC\uFE0F " })), _jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: displayName }), resource.serverName && (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: ["[", resource.serverName, "]"] }) }))] }), _jsx(Box, { marginLeft: isImage ? 3 : 0, children: _jsx(Text, { color: isSelected ? 'white' : 'gray', children: resource.uri }) }), resource.description && (_jsx(Box, { marginLeft: isImage ? 3 : 0, children: _jsx(Text, { color: isSelected ? 'white' : 'gray', children: resource.description }) }))] }) }, resource.uri));
257
+ // Truncate URI for display (show last 40 chars with ellipsis)
258
+ const truncatedUri = resource.uri.length > 50 ? '…' + resource.uri.slice(-49) : resource.uri;
259
+ return (_jsxs(Box, { children: [isImage && _jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: "\uD83D\uDDBC\uFE0F " }), _jsx(Text, { color: isSelected ? 'cyan' : 'white', bold: isSelected, children: displayName }), resource.serverName && (_jsxs(Text, { color: "gray", children: [" [", resource.serverName, "]"] })), _jsxs(Text, { color: "gray", children: [" ", truncatedUri] })] }, resource.uri));
254
260
  })] }));
255
261
  });
256
262
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA2gBtB"}
1
+ {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA2hBtB"}
@@ -250,14 +250,23 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
250
250
  buffer.backspace();
251
251
  checkRemovedImages();
252
252
  checkRemovedPasteBlocks();
253
+ // Check if we should close overlay after backspace
254
+ // NOTE: buffer.text is memoized and won't update until next render,
255
+ // so we calculate the expected new text ourselves
253
256
  if (onTriggerOverlay && cursorPos > 0) {
254
257
  const deletedChar = prevText[cursorPos - 1];
255
- const newText = buffer.text;
258
+ // Calculate what the text will be after backspace
259
+ const expectedNewText = prevText.slice(0, cursorPos - 1) + prevText.slice(cursorPos);
256
260
  if (deletedChar === '/' && cursorPos === 1) {
257
261
  onTriggerOverlay('close');
258
262
  }
259
- else if (deletedChar === '@' && !newText.includes('@')) {
260
- onTriggerOverlay('close');
263
+ else if (deletedChar === '@') {
264
+ // Close if no valid @ mention remains
265
+ // A valid @ is at start of text or after whitespace
266
+ const hasValidAt = /(^|[\s])@/.test(expectedNewText);
267
+ if (!hasValidAt) {
268
+ onTriggerOverlay('close');
269
+ }
261
270
  }
262
271
  }
263
272
  return;
@@ -388,6 +397,11 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
388
397
  else if (key.sequence === '@') {
389
398
  onTriggerOverlay('resource-autocomplete');
390
399
  }
400
+ else if (/\s/.test(key.sequence)) {
401
+ // Close resource autocomplete when user types whitespace
402
+ // Whitespace means user is done with the mention (either selected or abandoned)
403
+ onTriggerOverlay('close');
404
+ }
391
405
  }
392
406
  }
393
407
  }, [
@@ -419,6 +433,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
419
433
  const isShellMode = bufferText.startsWith('!');
420
434
  const promptPrefix = isShellMode ? '$ ' : '> ';
421
435
  const promptColor = isShellMode ? 'yellow' : 'green';
436
+ const separatorColor = isShellMode ? 'yellow' : 'gray';
422
437
  // Calculate visible window
423
438
  let startLine = 0;
424
439
  let endLine = totalLines;
@@ -435,7 +450,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
435
450
  if (bufferText === '') {
436
451
  return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: "gray", children: separator }), _jsxs(Box, { width: terminalWidth, children: [_jsx(Text, { color: "green", bold: true, children: '> ' }), _jsx(Text, { inverse: true, children: " " }), placeholder && _jsx(Text, { color: "gray", children: placeholder }), _jsx(Text, { children: ' '.repeat(Math.max(0, terminalWidth - 3 - (placeholder?.length || 0))) })] }), _jsx(Text, { color: "gray", children: separator })] }));
437
452
  }
438
- return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: "gray", children: separator }), startLine > 0 && (_jsxs(Text, { color: "gray", children: [' ', "\u2191 ", startLine, " more line", startLine > 1 ? 's' : '', " above (", KEY_LABELS.altUp, " to jump)"] })), visibleLines.map((line, idx) => {
453
+ return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: separatorColor, children: separator }), startLine > 0 && (_jsxs(Text, { color: "gray", children: [' ', "\u2191 ", startLine, " more line", startLine > 1 ? 's' : '', " above (", KEY_LABELS.altUp, " to jump)"] })), visibleLines.map((line, idx) => {
439
454
  const absoluteRow = startLine + idx;
440
455
  const isFirst = absoluteRow === 0;
441
456
  const prefix = isFirst ? promptPrefix : ' ';
@@ -447,7 +462,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
447
462
  const atCursor = line.charAt(cursorVisualCol) || ' ';
448
463
  const after = line.slice(cursorVisualCol + 1);
449
464
  return (_jsxs(Box, { width: terminalWidth, children: [_jsx(Text, { color: promptColor, bold: isFirst, children: prefix }), _jsx(HighlightedText, { text: before, query: highlightQuery }), _jsx(Text, { inverse: true, children: atCursor }), _jsx(HighlightedText, { text: after, query: highlightQuery }), _jsx(Text, { children: ' '.repeat(Math.max(0, terminalWidth - prefix.length - before.length - 1 - after.length)) })] }, absoluteRow));
450
- }), endLine < totalLines && (_jsxs(Text, { color: "gray", children: [' ', "\u2193 ", totalLines - endLine, " more line", totalLines - endLine > 1 ? 's' : '', ' ', "below (", KEY_LABELS.altDown, " to jump)"] })), pastedBlocks.length > 0 && (_jsx(PasteBlockHint, { pastedBlocks: pastedBlocks, expandedBlock: findExpandedBlock(), cursorOnCollapsed: findCollapsedBlockAtCursor() })), _jsx(Text, { color: "gray", children: separator })] }));
465
+ }), endLine < totalLines && (_jsxs(Text, { color: "gray", children: [' ', "\u2193 ", totalLines - endLine, " more line", totalLines - endLine > 1 ? 's' : '', ' ', "below (", KEY_LABELS.altDown, " to jump)"] })), pastedBlocks.length > 0 && (_jsx(PasteBlockHint, { pastedBlocks: pastedBlocks, expandedBlock: findExpandedBlock(), cursorOnCollapsed: findCollapsedBlockAtCursor() })), _jsx(Text, { color: separatorColor, children: separator })] }));
451
466
  }
452
467
  /** Hint component for paste blocks */
453
468
  function PasteBlockHint({ pastedBlocks, expandedBlock, cursorOnCollapsed, }) {