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
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Prompt Component - Claude Code style approval UI
|
|
3
|
+
*
|
|
4
|
+
* Claude Code design:
|
|
5
|
+
*
|
|
6
|
+
* Tool use
|
|
7
|
+
* Web Search("query text here")
|
|
8
|
+
*
|
|
9
|
+
* Do you want to proceed?
|
|
10
|
+
* [1] Yes
|
|
11
|
+
* [2] Yes, and don't ask again for Web Search in /path/to/project
|
|
12
|
+
* [3] No
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useState, useEffect } from 'react';
|
|
16
|
+
import { Box, Text, useInput } from 'ink';
|
|
17
|
+
import { colors, icons } from './theme.js';
|
|
18
|
+
import type { ApprovalAction, ApprovalSuggestion } from '../../permissions/types.js';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Types
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
interface PermissionPromptProps {
|
|
25
|
+
/** Tool name */
|
|
26
|
+
tool: string;
|
|
27
|
+
/** Tool input/parameters */
|
|
28
|
+
input: Record<string, unknown>;
|
|
29
|
+
/** Available approval options */
|
|
30
|
+
suggestions: ApprovalSuggestion[];
|
|
31
|
+
/** Callback when user makes a decision */
|
|
32
|
+
onDecision: (action: ApprovalAction) => void;
|
|
33
|
+
/** Project path for "don't ask again" option */
|
|
34
|
+
projectPath?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Helpers
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format tool name for display (Claude Code style: "Web Search" instead of "WebSearch")
|
|
43
|
+
*/
|
|
44
|
+
function formatToolName(tool: string): string {
|
|
45
|
+
// Convert camelCase/PascalCase to space-separated
|
|
46
|
+
return tool.replace(/([a-z])([A-Z])/g, '$1 $2');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Format tool input for display
|
|
51
|
+
*/
|
|
52
|
+
function formatInput(tool: string, input: Record<string, unknown>): string {
|
|
53
|
+
switch (tool) {
|
|
54
|
+
case 'Bash':
|
|
55
|
+
return (input.command as string) ?? JSON.stringify(input);
|
|
56
|
+
|
|
57
|
+
case 'Read':
|
|
58
|
+
case 'Write':
|
|
59
|
+
case 'Edit':
|
|
60
|
+
return (input.file_path as string) ?? (input.path as string) ?? '';
|
|
61
|
+
|
|
62
|
+
case 'Glob':
|
|
63
|
+
return `${input.pattern ?? ''} in ${input.path ?? '.'}`;
|
|
64
|
+
|
|
65
|
+
case 'Grep':
|
|
66
|
+
return `${input.pattern ?? ''} in ${input.path ?? '.'}`;
|
|
67
|
+
|
|
68
|
+
case 'WebFetch':
|
|
69
|
+
return (input.url as string) ?? '';
|
|
70
|
+
|
|
71
|
+
case 'WebSearch':
|
|
72
|
+
return (input.query as string) ?? '';
|
|
73
|
+
|
|
74
|
+
case 'TodoWrite': {
|
|
75
|
+
const todos = input.todos as Array<{ content: string; status: string }> || [];
|
|
76
|
+
const inProgress = todos.filter(t => t.status === 'in_progress');
|
|
77
|
+
const pending = todos.filter(t => t.status === 'pending');
|
|
78
|
+
return `${todos.length} tasks: ${inProgress.length} active, ${pending.length} pending`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
default:
|
|
82
|
+
const str = JSON.stringify(input);
|
|
83
|
+
return str.length > 80 ? str.slice(0, 77) + '...' : str;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Format tool call in Claude Code style: Tool("input")
|
|
89
|
+
* Returns an array of lines for proper wrapping
|
|
90
|
+
*/
|
|
91
|
+
function formatToolCall(tool: string, input: Record<string, unknown>): { name: string; input: string } {
|
|
92
|
+
const displayName = formatToolName(tool);
|
|
93
|
+
let inputStr = formatInput(tool, input);
|
|
94
|
+
|
|
95
|
+
// Truncate very long inputs
|
|
96
|
+
const maxLen = 60;
|
|
97
|
+
if (inputStr.length > maxLen) {
|
|
98
|
+
inputStr = inputStr.slice(0, maxLen - 3) + '...';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { name: displayName, input: inputStr };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get icon for tool
|
|
106
|
+
*/
|
|
107
|
+
function getToolIcon(tool: string): string {
|
|
108
|
+
switch (tool) {
|
|
109
|
+
case 'Bash':
|
|
110
|
+
return '⚡';
|
|
111
|
+
case 'Read':
|
|
112
|
+
case 'Write':
|
|
113
|
+
case 'Edit':
|
|
114
|
+
return '📄';
|
|
115
|
+
case 'Glob':
|
|
116
|
+
case 'Grep':
|
|
117
|
+
return '🔍';
|
|
118
|
+
case 'WebFetch':
|
|
119
|
+
case 'WebSearch':
|
|
120
|
+
return '🌐';
|
|
121
|
+
case 'TodoWrite':
|
|
122
|
+
return '📋';
|
|
123
|
+
default:
|
|
124
|
+
return icons.tool;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get shortcut key display
|
|
130
|
+
*/
|
|
131
|
+
function getShortcutDisplay(shortcut?: string): string {
|
|
132
|
+
if (!shortcut) return '';
|
|
133
|
+
if (shortcut === 'n') return 'n';
|
|
134
|
+
return shortcut;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Permission Prompt Component
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
export function PermissionPrompt({
|
|
142
|
+
tool,
|
|
143
|
+
input,
|
|
144
|
+
suggestions,
|
|
145
|
+
onDecision,
|
|
146
|
+
projectPath,
|
|
147
|
+
}: PermissionPromptProps) {
|
|
148
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
149
|
+
const [expanded, setExpanded] = useState(false);
|
|
150
|
+
|
|
151
|
+
// Handle keyboard input
|
|
152
|
+
useInput((inputChar, key) => {
|
|
153
|
+
// Arrow navigation
|
|
154
|
+
if (key.upArrow) {
|
|
155
|
+
setSelectedIndex((i) => Math.max(0, i - 1));
|
|
156
|
+
} else if (key.downArrow) {
|
|
157
|
+
setSelectedIndex((i) => Math.min(suggestions.length - 1, i + 1));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Enter to select current option
|
|
161
|
+
if (key.return) {
|
|
162
|
+
const selected = suggestions[selectedIndex];
|
|
163
|
+
if (selected) {
|
|
164
|
+
onDecision(selected.action);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Escape to deny
|
|
169
|
+
if (key.escape) {
|
|
170
|
+
onDecision('deny');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Tab to toggle expand
|
|
174
|
+
if (key.tab) {
|
|
175
|
+
setExpanded((e) => !e);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Number shortcuts (1-3)
|
|
179
|
+
const num = parseInt(inputChar, 10);
|
|
180
|
+
if (num >= 1 && num <= suggestions.length) {
|
|
181
|
+
onDecision(suggestions[num - 1].action);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 'y' for allow once, 'n' for deny
|
|
185
|
+
if (inputChar.toLowerCase() === 'y') {
|
|
186
|
+
onDecision('allow_once');
|
|
187
|
+
} else if (inputChar.toLowerCase() === 'n') {
|
|
188
|
+
onDecision('deny');
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const toolInfo = formatToolCall(tool, input);
|
|
193
|
+
const displayToolName = formatToolName(tool);
|
|
194
|
+
const fullInput = formatInput(tool, input);
|
|
195
|
+
const isLongInput = fullInput.length > 60;
|
|
196
|
+
|
|
197
|
+
// Get dynamic label for "don't ask again" option
|
|
198
|
+
const getDynamicLabel = (suggestion: ApprovalSuggestion): string => {
|
|
199
|
+
if (suggestion.action === 'allow_always' && projectPath) {
|
|
200
|
+
// Shorten project path for display
|
|
201
|
+
const shortPath = projectPath.replace(process.env.HOME || '', '~');
|
|
202
|
+
return `Yes, don't ask again for ${displayToolName} in ${shortPath}`;
|
|
203
|
+
}
|
|
204
|
+
return suggestion.label;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<Box flexDirection="column" marginTop={1} borderStyle="round" borderColor={colors.warning} paddingX={1}>
|
|
209
|
+
{/* Header */}
|
|
210
|
+
<Box>
|
|
211
|
+
<Text color={colors.warning}>⚡ </Text>
|
|
212
|
+
<Text bold>{toolInfo.name}</Text>
|
|
213
|
+
</Box>
|
|
214
|
+
|
|
215
|
+
{/* Tool input - collapsible for long content */}
|
|
216
|
+
<Box flexDirection="column" marginLeft={2}>
|
|
217
|
+
{expanded || !isLongInput ? (
|
|
218
|
+
<Text color={colors.textSecondary}>{fullInput}</Text>
|
|
219
|
+
) : (
|
|
220
|
+
<Box>
|
|
221
|
+
<Text color={colors.textSecondary}>{toolInfo.input}</Text>
|
|
222
|
+
<Text color={colors.textMuted}>{' [-> expand]'}</Text>
|
|
223
|
+
</Box>
|
|
224
|
+
)}
|
|
225
|
+
</Box>
|
|
226
|
+
|
|
227
|
+
{/* Question */}
|
|
228
|
+
<Box marginTop={1}>
|
|
229
|
+
<Text>Allow this action?</Text>
|
|
230
|
+
</Box>
|
|
231
|
+
|
|
232
|
+
{/* Options */}
|
|
233
|
+
<Box flexDirection="column" marginTop={0} marginLeft={2}>
|
|
234
|
+
{suggestions.map((suggestion, index) => {
|
|
235
|
+
const isSelected = index === selectedIndex;
|
|
236
|
+
const shortcut = getShortcutDisplay(suggestion.shortcut);
|
|
237
|
+
const label = getDynamicLabel(suggestion);
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<Box key={suggestion.action}>
|
|
241
|
+
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
242
|
+
{isSelected ? '▶' : ' '}
|
|
243
|
+
</Text>
|
|
244
|
+
<Text color={colors.textMuted}> [{shortcut}] </Text>
|
|
245
|
+
<Text color={isSelected ? colors.text : colors.textSecondary}>
|
|
246
|
+
{label}
|
|
247
|
+
</Text>
|
|
248
|
+
</Box>
|
|
249
|
+
);
|
|
250
|
+
})}
|
|
251
|
+
</Box>
|
|
252
|
+
</Box>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ============================================================================
|
|
257
|
+
// Simple Confirm Prompt (backward compatible)
|
|
258
|
+
// ============================================================================
|
|
259
|
+
|
|
260
|
+
interface SimpleConfirmProps {
|
|
261
|
+
message: string;
|
|
262
|
+
onConfirm: (confirmed: boolean) => void;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function SimpleConfirmPrompt({ message, onConfirm }: SimpleConfirmProps) {
|
|
266
|
+
useInput((inputChar, key) => {
|
|
267
|
+
if (inputChar.toLowerCase() === 'y' || key.return) {
|
|
268
|
+
onConfirm(true);
|
|
269
|
+
} else if (inputChar.toLowerCase() === 'n' || key.escape) {
|
|
270
|
+
onConfirm(false);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<Box>
|
|
276
|
+
<Text color={colors.warning}>{icons.warning} </Text>
|
|
277
|
+
<Text>{message} </Text>
|
|
278
|
+
<Text color={colors.textMuted}>[y/n] </Text>
|
|
279
|
+
</Box>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// Permission Rules Display
|
|
285
|
+
// ============================================================================
|
|
286
|
+
|
|
287
|
+
interface PermissionRule {
|
|
288
|
+
type: string;
|
|
289
|
+
tool: string;
|
|
290
|
+
pattern?: string;
|
|
291
|
+
scope: string;
|
|
292
|
+
mode: string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
interface PermissionRulesProps {
|
|
296
|
+
rules: PermissionRule[];
|
|
297
|
+
allowedPrompts?: { tool: string; prompt: string }[];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function PermissionRulesDisplay({ rules, allowedPrompts }: PermissionRulesProps) {
|
|
301
|
+
return (
|
|
302
|
+
<Box flexDirection="column">
|
|
303
|
+
<Text color={colors.primary} bold>Permission Rules</Text>
|
|
304
|
+
<Box flexDirection="column" marginTop={1}>
|
|
305
|
+
{/* Header */}
|
|
306
|
+
<Text color={colors.textMuted}>
|
|
307
|
+
{'Type'.padEnd(10)}{'Tool'.padEnd(12)}{'Pattern'.padEnd(20)}{'Scope'.padEnd(10)}Mode
|
|
308
|
+
</Text>
|
|
309
|
+
<Text color={colors.textMuted}>{'─'.repeat(60)}</Text>
|
|
310
|
+
|
|
311
|
+
{/* Rules */}
|
|
312
|
+
{rules.map((rule, i) => (
|
|
313
|
+
<Text key={i}>
|
|
314
|
+
<Text color={colors.textSecondary}>{rule.type.padEnd(10)}</Text>
|
|
315
|
+
<Text color={colors.tool}>{rule.tool.padEnd(12)}</Text>
|
|
316
|
+
<Text>{(rule.pattern ?? '*').slice(0, 18).padEnd(20)}</Text>
|
|
317
|
+
<Text color={colors.textMuted}>{rule.scope.padEnd(10)}</Text>
|
|
318
|
+
<Text color={rule.mode === 'auto' ? colors.success : rule.mode === 'deny' ? colors.error : colors.warning}>
|
|
319
|
+
{rule.mode}
|
|
320
|
+
</Text>
|
|
321
|
+
</Text>
|
|
322
|
+
))}
|
|
323
|
+
</Box>
|
|
324
|
+
|
|
325
|
+
{/* Allowed prompts */}
|
|
326
|
+
{allowedPrompts && allowedPrompts.length > 0 && (
|
|
327
|
+
<Box flexDirection="column" marginTop={1}>
|
|
328
|
+
<Text color={colors.primary}>Pending Prompts (from plan approval):</Text>
|
|
329
|
+
{allowedPrompts.map((p, i) => (
|
|
330
|
+
<Text key={i} color={colors.textSecondary}>
|
|
331
|
+
• {p.tool}: {p.prompt}
|
|
332
|
+
</Text>
|
|
333
|
+
))}
|
|
334
|
+
</Box>
|
|
335
|
+
)}
|
|
336
|
+
</Box>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ============================================================================
|
|
341
|
+
// Permission Audit Display
|
|
342
|
+
// ============================================================================
|
|
343
|
+
|
|
344
|
+
interface AuditEntry {
|
|
345
|
+
time: string;
|
|
346
|
+
tool: string;
|
|
347
|
+
input: string;
|
|
348
|
+
decision: string;
|
|
349
|
+
rule?: string;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
interface PermissionAuditProps {
|
|
353
|
+
entries: AuditEntry[];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function PermissionAuditDisplay({ entries }: PermissionAuditProps) {
|
|
357
|
+
return (
|
|
358
|
+
<Box flexDirection="column">
|
|
359
|
+
<Text color={colors.primary} bold>Recent Permission Decisions</Text>
|
|
360
|
+
<Box flexDirection="column" marginTop={1}>
|
|
361
|
+
{/* Header */}
|
|
362
|
+
<Text color={colors.textMuted}>
|
|
363
|
+
{'Time'.padEnd(8)}{'Decision'.padEnd(11)}{'Tool'.padEnd(12)}Input
|
|
364
|
+
</Text>
|
|
365
|
+
<Text color={colors.textMuted}>{'─'.repeat(60)}</Text>
|
|
366
|
+
|
|
367
|
+
{/* Entries */}
|
|
368
|
+
{entries.slice(0, 10).map((entry, i) => {
|
|
369
|
+
const decisionColor =
|
|
370
|
+
entry.decision === 'allowed' || entry.decision === 'confirmed'
|
|
371
|
+
? colors.success
|
|
372
|
+
: entry.decision === 'denied' || entry.decision === 'rejected'
|
|
373
|
+
? colors.error
|
|
374
|
+
: colors.warning;
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<Text key={i}>
|
|
378
|
+
<Text color={colors.textMuted}>{entry.time.padEnd(8)}</Text>
|
|
379
|
+
<Text color={decisionColor}>{entry.decision.padEnd(11)}</Text>
|
|
380
|
+
<Text color={colors.tool}>{entry.tool.padEnd(12)}</Text>
|
|
381
|
+
<Text>{entry.input.slice(0, 30)}</Text>
|
|
382
|
+
</Text>
|
|
383
|
+
);
|
|
384
|
+
})}
|
|
385
|
+
</Box>
|
|
386
|
+
</Box>
|
|
387
|
+
);
|
|
388
|
+
}
|
|
@@ -424,15 +424,15 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
424
424
|
return (
|
|
425
425
|
<Box key={item.provider.id} paddingLeft={2}>
|
|
426
426
|
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
427
|
-
{isSelected ? icons.arrow : ' '}
|
|
427
|
+
{isSelected ? icons.arrow : ' '}{' '}
|
|
428
428
|
</Text>
|
|
429
|
-
<Text color={colors.success}>{icons.success} </Text>
|
|
430
429
|
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
431
430
|
{item.provider.name}
|
|
432
431
|
</Text>
|
|
433
432
|
<Text color={colors.textMuted}>
|
|
434
433
|
{' '}({connName}) · {item.modelCount} models
|
|
435
434
|
</Text>
|
|
435
|
+
<Text color={colors.success}> {icons.success}</Text>
|
|
436
436
|
</Box>
|
|
437
437
|
);
|
|
438
438
|
})}
|
|
@@ -456,7 +456,7 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
456
456
|
return (
|
|
457
457
|
<Box key={item.provider.id} paddingLeft={2}>
|
|
458
458
|
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
459
|
-
{isSelected ? icons.arrow : ' '}
|
|
459
|
+
{isSelected ? icons.arrow : ' '}{' '}
|
|
460
460
|
</Text>
|
|
461
461
|
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
462
462
|
{item.provider.name}
|
|
@@ -496,9 +496,8 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
496
496
|
<Box key={item.provider.id} paddingLeft={2} flexDirection="column">
|
|
497
497
|
<Box>
|
|
498
498
|
<Text color={isSelected ? colors.primary : colors.textMuted}>
|
|
499
|
-
{isSelected ? icons.arrow : ' '}
|
|
499
|
+
{isSelected ? icons.arrow : ' '}{' '}
|
|
500
500
|
</Text>
|
|
501
|
-
{item.isSelected && <Text color={colors.success}>{icons.success} </Text>}
|
|
502
501
|
<Text color={isSelected ? colors.text : colors.textSecondary} bold={isSelected}>
|
|
503
502
|
{item.provider.name}
|
|
504
503
|
</Text>
|
|
@@ -511,6 +510,7 @@ export function ProviderManager({ onClose }: ProviderManagerProps) {
|
|
|
511
510
|
{item.provider.requiresKey && !item.isAvailable && (
|
|
512
511
|
<Text color={colors.textMuted}> (not configured)</Text>
|
|
513
512
|
)}
|
|
513
|
+
{item.isSelected && <Text color={colors.success}> {icons.success}</Text>}
|
|
514
514
|
</Box>
|
|
515
515
|
{isSelected && item.provider.requiresKey && !item.isAvailable && (
|
|
516
516
|
<Text color={colors.textMuted} dimColor>
|