gencode-ai 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gencode/settings.local.json +7 -0
- package/README.md +11 -11
- package/dist/agent/agent.d.ts +42 -1
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +82 -15
- package/dist/agent/agent.js.map +1 -1
- package/dist/cli/components/App.d.ts +8 -1
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +231 -29
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -1
- package/dist/cli/components/CommandSuggestions.js +2 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -1
- package/dist/cli/components/Header.d.ts +1 -1
- package/dist/cli/components/Header.d.ts.map +1 -1
- package/dist/cli/components/Header.js +4 -6
- package/dist/cli/components/Header.js.map +1 -1
- package/dist/cli/components/Logo.d.ts +1 -0
- package/dist/cli/components/Logo.d.ts.map +1 -1
- package/dist/cli/components/Logo.js +16 -3
- package/dist/cli/components/Logo.js.map +1 -1
- package/dist/cli/components/Messages.d.ts +4 -4
- package/dist/cli/components/Messages.d.ts.map +1 -1
- package/dist/cli/components/Messages.js +51 -25
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/PermissionPrompt.d.ts +60 -0
- package/dist/cli/components/PermissionPrompt.d.ts.map +1 -0
- package/dist/cli/components/PermissionPrompt.js +192 -0
- package/dist/cli/components/PermissionPrompt.js.map +1 -0
- package/dist/cli/components/ProviderManager.js +3 -3
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/components/Spinner.d.ts +7 -2
- package/dist/cli/components/Spinner.d.ts.map +1 -1
- package/dist/cli/components/Spinner.js +116 -25
- package/dist/cli/components/Spinner.js.map +1 -1
- package/dist/cli/components/TodoList.d.ts +7 -0
- package/dist/cli/components/TodoList.d.ts.map +1 -0
- package/dist/cli/components/TodoList.js +34 -0
- package/dist/cli/components/TodoList.js.map +1 -0
- package/dist/cli/components/index.d.ts +1 -0
- package/dist/cli/components/index.d.ts.map +1 -1
- package/dist/cli/components/index.js +1 -0
- package/dist/cli/components/index.js.map +1 -1
- package/dist/cli/index.js +47 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +13 -4
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +18 -3
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +49 -0
- package/dist/config/levels.d.ts.map +1 -0
- package/dist/config/levels.js +222 -0
- package/dist/config/levels.js.map +1 -0
- package/dist/config/loader.d.ts +46 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +153 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +115 -15
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +260 -34
- package/dist/config/manager.js.map +1 -1
- package/dist/config/manager.test.d.ts +5 -0
- package/dist/config/manager.test.d.ts.map +1 -0
- package/dist/config/manager.test.js +192 -0
- package/dist/config/manager.test.js.map +1 -0
- package/dist/config/merger.d.ts +56 -0
- package/dist/config/merger.d.ts.map +1 -0
- package/dist/config/merger.js +177 -0
- package/dist/config/merger.js.map +1 -0
- package/dist/config/test-utils.d.ts +24 -0
- package/dist/config/test-utils.d.ts.map +1 -0
- package/dist/config/test-utils.js +55 -0
- package/dist/config/test-utils.js.map +1 -0
- package/dist/config/types.d.ts +78 -9
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +52 -2
- package/dist/config/types.js.map +1 -1
- package/dist/memory/import-resolver.d.ts +46 -0
- package/dist/memory/import-resolver.d.ts.map +1 -0
- package/dist/memory/import-resolver.js +117 -0
- package/dist/memory/import-resolver.js.map +1 -0
- package/dist/memory/index.d.ts +7 -6
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +7 -5
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/init-prompt.d.ts +22 -0
- package/dist/memory/init-prompt.d.ts.map +1 -0
- package/dist/memory/init-prompt.js +103 -0
- package/dist/memory/init-prompt.js.map +1 -0
- package/dist/memory/memory-manager.d.ts +119 -0
- package/dist/memory/memory-manager.d.ts.map +1 -0
- package/dist/memory/memory-manager.js +587 -0
- package/dist/memory/memory-manager.js.map +1 -0
- package/dist/memory/rules-parser.d.ts +38 -0
- package/dist/memory/rules-parser.d.ts.map +1 -0
- package/dist/memory/rules-parser.js +69 -0
- package/dist/memory/rules-parser.js.map +1 -0
- package/dist/memory/test-utils.d.ts +20 -0
- package/dist/memory/test-utils.d.ts.map +1 -0
- package/dist/memory/test-utils.js +44 -0
- package/dist/memory/test-utils.js.map +1 -0
- package/dist/memory/types.d.ts +70 -63
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +42 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/permissions/audit.d.ts +82 -0
- package/dist/permissions/audit.d.ts.map +1 -0
- package/dist/permissions/audit.js +229 -0
- package/dist/permissions/audit.js.map +1 -0
- package/dist/permissions/index.d.ts +11 -1
- package/dist/permissions/index.d.ts.map +1 -1
- package/dist/permissions/index.js +15 -0
- package/dist/permissions/index.js.map +1 -1
- package/dist/permissions/manager.d.ts +149 -13
- package/dist/permissions/manager.d.ts.map +1 -1
- package/dist/permissions/manager.js +480 -35
- package/dist/permissions/manager.js.map +1 -1
- package/dist/permissions/manager.test.d.ts +5 -0
- package/dist/permissions/manager.test.d.ts.map +1 -0
- package/dist/permissions/manager.test.js +213 -0
- package/dist/permissions/manager.test.js.map +1 -0
- package/dist/permissions/persistence.d.ts +74 -0
- package/dist/permissions/persistence.d.ts.map +1 -0
- package/dist/permissions/persistence.js +248 -0
- package/dist/permissions/persistence.js.map +1 -0
- package/dist/permissions/persistence.test.d.ts +5 -0
- package/dist/permissions/persistence.test.d.ts.map +1 -0
- package/dist/permissions/persistence.test.js +171 -0
- package/dist/permissions/persistence.test.js.map +1 -0
- package/dist/permissions/prompt-matcher.d.ts +64 -0
- package/dist/permissions/prompt-matcher.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.js +415 -0
- package/dist/permissions/prompt-matcher.js.map +1 -0
- package/dist/permissions/prompt-matcher.test.d.ts +5 -0
- package/dist/permissions/prompt-matcher.test.d.ts.map +1 -0
- package/dist/permissions/prompt-matcher.test.js +107 -0
- package/dist/permissions/prompt-matcher.test.js.map +1 -0
- package/dist/permissions/types.d.ts +157 -0
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/permissions/types.js +43 -8
- package/dist/permissions/types.js.map +1 -1
- package/dist/prompts/index.d.ts +92 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +241 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -1
- package/dist/tools/builtin/bash.js +2 -1
- package/dist/tools/builtin/bash.js.map +1 -1
- package/dist/tools/builtin/edit.d.ts.map +1 -1
- package/dist/tools/builtin/edit.js +2 -1
- package/dist/tools/builtin/edit.js.map +1 -1
- package/dist/tools/builtin/glob.d.ts.map +1 -1
- package/dist/tools/builtin/glob.js +2 -1
- package/dist/tools/builtin/glob.js.map +1 -1
- package/dist/tools/builtin/grep.d.ts.map +1 -1
- package/dist/tools/builtin/grep.js +2 -1
- package/dist/tools/builtin/grep.js.map +1 -1
- package/dist/tools/builtin/read.d.ts.map +1 -1
- package/dist/tools/builtin/read.js +2 -1
- package/dist/tools/builtin/read.js.map +1 -1
- package/dist/tools/builtin/todowrite.d.ts +15 -0
- package/dist/tools/builtin/todowrite.d.ts.map +1 -0
- package/dist/tools/builtin/todowrite.js +88 -0
- package/dist/tools/builtin/todowrite.js.map +1 -0
- package/dist/tools/builtin/webfetch.d.ts.map +1 -1
- package/dist/tools/builtin/webfetch.js +2 -5
- package/dist/tools/builtin/webfetch.js.map +1 -1
- package/dist/tools/builtin/websearch.d.ts.map +1 -1
- package/dist/tools/builtin/websearch.js +2 -16
- package/dist/tools/builtin/websearch.js.map +1 -1
- package/dist/tools/builtin/write.d.ts.map +1 -1
- package/dist/tools/builtin/write.js +2 -1
- package/dist/tools/builtin/write.js.map +1 -1
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +22 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +8 -0
- package/dist/tools/types.js.map +1 -1
- package/docs/config-system-comparison.md +707 -0
- package/docs/memory-system.md +238 -0
- package/docs/permissions.md +368 -0
- package/docs/proposals/0005-todo-system.md +350 -85
- package/docs/proposals/0006-memory-system.md +11 -10
- package/docs/proposals/0012-ask-user-question.md +941 -206
- package/docs/proposals/0023-permission-enhancements.md +61 -2
- package/docs/proposals/0041-configuration-system.md +33 -2
- package/docs/proposals/0042-prompt-optimization.md +866 -0
- package/docs/proposals/README.md +6 -5
- package/jest.config.js +26 -0
- package/package.json +8 -2
- package/src/agent/agent.ts +111 -16
- package/src/cli/components/App.tsx +309 -36
- package/src/cli/components/CommandSuggestions.tsx +2 -0
- package/src/cli/components/Header.tsx +11 -17
- package/src/cli/components/Logo.tsx +76 -9
- package/src/cli/components/Messages.tsx +73 -53
- package/src/cli/components/PermissionPrompt.tsx +388 -0
- package/src/cli/components/ProviderManager.tsx +5 -5
- package/src/cli/components/Spinner.tsx +138 -25
- package/src/cli/components/TodoList.tsx +54 -0
- package/src/cli/components/index.ts +6 -0
- package/src/cli/index.tsx +54 -6
- package/src/config/index.ts +78 -4
- package/src/config/levels.test.ts +163 -0
- package/src/config/levels.ts +285 -0
- package/src/config/loader.test.ts +120 -0
- package/src/config/loader.ts +178 -0
- package/src/config/manager.test.ts +215 -0
- package/src/config/manager.ts +328 -40
- package/src/config/merger.test.ts +360 -0
- package/src/config/merger.ts +221 -0
- package/src/config/test-utils.ts +79 -0
- package/src/config/types.ts +152 -9
- package/src/memory/import-resolver.test.ts +117 -0
- package/src/memory/import-resolver.ts +149 -0
- package/src/memory/index.ts +11 -0
- package/src/memory/init-prompt.ts +113 -0
- package/src/memory/memory-manager.test.ts +198 -0
- package/src/memory/memory-manager.ts +716 -0
- package/src/memory/rules-parser.test.ts +182 -0
- package/src/memory/rules-parser.ts +82 -0
- package/src/memory/test-utils.ts +60 -0
- package/src/memory/types.ts +119 -0
- package/src/permissions/audit.ts +284 -0
- package/src/permissions/index.ts +20 -1
- package/src/permissions/manager.test.ts +260 -0
- package/src/permissions/manager.ts +592 -40
- package/src/permissions/persistence.test.ts +220 -0
- package/src/permissions/persistence.ts +301 -0
- package/src/permissions/prompt-matcher.test.ts +213 -0
- package/src/permissions/prompt-matcher.ts +472 -0
- package/src/permissions/types.ts +236 -8
- package/src/prompts/index.test.ts +279 -0
- package/src/prompts/index.ts +306 -0
- package/src/prompts/system/anthropic.txt +29 -0
- package/src/prompts/system/base.txt +124 -0
- package/src/prompts/system/gemini.txt +35 -0
- package/src/prompts/system/generic.txt +128 -0
- package/src/prompts/system/openai.txt +29 -0
- package/src/prompts/tools/bash.txt +60 -0
- package/src/prompts/tools/edit.txt +29 -0
- package/src/prompts/tools/glob.txt +35 -0
- package/src/prompts/tools/grep.txt +43 -0
- package/src/prompts/tools/read.txt +22 -0
- package/src/prompts/tools/todowrite.txt +71 -0
- package/src/prompts/tools/webfetch.txt +34 -0
- package/src/prompts/tools/websearch.txt +41 -0
- package/src/prompts/tools/write.txt +23 -0
- package/src/tools/builtin/bash.ts +2 -1
- package/src/tools/builtin/edit.ts +2 -1
- package/src/tools/builtin/glob.ts +2 -1
- package/src/tools/builtin/grep.ts +2 -1
- package/src/tools/builtin/read.ts +2 -1
- package/src/tools/builtin/todowrite.ts +102 -0
- package/src/tools/builtin/webfetch.ts +2 -5
- package/src/tools/builtin/websearch.ts +2 -16
- package/src/tools/builtin/write.ts +2 -1
- package/src/tools/index.ts +4 -0
- package/src/tools/types.ts +12 -0
- package/tsconfig.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spinner Component -
|
|
2
|
+
* Spinner Component - Vivid thinking animation
|
|
3
3
|
*/
|
|
4
|
-
import { useState, useEffect } from 'react';
|
|
4
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
5
5
|
import { Box, Text } from 'ink';
|
|
6
6
|
import InkSpinner from 'ink-spinner';
|
|
7
7
|
import { colors } from './theme.js';
|
|
@@ -33,40 +33,153 @@ export function LoadingSpinner({ text = 'Loading...' }: SpinnerProps) {
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Thinking phrases that rotate during processing
|
|
37
|
+
const thinkingPhrases = [
|
|
38
|
+
'Thinking',
|
|
39
|
+
'Pondering',
|
|
40
|
+
'Analyzing',
|
|
41
|
+
'Processing',
|
|
42
|
+
'Reasoning',
|
|
43
|
+
'Contemplating',
|
|
44
|
+
'Figuring out',
|
|
45
|
+
'Working on it',
|
|
46
|
+
'Almost there',
|
|
47
|
+
'Crafting response',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// Animation frames for different styles
|
|
51
|
+
const animations = {
|
|
52
|
+
// Brainwave animation
|
|
53
|
+
brainwave: ['🧠 ∿∿∿', '🧠∿ ∿∿', '🧠∿∿ ∿', '🧠∿∿∿ ', '🧠 ∿∿∿', '🧠∿ ∿∿'],
|
|
54
|
+
// Sparkle animation
|
|
55
|
+
sparkle: ['✨ ', ' ✨ ', ' ✨ ', ' ✨ ', ' ✨', ' ✨ ', ' ✨ ', ' ✨ '],
|
|
56
|
+
// DNA helix
|
|
57
|
+
dna: ['🔬 ⌬⌬⌬', '🔬⌬ ⌬⌬', '🔬⌬⌬ ⌬', '🔬⌬⌬⌬ ', '🔬 ⌬⌬⌬'],
|
|
58
|
+
// Pulse dots
|
|
59
|
+
pulse: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
|
|
60
|
+
// Wave animation
|
|
61
|
+
wave: ['≋≈∼∽', '∽≋≈∼', '∼∽≋≈', '≈∼∽≋'],
|
|
62
|
+
// Bounce bar with gradient
|
|
63
|
+
bounceGradient: [
|
|
64
|
+
'█▓▒░ ',
|
|
65
|
+
' █▓▒░ ',
|
|
66
|
+
' █▓▒░ ',
|
|
67
|
+
' █▓▒░ ',
|
|
68
|
+
' █▓▒░',
|
|
69
|
+
' ░▒▓█ ',
|
|
70
|
+
' ░▒▓█ ',
|
|
71
|
+
' ░▒▓█ ',
|
|
72
|
+
'░▒▓█ ',
|
|
73
|
+
],
|
|
74
|
+
// Orbit animation
|
|
75
|
+
orbit: ['◐', '◓', '◑', '◒'],
|
|
76
|
+
// Loading bar with shimmer
|
|
77
|
+
shimmer: [
|
|
78
|
+
'▓▓▓▓▓░░░',
|
|
79
|
+
'░▓▓▓▓▓░░',
|
|
80
|
+
'░░▓▓▓▓▓░',
|
|
81
|
+
'░░░▓▓▓▓▓',
|
|
82
|
+
'░░░░▓▓▓▓',
|
|
83
|
+
'░░░▓▓▓▓▓',
|
|
84
|
+
'░░▓▓▓▓▓░',
|
|
85
|
+
'░▓▓▓▓▓░░',
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type AnimationType = keyof typeof animations;
|
|
90
|
+
const animationTypes = Object.keys(animations) as AnimationType[];
|
|
91
|
+
|
|
92
|
+
// Format elapsed time
|
|
93
|
+
function formatElapsed(ms: number): string {
|
|
94
|
+
const secs = Math.floor(ms / 1000);
|
|
95
|
+
if (secs < 60) return `${secs}s`;
|
|
96
|
+
const mins = Math.floor(secs / 60);
|
|
97
|
+
const remainSecs = secs % 60;
|
|
98
|
+
return `${mins}m ${remainSecs}s`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Format token count
|
|
102
|
+
function formatTokens(count: number): string {
|
|
103
|
+
if (count >= 1000) {
|
|
104
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
105
|
+
}
|
|
106
|
+
return `${count}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface ProgressBarProps {
|
|
110
|
+
startTime?: number;
|
|
111
|
+
tokenCount?: number;
|
|
112
|
+
isThinking?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
36
115
|
/**
|
|
37
116
|
* Progress bar animation for processing state
|
|
38
|
-
*
|
|
117
|
+
* Claude Code style with time, tokens, and thinking status
|
|
39
118
|
*/
|
|
40
|
-
export function ProgressBar() {
|
|
119
|
+
export function ProgressBar({ startTime, tokenCount = 0, isThinking = false }: ProgressBarProps) {
|
|
41
120
|
const [frame, setFrame] = useState(0);
|
|
121
|
+
const [phraseIndex, setPhraseIndex] = useState(0);
|
|
122
|
+
const [elapsed, setElapsed] = useState(0);
|
|
42
123
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return () => clearInterval(timer);
|
|
124
|
+
// Pick a random animation style on mount
|
|
125
|
+
const animStyle = useMemo(() => {
|
|
126
|
+
const randomIndex = Math.floor(Math.random() * animationTypes.length);
|
|
127
|
+
return animationTypes[randomIndex];
|
|
48
128
|
}, []);
|
|
49
129
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
130
|
+
const currentAnim = animations[animStyle];
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
// Fast animation update
|
|
134
|
+
const animTimer = setInterval(() => {
|
|
135
|
+
setFrame((f) => (f + 1) % currentAnim.length);
|
|
136
|
+
}, 120);
|
|
137
|
+
|
|
138
|
+
// Slower phrase rotation (every 2.5 seconds)
|
|
139
|
+
const phraseTimer = setInterval(() => {
|
|
140
|
+
setPhraseIndex((p) => (p + 1) % thinkingPhrases.length);
|
|
141
|
+
}, 2500);
|
|
142
|
+
|
|
143
|
+
// Update elapsed time every second
|
|
144
|
+
const elapsedTimer = setInterval(() => {
|
|
145
|
+
if (startTime) {
|
|
146
|
+
setElapsed(Date.now() - startTime);
|
|
147
|
+
}
|
|
148
|
+
}, 1000);
|
|
149
|
+
|
|
150
|
+
return () => {
|
|
151
|
+
clearInterval(animTimer);
|
|
152
|
+
clearInterval(phraseTimer);
|
|
153
|
+
clearInterval(elapsedTimer);
|
|
154
|
+
};
|
|
155
|
+
}, [currentAnim.length, startTime]);
|
|
156
|
+
|
|
157
|
+
const animFrame = currentAnim[frame];
|
|
158
|
+
const phrase = thinkingPhrases[phraseIndex];
|
|
159
|
+
|
|
160
|
+
// Animated ellipsis
|
|
161
|
+
const ellipsis = '.'.repeat((frame % 3) + 1).padEnd(3, ' ');
|
|
162
|
+
|
|
163
|
+
// Build status parts
|
|
164
|
+
const parts: string[] = [];
|
|
165
|
+
if (startTime && elapsed > 0) {
|
|
166
|
+
parts.push(formatElapsed(elapsed));
|
|
167
|
+
}
|
|
168
|
+
if (tokenCount > 0) {
|
|
169
|
+
parts.push(`↓ ${formatTokens(tokenCount)} tokens`);
|
|
64
170
|
}
|
|
171
|
+
if (isThinking) {
|
|
172
|
+
parts.push('thinking');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const statusText = parts.length > 0 ? ` · ${parts.join(' · ')}` : '';
|
|
65
176
|
|
|
66
177
|
return (
|
|
67
178
|
<Box>
|
|
68
|
-
<Text color={colors.brand}>{
|
|
69
|
-
<Text color={colors.
|
|
179
|
+
<Text color={colors.brand}>{animFrame}</Text>
|
|
180
|
+
<Text color={colors.textSecondary}> {phrase}</Text>
|
|
181
|
+
<Text color={colors.textMuted}>{ellipsis}</Text>
|
|
182
|
+
<Text color={colors.textMuted}>(esc to stop{statusText})</Text>
|
|
70
183
|
</Box>
|
|
71
184
|
);
|
|
72
185
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TodoList Component - Display current todos in CLI
|
|
3
|
+
* Design: Minimal, clean, status-driven with clear visual hierarchy
|
|
4
|
+
*/
|
|
5
|
+
import { Box, Text } from 'ink';
|
|
6
|
+
import { colors } from './theme.js';
|
|
7
|
+
import type { TodoItem } from '../../tools/types.js';
|
|
8
|
+
|
|
9
|
+
interface TodoListProps {
|
|
10
|
+
todos: TodoItem[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function TodoList({ todos }: TodoListProps) {
|
|
14
|
+
if (todos.length === 0) return null;
|
|
15
|
+
|
|
16
|
+
const completed = todos.filter((t) => t.status === 'completed').length;
|
|
17
|
+
const total = todos.length;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Box flexDirection="column" marginTop={1} marginLeft={2}>
|
|
21
|
+
{/* Header with count */}
|
|
22
|
+
<Text color={colors.textMuted}>
|
|
23
|
+
Tasks {completed}/{total}
|
|
24
|
+
</Text>
|
|
25
|
+
{/* Task list */}
|
|
26
|
+
{todos.map((todo, i) => {
|
|
27
|
+
const isCompleted = todo.status === 'completed';
|
|
28
|
+
const isInProgress = todo.status === 'in_progress';
|
|
29
|
+
|
|
30
|
+
// Status indicators: [x] done, [>] active, [ ] pending
|
|
31
|
+
let bracket: string;
|
|
32
|
+
let bracketColor: string;
|
|
33
|
+
|
|
34
|
+
if (isCompleted) {
|
|
35
|
+
bracket = '[x]';
|
|
36
|
+
bracketColor = colors.success;
|
|
37
|
+
} else if (isInProgress) {
|
|
38
|
+
bracket = '[>]';
|
|
39
|
+
bracketColor = colors.warning;
|
|
40
|
+
} else {
|
|
41
|
+
bracket = '[ ]';
|
|
42
|
+
bracketColor = colors.textMuted;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Text key={i} dimColor={isCompleted}>
|
|
47
|
+
<Text color={bracketColor}>{bracket}</Text>
|
|
48
|
+
<Text strikethrough={isCompleted}> {todo.content}</Text>
|
|
49
|
+
</Text>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</Box>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -19,3 +19,9 @@ export { PromptInput, ConfirmPrompt } from './Input.js';
|
|
|
19
19
|
export { colors, icons } from './theme.js';
|
|
20
20
|
export { ModelSelector } from './ModelSelector.js';
|
|
21
21
|
export { CommandSuggestions, COMMANDS, getFilteredCommands } from './CommandSuggestions.js';
|
|
22
|
+
export {
|
|
23
|
+
PermissionPrompt,
|
|
24
|
+
SimpleConfirmPrompt,
|
|
25
|
+
PermissionRulesDisplay,
|
|
26
|
+
PermissionAuditDisplay,
|
|
27
|
+
} from './PermissionPrompt.js';
|
package/src/cli/index.tsx
CHANGED
|
@@ -88,10 +88,21 @@ function detectConfig(settings: Settings, providersConfig: ProvidersConfigManage
|
|
|
88
88
|
// ============================================================================
|
|
89
89
|
function parseArgs() {
|
|
90
90
|
const args = process.argv.slice(2);
|
|
91
|
+
|
|
92
|
+
// Extract prompt value from -p "message" or --prompt "message"
|
|
93
|
+
let prompt: string | undefined;
|
|
94
|
+
for (let i = 0; i < args.length; i++) {
|
|
95
|
+
if ((args[i] === '-p' || args[i] === '--prompt') && args[i + 1]) {
|
|
96
|
+
prompt = args[i + 1];
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
return {
|
|
92
102
|
continue: args.includes('-c') || args.includes('--continue'),
|
|
93
103
|
resume: args.includes('-r') || args.includes('--resume'),
|
|
94
104
|
help: args.includes('-h') || args.includes('--help'),
|
|
105
|
+
prompt,
|
|
95
106
|
};
|
|
96
107
|
}
|
|
97
108
|
|
|
@@ -102,17 +113,47 @@ function printUsage(): void {
|
|
|
102
113
|
console.log(' Usage: gencode [options]');
|
|
103
114
|
console.log();
|
|
104
115
|
console.log(' Options:');
|
|
105
|
-
console.log(' -c, --continue
|
|
106
|
-
console.log(' -r, --resume
|
|
107
|
-
console.log(' -
|
|
116
|
+
console.log(' -c, --continue Resume the most recent session');
|
|
117
|
+
console.log(' -r, --resume Select a session interactively');
|
|
118
|
+
console.log(' -p, --prompt <msg> Run a single prompt (non-interactive)');
|
|
119
|
+
console.log(' -h, --help Show this help');
|
|
108
120
|
console.log();
|
|
109
121
|
console.log(' Examples:');
|
|
110
|
-
console.log(' gencode
|
|
111
|
-
console.log(' gencode -c
|
|
112
|
-
console.log(' gencode -r
|
|
122
|
+
console.log(' gencode Start new session');
|
|
123
|
+
console.log(' gencode -c Continue last session');
|
|
124
|
+
console.log(' gencode -r Pick a session');
|
|
125
|
+
console.log(' gencode -p "2+2" Run single prompt');
|
|
113
126
|
console.log();
|
|
114
127
|
}
|
|
115
128
|
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Non-interactive mode
|
|
131
|
+
// ============================================================================
|
|
132
|
+
async function runNonInteractive(prompt: string, config: AgentConfig): Promise<void> {
|
|
133
|
+
const { Agent } = await import('../agent/agent.js');
|
|
134
|
+
|
|
135
|
+
const agent = new Agent(config);
|
|
136
|
+
|
|
137
|
+
let response = '';
|
|
138
|
+
for await (const event of agent.run(prompt)) {
|
|
139
|
+
switch (event.type) {
|
|
140
|
+
case 'text':
|
|
141
|
+
response += event.text;
|
|
142
|
+
break;
|
|
143
|
+
case 'tool_start':
|
|
144
|
+
console.error(`[tool] ${event.name}`);
|
|
145
|
+
break;
|
|
146
|
+
case 'error':
|
|
147
|
+
console.error(`[error] ${event.error.message}`);
|
|
148
|
+
break;
|
|
149
|
+
case 'done':
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(response);
|
|
155
|
+
}
|
|
156
|
+
|
|
116
157
|
// ============================================================================
|
|
117
158
|
// Main
|
|
118
159
|
// ============================================================================
|
|
@@ -135,12 +176,19 @@ async function main() {
|
|
|
135
176
|
|
|
136
177
|
const config = detectConfig(settings, providersConfig);
|
|
137
178
|
|
|
179
|
+
// Non-interactive mode with -p flag
|
|
180
|
+
if (args.prompt) {
|
|
181
|
+
await runNonInteractive(args.prompt, config);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
138
185
|
// Render the Ink app
|
|
139
186
|
render(
|
|
140
187
|
<App
|
|
141
188
|
config={config}
|
|
142
189
|
settingsManager={settingsManager}
|
|
143
190
|
resumeLatest={args.continue}
|
|
191
|
+
permissionSettings={settings.permissions}
|
|
144
192
|
/>
|
|
145
193
|
);
|
|
146
194
|
}
|
package/src/config/index.ts
CHANGED
|
@@ -1,8 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Configuration Module - Multi-level configuration system (Claude Code compatible)
|
|
3
|
+
*
|
|
4
|
+
* This module provides a hierarchical configuration system that:
|
|
5
|
+
* - Supports multiple levels: user, project, local, managed
|
|
6
|
+
* - Merges .gencode and .claude directories at each level (gencode wins)
|
|
7
|
+
* - Supports GENCODE_CONFIG_DIRS environment variable for extra config dirs
|
|
8
|
+
* - Enforces managed settings that cannot be overridden
|
|
3
9
|
*/
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
// Core types
|
|
12
|
+
export type {
|
|
13
|
+
Settings,
|
|
14
|
+
SettingsManagerOptions,
|
|
15
|
+
ProviderName,
|
|
16
|
+
PermissionRules,
|
|
17
|
+
ConfigLevelType,
|
|
18
|
+
ConfigLevel,
|
|
19
|
+
ConfigSource,
|
|
20
|
+
MergedConfig,
|
|
21
|
+
ProvidersConfig,
|
|
22
|
+
ProviderConnection,
|
|
23
|
+
CachedModel,
|
|
24
|
+
ProviderModels,
|
|
25
|
+
} from './types.js';
|
|
26
|
+
|
|
27
|
+
// Constants
|
|
28
|
+
export {
|
|
29
|
+
DEFAULT_SETTINGS_DIR,
|
|
30
|
+
PROJECT_SETTINGS_DIR,
|
|
31
|
+
FALLBACK_SETTINGS_DIR,
|
|
32
|
+
FALLBACK_PROJECT_DIR,
|
|
33
|
+
SETTINGS_FILE_NAME,
|
|
34
|
+
SETTINGS_LOCAL_FILE_NAME,
|
|
35
|
+
MANAGED_SETTINGS_FILE_NAME,
|
|
36
|
+
PROVIDERS_FILE_NAME,
|
|
37
|
+
GENCODE_DIR,
|
|
38
|
+
CLAUDE_DIR,
|
|
39
|
+
USER_GENCODE_DIR,
|
|
40
|
+
USER_CLAUDE_DIR,
|
|
41
|
+
GENCODE_CONFIG_DIRS_ENV,
|
|
42
|
+
getManagedPaths,
|
|
43
|
+
} from './types.js';
|
|
44
|
+
|
|
45
|
+
// Configuration levels
|
|
46
|
+
export {
|
|
47
|
+
findProjectRoot,
|
|
48
|
+
parseExtraConfigDirs,
|
|
49
|
+
getConfigLevels,
|
|
50
|
+
getPrimarySettingsDir,
|
|
51
|
+
getSettingsFilePath,
|
|
52
|
+
type ConfigPathInfo,
|
|
53
|
+
type ResolvedLevel,
|
|
54
|
+
} from './levels.js';
|
|
55
|
+
|
|
56
|
+
// Configuration loader
|
|
57
|
+
export {
|
|
58
|
+
loadAllSources,
|
|
59
|
+
loadSourcesByLevel,
|
|
60
|
+
loadUserSettings,
|
|
61
|
+
loadProjectSettings,
|
|
62
|
+
loadManagedSettings,
|
|
63
|
+
getConfigInfo,
|
|
64
|
+
getExistingConfigFiles,
|
|
65
|
+
} from './loader.js';
|
|
66
|
+
|
|
67
|
+
// Configuration merger
|
|
68
|
+
export {
|
|
69
|
+
deepMerge,
|
|
70
|
+
mergeSettings,
|
|
71
|
+
extractManagedDeny,
|
|
72
|
+
applyManagedRestrictions,
|
|
73
|
+
mergeAllSources,
|
|
74
|
+
mergeWithCliArgs,
|
|
75
|
+
createMergeSummary,
|
|
76
|
+
} from './merger.js';
|
|
77
|
+
|
|
78
|
+
// Configuration managers
|
|
79
|
+
export { ConfigManager, SettingsManager } from './manager.js';
|
|
80
|
+
|
|
81
|
+
// Providers configuration
|
|
6
82
|
export { ProvidersConfigManager } from './providers-config.js';
|
|
7
|
-
export type { Settings, SettingsManagerOptions, ProviderName, ProvidersConfig } from './types.js';
|
|
8
|
-
export { DEFAULT_SETTINGS_DIR, SETTINGS_FILE_NAME, PROVIDERS_FILE_NAME } from './types.js';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Levels Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
9
|
+
import {
|
|
10
|
+
findProjectRoot,
|
|
11
|
+
parseExtraConfigDirs,
|
|
12
|
+
getConfigLevels,
|
|
13
|
+
getPrimarySettingsDir,
|
|
14
|
+
getSettingsFilePath,
|
|
15
|
+
} from './levels.js';
|
|
16
|
+
import { createTestProject, type TestProject } from './test-utils.js';
|
|
17
|
+
|
|
18
|
+
describe('findProjectRoot', () => {
|
|
19
|
+
let test: TestProject;
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
test = await createTestProject('gencode-levels-');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => test.cleanup());
|
|
26
|
+
|
|
27
|
+
it('should find git root', async () => {
|
|
28
|
+
const subDir = path.join(test.projectDir, 'src', 'components');
|
|
29
|
+
await fs.mkdir(subDir, { recursive: true });
|
|
30
|
+
|
|
31
|
+
expect(await findProjectRoot(subDir)).toBe(test.projectDir);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should find .gencode directory as project root', async () => {
|
|
35
|
+
// Remove .git, add .gencode
|
|
36
|
+
await fs.rm(path.join(test.projectDir, '.git'), { recursive: true });
|
|
37
|
+
await fs.mkdir(path.join(test.projectDir, '.gencode'));
|
|
38
|
+
const subDir = path.join(test.projectDir, 'src');
|
|
39
|
+
await fs.mkdir(subDir, { recursive: true });
|
|
40
|
+
|
|
41
|
+
expect(await findProjectRoot(subDir)).toBe(test.projectDir);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should find .claude directory as project root', async () => {
|
|
45
|
+
await fs.rm(path.join(test.projectDir, '.git'), { recursive: true });
|
|
46
|
+
await fs.mkdir(path.join(test.projectDir, '.claude'));
|
|
47
|
+
const subDir = path.join(test.projectDir, 'lib');
|
|
48
|
+
await fs.mkdir(subDir, { recursive: true });
|
|
49
|
+
|
|
50
|
+
expect(await findProjectRoot(subDir)).toBe(test.projectDir);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return cwd if no project markers found', async () => {
|
|
54
|
+
const subDir = path.join(test.tempDir, 'random', 'path');
|
|
55
|
+
await fs.mkdir(subDir, { recursive: true });
|
|
56
|
+
|
|
57
|
+
expect(await findProjectRoot(subDir)).toBe(subDir);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('parseExtraConfigDirs', () => {
|
|
62
|
+
const originalEnv = process.env.GENCODE_CONFIG_DIRS;
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
if (originalEnv === undefined) {
|
|
66
|
+
delete process.env.GENCODE_CONFIG_DIRS;
|
|
67
|
+
} else {
|
|
68
|
+
process.env.GENCODE_CONFIG_DIRS = originalEnv;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return empty array when env var not set', () => {
|
|
73
|
+
delete process.env.GENCODE_CONFIG_DIRS;
|
|
74
|
+
expect(parseExtraConfigDirs()).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should parse single directory', () => {
|
|
78
|
+
process.env.GENCODE_CONFIG_DIRS = '/team/config';
|
|
79
|
+
expect(parseExtraConfigDirs()).toEqual(['/team/config']);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should parse multiple directories', () => {
|
|
83
|
+
process.env.GENCODE_CONFIG_DIRS = '/team/config:/shared/rules';
|
|
84
|
+
expect(parseExtraConfigDirs()).toEqual(['/team/config', '/shared/rules']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should expand tilde to home directory', () => {
|
|
88
|
+
process.env.GENCODE_CONFIG_DIRS = '~/my-config';
|
|
89
|
+
expect(parseExtraConfigDirs()[0]).toBe(path.join(os.homedir(), 'my-config'));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should trim whitespace and filter empty strings', () => {
|
|
93
|
+
process.env.GENCODE_CONFIG_DIRS = ' /path/one : : /path/two ';
|
|
94
|
+
expect(parseExtraConfigDirs()).toEqual(['/path/one', '/path/two']);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('getConfigLevels', () => {
|
|
99
|
+
let test: TestProject;
|
|
100
|
+
|
|
101
|
+
beforeEach(async () => {
|
|
102
|
+
test = await createTestProject('gencode-levels-');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
afterEach(() => test.cleanup());
|
|
106
|
+
|
|
107
|
+
it('should return levels in priority order', async () => {
|
|
108
|
+
const types = (await getConfigLevels(test.projectDir)).map((l) => l.type);
|
|
109
|
+
|
|
110
|
+
expect(types.indexOf('user')).toBeLessThan(types.indexOf('project'));
|
|
111
|
+
expect(types.indexOf('project')).toBeLessThan(types.indexOf('local'));
|
|
112
|
+
expect(types.indexOf('local')).toBeLessThan(types.indexOf('managed'));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should include both claude and gencode paths at each level', async () => {
|
|
116
|
+
const userLevel = (await getConfigLevels(test.projectDir)).find((l) => l.type === 'user');
|
|
117
|
+
|
|
118
|
+
expect(userLevel?.paths.length).toBe(2);
|
|
119
|
+
expect(userLevel?.paths.some((p) => p.namespace === 'claude')).toBe(true);
|
|
120
|
+
expect(userLevel?.paths.some((p) => p.namespace === 'gencode')).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should include extra dirs when env var is set', async () => {
|
|
124
|
+
process.env.GENCODE_CONFIG_DIRS = '/team/config';
|
|
125
|
+
const extraLevels = (await getConfigLevels(test.projectDir)).filter((l) => l.type === 'extra');
|
|
126
|
+
|
|
127
|
+
expect(extraLevels.length).toBeGreaterThan(0);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should have claude before gencode in each level (for merge order)', async () => {
|
|
131
|
+
for (const level of await getConfigLevels(test.projectDir)) {
|
|
132
|
+
if (level.paths.length >= 2) {
|
|
133
|
+
const claudeIdx = level.paths.findIndex((p) => p.namespace === 'claude');
|
|
134
|
+
const gencodeIdx = level.paths.findIndex((p) => p.namespace === 'gencode');
|
|
135
|
+
if (claudeIdx !== -1 && gencodeIdx !== -1) {
|
|
136
|
+
expect(claudeIdx).toBeLessThan(gencodeIdx);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe('getPrimarySettingsDir', () => {
|
|
144
|
+
it('should return ~/.gencode for user level', () => {
|
|
145
|
+
expect(getPrimarySettingsDir('user', '/project')).toBe(path.join(os.homedir(), '.gencode'));
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should return project/.gencode for project and local levels', () => {
|
|
149
|
+
expect(getPrimarySettingsDir('project', '/my/project')).toBe('/my/project/.gencode');
|
|
150
|
+
expect(getPrimarySettingsDir('local', '/my/project')).toBe('/my/project/.gencode');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('getSettingsFilePath', () => {
|
|
155
|
+
it('should return correct paths for each level', () => {
|
|
156
|
+
expect(getSettingsFilePath('user', '/project'))
|
|
157
|
+
.toBe(path.join(os.homedir(), '.gencode', 'settings.json'));
|
|
158
|
+
expect(getSettingsFilePath('project', '/my/project'))
|
|
159
|
+
.toBe('/my/project/.gencode/settings.json');
|
|
160
|
+
expect(getSettingsFilePath('local', '/my/project'))
|
|
161
|
+
.toBe('/my/project/.gencode/settings.local.json');
|
|
162
|
+
});
|
|
163
|
+
});
|