gencode-ai 0.2.0 → 0.3.0
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/dist/agent/agent.d.ts +9 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +37 -8
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/types.d.ts +5 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/cli/components/App.d.ts.map +1 -1
- package/dist/cli/components/App.js +15 -9
- package/dist/cli/components/App.js.map +1 -1
- package/dist/cli/components/Messages.js +1 -1
- package/dist/cli/components/Messages.js.map +1 -1
- package/dist/cli/components/ModelSelector.d.ts +4 -3
- package/dist/cli/components/ModelSelector.d.ts.map +1 -1
- package/dist/cli/components/ModelSelector.js +54 -37
- package/dist/cli/components/ModelSelector.js.map +1 -1
- package/dist/cli/components/ProviderManager.d.ts +2 -2
- package/dist/cli/components/ProviderManager.d.ts.map +1 -1
- package/dist/cli/components/ProviderManager.js +137 -156
- package/dist/cli/components/ProviderManager.js.map +1 -1
- package/dist/cli/index.js +30 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/levels.d.ts +5 -5
- package/dist/config/levels.d.ts.map +1 -1
- package/dist/config/levels.js +20 -20
- package/dist/config/levels.js.map +1 -1
- package/dist/config/merger.js +1 -1
- package/dist/config/merger.js.map +1 -1
- package/dist/config/providers-config.d.ts +8 -5
- package/dist/config/providers-config.d.ts.map +1 -1
- package/dist/config/providers-config.js +19 -22
- package/dist/config/providers-config.js.map +1 -1
- package/dist/config/test-utils.d.ts +2 -2
- package/dist/config/test-utils.d.ts.map +1 -1
- package/dist/config/test-utils.js +4 -4
- package/dist/config/test-utils.js.map +1 -1
- package/dist/config/types.d.ts +23 -17
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +14 -14
- package/dist/config/types.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +25 -12
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +241 -112
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/test-utils.d.ts +1 -1
- package/dist/memory/test-utils.d.ts.map +1 -1
- package/dist/memory/test-utils.js +3 -3
- package/dist/memory/test-utils.js.map +1 -1
- package/dist/memory/types.d.ts +20 -10
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +13 -13
- package/dist/memory/types.js.map +1 -1
- package/dist/migration/migrate.d.ts +24 -0
- package/dist/migration/migrate.d.ts.map +1 -0
- package/dist/migration/migrate.js +164 -0
- package/dist/migration/migrate.js.map +1 -0
- package/dist/permissions/persistence.d.ts +2 -2
- package/dist/permissions/persistence.js +4 -4
- package/dist/permissions/persistence.js.map +1 -1
- package/dist/planning/plan-file.d.ts +1 -1
- package/dist/planning/plan-file.js +2 -2
- package/dist/planning/plan-file.js.map +1 -1
- package/dist/prompts/index.d.ts +5 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +11 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +7 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/gemini.d.ts +2 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +7 -0
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +20 -10
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +48 -24
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai.d.ts +2 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +7 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/registry.d.ts +48 -34
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +72 -88
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/store.d.ts +43 -17
- package/dist/providers/store.d.ts.map +1 -1
- package/dist/providers/store.js +112 -19
- package/dist/providers/store.js.map +1 -1
- package/dist/providers/types.d.ts +23 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/vertex-ai.d.ts +15 -7
- package/dist/providers/vertex-ai.d.ts.map +1 -1
- package/dist/providers/vertex-ai.js +46 -13
- package/dist/providers/vertex-ai.js.map +1 -1
- package/dist/session/types.js +1 -1
- package/dist/session/types.js.map +1 -1
- package/docs/config-system-comparison.md +50 -50
- package/docs/cost-tracking-comparison.md +2 -2
- package/docs/memory-system.md +124 -31
- package/docs/permissions.md +2 -2
- package/docs/proposals/0006-memory-system.md +4 -4
- package/docs/proposals/0008-checkpointing.md +109 -2
- package/docs/proposals/0011-custom-commands.md +2 -1
- package/docs/proposals/0021-skills-system.md +2 -1
- package/docs/proposals/0023-permission-enhancements.md +2 -2
- package/docs/proposals/0033-enterprise-deployment.md +1 -1
- package/docs/proposals/0041-configuration-system.md +17 -19
- package/docs/proposals/0042-prompt-optimization.md +17 -9
- package/docs/proposals/README.md +5 -5
- package/docs/providers.md +94 -9
- package/package.json +3 -2
- package/scripts/migrate.ts +449 -0
- package/src/agent/agent.ts +51 -9
- package/src/agent/types.ts +5 -1
- package/src/cli/components/App.tsx +17 -8
- package/src/cli/components/Messages.tsx +1 -1
- package/src/cli/components/ModelSelector.tsx +62 -43
- package/src/cli/components/ProviderManager.tsx +278 -323
- package/src/cli/index.tsx +36 -17
- package/src/config/index.ts +5 -3
- package/src/config/levels.test.ts +22 -22
- package/src/config/levels.ts +22 -22
- package/src/config/loader.test.ts +14 -14
- package/src/config/manager.test.ts +19 -19
- package/src/config/merger.test.ts +23 -23
- package/src/config/merger.ts +1 -1
- package/src/config/providers-config.ts +23 -21
- package/src/config/test-utils.ts +6 -6
- package/src/config/types.ts +30 -20
- package/src/memory/memory-manager.test.ts +242 -24
- package/src/memory/memory-manager.ts +270 -141
- package/src/memory/test-utils.ts +4 -4
- package/src/memory/types.ts +28 -17
- package/src/permissions/persistence.ts +4 -4
- package/src/planning/plan-file.ts +2 -2
- package/src/prompts/index.ts +13 -9
- package/src/providers/anthropic.ts +9 -0
- package/src/providers/gemini.ts +9 -0
- package/src/providers/index.ts +76 -33
- package/src/providers/openai.ts +9 -0
- package/src/providers/registry.ts +116 -111
- package/src/providers/store.ts +130 -28
- package/src/providers/types.ts +33 -1
- package/src/providers/vertex-ai.ts +49 -13
- package/src/session/types.ts +1 -1
package/src/cli/index.tsx
CHANGED
|
@@ -9,7 +9,9 @@ import { render } from 'ink';
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { App } from './components/App.js';
|
|
11
11
|
import type { AgentConfig } from '../agent/types.js';
|
|
12
|
-
import { SettingsManager, ProvidersConfigManager, type Settings, type
|
|
12
|
+
import { SettingsManager, ProvidersConfigManager, type Settings, type Provider } from '../config/index.js';
|
|
13
|
+
import type { AuthMethod } from '../providers/types.js';
|
|
14
|
+
import { inferProvider, inferAuthMethod } from '../providers/index.js';
|
|
13
15
|
|
|
14
16
|
// ============================================================================
|
|
15
17
|
// Proxy Setup
|
|
@@ -32,32 +34,43 @@ async function setupProxy(): Promise<void> {
|
|
|
32
34
|
// Configuration
|
|
33
35
|
// ============================================================================
|
|
34
36
|
function detectConfig(settings: Settings, providersConfig: ProvidersConfigManager): AgentConfig {
|
|
35
|
-
let provider:
|
|
37
|
+
let provider: Provider = 'gemini';
|
|
38
|
+
let authMethod: AuthMethod | undefined;
|
|
36
39
|
let model = 'gemini-2.0-flash';
|
|
37
40
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
// Auto-detect from environment variables
|
|
42
|
+
// Check Vertex AI first (requires explicit opt-in)
|
|
43
|
+
const useVertex = process.env.CLAUDE_CODE_USE_VERTEX === '1' || process.env.CLAUDE_CODE_USE_VERTEX === 'true';
|
|
44
|
+
const hasVertexProject = !!(
|
|
45
|
+
process.env.ANTHROPIC_VERTEX_PROJECT_ID ||
|
|
46
|
+
process.env.GCLOUD_PROJECT ||
|
|
47
|
+
process.env.GOOGLE_CLOUD_PROJECT
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
if (useVertex && hasVertexProject) {
|
|
51
|
+
provider = 'anthropic';
|
|
52
|
+
authMethod = 'vertex';
|
|
53
|
+
model = 'claude-sonnet-4-5@20250929';
|
|
54
|
+
} else if (process.env.ANTHROPIC_API_KEY) {
|
|
45
55
|
provider = 'anthropic';
|
|
56
|
+
authMethod = 'api_key';
|
|
46
57
|
model = 'claude-sonnet-4-20250514';
|
|
47
58
|
} else if (process.env.OPENAI_API_KEY) {
|
|
48
59
|
provider = 'openai';
|
|
60
|
+
authMethod = 'api_key';
|
|
49
61
|
model = 'gpt-4o';
|
|
50
62
|
} else if (process.env.GOOGLE_API_KEY) {
|
|
51
63
|
provider = 'gemini';
|
|
64
|
+
authMethod = 'api_key';
|
|
52
65
|
model = 'gemini-2.0-flash';
|
|
53
66
|
}
|
|
54
67
|
|
|
55
68
|
// Override from env vars
|
|
56
|
-
if (process.env.
|
|
57
|
-
provider = process.env.
|
|
69
|
+
if (process.env.GEN_PROVIDER) {
|
|
70
|
+
provider = process.env.GEN_PROVIDER as Provider;
|
|
58
71
|
}
|
|
59
|
-
if (process.env.
|
|
60
|
-
model = process.env.
|
|
72
|
+
if (process.env.GEN_MODEL) {
|
|
73
|
+
model = process.env.GEN_MODEL;
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
// Override from saved settings (highest priority)
|
|
@@ -66,17 +79,23 @@ function detectConfig(settings: Settings, providersConfig: ProvidersConfigManage
|
|
|
66
79
|
}
|
|
67
80
|
if (settings.model) {
|
|
68
81
|
model = settings.model;
|
|
69
|
-
//
|
|
82
|
+
// Try to infer provider and authMethod from cached models first
|
|
70
83
|
if (!settings.provider) {
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
provider =
|
|
84
|
+
const cached = providersConfig.inferProviderFromCache(model);
|
|
85
|
+
if (cached) {
|
|
86
|
+
provider = cached.provider;
|
|
87
|
+
authMethod = cached.authMethod;
|
|
88
|
+
} else {
|
|
89
|
+
// Fall back to model name inference
|
|
90
|
+
provider = inferProvider(model);
|
|
91
|
+
authMethod = inferAuthMethod(model);
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
return {
|
|
79
97
|
provider,
|
|
98
|
+
authMethod,
|
|
80
99
|
model,
|
|
81
100
|
cwd: process.cwd(),
|
|
82
101
|
maxTurns: 20,
|
package/src/config/index.ts
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
export type {
|
|
13
13
|
Settings,
|
|
14
14
|
SettingsManagerOptions,
|
|
15
|
+
Provider,
|
|
16
|
+
AuthMethod,
|
|
15
17
|
ProviderName,
|
|
16
18
|
PermissionRules,
|
|
17
19
|
ConfigLevelType,
|
|
@@ -34,11 +36,11 @@ export {
|
|
|
34
36
|
SETTINGS_LOCAL_FILE_NAME,
|
|
35
37
|
MANAGED_SETTINGS_FILE_NAME,
|
|
36
38
|
PROVIDERS_FILE_NAME,
|
|
37
|
-
|
|
39
|
+
GEN_DIR,
|
|
38
40
|
CLAUDE_DIR,
|
|
39
|
-
|
|
41
|
+
USER_GEN_DIR,
|
|
40
42
|
USER_CLAUDE_DIR,
|
|
41
|
-
|
|
43
|
+
GEN_CONFIG_ENV,
|
|
42
44
|
getManagedPaths,
|
|
43
45
|
} from './types.js';
|
|
44
46
|
|
|
@@ -31,10 +31,10 @@ describe('findProjectRoot', () => {
|
|
|
31
31
|
expect(await findProjectRoot(subDir)).toBe(test.projectDir);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it('should find .
|
|
35
|
-
// Remove .git, add .
|
|
34
|
+
it('should find .gen directory as project root', async () => {
|
|
35
|
+
// Remove .git, add .gen
|
|
36
36
|
await fs.rm(path.join(test.projectDir, '.git'), { recursive: true });
|
|
37
|
-
await fs.mkdir(path.join(test.projectDir, '.
|
|
37
|
+
await fs.mkdir(path.join(test.projectDir, '.gen'));
|
|
38
38
|
const subDir = path.join(test.projectDir, 'src');
|
|
39
39
|
await fs.mkdir(subDir, { recursive: true });
|
|
40
40
|
|
|
@@ -59,38 +59,38 @@ describe('findProjectRoot', () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
describe('parseExtraConfigDirs', () => {
|
|
62
|
-
const originalEnv = process.env.
|
|
62
|
+
const originalEnv = process.env.GEN_CONFIG;
|
|
63
63
|
|
|
64
64
|
afterEach(() => {
|
|
65
65
|
if (originalEnv === undefined) {
|
|
66
|
-
delete process.env.
|
|
66
|
+
delete process.env.GEN_CONFIG;
|
|
67
67
|
} else {
|
|
68
|
-
process.env.
|
|
68
|
+
process.env.GEN_CONFIG = originalEnv;
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
it('should return empty array when env var not set', () => {
|
|
73
|
-
delete process.env.
|
|
73
|
+
delete process.env.GEN_CONFIG;
|
|
74
74
|
expect(parseExtraConfigDirs()).toEqual([]);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
it('should parse single directory', () => {
|
|
78
|
-
process.env.
|
|
78
|
+
process.env.GEN_CONFIG = '/team/config';
|
|
79
79
|
expect(parseExtraConfigDirs()).toEqual(['/team/config']);
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
it('should parse multiple directories', () => {
|
|
83
|
-
process.env.
|
|
83
|
+
process.env.GEN_CONFIG = '/team/config:/shared/rules';
|
|
84
84
|
expect(parseExtraConfigDirs()).toEqual(['/team/config', '/shared/rules']);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
87
|
it('should expand tilde to home directory', () => {
|
|
88
|
-
process.env.
|
|
88
|
+
process.env.GEN_CONFIG = '~/my-config';
|
|
89
89
|
expect(parseExtraConfigDirs()[0]).toBe(path.join(os.homedir(), 'my-config'));
|
|
90
90
|
});
|
|
91
91
|
|
|
92
92
|
it('should trim whitespace and filter empty strings', () => {
|
|
93
|
-
process.env.
|
|
93
|
+
process.env.GEN_CONFIG = ' /path/one : : /path/two ';
|
|
94
94
|
expect(parseExtraConfigDirs()).toEqual(['/path/one', '/path/two']);
|
|
95
95
|
});
|
|
96
96
|
});
|
|
@@ -117,11 +117,11 @@ describe('getConfigLevels', () => {
|
|
|
117
117
|
|
|
118
118
|
expect(userLevel?.paths.length).toBe(2);
|
|
119
119
|
expect(userLevel?.paths.some((p) => p.namespace === 'claude')).toBe(true);
|
|
120
|
-
expect(userLevel?.paths.some((p) => p.namespace === '
|
|
120
|
+
expect(userLevel?.paths.some((p) => p.namespace === 'gen')).toBe(true);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
it('should include extra dirs when env var is set', async () => {
|
|
124
|
-
process.env.
|
|
124
|
+
process.env.GEN_CONFIG = '/team/config';
|
|
125
125
|
const extraLevels = (await getConfigLevels(test.projectDir)).filter((l) => l.type === 'extra');
|
|
126
126
|
|
|
127
127
|
expect(extraLevels.length).toBeGreaterThan(0);
|
|
@@ -131,7 +131,7 @@ describe('getConfigLevels', () => {
|
|
|
131
131
|
for (const level of await getConfigLevels(test.projectDir)) {
|
|
132
132
|
if (level.paths.length >= 2) {
|
|
133
133
|
const claudeIdx = level.paths.findIndex((p) => p.namespace === 'claude');
|
|
134
|
-
const gencodeIdx = level.paths.findIndex((p) => p.namespace === '
|
|
134
|
+
const gencodeIdx = level.paths.findIndex((p) => p.namespace === 'gen');
|
|
135
135
|
if (claudeIdx !== -1 && gencodeIdx !== -1) {
|
|
136
136
|
expect(claudeIdx).toBeLessThan(gencodeIdx);
|
|
137
137
|
}
|
|
@@ -141,23 +141,23 @@ describe('getConfigLevels', () => {
|
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
describe('getPrimarySettingsDir', () => {
|
|
144
|
-
it('should return ~/.
|
|
145
|
-
expect(getPrimarySettingsDir('user', '/project')).toBe(path.join(os.homedir(), '.
|
|
144
|
+
it('should return ~/.gen for user level', () => {
|
|
145
|
+
expect(getPrimarySettingsDir('user', '/project')).toBe(path.join(os.homedir(), '.gen'));
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
it('should return project/.
|
|
149
|
-
expect(getPrimarySettingsDir('project', '/my/project')).toBe('/my/project/.
|
|
150
|
-
expect(getPrimarySettingsDir('local', '/my/project')).toBe('/my/project/.
|
|
148
|
+
it('should return project/.gen for project and local levels', () => {
|
|
149
|
+
expect(getPrimarySettingsDir('project', '/my/project')).toBe('/my/project/.gen');
|
|
150
|
+
expect(getPrimarySettingsDir('local', '/my/project')).toBe('/my/project/.gen');
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
describe('getSettingsFilePath', () => {
|
|
155
155
|
it('should return correct paths for each level', () => {
|
|
156
156
|
expect(getSettingsFilePath('user', '/project'))
|
|
157
|
-
.toBe(path.join(os.homedir(), '.
|
|
157
|
+
.toBe(path.join(os.homedir(), '.gen', 'settings.json'));
|
|
158
158
|
expect(getSettingsFilePath('project', '/my/project'))
|
|
159
|
-
.toBe('/my/project/.
|
|
159
|
+
.toBe('/my/project/.gen/settings.json');
|
|
160
160
|
expect(getSettingsFilePath('local', '/my/project'))
|
|
161
|
-
.toBe('/my/project/.
|
|
161
|
+
.toBe('/my/project/.gen/settings.local.json');
|
|
162
162
|
});
|
|
163
163
|
});
|
package/src/config/levels.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Configuration Levels - Path resolution for multi-level config
|
|
3
3
|
*
|
|
4
4
|
* Defines the configuration hierarchy and resolves paths for each level.
|
|
5
|
-
* At each level, both .
|
|
6
|
-
* with .
|
|
5
|
+
* At each level, both .gen and .claude directories are loaded and merged,
|
|
6
|
+
* with .gen taking higher priority.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as path from 'path';
|
|
@@ -11,12 +11,12 @@ import * as os from 'os';
|
|
|
11
11
|
import * as fs from 'fs/promises';
|
|
12
12
|
import {
|
|
13
13
|
ConfigLevelType,
|
|
14
|
-
|
|
14
|
+
GEN_DIR,
|
|
15
15
|
CLAUDE_DIR,
|
|
16
16
|
SETTINGS_FILE_NAME,
|
|
17
17
|
SETTINGS_LOCAL_FILE_NAME,
|
|
18
18
|
MANAGED_SETTINGS_FILE_NAME,
|
|
19
|
-
|
|
19
|
+
GEN_CONFIG_ENV,
|
|
20
20
|
getManagedPaths,
|
|
21
21
|
} from './types.js';
|
|
22
22
|
|
|
@@ -27,7 +27,7 @@ export interface ConfigPathInfo {
|
|
|
27
27
|
settingsPath: string;
|
|
28
28
|
localSettingsPath?: string;
|
|
29
29
|
dir: string;
|
|
30
|
-
namespace: '
|
|
30
|
+
namespace: 'gen' | 'claude' | 'extra';
|
|
31
31
|
exists: boolean;
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -57,9 +57,9 @@ export async function findProjectRoot(cwd: string): Promise<string> {
|
|
|
57
57
|
// Not a git root, continue
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// Check for .
|
|
60
|
+
// Check for .gen or .claude directories
|
|
61
61
|
try {
|
|
62
|
-
await fs.access(path.join(current,
|
|
62
|
+
await fs.access(path.join(current, GEN_DIR));
|
|
63
63
|
return current;
|
|
64
64
|
} catch {
|
|
65
65
|
// Continue
|
|
@@ -93,10 +93,10 @@ async function pathExists(filePath: string): Promise<boolean> {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
/**
|
|
96
|
-
* Parse
|
|
96
|
+
* Parse GEN_CONFIG environment variable
|
|
97
97
|
*/
|
|
98
98
|
export function parseExtraConfigDirs(): string[] {
|
|
99
|
-
const value = process.env[
|
|
99
|
+
const value = process.env[GEN_CONFIG_ENV];
|
|
100
100
|
if (!value) return [];
|
|
101
101
|
|
|
102
102
|
return value
|
|
@@ -130,11 +130,11 @@ export async function getConfigLevels(cwd: string): Promise<ResolvedLevel[]> {
|
|
|
130
130
|
});
|
|
131
131
|
|
|
132
132
|
// GenCode second (higher priority within level)
|
|
133
|
-
const userGencodeDir = path.join(home,
|
|
133
|
+
const userGencodeDir = path.join(home, GEN_DIR);
|
|
134
134
|
userPaths.push({
|
|
135
135
|
settingsPath: path.join(userGencodeDir, SETTINGS_FILE_NAME),
|
|
136
136
|
dir: userGencodeDir,
|
|
137
|
-
namespace: '
|
|
137
|
+
namespace: 'gen',
|
|
138
138
|
exists: await pathExists(path.join(userGencodeDir, SETTINGS_FILE_NAME)),
|
|
139
139
|
});
|
|
140
140
|
|
|
@@ -180,11 +180,11 @@ export async function getConfigLevels(cwd: string): Promise<ResolvedLevel[]> {
|
|
|
180
180
|
});
|
|
181
181
|
|
|
182
182
|
// GenCode second (higher priority within level)
|
|
183
|
-
const projectGencodeDir = path.join(projectRoot,
|
|
183
|
+
const projectGencodeDir = path.join(projectRoot, GEN_DIR);
|
|
184
184
|
projectPaths.push({
|
|
185
185
|
settingsPath: path.join(projectGencodeDir, SETTINGS_FILE_NAME),
|
|
186
186
|
dir: projectGencodeDir,
|
|
187
|
-
namespace: '
|
|
187
|
+
namespace: 'gen',
|
|
188
188
|
exists: await pathExists(path.join(projectGencodeDir, SETTINGS_FILE_NAME)),
|
|
189
189
|
});
|
|
190
190
|
|
|
@@ -210,7 +210,7 @@ export async function getConfigLevels(cwd: string): Promise<ResolvedLevel[]> {
|
|
|
210
210
|
localPaths.push({
|
|
211
211
|
settingsPath: path.join(projectGencodeDir, SETTINGS_LOCAL_FILE_NAME),
|
|
212
212
|
dir: projectGencodeDir,
|
|
213
|
-
namespace: '
|
|
213
|
+
namespace: 'gen',
|
|
214
214
|
exists: await pathExists(path.join(projectGencodeDir, SETTINGS_LOCAL_FILE_NAME)),
|
|
215
215
|
});
|
|
216
216
|
|
|
@@ -234,10 +234,10 @@ export async function getConfigLevels(cwd: string): Promise<ResolvedLevel[]> {
|
|
|
234
234
|
|
|
235
235
|
// GenCode second (higher priority within level)
|
|
236
236
|
managedPathsList.push({
|
|
237
|
-
settingsPath: path.join(managedPaths.
|
|
238
|
-
dir: managedPaths.
|
|
239
|
-
namespace: '
|
|
240
|
-
exists: await pathExists(path.join(managedPaths.
|
|
237
|
+
settingsPath: path.join(managedPaths.gen, MANAGED_SETTINGS_FILE_NAME),
|
|
238
|
+
dir: managedPaths.gen,
|
|
239
|
+
namespace: 'gen',
|
|
240
|
+
exists: await pathExists(path.join(managedPaths.gen, MANAGED_SETTINGS_FILE_NAME)),
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
levels.push({
|
|
@@ -253,7 +253,7 @@ export async function getConfigLevels(cwd: string): Promise<ResolvedLevel[]> {
|
|
|
253
253
|
|
|
254
254
|
/**
|
|
255
255
|
* Get the primary settings directory for saving
|
|
256
|
-
* Prefers .
|
|
256
|
+
* Prefers .gen if it exists, otherwise creates it
|
|
257
257
|
*/
|
|
258
258
|
export function getPrimarySettingsDir(
|
|
259
259
|
level: 'user' | 'project' | 'local',
|
|
@@ -263,12 +263,12 @@ export function getPrimarySettingsDir(
|
|
|
263
263
|
|
|
264
264
|
switch (level) {
|
|
265
265
|
case 'user':
|
|
266
|
-
return path.join(home,
|
|
266
|
+
return path.join(home, GEN_DIR);
|
|
267
267
|
case 'project':
|
|
268
268
|
case 'local':
|
|
269
|
-
return path.join(projectRoot,
|
|
269
|
+
return path.join(projectRoot, GEN_DIR);
|
|
270
270
|
default:
|
|
271
|
-
return path.join(home,
|
|
271
|
+
return path.join(home, GEN_DIR);
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -31,28 +31,28 @@ describe('Config Loader', () => {
|
|
|
31
31
|
expect(source?.settings.provider).toBe('openai');
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it('should load settings from project .
|
|
35
|
-
await writeSettings(test.projectDir, '
|
|
34
|
+
it('should load settings from project .gen directory', async () => {
|
|
35
|
+
await writeSettings(test.projectDir, 'gen', { provider: 'anthropic' });
|
|
36
36
|
const sources = await loadAllSources(test.projectDir);
|
|
37
37
|
|
|
38
|
-
const source = sources.find((s) => s.level === 'project' && s.namespace === '
|
|
38
|
+
const source = sources.find((s) => s.level === 'project' && s.namespace === 'gen');
|
|
39
39
|
expect(source?.settings.provider).toBe('anthropic');
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
it('should load both .claude and .
|
|
42
|
+
it('should load both .claude and .gen settings at same level', async () => {
|
|
43
43
|
await writeSettings(test.projectDir, 'claude', { provider: 'openai', model: 'gpt-4' });
|
|
44
|
-
await writeSettings(test.projectDir, '
|
|
44
|
+
await writeSettings(test.projectDir, 'gen', { provider: 'anthropic' });
|
|
45
45
|
|
|
46
46
|
const sources = await loadAllSources(test.projectDir);
|
|
47
47
|
const claude = sources.find((s) => s.level === 'project' && s.namespace === 'claude');
|
|
48
|
-
const gencode = sources.find((s) => s.level === 'project' && s.namespace === '
|
|
48
|
+
const gencode = sources.find((s) => s.level === 'project' && s.namespace === 'gen');
|
|
49
49
|
|
|
50
50
|
expect(claude?.settings.provider).toBe('openai');
|
|
51
51
|
expect(gencode?.settings.provider).toBe('anthropic');
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
it('should load local settings', async () => {
|
|
55
|
-
await writeSettings(test.projectDir, '
|
|
55
|
+
await writeSettings(test.projectDir, 'gen', { alwaysThinkingEnabled: true }, true);
|
|
56
56
|
const sources = await loadAllSources(test.projectDir);
|
|
57
57
|
|
|
58
58
|
const local = sources.find((s) => s.level === 'local');
|
|
@@ -63,7 +63,7 @@ describe('Config Loader', () => {
|
|
|
63
63
|
const extraDir = path.join(test.tempDir, 'extra-config');
|
|
64
64
|
await fs.mkdir(extraDir, { recursive: true });
|
|
65
65
|
await fs.writeFile(path.join(extraDir, 'settings.json'), JSON.stringify({ theme: 'dark' }));
|
|
66
|
-
process.env.
|
|
66
|
+
process.env.GEN_CONFIG = extraDir;
|
|
67
67
|
|
|
68
68
|
const sources = await loadAllSources(test.projectDir);
|
|
69
69
|
const extra = sources.find((s) => s.level === 'extra');
|
|
@@ -74,7 +74,7 @@ describe('Config Loader', () => {
|
|
|
74
74
|
describe('loadSourcesByLevel', () => {
|
|
75
75
|
it('should group sources by level', async () => {
|
|
76
76
|
await writeSettings(test.projectDir, 'claude', { model: 'gpt-4' });
|
|
77
|
-
await writeSettings(test.projectDir, '
|
|
77
|
+
await writeSettings(test.projectDir, 'gen', { model: 'claude-sonnet' });
|
|
78
78
|
|
|
79
79
|
const sourcesByLevel = await loadSourcesByLevel(test.projectDir);
|
|
80
80
|
|
|
@@ -85,8 +85,8 @@ describe('Config Loader', () => {
|
|
|
85
85
|
|
|
86
86
|
describe('loadProjectSettings', () => {
|
|
87
87
|
it('should only load project-level settings', async () => {
|
|
88
|
-
await writeSettings(test.projectDir, '
|
|
89
|
-
await writeSettings(test.projectDir, '
|
|
88
|
+
await writeSettings(test.projectDir, 'gen', { model: 'claude-sonnet' });
|
|
89
|
+
await writeSettings(test.projectDir, 'gen', { theme: 'dark' }, true);
|
|
90
90
|
|
|
91
91
|
const sources = await loadProjectSettings(test.projectDir);
|
|
92
92
|
|
|
@@ -99,14 +99,14 @@ describe('Config Loader', () => {
|
|
|
99
99
|
describe('getExistingConfigFiles', () => {
|
|
100
100
|
it('should list all existing config files', async () => {
|
|
101
101
|
await writeSettings(test.projectDir, 'claude', {});
|
|
102
|
-
await writeSettings(test.projectDir, '
|
|
103
|
-
await writeSettings(test.projectDir, '
|
|
102
|
+
await writeSettings(test.projectDir, 'gen', {});
|
|
103
|
+
await writeSettings(test.projectDir, 'gen', {}, true);
|
|
104
104
|
|
|
105
105
|
const files = await getExistingConfigFiles(test.projectDir);
|
|
106
106
|
|
|
107
107
|
expect(files.length).toBeGreaterThanOrEqual(3);
|
|
108
108
|
expect(files.some((f) => f.includes('.claude/settings.json'))).toBe(true);
|
|
109
|
-
expect(files.some((f) => f.includes('.
|
|
109
|
+
expect(files.some((f) => f.includes('.gen/settings.json'))).toBe(true);
|
|
110
110
|
expect(files.some((f) => f.includes('settings.local.json'))).toBe(true);
|
|
111
111
|
});
|
|
112
112
|
|
|
@@ -18,8 +18,8 @@ describe('ConfigManager', () => {
|
|
|
18
18
|
afterEach(() => test.cleanup());
|
|
19
19
|
|
|
20
20
|
describe('load', () => {
|
|
21
|
-
it('should load settings from .
|
|
22
|
-
await writeSettings(test.projectDir, '
|
|
21
|
+
it('should load settings from .gen directory', async () => {
|
|
22
|
+
await writeSettings(test.projectDir, 'gen', { provider: 'anthropic', model: 'claude-sonnet' });
|
|
23
23
|
|
|
24
24
|
const config = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
25
25
|
|
|
@@ -36,9 +36,9 @@ describe('ConfigManager', () => {
|
|
|
36
36
|
expect(config.settings.model).toBe('gpt-4');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it('should merge both .claude and .
|
|
39
|
+
it('should merge both .claude and .gen with gencode winning', async () => {
|
|
40
40
|
await writeSettings(test.projectDir, 'claude', { provider: 'openai', model: 'gpt-4', theme: 'dark' });
|
|
41
|
-
await writeSettings(test.projectDir, '
|
|
41
|
+
await writeSettings(test.projectDir, 'gen', { provider: 'anthropic' });
|
|
42
42
|
|
|
43
43
|
const config = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
44
44
|
|
|
@@ -51,7 +51,7 @@ describe('ConfigManager', () => {
|
|
|
51
51
|
await writeSettings(test.projectDir, 'claude', {
|
|
52
52
|
permissions: { allow: ['Bash(git:*)'], deny: ['WebFetch'] },
|
|
53
53
|
});
|
|
54
|
-
await writeSettings(test.projectDir, '
|
|
54
|
+
await writeSettings(test.projectDir, 'gen', {
|
|
55
55
|
permissions: { allow: ['Bash(npm:*)'], deny: ['Bash(rm -rf:*)'] },
|
|
56
56
|
});
|
|
57
57
|
|
|
@@ -64,8 +64,8 @@ describe('ConfigManager', () => {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('should load local settings with higher priority', async () => {
|
|
67
|
-
await writeSettings(test.projectDir, '
|
|
68
|
-
await writeSettings(test.projectDir, '
|
|
67
|
+
await writeSettings(test.projectDir, 'gen', { model: 'claude-sonnet', theme: 'light' });
|
|
68
|
+
await writeSettings(test.projectDir, 'gen', { model: 'claude-opus' }, true);
|
|
69
69
|
|
|
70
70
|
const { settings } = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
71
71
|
|
|
@@ -77,7 +77,7 @@ describe('ConfigManager', () => {
|
|
|
77
77
|
const extraDir = path.join(test.tempDir, 'team-config');
|
|
78
78
|
await fs.mkdir(extraDir, { recursive: true });
|
|
79
79
|
await fs.writeFile(path.join(extraDir, 'settings.json'), JSON.stringify({ teamSetting: 'enabled' }));
|
|
80
|
-
process.env.
|
|
80
|
+
process.env.GEN_CONFIG = extraDir;
|
|
81
81
|
|
|
82
82
|
const { settings } = await new ConfigManager({ cwd: test.projectDir }).load();
|
|
83
83
|
|
|
@@ -87,7 +87,7 @@ describe('ConfigManager', () => {
|
|
|
87
87
|
|
|
88
88
|
describe('setCliArgs', () => {
|
|
89
89
|
it('should apply CLI args with highest priority', async () => {
|
|
90
|
-
await writeSettings(test.projectDir, '
|
|
90
|
+
await writeSettings(test.projectDir, 'gen', { model: 'claude-sonnet', provider: 'anthropic' });
|
|
91
91
|
|
|
92
92
|
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
93
93
|
manager.setCliArgs({ model: 'gpt-4o' });
|
|
@@ -107,10 +107,10 @@ describe('ConfigManager', () => {
|
|
|
107
107
|
await manager.saveToLevel({ debug: true }, 'local');
|
|
108
108
|
|
|
109
109
|
const projectContent = JSON.parse(await fs.readFile(
|
|
110
|
-
path.join(test.projectDir, '.
|
|
110
|
+
path.join(test.projectDir, '.gen', 'settings.json'), 'utf-8'
|
|
111
111
|
));
|
|
112
112
|
const localContent = JSON.parse(await fs.readFile(
|
|
113
|
-
path.join(test.projectDir, '.
|
|
113
|
+
path.join(test.projectDir, '.gen', 'settings.local.json'), 'utf-8'
|
|
114
114
|
));
|
|
115
115
|
|
|
116
116
|
expect(projectContent.model).toBe('project-model');
|
|
@@ -118,14 +118,14 @@ describe('ConfigManager', () => {
|
|
|
118
118
|
});
|
|
119
119
|
|
|
120
120
|
it('should merge with existing settings', async () => {
|
|
121
|
-
await writeSettings(test.projectDir, '
|
|
121
|
+
await writeSettings(test.projectDir, 'gen', { model: 'old', theme: 'dark' });
|
|
122
122
|
|
|
123
123
|
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
124
124
|
await manager.load();
|
|
125
125
|
await manager.saveToLevel({ model: 'new' }, 'project');
|
|
126
126
|
|
|
127
127
|
const saved = JSON.parse(await fs.readFile(
|
|
128
|
-
path.join(test.projectDir, '.
|
|
128
|
+
path.join(test.projectDir, '.gen', 'settings.json'), 'utf-8'
|
|
129
129
|
));
|
|
130
130
|
|
|
131
131
|
expect(saved.model).toBe('new');
|
|
@@ -142,7 +142,7 @@ describe('ConfigManager', () => {
|
|
|
142
142
|
await manager.addPermissionRule('Bash(rm:*)', 'deny', 'project');
|
|
143
143
|
|
|
144
144
|
const saved = JSON.parse(await fs.readFile(
|
|
145
|
-
path.join(test.projectDir, '.
|
|
145
|
+
path.join(test.projectDir, '.gen', 'settings.json'), 'utf-8'
|
|
146
146
|
));
|
|
147
147
|
|
|
148
148
|
expect(saved.permissions?.allow).toContain('Bash(npm:*)');
|
|
@@ -152,7 +152,7 @@ describe('ConfigManager', () => {
|
|
|
152
152
|
|
|
153
153
|
describe('getEffectivePermissions', () => {
|
|
154
154
|
it('should return all permission lists', async () => {
|
|
155
|
-
await writeSettings(test.projectDir, '
|
|
155
|
+
await writeSettings(test.projectDir, 'gen', {
|
|
156
156
|
permissions: { allow: ['A'], ask: ['B'], deny: ['C'] },
|
|
157
157
|
});
|
|
158
158
|
|
|
@@ -168,7 +168,7 @@ describe('ConfigManager', () => {
|
|
|
168
168
|
|
|
169
169
|
describe('isAllowed and shouldAsk', () => {
|
|
170
170
|
it('should check permissions correctly', async () => {
|
|
171
|
-
await writeSettings(test.projectDir, '
|
|
171
|
+
await writeSettings(test.projectDir, 'gen', {
|
|
172
172
|
permissions: { allow: ['Bash(git:*)'], deny: ['Bash(rm:*)'], ask: ['WebFetch'] },
|
|
173
173
|
});
|
|
174
174
|
|
|
@@ -189,14 +189,14 @@ describe('ConfigManager', () => {
|
|
|
189
189
|
describe('getSources', () => {
|
|
190
190
|
it('should return all loaded sources', async () => {
|
|
191
191
|
await writeSettings(test.projectDir, 'claude', { model: 'gpt-4' });
|
|
192
|
-
await writeSettings(test.projectDir, '
|
|
192
|
+
await writeSettings(test.projectDir, 'gen', { provider: 'anthropic' });
|
|
193
193
|
|
|
194
194
|
const manager = new ConfigManager({ cwd: test.projectDir });
|
|
195
195
|
await manager.load();
|
|
196
196
|
const sources = manager.getSources();
|
|
197
197
|
|
|
198
198
|
expect(sources.find((s) => s.namespace === 'claude')).toBeDefined();
|
|
199
|
-
expect(sources.find((s) => s.namespace === '
|
|
199
|
+
expect(sources.find((s) => s.namespace === 'gen')).toBeDefined();
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
|
|
@@ -206,7 +206,7 @@ describe('ConfigManager', () => {
|
|
|
206
206
|
|
|
207
207
|
expect(manager.getDebugSummary()).toBe('Configuration not loaded');
|
|
208
208
|
|
|
209
|
-
await writeSettings(test.projectDir, '
|
|
209
|
+
await writeSettings(test.projectDir, 'gen', { model: 'test' });
|
|
210
210
|
await manager.load();
|
|
211
211
|
|
|
212
212
|
expect(manager.getDebugSummary()).toContain('Configuration Sources');
|