gencode-ai 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gencode/settings.local.json +7 -0
- package/README.md +20 -102
- package/dist/agent/agent.d.ts +43 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +90 -17
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +9 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/cli/components/AllModelsSelector.d.ts +11 -0
- package/dist/cli/components/AllModelsSelector.d.ts.map +1 -0
- package/dist/cli/components/AllModelsSelector.js +153 -0
- package/dist/cli/components/AllModelsSelector.js.map +1 -0
- 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 +276 -40
- 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 +3 -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 +17 -3
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +70 -18
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModelSelector.d.ts +7 -7
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +116 -33
- package/dist/cli/components/ModelSelector.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.d.ts +8 -0
- package/dist/cli/components/ProviderManager.d.ts.map +1 -0
- package/dist/cli/components/ProviderManager.js +280 -0
- package/dist/cli/components/ProviderManager.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 +1 -0
- package/dist/cli/components/index.d.ts.map +1 -1
- package/dist/cli/components/index.js +1 -0
- package/dist/cli/components/index.js.map +1 -1
- package/dist/cli/components/markdown.d.ts +9 -0
- package/dist/cli/components/markdown.d.ts.map +1 -0
- package/dist/cli/components/markdown.js +129 -0
- package/dist/cli/components/markdown.js.map +1 -0
- package/dist/cli/components/theme.d.ts +5 -0
- package/dist/cli/components/theme.d.ts.map +1 -1
- package/dist/cli/components/theme.js +7 -0
- package/dist/cli/components/theme.js.map +1 -1
- package/dist/cli/index.js +66 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +14 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +19 -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/providers-config.d.ts +28 -0
- package/dist/config/providers-config.d.ts.map +1 -0
- package/dist/config/providers-config.js +79 -0
- package/dist/config/providers-config.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 +108 -9
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +53 -2
- package/dist/config/types.js.map +1 -1
- package/dist/memory/import-resolver.d.ts +46 -0
- package/dist/memory/import-resolver.d.ts.map +1 -0
- package/dist/memory/import-resolver.js +117 -0
- package/dist/memory/import-resolver.js.map +1 -0
- package/dist/memory/index.d.ts +7 -6
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +7 -5
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/init-prompt.d.ts +22 -0
- package/dist/memory/init-prompt.d.ts.map +1 -0
- package/dist/memory/init-prompt.js +103 -0
- package/dist/memory/init-prompt.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +119 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +587 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/memory/rules-parser.d.ts +38 -0
- package/dist/memory/rules-parser.d.ts.map +1 -0
- package/dist/memory/rules-parser.js +69 -0
- package/dist/memory/rules-parser.js.map +1 -0
- package/dist/memory/test-utils.d.ts +20 -0
- package/dist/memory/test-utils.d.ts.map +1 -0
- package/dist/memory/test-utils.js +44 -0
- package/dist/memory/test-utils.js.map +1 -0
- package/dist/memory/types.d.ts +70 -63
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +42 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/permissions/audit.d.ts +82 -0
- package/dist/permissions/audit.d.ts.map +1 -0
- package/dist/permissions/audit.js +229 -0
- package/dist/permissions/audit.js.map +1 -0
- package/dist/permissions/index.d.ts +11 -1
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +15 -0
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/manager.d.ts +149 -13
- package/dist/permissions/manager.d.ts.map +1 -1
- package/dist/permissions/manager.js +480 -35
- package/dist/permissions/manager.js.map +1 -1
- package/dist/permissions/manager.test.d.ts +5 -0
- package/dist/permissions/manager.test.d.ts.map +1 -0
- package/dist/permissions/manager.test.js +213 -0
- package/dist/permissions/manager.test.js.map +1 -0
- package/dist/permissions/persistence.d.ts +74 -0
- package/dist/permissions/persistence.d.ts.map +1 -0
- package/dist/permissions/persistence.js +248 -0
- package/dist/permissions/persistence.js.map +1 -0
- package/dist/permissions/persistence.test.d.ts +5 -0
- package/dist/permissions/persistence.test.d.ts.map +1 -0
- package/dist/permissions/persistence.test.js +171 -0
- package/dist/permissions/persistence.test.js.map +1 -0
- package/dist/permissions/prompt-matcher.d.ts +64 -0
- package/dist/permissions/prompt-matcher.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.js +415 -0
- package/dist/permissions/prompt-matcher.js.map +1 -0
- package/dist/permissions/prompt-matcher.test.d.ts +5 -0
- package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.test.js +107 -0
- package/dist/permissions/prompt-matcher.test.js.map +1 -0
- package/dist/permissions/types.d.ts +157 -0
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/permissions/types.js +43 -8
- package/dist/permissions/types.js.map +1 -1
- package/dist/prompts/index.d.ts +92 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +241 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +14 -3
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +5 -3
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +13 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/registry.d.ts +66 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +158 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/search/brave.d.ts +14 -0
- package/dist/providers/search/brave.d.ts.map +1 -0
- package/dist/providers/search/brave.js +87 -0
- package/dist/providers/search/brave.js.map +1 -0
- package/dist/providers/search/exa.d.ts +12 -0
- package/dist/providers/search/exa.d.ts.map +1 -0
- package/dist/providers/search/exa.js +158 -0
- package/dist/providers/search/exa.js.map +1 -0
- package/dist/providers/search/index.d.ts +31 -0
- package/dist/providers/search/index.d.ts.map +1 -0
- package/dist/providers/search/index.js +75 -0
- package/dist/providers/search/index.js.map +1 -0
- package/dist/providers/search/serper.d.ts +14 -0
- package/dist/providers/search/serper.d.ts.map +1 -0
- package/dist/providers/search/serper.js +87 -0
- package/dist/providers/search/serper.js.map +1 -0
- package/dist/providers/search/types.d.ts +21 -0
- package/dist/providers/search/types.d.ts.map +1 -0
- package/dist/providers/search/types.js +5 -0
- package/dist/providers/search/types.js.map +1 -0
- package/dist/providers/store.d.ts +104 -0
- package/dist/providers/store.d.ts.map +1 -0
- package/dist/providers/store.js +171 -0
- package/dist/providers/store.js.map +1 -0
- package/dist/providers/types.d.ts +7 -1
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +33 -0
- package/dist/providers/vertex-ai.d.ts.map +1 -0
- package/dist/providers/vertex-ai.js +407 -0
- package/dist/providers/vertex-ai.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 +20 -0
- package/dist/tools/builtin/webfetch.d.ts.map +1 -0
- package/dist/tools/builtin/webfetch.js +228 -0
- package/dist/tools/builtin/webfetch.js.map +1 -0
- package/dist/tools/builtin/websearch.d.ts +17 -0
- package/dist/tools/builtin/websearch.d.ts.map +1 -0
- package/dist/tools/builtin/websearch.js +87 -0
- package/dist/tools/builtin/websearch.js.map +1 -0
- 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 +18 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +28 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +41 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +16 -0
- package/dist/tools/types.js.map +1 -1
- package/dist/tools/utils/ssrf.d.ts +18 -0
- package/dist/tools/utils/ssrf.d.ts.map +1 -0
- package/dist/tools/utils/ssrf.js +70 -0
- package/dist/tools/utils/ssrf.js.map +1 -0
- package/docs/README.md +5 -4
- package/docs/config-system-comparison.md +707 -0
- package/docs/memory-system.md +238 -0
- package/docs/permissions.md +368 -0
- package/docs/proposals/0001-web-fetch-tool.md +32 -2
- package/docs/proposals/0002-web-search-tool.md +59 -2
- package/docs/proposals/0005-todo-system.md +350 -85
- package/docs/proposals/0006-memory-system.md +11 -10
- package/docs/proposals/0012-ask-user-question.md +941 -206
- package/docs/proposals/0023-permission-enhancements.md +61 -2
- package/docs/proposals/0041-configuration-system.md +587 -0
- package/docs/proposals/0042-prompt-optimization.md +866 -0
- package/docs/proposals/README.md +8 -6
- package/docs/providers.md +220 -0
- package/jest.config.js +26 -0
- package/package.json +14 -3
- package/src/agent/agent.ts +120 -18
- package/src/agent/types.ts +9 -1
- package/src/cli/components/App.tsx +369 -47
- package/src/cli/components/CommandSuggestions.tsx +3 -0
- package/src/cli/components/Header.tsx +11 -17
- package/src/cli/components/Logo.tsx +76 -9
- package/src/cli/components/Messages.tsx +146 -38
- package/src/cli/components/ModelSelector.tsx +169 -52
- package/src/cli/components/PermissionPrompt.tsx +388 -0
- package/src/cli/components/ProviderManager.tsx +534 -0
- package/src/cli/components/Spinner.tsx +138 -25
- package/src/cli/components/TodoList.tsx +54 -0
- package/src/cli/components/index.ts +6 -0
- package/src/cli/components/markdown.ts +157 -0
- package/src/cli/components/theme.ts +7 -0
- package/src/cli/index.tsx +76 -13
- package/src/config/index.ts +79 -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/providers-config.ts +85 -0
- package/src/config/test-utils.ts +79 -0
- package/src/config/types.ts +186 -9
- package/src/memory/import-resolver.test.ts +117 -0
- package/src/memory/import-resolver.ts +149 -0
- package/src/memory/index.ts +11 -0
- package/src/memory/init-prompt.ts +113 -0
- package/src/memory/memory-manager.test.ts +198 -0
- package/src/memory/memory-manager.ts +716 -0
- package/src/memory/rules-parser.test.ts +182 -0
- package/src/memory/rules-parser.ts +82 -0
- package/src/memory/test-utils.ts +60 -0
- package/src/memory/types.ts +119 -0
- package/src/permissions/audit.ts +284 -0
- package/src/permissions/index.ts +20 -1
- package/src/permissions/manager.test.ts +260 -0
- package/src/permissions/manager.ts +592 -40
- package/src/permissions/persistence.test.ts +220 -0
- package/src/permissions/persistence.ts +301 -0
- package/src/permissions/prompt-matcher.test.ts +213 -0
- package/src/permissions/prompt-matcher.ts +472 -0
- package/src/permissions/types.ts +236 -8
- package/src/prompts/index.test.ts +279 -0
- package/src/prompts/index.ts +306 -0
- package/src/prompts/system/anthropic.txt +29 -0
- package/src/prompts/system/base.txt +124 -0
- package/src/prompts/system/gemini.txt +35 -0
- package/src/prompts/system/generic.txt +128 -0
- package/src/prompts/system/openai.txt +29 -0
- package/src/prompts/tools/bash.txt +60 -0
- package/src/prompts/tools/edit.txt +29 -0
- package/src/prompts/tools/glob.txt +35 -0
- package/src/prompts/tools/grep.txt +43 -0
- package/src/prompts/tools/read.txt +22 -0
- package/src/prompts/tools/todowrite.txt +71 -0
- package/src/prompts/tools/webfetch.txt +34 -0
- package/src/prompts/tools/websearch.txt +41 -0
- package/src/prompts/tools/write.txt +23 -0
- package/src/providers/gemini.ts +20 -4
- package/src/providers/index.ts +18 -3
- package/src/providers/registry.ts +198 -0
- package/src/providers/search/brave.ts +132 -0
- package/src/providers/search/exa.ts +217 -0
- package/src/providers/search/index.ts +79 -0
- package/src/providers/search/serper.ts +133 -0
- package/src/providers/search/types.ts +24 -0
- package/src/providers/store.ts +216 -0
- package/src/providers/types.ts +9 -1
- package/src/providers/vertex-ai.ts +594 -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 +261 -0
- package/src/tools/builtin/websearch.ts +103 -0
- package/src/tools/builtin/write.ts +2 -1
- package/src/tools/index.ts +28 -2
- package/src/tools/types.ts +32 -0
- package/src/tools/utils/ssrf.ts +79 -0
- package/tsconfig.json +1 -1
- package/CLAUDE.md +0 -70
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AssistantMessage,
|
|
12
12
|
ToolCall,
|
|
13
13
|
ToolResult,
|
|
14
|
+
PendingToolCall,
|
|
14
15
|
InfoMessage,
|
|
15
16
|
WelcomeMessage,
|
|
16
17
|
CompletionMessage,
|
|
@@ -19,13 +20,24 @@ import { Header } from './Header.js';
|
|
|
19
20
|
import { ProgressBar } from './Spinner.js';
|
|
20
21
|
import { PromptInput, ConfirmPrompt } from './Input.js';
|
|
21
22
|
import { ModelSelector } from './ModelSelector.js';
|
|
23
|
+
import { ProviderManager } from './ProviderManager.js';
|
|
22
24
|
import { CommandSuggestions, getFilteredCommands } from './CommandSuggestions.js';
|
|
25
|
+
import {
|
|
26
|
+
PermissionPrompt,
|
|
27
|
+
PermissionRulesDisplay,
|
|
28
|
+
PermissionAuditDisplay,
|
|
29
|
+
} from './PermissionPrompt.js';
|
|
30
|
+
import { TodoList } from './TodoList.js';
|
|
23
31
|
import { colors, icons } from './theme.js';
|
|
32
|
+
import { getTodos } from '../../tools/index.js';
|
|
33
|
+
import type { ProviderName } from '../../providers/index.js';
|
|
34
|
+
import type { ApprovalAction, ApprovalSuggestion } from '../../permissions/types.js';
|
|
35
|
+
import { gatherContextFiles, buildInitPrompt, getContextSummary } from '../../memory/index.js';
|
|
24
36
|
|
|
25
37
|
// Types
|
|
26
38
|
interface HistoryItem {
|
|
27
39
|
id: string;
|
|
28
|
-
type: 'header' | 'welcome' | 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'info' | 'completion';
|
|
40
|
+
type: 'header' | 'welcome' | 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'info' | 'completion' | 'todos';
|
|
29
41
|
content: string;
|
|
30
42
|
meta?: Record<string, unknown>;
|
|
31
43
|
}
|
|
@@ -33,11 +45,14 @@ interface HistoryItem {
|
|
|
33
45
|
interface ConfirmState {
|
|
34
46
|
tool: string;
|
|
35
47
|
input: Record<string, unknown>;
|
|
36
|
-
|
|
48
|
+
suggestions: ApprovalSuggestion[];
|
|
49
|
+
resolve: (action: ApprovalAction) => void;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
interface SettingsManager {
|
|
40
53
|
save: (settings: { model?: string }) => Promise<void>;
|
|
54
|
+
getCwd?: () => string;
|
|
55
|
+
addPermissionRule?: (pattern: string, type: 'allow' | 'deny', level?: 'global' | 'project' | 'local') => Promise<void>;
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
interface Session {
|
|
@@ -46,10 +61,16 @@ interface Session {
|
|
|
46
61
|
updatedAt: string;
|
|
47
62
|
}
|
|
48
63
|
|
|
64
|
+
interface PermissionSettings {
|
|
65
|
+
allow?: string[];
|
|
66
|
+
deny?: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
49
69
|
interface AppProps {
|
|
50
70
|
config: AgentConfig;
|
|
51
71
|
settingsManager?: SettingsManager;
|
|
52
72
|
resumeLatest?: boolean;
|
|
73
|
+
permissionSettings?: PermissionSettings;
|
|
53
74
|
}
|
|
54
75
|
|
|
55
76
|
// ============================================================================
|
|
@@ -60,12 +81,28 @@ function useAgent(config: AgentConfig) {
|
|
|
60
81
|
return agent;
|
|
61
82
|
}
|
|
62
83
|
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// Utils
|
|
86
|
+
// ============================================================================
|
|
87
|
+
const genId = () => Math.random().toString(36).slice(2);
|
|
88
|
+
|
|
89
|
+
const formatRelativeTime = (dateStr: string) => {
|
|
90
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
91
|
+
const mins = Math.floor(diff / 60000);
|
|
92
|
+
const hrs = Math.floor(mins / 60);
|
|
93
|
+
const days = Math.floor(hrs / 24);
|
|
94
|
+
if (mins < 60) return `${mins}m`;
|
|
95
|
+
if (hrs < 24) return `${hrs}h`;
|
|
96
|
+
return `${days}d`;
|
|
97
|
+
};
|
|
98
|
+
|
|
63
99
|
// ============================================================================
|
|
64
100
|
// Help Component
|
|
65
101
|
// ============================================================================
|
|
66
102
|
function HelpPanel() {
|
|
67
103
|
const commands: [string, string][] = [
|
|
68
104
|
['/model [name]', 'Switch model'],
|
|
105
|
+
['/provider', 'Manage providers'],
|
|
69
106
|
['/sessions', 'List sessions'],
|
|
70
107
|
['/resume [n]', 'Resume session'],
|
|
71
108
|
['/new', 'New session'],
|
|
@@ -92,16 +129,6 @@ interface SessionsTableProps {
|
|
|
92
129
|
}
|
|
93
130
|
|
|
94
131
|
function SessionsTable({ sessions }: SessionsTableProps) {
|
|
95
|
-
const formatTime = (dateStr: string) => {
|
|
96
|
-
const diff = Date.now() - new Date(dateStr).getTime();
|
|
97
|
-
const mins = Math.floor(diff / 60000);
|
|
98
|
-
const hrs = Math.floor(mins / 60);
|
|
99
|
-
const days = Math.floor(hrs / 24);
|
|
100
|
-
if (mins < 60) return `${mins}m`;
|
|
101
|
-
if (hrs < 24) return `${hrs}h`;
|
|
102
|
-
return `${days}d`;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
132
|
return (
|
|
106
133
|
<Box flexDirection="column">
|
|
107
134
|
{sessions.slice(0, 6).map((s, i) => (
|
|
@@ -109,23 +136,74 @@ function SessionsTable({ sessions }: SessionsTableProps) {
|
|
|
109
136
|
<Text color={colors.textMuted}>{String(i + 1).padEnd(2)}</Text>
|
|
110
137
|
<Text color={colors.primary}>{s.id.slice(0, 7).padEnd(8)}</Text>
|
|
111
138
|
<Text>{s.title.slice(0, 25).padEnd(26)}</Text>
|
|
112
|
-
<Text color={colors.textMuted}>{
|
|
139
|
+
<Text color={colors.textMuted}>{formatRelativeTime(s.updatedAt)}</Text>
|
|
113
140
|
</Text>
|
|
114
141
|
))}
|
|
115
142
|
</Box>
|
|
116
143
|
);
|
|
117
144
|
}
|
|
118
145
|
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Memory Files Display Component
|
|
148
|
+
// ============================================================================
|
|
149
|
+
interface MemoryFileInfo {
|
|
150
|
+
path: string;
|
|
151
|
+
level: string;
|
|
152
|
+
size: number;
|
|
153
|
+
type: 'file' | 'rule';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function MemoryFilesDisplay({ files }: { files: MemoryFileInfo[] }) {
|
|
157
|
+
const formatSize = (bytes: number): string => {
|
|
158
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
159
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const memoryFiles = files.filter((f) => f.type === 'file');
|
|
163
|
+
const ruleFiles = files.filter((f) => f.type === 'rule');
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<Box flexDirection="column">
|
|
167
|
+
{memoryFiles.length > 0 && (
|
|
168
|
+
<>
|
|
169
|
+
<Text color={colors.info}>Loaded Memory Files:</Text>
|
|
170
|
+
{memoryFiles.map((f, i) => (
|
|
171
|
+
<Text key={f.path}>
|
|
172
|
+
<Text color={colors.textMuted}> [{i + 1}] </Text>
|
|
173
|
+
<Text color={colors.primary}>{f.path} </Text>
|
|
174
|
+
<Text color={colors.textMuted}>({f.level}, {formatSize(f.size)})</Text>
|
|
175
|
+
</Text>
|
|
176
|
+
))}
|
|
177
|
+
</>
|
|
178
|
+
)}
|
|
179
|
+
{ruleFiles.length > 0 && (
|
|
180
|
+
<Box flexDirection="column" marginTop={memoryFiles.length > 0 ? 1 : 0}>
|
|
181
|
+
<Text color={colors.info}>
|
|
182
|
+
Loaded Rules:
|
|
183
|
+
</Text>
|
|
184
|
+
{ruleFiles.map((f, i) => (
|
|
185
|
+
<Text key={f.path}>
|
|
186
|
+
<Text color={colors.textMuted}> [{i + 1}] </Text>
|
|
187
|
+
<Text color={colors.warning}>{f.path} </Text>
|
|
188
|
+
<Text color={colors.textMuted}>({f.level}, {formatSize(f.size)})</Text>
|
|
189
|
+
</Text>
|
|
190
|
+
))}
|
|
191
|
+
</Box>
|
|
192
|
+
)}
|
|
193
|
+
{files.length === 0 && (
|
|
194
|
+
<Text color={colors.textMuted}>No memory files loaded</Text>
|
|
195
|
+
)}
|
|
196
|
+
</Box>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
119
200
|
// ============================================================================
|
|
120
201
|
// Main App
|
|
121
202
|
// ============================================================================
|
|
122
|
-
export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
203
|
+
export function App({ config, settingsManager, resumeLatest, permissionSettings }: AppProps) {
|
|
123
204
|
const { exit } = useApp();
|
|
124
205
|
const agent = useAgent(config);
|
|
125
206
|
|
|
126
|
-
// Generate unique ID
|
|
127
|
-
const genId = () => Math.random().toString(36).slice(2);
|
|
128
|
-
|
|
129
207
|
// Initial header item
|
|
130
208
|
const cwd = config.cwd || process.cwd();
|
|
131
209
|
const home = process.env.HOME || '';
|
|
@@ -154,11 +232,17 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
154
232
|
const [isThinking, setIsThinking] = useState(false);
|
|
155
233
|
const [streamingText, setStreamingText] = useState('');
|
|
156
234
|
const streamingTextRef = useRef(''); // Track current streaming text for closure
|
|
235
|
+
const [processingStartTime, setProcessingStartTime] = useState<number | undefined>(undefined);
|
|
236
|
+
const [tokenCount, setTokenCount] = useState(0);
|
|
157
237
|
const [confirmState, setConfirmState] = useState<ConfirmState | null>(null);
|
|
158
238
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
239
|
+
const [showProviderManager, setShowProviderManager] = useState(false);
|
|
159
240
|
const [currentModel, setCurrentModel] = useState(config.model);
|
|
160
241
|
const [cmdSuggestionIndex, setCmdSuggestionIndex] = useState(0);
|
|
161
242
|
const [inputKey, setInputKey] = useState(0); // Force cursor to end after autocomplete
|
|
243
|
+
const [pendingTool, setPendingTool] = useState<{ name: string; input: Record<string, unknown> } | null>(null);
|
|
244
|
+
const pendingToolRef = useRef<{ name: string; input: Record<string, unknown> } | null>(null);
|
|
245
|
+
const [todos, setTodos] = useState<ReturnType<typeof getTodos>>([]);
|
|
162
246
|
|
|
163
247
|
// Check if showing command suggestions
|
|
164
248
|
const showCmdSuggestions = input.startsWith('/') && !isProcessing;
|
|
@@ -177,12 +261,30 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
177
261
|
// Initialize
|
|
178
262
|
useEffect(() => {
|
|
179
263
|
const init = async () => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
264
|
+
// Initialize permission system with settings
|
|
265
|
+
await agent.initializePermissions(permissionSettings);
|
|
266
|
+
|
|
267
|
+
// Set enhanced confirm callback with approval options
|
|
268
|
+
agent.setEnhancedConfirmCallback(async (tool, toolInput, suggestions) => {
|
|
269
|
+
return new Promise<ApprovalAction>((resolve) => {
|
|
270
|
+
setConfirmState({
|
|
271
|
+
tool,
|
|
272
|
+
input: toolInput as Record<string, unknown>,
|
|
273
|
+
suggestions,
|
|
274
|
+
resolve,
|
|
275
|
+
});
|
|
183
276
|
});
|
|
184
277
|
});
|
|
185
278
|
|
|
279
|
+
// Set callback to save permission rules to settings.local.json
|
|
280
|
+
if (settingsManager?.addPermissionRule) {
|
|
281
|
+
agent.setSaveRuleCallback(async (tool, pattern) => {
|
|
282
|
+
// Format as Claude Code style pattern: Tool(pattern) or just Tool
|
|
283
|
+
const rulePattern = pattern ? `${tool}(${pattern})` : tool;
|
|
284
|
+
await settingsManager.addPermissionRule!(rulePattern, 'allow', 'local');
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
186
288
|
if (resumeLatest) {
|
|
187
289
|
const resumed = await agent.resumeLatest();
|
|
188
290
|
if (resumed) {
|
|
@@ -191,22 +293,27 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
191
293
|
}
|
|
192
294
|
};
|
|
193
295
|
init();
|
|
194
|
-
}, [agent, resumeLatest, addHistory]);
|
|
296
|
+
}, [agent, resumeLatest, addHistory, permissionSettings, settingsManager]);
|
|
195
297
|
|
|
196
|
-
// Handle
|
|
197
|
-
const
|
|
298
|
+
// Handle permission decision
|
|
299
|
+
const handlePermissionDecision = (action: ApprovalAction) => {
|
|
198
300
|
if (confirmState) {
|
|
199
|
-
confirmState.resolve(
|
|
301
|
+
confirmState.resolve(action);
|
|
200
302
|
setConfirmState(null);
|
|
201
303
|
}
|
|
202
304
|
};
|
|
203
305
|
|
|
204
306
|
// Handle model selection
|
|
205
|
-
const handleModelSelect = async (model: string) => {
|
|
307
|
+
const handleModelSelect = async (model: string, providerId?: ProviderName) => {
|
|
206
308
|
agent.setModel(model);
|
|
207
309
|
setCurrentModel(model);
|
|
208
310
|
setShowModelSelector(false);
|
|
209
|
-
|
|
311
|
+
|
|
312
|
+
if (providerId) {
|
|
313
|
+
addHistory({ type: 'info', content: `${providerId}: ${model}` });
|
|
314
|
+
} else {
|
|
315
|
+
addHistory({ type: 'info', content: `Model: ${model}` });
|
|
316
|
+
}
|
|
210
317
|
|
|
211
318
|
// Save to settings for next startup
|
|
212
319
|
if (settingsManager) {
|
|
@@ -302,13 +409,118 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
302
409
|
return true;
|
|
303
410
|
}
|
|
304
411
|
|
|
412
|
+
case 'provider': {
|
|
413
|
+
setShowProviderManager(true);
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
case 'permissions': {
|
|
418
|
+
const permManager = agent.getPermissionManager();
|
|
419
|
+
|
|
420
|
+
if (arg === 'audit') {
|
|
421
|
+
// Show audit log
|
|
422
|
+
const auditLog = permManager.getAuditLog(20);
|
|
423
|
+
if (auditLog.length === 0) {
|
|
424
|
+
addHistory({ type: 'info', content: 'No permission decisions recorded yet' });
|
|
425
|
+
} else {
|
|
426
|
+
const entries = auditLog.map((e) => ({
|
|
427
|
+
time: e.timestamp.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }),
|
|
428
|
+
tool: e.tool,
|
|
429
|
+
input: e.inputSummary,
|
|
430
|
+
decision: e.decision,
|
|
431
|
+
rule: e.matchedRule,
|
|
432
|
+
}));
|
|
433
|
+
addHistory({
|
|
434
|
+
type: 'info',
|
|
435
|
+
content: '__PERMISSION_AUDIT__',
|
|
436
|
+
meta: { entries },
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
} else if (arg === 'stats') {
|
|
440
|
+
// Show statistics
|
|
441
|
+
const stats = permManager.getAuditStats();
|
|
442
|
+
addHistory({
|
|
443
|
+
type: 'info',
|
|
444
|
+
content: `Permissions: ${stats.allowed + stats.confirmed} allowed, ${stats.denied + stats.rejected} denied`,
|
|
445
|
+
});
|
|
446
|
+
} else {
|
|
447
|
+
// Show rules
|
|
448
|
+
const rules = permManager.getRules();
|
|
449
|
+
const prompts = permManager.getAllowedPrompts();
|
|
450
|
+
const displayRules = rules.map((r) => ({
|
|
451
|
+
type: r.scope === 'session' ? 'Session' : r.description?.startsWith('Settings') ? 'Settings' : 'Built-in',
|
|
452
|
+
tool: typeof r.tool === 'string' ? r.tool : r.tool.toString(),
|
|
453
|
+
pattern: typeof r.pattern === 'string' ? r.pattern : r.pattern?.toString(),
|
|
454
|
+
scope: r.scope ?? 'session',
|
|
455
|
+
mode: r.mode,
|
|
456
|
+
}));
|
|
457
|
+
addHistory({
|
|
458
|
+
type: 'info',
|
|
459
|
+
content: '__PERMISSIONS__',
|
|
460
|
+
meta: { rules: displayRules, prompts },
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
305
466
|
case 'init': {
|
|
306
|
-
|
|
467
|
+
// Gather context files and generate AGENT.md
|
|
468
|
+
addHistory({ type: 'info', content: 'Analyzing codebase...' });
|
|
469
|
+
|
|
470
|
+
const contextFiles = await gatherContextFiles(cwd);
|
|
471
|
+
addHistory({ type: 'info', content: getContextSummary(contextFiles) });
|
|
472
|
+
|
|
473
|
+
// Check if AGENT.md already exists
|
|
474
|
+
const memoryManager = agent.getMemoryManager();
|
|
475
|
+
const existingPath = await memoryManager.getExistingProjectMemoryPath(cwd);
|
|
476
|
+
let existingContent: string | undefined;
|
|
477
|
+
|
|
478
|
+
if (existingPath) {
|
|
479
|
+
try {
|
|
480
|
+
const fs = await import('fs/promises');
|
|
481
|
+
existingContent = await fs.readFile(existingPath, 'utf-8');
|
|
482
|
+
addHistory({
|
|
483
|
+
type: 'info',
|
|
484
|
+
content: `Found existing: ${existingPath.replace(cwd, '.')}`,
|
|
485
|
+
});
|
|
486
|
+
} catch {
|
|
487
|
+
// File doesn't exist or can't be read
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Build init prompt and run through agent
|
|
492
|
+
const initPrompt = buildInitPrompt(contextFiles, existingContent);
|
|
493
|
+
addHistory({ type: 'info', content: 'Generating AGENT.md...' });
|
|
494
|
+
addHistory({ type: 'user', content: '/init' });
|
|
495
|
+
await runAgent(initPrompt);
|
|
307
496
|
return true;
|
|
308
497
|
}
|
|
309
498
|
|
|
310
499
|
case 'memory': {
|
|
311
|
-
|
|
500
|
+
// Show loaded memory files
|
|
501
|
+
const memoryManager = agent.getMemoryManager();
|
|
502
|
+
const loadedFiles = memoryManager.getLoadedFileList();
|
|
503
|
+
|
|
504
|
+
if (loadedFiles.length === 0) {
|
|
505
|
+
// Try to load memory first
|
|
506
|
+
await agent.loadMemory();
|
|
507
|
+
const filesAfterLoad = memoryManager.getLoadedFileList();
|
|
508
|
+
if (filesAfterLoad.length === 0) {
|
|
509
|
+
addHistory({ type: 'info', content: 'No memory files found' });
|
|
510
|
+
} else {
|
|
511
|
+
addHistory({
|
|
512
|
+
type: 'info',
|
|
513
|
+
content: '__MEMORY__',
|
|
514
|
+
meta: { files: filesAfterLoad },
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
} else {
|
|
518
|
+
addHistory({
|
|
519
|
+
type: 'info',
|
|
520
|
+
content: '__MEMORY__',
|
|
521
|
+
meta: { files: loadedFiles },
|
|
522
|
+
});
|
|
523
|
+
}
|
|
312
524
|
return true;
|
|
313
525
|
}
|
|
314
526
|
|
|
@@ -328,6 +540,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
328
540
|
streamingTextRef.current = '';
|
|
329
541
|
interruptFlagRef.current = false;
|
|
330
542
|
const startTime = Date.now();
|
|
543
|
+
setProcessingStartTime(startTime);
|
|
544
|
+
setTokenCount(0);
|
|
331
545
|
|
|
332
546
|
try {
|
|
333
547
|
for await (const event of agent.run(prompt)) {
|
|
@@ -341,6 +555,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
341
555
|
setIsThinking(false);
|
|
342
556
|
streamingTextRef.current += event.text;
|
|
343
557
|
setStreamingText(streamingTextRef.current);
|
|
558
|
+
// Estimate token count (roughly 4 chars per token)
|
|
559
|
+
setTokenCount((prev) => prev + Math.max(1, Math.ceil(event.text.length / 4)));
|
|
344
560
|
break;
|
|
345
561
|
|
|
346
562
|
case 'tool_start':
|
|
@@ -350,19 +566,46 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
350
566
|
streamingTextRef.current = '';
|
|
351
567
|
setStreamingText('');
|
|
352
568
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
});
|
|
569
|
+
// Set pending tool for spinner animation (use both state and ref)
|
|
570
|
+
const toolInfo = { name: event.name, input: event.input as Record<string, unknown> };
|
|
571
|
+
pendingToolRef.current = toolInfo;
|
|
572
|
+
setPendingTool(toolInfo);
|
|
358
573
|
break;
|
|
359
574
|
|
|
360
575
|
case 'tool_result':
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
576
|
+
// For TodoWrite: add todos first, then hide tool_call/tool_result
|
|
577
|
+
if (event.name === 'TodoWrite') {
|
|
578
|
+
const currentTodos = getTodos();
|
|
579
|
+
setTodos(currentTodos);
|
|
580
|
+
if (currentTodos.length > 0) {
|
|
581
|
+
addHistory({
|
|
582
|
+
type: 'todos',
|
|
583
|
+
content: '',
|
|
584
|
+
meta: { todos: currentTodos },
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
// Add tool_call to history (now completed) - use ref for correct value
|
|
589
|
+
if (pendingToolRef.current) {
|
|
590
|
+
addHistory({
|
|
591
|
+
type: 'tool_call',
|
|
592
|
+
content: pendingToolRef.current.name,
|
|
593
|
+
meta: { toolName: pendingToolRef.current.name, input: pendingToolRef.current.input },
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
// Add tool_result to history
|
|
597
|
+
addHistory({
|
|
598
|
+
type: 'tool_result',
|
|
599
|
+
content: event.result.output,
|
|
600
|
+
meta: {
|
|
601
|
+
toolName: event.name,
|
|
602
|
+
success: event.result.success,
|
|
603
|
+
metadata: event.result.metadata,
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
pendingToolRef.current = null;
|
|
608
|
+
setPendingTool(null);
|
|
366
609
|
setIsThinking(true);
|
|
367
610
|
break;
|
|
368
611
|
|
|
@@ -380,6 +623,7 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
380
623
|
// Add completion message with duration
|
|
381
624
|
const durationMs = Date.now() - startTime;
|
|
382
625
|
addHistory({ type: 'completion', content: '', meta: { durationMs } });
|
|
626
|
+
setProcessingStartTime(undefined);
|
|
383
627
|
break;
|
|
384
628
|
}
|
|
385
629
|
}
|
|
@@ -421,6 +665,47 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
421
665
|
return;
|
|
422
666
|
}
|
|
423
667
|
|
|
668
|
+
// Handle # prefix for quick memory adds
|
|
669
|
+
// ## note -> user memory (~/.gencode/AGENT.md)
|
|
670
|
+
// # note -> project memory (./AGENT.md)
|
|
671
|
+
if (trimmed.startsWith('#') && !trimmed.startsWith('#!/')) {
|
|
672
|
+
const memoryManager = agent.getMemoryManager();
|
|
673
|
+
let level: 'user' | 'project';
|
|
674
|
+
let content: string;
|
|
675
|
+
|
|
676
|
+
if (trimmed.startsWith('## ')) {
|
|
677
|
+
level = 'user';
|
|
678
|
+
content = trimmed.slice(3).trim();
|
|
679
|
+
} else if (trimmed.startsWith('# ')) {
|
|
680
|
+
level = 'project';
|
|
681
|
+
content = trimmed.slice(2).trim();
|
|
682
|
+
} else {
|
|
683
|
+
// Just # with no space, treat as project
|
|
684
|
+
level = 'project';
|
|
685
|
+
content = trimmed.slice(1).trim();
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (!content) {
|
|
689
|
+
addHistory({ type: 'info', content: 'Empty memory entry ignored' });
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
const savedPath = await memoryManager.quickAdd(content, level, cwd);
|
|
695
|
+
const displayPath = savedPath.replace(process.env.HOME || '', '~');
|
|
696
|
+
addHistory({
|
|
697
|
+
type: 'info',
|
|
698
|
+
content: `Added to ${level} memory: ${displayPath}`,
|
|
699
|
+
});
|
|
700
|
+
} catch (error) {
|
|
701
|
+
addHistory({
|
|
702
|
+
type: 'info',
|
|
703
|
+
content: `Failed to add to memory: ${error instanceof Error ? error.message : String(error)}`,
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
|
|
424
709
|
if (trimmed.startsWith('/')) {
|
|
425
710
|
const handled = await handleCommand(trimmed);
|
|
426
711
|
if (!handled) {
|
|
@@ -496,6 +781,7 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
496
781
|
name={(item.meta?.toolName as string) || ''}
|
|
497
782
|
success={(item.meta?.success as boolean) ?? true}
|
|
498
783
|
output={item.content}
|
|
784
|
+
metadata={item.meta?.metadata as Record<string, unknown> | undefined}
|
|
499
785
|
/>
|
|
500
786
|
);
|
|
501
787
|
case 'info':
|
|
@@ -503,9 +789,29 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
503
789
|
if (item.content === '__SESSIONS__' && item.meta?.input) {
|
|
504
790
|
return <SessionsTable sessions={item.meta.input as Session[]} />;
|
|
505
791
|
}
|
|
792
|
+
if (item.content === '__PERMISSIONS__' && item.meta?.rules) {
|
|
793
|
+
return (
|
|
794
|
+
<PermissionRulesDisplay
|
|
795
|
+
rules={item.meta.rules as { type: string; tool: string; pattern?: string; scope: string; mode: string }[]}
|
|
796
|
+
allowedPrompts={item.meta.prompts as { tool: string; prompt: string }[] | undefined}
|
|
797
|
+
/>
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
if (item.content === '__PERMISSION_AUDIT__' && item.meta?.entries) {
|
|
801
|
+
return (
|
|
802
|
+
<PermissionAuditDisplay
|
|
803
|
+
entries={item.meta.entries as { time: string; tool: string; input: string; decision: string; rule?: string }[]}
|
|
804
|
+
/>
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
if (item.content === '__MEMORY__' && item.meta?.files) {
|
|
808
|
+
return <MemoryFilesDisplay files={item.meta.files as MemoryFileInfo[]} />;
|
|
809
|
+
}
|
|
506
810
|
return <InfoMessage text={item.content} />;
|
|
507
811
|
case 'completion':
|
|
508
812
|
return <CompletionMessage durationMs={(item.meta?.durationMs as number) || 0} />;
|
|
813
|
+
case 'todos':
|
|
814
|
+
return <TodoList todos={item.meta?.todos as ReturnType<typeof getTodos>} />;
|
|
509
815
|
default:
|
|
510
816
|
return null;
|
|
511
817
|
}
|
|
@@ -517,15 +823,18 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
517
823
|
{(item) => <Box key={item.id}>{renderHistoryItem(item)}</Box>}
|
|
518
824
|
</Static>
|
|
519
825
|
|
|
826
|
+
{pendingTool && !confirmState && <PendingToolCall name={pendingTool.name} input={pendingTool.input} />}
|
|
827
|
+
|
|
520
828
|
{streamingText && <AssistantMessage text={streamingText} streaming />}
|
|
521
829
|
|
|
522
830
|
{confirmState && (
|
|
523
|
-
<
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
831
|
+
<PermissionPrompt
|
|
832
|
+
tool={confirmState.tool}
|
|
833
|
+
input={confirmState.input}
|
|
834
|
+
suggestions={confirmState.suggestions}
|
|
835
|
+
onDecision={handlePermissionDecision}
|
|
836
|
+
projectPath={settingsManager?.getCwd?.() ?? process.cwd()}
|
|
837
|
+
/>
|
|
529
838
|
)}
|
|
530
839
|
|
|
531
840
|
{showModelSelector && (
|
|
@@ -539,7 +848,20 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
539
848
|
</Box>
|
|
540
849
|
)}
|
|
541
850
|
|
|
542
|
-
{
|
|
851
|
+
{showProviderManager && (
|
|
852
|
+
<Box marginTop={1}>
|
|
853
|
+
<ProviderManager
|
|
854
|
+
onClose={() => setShowProviderManager(false)}
|
|
855
|
+
onProviderChange={(providerId, model) => {
|
|
856
|
+
agent.setModel(model);
|
|
857
|
+
setCurrentModel(model);
|
|
858
|
+
addHistory({ type: 'info', content: `Switched to ${providerId}: ${model}` });
|
|
859
|
+
}}
|
|
860
|
+
/>
|
|
861
|
+
</Box>
|
|
862
|
+
)}
|
|
863
|
+
|
|
864
|
+
{!confirmState && !showModelSelector && !showProviderManager && (
|
|
543
865
|
<Box flexDirection="column" marginTop={1}>
|
|
544
866
|
<PromptInput
|
|
545
867
|
key={inputKey}
|
|
@@ -553,8 +875,8 @@ export function App({ config, settingsManager, resumeLatest }: AppProps) {
|
|
|
553
875
|
</Box>
|
|
554
876
|
)}
|
|
555
877
|
|
|
556
|
-
{isProcessing ? (
|
|
557
|
-
<ProgressBar />
|
|
878
|
+
{isProcessing && !confirmState ? (
|
|
879
|
+
<ProgressBar startTime={processingStartTime} tokenCount={tokenCount} isThinking={isThinking} />
|
|
558
880
|
) : showCmdSuggestions && cmdSuggestions.length > 0 ? (
|
|
559
881
|
<Box marginTop={1}>
|
|
560
882
|
<Text color={colors.textMuted}> Tab to complete · ↑↓ navigate</Text>
|
|
@@ -8,6 +8,9 @@ interface Command {
|
|
|
8
8
|
|
|
9
9
|
export const COMMANDS: Command[] = [
|
|
10
10
|
{ name: '/model', description: 'Switch model' },
|
|
11
|
+
{ name: '/provider', description: 'Manage providers' },
|
|
12
|
+
{ name: '/permissions', description: 'View permission rules' },
|
|
13
|
+
{ name: '/permissions audit', description: 'View permission audit log' },
|
|
11
14
|
{ name: '/sessions', description: 'List sessions' },
|
|
12
15
|
{ name: '/resume', description: 'Resume session' },
|
|
13
16
|
{ name: '/new', description: 'New session' },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, Text } from 'ink';
|
|
2
2
|
import { colors } from './theme.js';
|
|
3
|
-
import {
|
|
3
|
+
import { BigLogo } from './Logo.js';
|
|
4
4
|
|
|
5
5
|
interface HeaderProps {
|
|
6
6
|
provider: string;
|
|
@@ -8,20 +8,14 @@ interface HeaderProps {
|
|
|
8
8
|
cwd: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function Header({
|
|
12
|
-
const home = process.env.HOME || '';
|
|
13
|
-
const cwdDisplay = cwd.startsWith(home) ? '~' + cwd.slice(home.length) : cwd;
|
|
14
|
-
|
|
11
|
+
export function Header({ model, cwd }: HeaderProps) {
|
|
15
12
|
return (
|
|
16
|
-
<Box flexDirection="
|
|
17
|
-
<
|
|
18
|
-
<Box
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</Box>
|
|
23
|
-
<Text color={colors.textMuted}>{model} · API Usage Billing</Text>
|
|
24
|
-
<Text color={colors.textMuted}>{cwdDisplay}</Text>
|
|
13
|
+
<Box flexDirection="column" marginTop={1}>
|
|
14
|
+
<BigLogo />
|
|
15
|
+
<Box marginTop={1}>
|
|
16
|
+
<Text color={colors.textSecondary}>{model}</Text>
|
|
17
|
+
<Text color={colors.textMuted}> · </Text>
|
|
18
|
+
<Text color={colors.textMuted}>{cwd}</Text>
|
|
25
19
|
</Box>
|
|
26
20
|
</Box>
|
|
27
21
|
);
|
|
@@ -29,8 +23,8 @@ export function Header({ provider, model, cwd }: HeaderProps) {
|
|
|
29
23
|
|
|
30
24
|
export function Welcome() {
|
|
31
25
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
</
|
|
26
|
+
<Box marginTop={1}>
|
|
27
|
+
<Text color={colors.textMuted}>? for help · Ctrl+C to exit</Text>
|
|
28
|
+
</Box>
|
|
35
29
|
);
|
|
36
30
|
}
|