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
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Loader - Load settings from various sources
|
|
3
|
+
*
|
|
4
|
+
* Loads configuration files from all levels and namespaces.
|
|
5
|
+
* Each level loads both .gencode and .claude directories.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import type { Settings, ConfigSource, ConfigLevelType } from './types.js';
|
|
10
|
+
import { getConfigLevels, type ResolvedLevel, type ConfigPathInfo } from './levels.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Load a single JSON settings file
|
|
14
|
+
*/
|
|
15
|
+
async function loadJsonFile(filePath: string): Promise<Settings | null> {
|
|
16
|
+
try {
|
|
17
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
18
|
+
return JSON.parse(content);
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Load settings from a specific path info
|
|
26
|
+
*/
|
|
27
|
+
async function loadFromPathInfo(
|
|
28
|
+
pathInfo: ConfigPathInfo,
|
|
29
|
+
levelType: ConfigLevelType
|
|
30
|
+
): Promise<ConfigSource | null> {
|
|
31
|
+
const settings = await loadJsonFile(pathInfo.settingsPath);
|
|
32
|
+
if (!settings) return null;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
level: levelType,
|
|
36
|
+
path: pathInfo.settingsPath,
|
|
37
|
+
namespace: pathInfo.namespace,
|
|
38
|
+
settings,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Load all configuration sources in order
|
|
44
|
+
*
|
|
45
|
+
* Returns an array of ConfigSource in priority order (lowest first).
|
|
46
|
+
* Within each level:
|
|
47
|
+
* - .claude is loaded first (lower priority)
|
|
48
|
+
* - .gencode is loaded second (higher priority)
|
|
49
|
+
*
|
|
50
|
+
* This allows proper merging where:
|
|
51
|
+
* - Later sources override earlier ones
|
|
52
|
+
* - GenCode settings override Claude settings at the same level
|
|
53
|
+
*/
|
|
54
|
+
export async function loadAllSources(cwd: string): Promise<ConfigSource[]> {
|
|
55
|
+
const levels = await getConfigLevels(cwd);
|
|
56
|
+
const sources: ConfigSource[] = [];
|
|
57
|
+
|
|
58
|
+
for (const level of levels) {
|
|
59
|
+
// Load from each path in the level (claude first, then gencode)
|
|
60
|
+
for (const pathInfo of level.paths) {
|
|
61
|
+
const source = await loadFromPathInfo(pathInfo, level.type);
|
|
62
|
+
if (source) {
|
|
63
|
+
sources.push(source);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return sources;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Load configuration sources grouped by level
|
|
73
|
+
*/
|
|
74
|
+
export async function loadSourcesByLevel(
|
|
75
|
+
cwd: string
|
|
76
|
+
): Promise<Map<ConfigLevelType, ConfigSource[]>> {
|
|
77
|
+
const levels = await getConfigLevels(cwd);
|
|
78
|
+
const sourcesByLevel = new Map<ConfigLevelType, ConfigSource[]>();
|
|
79
|
+
|
|
80
|
+
for (const level of levels) {
|
|
81
|
+
const levelSources: ConfigSource[] = [];
|
|
82
|
+
|
|
83
|
+
for (const pathInfo of level.paths) {
|
|
84
|
+
const source = await loadFromPathInfo(pathInfo, level.type);
|
|
85
|
+
if (source) {
|
|
86
|
+
levelSources.push(source);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (levelSources.length > 0) {
|
|
91
|
+
sourcesByLevel.set(level.type, levelSources);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return sourcesByLevel;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Load only user-level settings
|
|
100
|
+
*/
|
|
101
|
+
export async function loadUserSettings(cwd: string): Promise<ConfigSource[]> {
|
|
102
|
+
const levels = await getConfigLevels(cwd);
|
|
103
|
+
const userLevel = levels.find((l) => l.type === 'user');
|
|
104
|
+
if (!userLevel) return [];
|
|
105
|
+
|
|
106
|
+
const sources: ConfigSource[] = [];
|
|
107
|
+
for (const pathInfo of userLevel.paths) {
|
|
108
|
+
const source = await loadFromPathInfo(pathInfo, 'user');
|
|
109
|
+
if (source) {
|
|
110
|
+
sources.push(source);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return sources;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Load only project-level settings
|
|
119
|
+
*/
|
|
120
|
+
export async function loadProjectSettings(cwd: string): Promise<ConfigSource[]> {
|
|
121
|
+
const levels = await getConfigLevels(cwd);
|
|
122
|
+
const projectLevel = levels.find((l) => l.type === 'project');
|
|
123
|
+
if (!projectLevel) return [];
|
|
124
|
+
|
|
125
|
+
const sources: ConfigSource[] = [];
|
|
126
|
+
for (const pathInfo of projectLevel.paths) {
|
|
127
|
+
const source = await loadFromPathInfo(pathInfo, 'project');
|
|
128
|
+
if (source) {
|
|
129
|
+
sources.push(source);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return sources;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Load only managed settings
|
|
138
|
+
*/
|
|
139
|
+
export async function loadManagedSettings(cwd: string): Promise<ConfigSource[]> {
|
|
140
|
+
const levels = await getConfigLevels(cwd);
|
|
141
|
+
const managedLevel = levels.find((l) => l.type === 'managed');
|
|
142
|
+
if (!managedLevel) return [];
|
|
143
|
+
|
|
144
|
+
const sources: ConfigSource[] = [];
|
|
145
|
+
for (const pathInfo of managedLevel.paths) {
|
|
146
|
+
const source = await loadFromPathInfo(pathInfo, 'managed');
|
|
147
|
+
if (source) {
|
|
148
|
+
sources.push(source);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return sources;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get information about all configuration levels
|
|
157
|
+
*/
|
|
158
|
+
export async function getConfigInfo(cwd: string): Promise<ResolvedLevel[]> {
|
|
159
|
+
return getConfigLevels(cwd);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check which config files exist
|
|
164
|
+
*/
|
|
165
|
+
export async function getExistingConfigFiles(cwd: string): Promise<string[]> {
|
|
166
|
+
const levels = await getConfigLevels(cwd);
|
|
167
|
+
const existingFiles: string[] = [];
|
|
168
|
+
|
|
169
|
+
for (const level of levels) {
|
|
170
|
+
for (const pathInfo of level.paths) {
|
|
171
|
+
if (pathInfo.exists) {
|
|
172
|
+
existingFiles.push(pathInfo.settingsPath);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return existingFiles;
|
|
178
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfigManager Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
8
|
+
import { ConfigManager } from './manager.js';
|
|
9
|
+
import { createTestProject, writeSettings, type TestProject } from './test-utils.js';
|
|
10
|
+
|
|
11
|
+
describe('ConfigManager', () => {
|
|
12
|
+
let test: TestProject;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
test = await createTestProject('gencode-config-');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => test.cleanup());
|
|
19
|
+
|
|
20
|
+
describe('load', () => {
|
|
21
|
+
it('should load settings from .gencode directory', async () => {
|
|
22
|
+
await writeSettings(test.projectDir, 'gencode', { provider: 'anthropic', model: 'claude-sonnet' });
|
|
23
|
+
|
|
24
|
+
const config = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
25
|
+
|
|
26
|
+
expect(config.settings.provider).toBe('anthropic');
|
|
27
|
+
expect(config.settings.model).toBe('claude-sonnet');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should load settings from .claude directory', async () => {
|
|
31
|
+
await writeSettings(test.projectDir, 'claude', { provider: 'openai', model: 'gpt-4' });
|
|
32
|
+
|
|
33
|
+
const config = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
34
|
+
|
|
35
|
+
expect(config.settings.provider).toBe('openai');
|
|
36
|
+
expect(config.settings.model).toBe('gpt-4');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should merge both .claude and .gencode with gencode winning', async () => {
|
|
40
|
+
await writeSettings(test.projectDir, 'claude', { provider: 'openai', model: 'gpt-4', theme: 'dark' });
|
|
41
|
+
await writeSettings(test.projectDir, 'gencode', { provider: 'anthropic' });
|
|
42
|
+
|
|
43
|
+
const config = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
44
|
+
|
|
45
|
+
expect(config.settings.provider).toBe('anthropic'); // gencode wins
|
|
46
|
+
expect(config.settings.model).toBe('gpt-4'); // preserved from claude
|
|
47
|
+
expect(config.settings.theme).toBe('dark'); // preserved from claude
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should concatenate permission arrays from both namespaces', async () => {
|
|
51
|
+
await writeSettings(test.projectDir, 'claude', {
|
|
52
|
+
permissions: { allow: ['Bash(git:*)'], deny: ['WebFetch'] },
|
|
53
|
+
});
|
|
54
|
+
await writeSettings(test.projectDir, 'gencode', {
|
|
55
|
+
permissions: { allow: ['Bash(npm:*)'], deny: ['Bash(rm -rf:*)'] },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const { settings } = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
59
|
+
|
|
60
|
+
expect(settings.permissions?.allow).toContain('Bash(git:*)');
|
|
61
|
+
expect(settings.permissions?.allow).toContain('Bash(npm:*)');
|
|
62
|
+
expect(settings.permissions?.deny).toContain('WebFetch');
|
|
63
|
+
expect(settings.permissions?.deny).toContain('Bash(rm -rf:*)');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should load local settings with higher priority', async () => {
|
|
67
|
+
await writeSettings(test.projectDir, 'gencode', { model: 'claude-sonnet', theme: 'light' });
|
|
68
|
+
await writeSettings(test.projectDir, 'gencode', { model: 'claude-opus' }, true);
|
|
69
|
+
|
|
70
|
+
const { settings } = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
71
|
+
|
|
72
|
+
expect(settings.model).toBe('claude-opus'); // local wins
|
|
73
|
+
expect(settings.theme).toBe('light'); // preserved
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should load from extra config dirs', async () => {
|
|
77
|
+
const extraDir = path.join(test.tempDir, 'team-config');
|
|
78
|
+
await fs.mkdir(extraDir, { recursive: true });
|
|
79
|
+
await fs.writeFile(path.join(extraDir, 'settings.json'), JSON.stringify({ teamSetting: 'enabled' }));
|
|
80
|
+
process.env.GENCODE_CONFIG_DIRS = extraDir;
|
|
81
|
+
|
|
82
|
+
const { settings } = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
83
|
+
|
|
84
|
+
expect(settings.teamSetting).toBe('enabled');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('setCliArgs', () => {
|
|
89
|
+
it('should apply CLI args with highest priority', async () => {
|
|
90
|
+
await writeSettings(test.projectDir, 'gencode', { model: 'claude-sonnet', provider: 'anthropic' });
|
|
91
|
+
|
|
92
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
93
|
+
manager.setCliArgs({ model: 'gpt-4o' });
|
|
94
|
+
const { settings } = await manager.load();
|
|
95
|
+
|
|
96
|
+
expect(settings.model).toBe('gpt-4o'); // CLI wins
|
|
97
|
+
expect(settings.provider).toBe('anthropic'); // unchanged
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('saveToLevel', () => {
|
|
102
|
+
it('should save to project and local levels', async () => {
|
|
103
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
104
|
+
await manager.load();
|
|
105
|
+
|
|
106
|
+
await manager.saveToLevel({ model: 'project-model' }, 'project');
|
|
107
|
+
await manager.saveToLevel({ debug: true }, 'local');
|
|
108
|
+
|
|
109
|
+
const projectContent = JSON.parse(await fs.readFile(
|
|
110
|
+
path.join(test.projectDir, '.gencode', 'settings.json'), 'utf-8'
|
|
111
|
+
));
|
|
112
|
+
const localContent = JSON.parse(await fs.readFile(
|
|
113
|
+
path.join(test.projectDir, '.gencode', 'settings.local.json'), 'utf-8'
|
|
114
|
+
));
|
|
115
|
+
|
|
116
|
+
expect(projectContent.model).toBe('project-model');
|
|
117
|
+
expect(localContent.debug).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should merge with existing settings', async () => {
|
|
121
|
+
await writeSettings(test.projectDir, 'gencode', { model: 'old', theme: 'dark' });
|
|
122
|
+
|
|
123
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
124
|
+
await manager.load();
|
|
125
|
+
await manager.saveToLevel({ model: 'new' }, 'project');
|
|
126
|
+
|
|
127
|
+
const saved = JSON.parse(await fs.readFile(
|
|
128
|
+
path.join(test.projectDir, '.gencode', 'settings.json'), 'utf-8'
|
|
129
|
+
));
|
|
130
|
+
|
|
131
|
+
expect(saved.model).toBe('new');
|
|
132
|
+
expect(saved.theme).toBe('dark'); // preserved
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('addPermissionRule', () => {
|
|
137
|
+
it('should add allow and deny rules', async () => {
|
|
138
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
139
|
+
await manager.load();
|
|
140
|
+
|
|
141
|
+
await manager.addPermissionRule('Bash(npm:*)', 'allow', 'project');
|
|
142
|
+
await manager.addPermissionRule('Bash(rm:*)', 'deny', 'project');
|
|
143
|
+
|
|
144
|
+
const saved = JSON.parse(await fs.readFile(
|
|
145
|
+
path.join(test.projectDir, '.gencode', 'settings.json'), 'utf-8'
|
|
146
|
+
));
|
|
147
|
+
|
|
148
|
+
expect(saved.permissions?.allow).toContain('Bash(npm:*)');
|
|
149
|
+
expect(saved.permissions?.deny).toContain('Bash(rm:*)');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('getEffectivePermissions', () => {
|
|
154
|
+
it('should return all permission lists', async () => {
|
|
155
|
+
await writeSettings(test.projectDir, 'gencode', {
|
|
156
|
+
permissions: { allow: ['A'], ask: ['B'], deny: ['C'] },
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
160
|
+
await manager.load();
|
|
161
|
+
const perms = manager.getEffectivePermissions();
|
|
162
|
+
|
|
163
|
+
expect(perms.allow).toContain('A');
|
|
164
|
+
expect(perms.ask).toContain('B');
|
|
165
|
+
expect(perms.deny).toContain('C');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('isAllowed and shouldAsk', () => {
|
|
170
|
+
it('should check permissions correctly', async () => {
|
|
171
|
+
await writeSettings(test.projectDir, 'gencode', {
|
|
172
|
+
permissions: { allow: ['Bash(git:*)'], deny: ['Bash(rm:*)'], ask: ['WebFetch'] },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
176
|
+
await manager.load();
|
|
177
|
+
|
|
178
|
+
expect(manager.isAllowed('Bash(git:status)')).toBe(true);
|
|
179
|
+
expect(manager.isAllowed('Bash(rm:file)')).toBe(false);
|
|
180
|
+
expect(manager.isAllowed('Unknown')).toBe(false);
|
|
181
|
+
|
|
182
|
+
expect(manager.shouldAsk('Bash(git:status)')).toBe(false); // allowed
|
|
183
|
+
expect(manager.shouldAsk('Bash(rm:file)')).toBe(false); // denied
|
|
184
|
+
expect(manager.shouldAsk('WebFetch')).toBe(true);
|
|
185
|
+
expect(manager.shouldAsk('Unknown')).toBe(true); // default ask
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('getSources', () => {
|
|
190
|
+
it('should return all loaded sources', async () => {
|
|
191
|
+
await writeSettings(test.projectDir, 'claude', { model: 'gpt-4' });
|
|
192
|
+
await writeSettings(test.projectDir, 'gencode', { provider: 'anthropic' });
|
|
193
|
+
|
|
194
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
195
|
+
await manager.load();
|
|
196
|
+
const sources = manager.getSources();
|
|
197
|
+
|
|
198
|
+
expect(sources.find((s) => s.namespace === 'claude')).toBeDefined();
|
|
199
|
+
expect(sources.find((s) => s.namespace === 'gencode')).toBeDefined();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('getDebugSummary', () => {
|
|
204
|
+
it('should return summary or indicate not loaded', async () => {
|
|
205
|
+
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
206
|
+
|
|
207
|
+
expect(manager.getDebugSummary()).toBe('Configuration not loaded');
|
|
208
|
+
|
|
209
|
+
await writeSettings(test.projectDir, 'gencode', { model: 'test' });
|
|
210
|
+
await manager.load();
|
|
211
|
+
|
|
212
|
+
expect(manager.getDebugSummary()).toContain('Configuration Sources');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|