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
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Migration Script: .gencode → .gen
|
|
4
|
+
*
|
|
5
|
+
* Migrates GenCode configuration from old naming to new:
|
|
6
|
+
* - ~/.gencode/ → ~/.gen/
|
|
7
|
+
* - ./.gencode/ → ./.gen/
|
|
8
|
+
* - AGENT.md → GEN.md
|
|
9
|
+
* - AGENT.local.md → GEN.local.md
|
|
10
|
+
* - providers.json: Old format → New format (provider:authMethod keys)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* npm run migrate # Run migration
|
|
14
|
+
* tsx scripts/migrate.ts # Direct execution
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import * as fs from 'fs/promises';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
import * as os from 'os';
|
|
20
|
+
import * as readline from 'readline';
|
|
21
|
+
|
|
22
|
+
interface MigrationResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
migratedPaths: string[];
|
|
25
|
+
errors: string[];
|
|
26
|
+
warnings: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if a path exists
|
|
31
|
+
*/
|
|
32
|
+
async function pathExists(p: string): Promise<boolean> {
|
|
33
|
+
try {
|
|
34
|
+
await fs.access(p);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Rename files within a directory (AGENT.md → GEN.md, AGENT.local.md → GEN.local.md)
|
|
43
|
+
*/
|
|
44
|
+
async function renameFilesInDir(dir: string): Promise<string[]> {
|
|
45
|
+
const renamed: string[] = [];
|
|
46
|
+
|
|
47
|
+
// Rename AGENT.md → GEN.md
|
|
48
|
+
const agentPath = path.join(dir, 'AGENT.md');
|
|
49
|
+
const genPath = path.join(dir, 'GEN.md');
|
|
50
|
+
|
|
51
|
+
if (await pathExists(agentPath)) {
|
|
52
|
+
if (await pathExists(genPath)) {
|
|
53
|
+
console.log(` ⚠️ Skipping ${agentPath} (${genPath} already exists)`);
|
|
54
|
+
} else {
|
|
55
|
+
try {
|
|
56
|
+
await fs.rename(agentPath, genPath);
|
|
57
|
+
renamed.push(`${agentPath} → ${genPath}`);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(`Failed to rename ${agentPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Rename AGENT.local.md → GEN.local.md
|
|
65
|
+
const agentLocalPath = path.join(dir, 'AGENT.local.md');
|
|
66
|
+
const genLocalPath = path.join(dir, 'GEN.local.md');
|
|
67
|
+
|
|
68
|
+
if (await pathExists(agentLocalPath)) {
|
|
69
|
+
if (await pathExists(genLocalPath)) {
|
|
70
|
+
console.log(` ⚠️ Skipping ${agentLocalPath} (${genLocalPath} already exists)`);
|
|
71
|
+
} else {
|
|
72
|
+
try {
|
|
73
|
+
await fs.rename(agentLocalPath, genLocalPath);
|
|
74
|
+
renamed.push(`${agentLocalPath} → ${genLocalPath}`);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(`Failed to rename ${agentLocalPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return renamed;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Migrate providers.json from old format to new format
|
|
86
|
+
* Old format: models[provider] = { provider, authMethod, cachedAt, list }
|
|
87
|
+
* New format: models["provider:authMethod"] = { cachedAt, list }
|
|
88
|
+
*/
|
|
89
|
+
async function migrateProvidersJson(dryRun = false): Promise<MigrationResult> {
|
|
90
|
+
const result: MigrationResult = {
|
|
91
|
+
success: true,
|
|
92
|
+
migratedPaths: [],
|
|
93
|
+
errors: [],
|
|
94
|
+
warnings: [],
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const providersPath = path.join(os.homedir(), '.gen', 'providers.json');
|
|
98
|
+
|
|
99
|
+
// Check if providers.json exists
|
|
100
|
+
if (!(await pathExists(providersPath))) {
|
|
101
|
+
return result; // Nothing to migrate
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Read current config
|
|
106
|
+
const content = await fs.readFile(providersPath, 'utf-8');
|
|
107
|
+
const config = JSON.parse(content);
|
|
108
|
+
|
|
109
|
+
// Check if migration is needed
|
|
110
|
+
let needsMigration = false;
|
|
111
|
+
const newModels: Record<string, any> = {};
|
|
112
|
+
|
|
113
|
+
if (config.models) {
|
|
114
|
+
for (const [key, value] of Object.entries(config.models)) {
|
|
115
|
+
// Detect old format: key doesn't contain ':' AND value has provider/authMethod fields
|
|
116
|
+
if (
|
|
117
|
+
!key.includes(':') &&
|
|
118
|
+
typeof value === 'object' &&
|
|
119
|
+
value !== null &&
|
|
120
|
+
'provider' in value &&
|
|
121
|
+
'authMethod' in value
|
|
122
|
+
) {
|
|
123
|
+
needsMigration = true;
|
|
124
|
+
const oldCache = value as any;
|
|
125
|
+
const newKey = `${oldCache.provider}:${oldCache.authMethod}`;
|
|
126
|
+
newModels[newKey] = {
|
|
127
|
+
cachedAt: oldCache.cachedAt,
|
|
128
|
+
list: oldCache.list,
|
|
129
|
+
};
|
|
130
|
+
} else {
|
|
131
|
+
// Already new format
|
|
132
|
+
newModels[key] = value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!needsMigration) {
|
|
138
|
+
return result; // Already in new format
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (dryRun) {
|
|
142
|
+
result.migratedPaths.push(`[DRY RUN] providers.json: Old format → New format (provider:authMethod keys)`);
|
|
143
|
+
const oldKeys = Object.keys(config.models || {}).filter(k => !k.includes(':'));
|
|
144
|
+
const newKeys = Object.keys(newModels).filter(k => k.includes(':'));
|
|
145
|
+
result.migratedPaths.push(` Old keys: ${oldKeys.join(', ') || 'none'}`);
|
|
146
|
+
result.migratedPaths.push(` New keys: ${newKeys.join(', ') || 'none'}`);
|
|
147
|
+
} else {
|
|
148
|
+
// Backup old config
|
|
149
|
+
const backupPath = `${providersPath}.backup`;
|
|
150
|
+
await fs.copyFile(providersPath, backupPath);
|
|
151
|
+
|
|
152
|
+
// Update config with new format
|
|
153
|
+
config.models = newModels;
|
|
154
|
+
|
|
155
|
+
// Also update connections to ensure authMethod is set
|
|
156
|
+
if (config.connections) {
|
|
157
|
+
for (const [provider, connection] of Object.entries(config.connections)) {
|
|
158
|
+
if (typeof connection === 'object' && connection !== null && !('authMethod' in connection)) {
|
|
159
|
+
// Try to infer authMethod from connection method name
|
|
160
|
+
const method = (connection as any).method;
|
|
161
|
+
if (method?.includes('Vertex')) {
|
|
162
|
+
(connection as any).authMethod = 'vertex';
|
|
163
|
+
} else if (method?.includes('Bedrock')) {
|
|
164
|
+
(connection as any).authMethod = 'bedrock';
|
|
165
|
+
} else if (method?.includes('Azure')) {
|
|
166
|
+
(connection as any).authMethod = 'azure';
|
|
167
|
+
} else {
|
|
168
|
+
(connection as any).authMethod = 'api_key';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Write new config
|
|
175
|
+
await fs.writeFile(providersPath, JSON.stringify(config, null, 2));
|
|
176
|
+
|
|
177
|
+
result.migratedPaths.push(`providers.json: Migrated to new format`);
|
|
178
|
+
result.migratedPaths.push(` Backup saved: ${backupPath}`);
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
result.errors.push(`Failed to migrate providers.json: ${error instanceof Error ? error.message : String(error)}`);
|
|
182
|
+
result.success = false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Migrate .gencode to .gen
|
|
190
|
+
*/
|
|
191
|
+
async function migrateToGen(dryRun = false): Promise<MigrationResult> {
|
|
192
|
+
const result: MigrationResult = {
|
|
193
|
+
success: true,
|
|
194
|
+
migratedPaths: [],
|
|
195
|
+
errors: [],
|
|
196
|
+
warnings: [],
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// 1. Migrate user directory (~/.gencode → ~/.gen)
|
|
200
|
+
const homeGencodePath = path.join(os.homedir(), '.gencode');
|
|
201
|
+
const homeGenPath = path.join(os.homedir(), '.gen');
|
|
202
|
+
|
|
203
|
+
if (await pathExists(homeGencodePath)) {
|
|
204
|
+
if (await pathExists(homeGenPath)) {
|
|
205
|
+
result.errors.push(`~/.gen already exists! Please manually merge ~/.gencode into it.`);
|
|
206
|
+
result.success = false;
|
|
207
|
+
} else {
|
|
208
|
+
if (!dryRun) {
|
|
209
|
+
try {
|
|
210
|
+
await fs.rename(homeGencodePath, homeGenPath);
|
|
211
|
+
const renamedFiles = await renameFilesInDir(homeGenPath);
|
|
212
|
+
result.migratedPaths.push(`${homeGencodePath} → ${homeGenPath}`);
|
|
213
|
+
result.migratedPaths.push(...renamedFiles);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
result.errors.push(`Failed to migrate ${homeGencodePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
216
|
+
result.success = false;
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
result.migratedPaths.push(`[DRY RUN] ${homeGencodePath} → ${homeGenPath}`);
|
|
220
|
+
result.migratedPaths.push(`[DRY RUN] Will rename AGENT.md → GEN.md in ${homeGenPath}`);
|
|
221
|
+
result.migratedPaths.push(`[DRY RUN] Will rename AGENT.local.md → GEN.local.md in ${homeGenPath}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 2. Migrate project directory (./.gencode → ./.gen)
|
|
227
|
+
const cwd = process.cwd();
|
|
228
|
+
const projectGencodePath = path.join(cwd, '.gencode');
|
|
229
|
+
const projectGenPath = path.join(cwd, '.gen');
|
|
230
|
+
|
|
231
|
+
if (await pathExists(projectGencodePath)) {
|
|
232
|
+
if (await pathExists(projectGenPath)) {
|
|
233
|
+
result.errors.push(`./.gen already exists! Please manually merge ./.gencode into it.`);
|
|
234
|
+
result.success = false;
|
|
235
|
+
} else {
|
|
236
|
+
if (!dryRun) {
|
|
237
|
+
try {
|
|
238
|
+
await fs.rename(projectGencodePath, projectGenPath);
|
|
239
|
+
const renamedFiles = await renameFilesInDir(projectGenPath);
|
|
240
|
+
result.migratedPaths.push(`${projectGencodePath} → ${projectGenPath}`);
|
|
241
|
+
result.migratedPaths.push(...renamedFiles);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
result.errors.push(`Failed to migrate ${projectGencodePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
244
|
+
result.success = false;
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
result.migratedPaths.push(`[DRY RUN] ${projectGencodePath} → ${projectGenPath}`);
|
|
248
|
+
result.migratedPaths.push(`[DRY RUN] Will rename AGENT.md → GEN.md in ${projectGenPath}`);
|
|
249
|
+
result.migratedPaths.push(`[DRY RUN] Will rename AGENT.local.md → GEN.local.md in ${projectGenPath}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 3. Migrate root-level files (./AGENT.md → ./GEN.md)
|
|
255
|
+
const rootAgentPath = path.join(cwd, 'AGENT.md');
|
|
256
|
+
const rootGenPath = path.join(cwd, 'GEN.md');
|
|
257
|
+
|
|
258
|
+
if (await pathExists(rootAgentPath)) {
|
|
259
|
+
if (await pathExists(rootGenPath)) {
|
|
260
|
+
result.warnings.push(`./GEN.md already exists! Skipping ./AGENT.md migration.`);
|
|
261
|
+
} else {
|
|
262
|
+
if (!dryRun) {
|
|
263
|
+
try {
|
|
264
|
+
await fs.rename(rootAgentPath, rootGenPath);
|
|
265
|
+
result.migratedPaths.push(`${rootAgentPath} → ${rootGenPath}`);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
result.errors.push(`Failed to migrate ${rootAgentPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
268
|
+
result.success = false;
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
result.migratedPaths.push(`[DRY RUN] ${rootAgentPath} → ${rootGenPath}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 4. Migrate root-level local files (./AGENT.local.md → ./GEN.local.md)
|
|
277
|
+
const rootAgentLocalPath = path.join(cwd, 'AGENT.local.md');
|
|
278
|
+
const rootGenLocalPath = path.join(cwd, 'GEN.local.md');
|
|
279
|
+
|
|
280
|
+
if (await pathExists(rootAgentLocalPath)) {
|
|
281
|
+
if (await pathExists(rootGenLocalPath)) {
|
|
282
|
+
result.warnings.push(`./GEN.local.md already exists! Skipping ./AGENT.local.md migration.`);
|
|
283
|
+
} else {
|
|
284
|
+
if (!dryRun) {
|
|
285
|
+
try {
|
|
286
|
+
await fs.rename(rootAgentLocalPath, rootGenLocalPath);
|
|
287
|
+
result.migratedPaths.push(`${rootAgentLocalPath} → ${rootGenLocalPath}`);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
result.errors.push(`Failed to migrate ${rootAgentLocalPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
290
|
+
result.success = false;
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
result.migratedPaths.push(`[DRY RUN] ${rootAgentLocalPath} → ${rootGenLocalPath}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// If no migrations needed
|
|
299
|
+
if (result.migratedPaths.length === 0 && result.errors.length === 0) {
|
|
300
|
+
result.warnings.push('No .gencode directories or AGENT.md files found. Already using .gen?');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Check if migration is needed
|
|
308
|
+
*/
|
|
309
|
+
async function needsMigration(): Promise<boolean> {
|
|
310
|
+
const homeGencodePath = path.join(os.homedir(), '.gencode');
|
|
311
|
+
const projectGencodePath = path.join(process.cwd(), '.gencode');
|
|
312
|
+
const rootAgentPath = path.join(process.cwd(), 'AGENT.md');
|
|
313
|
+
const rootAgentLocalPath = path.join(process.cwd(), 'AGENT.local.md');
|
|
314
|
+
|
|
315
|
+
return (
|
|
316
|
+
(await pathExists(homeGencodePath)) ||
|
|
317
|
+
(await pathExists(projectGencodePath)) ||
|
|
318
|
+
(await pathExists(rootAgentPath)) ||
|
|
319
|
+
(await pathExists(rootAgentLocalPath))
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Prompt user for confirmation
|
|
325
|
+
*/
|
|
326
|
+
async function confirm(message: string): Promise<boolean> {
|
|
327
|
+
const rl = readline.createInterface({
|
|
328
|
+
input: process.stdin,
|
|
329
|
+
output: process.stdout,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
return new Promise((resolve) => {
|
|
333
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
334
|
+
rl.close();
|
|
335
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Main migration flow
|
|
342
|
+
*/
|
|
343
|
+
async function main() {
|
|
344
|
+
console.log('🔄 GenCode Migration: .gencode → .gen\n');
|
|
345
|
+
|
|
346
|
+
// Check if migration is needed
|
|
347
|
+
const needs = await needsMigration();
|
|
348
|
+
if (!needs) {
|
|
349
|
+
console.log('✅ No .gencode directories or AGENT.md files found.');
|
|
350
|
+
console.log(' Already using .gen? Nothing to migrate.\n');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Always check providers.json migration (even if .gencode migration not needed)
|
|
354
|
+
console.log('📋 Scanning for files to migrate...\n');
|
|
355
|
+
|
|
356
|
+
// Perform dry run for providers.json
|
|
357
|
+
const providersResult = await migrateProvidersJson(true);
|
|
358
|
+
|
|
359
|
+
// Perform dry run for .gencode
|
|
360
|
+
const dryRunResult = await migrateToGen(true);
|
|
361
|
+
|
|
362
|
+
// Merge results
|
|
363
|
+
const allMigrations = [...providersResult.migratedPaths, ...dryRunResult.migratedPaths];
|
|
364
|
+
const allErrors = [...providersResult.errors, ...dryRunResult.errors];
|
|
365
|
+
const allWarnings = [...providersResult.warnings, ...dryRunResult.warnings];
|
|
366
|
+
|
|
367
|
+
if (allMigrations.length === 0 && allErrors.length === 0) {
|
|
368
|
+
console.log('✅ All configurations are up to date. Nothing to migrate.\n');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (allMigrations.length > 0) {
|
|
373
|
+
console.log('📦 Migration Plan:\n');
|
|
374
|
+
for (const path of allMigrations) {
|
|
375
|
+
console.log(` ✓ ${path}`);
|
|
376
|
+
}
|
|
377
|
+
console.log('');
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (allErrors.length > 0) {
|
|
381
|
+
console.log('❌ Errors:\n');
|
|
382
|
+
for (const error of allErrors) {
|
|
383
|
+
console.log(` ✗ ${error}`);
|
|
384
|
+
}
|
|
385
|
+
console.log('\n⚠️ Migration aborted. Please resolve conflicts manually.\n');
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (allWarnings.length > 0) {
|
|
390
|
+
console.log('⚠️ Warnings:\n');
|
|
391
|
+
for (const warning of allWarnings) {
|
|
392
|
+
console.log(` • ${warning}`);
|
|
393
|
+
}
|
|
394
|
+
console.log('');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Ask for confirmation
|
|
398
|
+
const confirmed = await confirm('Proceed with migration?');
|
|
399
|
+
if (!confirmed) {
|
|
400
|
+
console.log('\n❌ Migration cancelled.\n');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Execute migration
|
|
405
|
+
console.log('\n🚀 Executing migration...\n');
|
|
406
|
+
|
|
407
|
+
// Migrate providers.json first
|
|
408
|
+
const providersExecResult = await migrateProvidersJson(false);
|
|
409
|
+
|
|
410
|
+
// Then migrate .gencode
|
|
411
|
+
const result = await migrateToGen(false);
|
|
412
|
+
|
|
413
|
+
const allSuccess = providersExecResult.success && result.success;
|
|
414
|
+
const allMigratedPaths = [...providersExecResult.migratedPaths, ...result.migratedPaths];
|
|
415
|
+
const allExecErrors = [...providersExecResult.errors, ...result.errors];
|
|
416
|
+
|
|
417
|
+
if (allSuccess) {
|
|
418
|
+
console.log('✅ Migration completed successfully!\n');
|
|
419
|
+
if (allMigratedPaths.length > 0) {
|
|
420
|
+
console.log('Migrated paths:\n');
|
|
421
|
+
for (const path of allMigratedPaths) {
|
|
422
|
+
console.log(` ✓ ${path}`);
|
|
423
|
+
}
|
|
424
|
+
console.log('');
|
|
425
|
+
}
|
|
426
|
+
console.log('📝 Next steps:\n');
|
|
427
|
+
console.log(' 1. Update environment variables: GENCODE_* → GEN_*');
|
|
428
|
+
console.log(' - GENCODE_PROVIDER → GEN_PROVIDER');
|
|
429
|
+
console.log(' - GENCODE_MODEL → GEN_MODEL');
|
|
430
|
+
console.log(' - GENCODE_CONFIG_DIRS → GEN_CONFIG\n');
|
|
431
|
+
console.log(' 2. Update any scripts or CI/CD configs\n');
|
|
432
|
+
console.log(' 3. Restart GenCode to load from .gen directories\n');
|
|
433
|
+
} else {
|
|
434
|
+
console.log('❌ Migration failed!\n');
|
|
435
|
+
if (allExecErrors.length > 0) {
|
|
436
|
+
console.log('Errors:\n');
|
|
437
|
+
for (const error of allExecErrors) {
|
|
438
|
+
console.log(` ✗ ${error}`);
|
|
439
|
+
}
|
|
440
|
+
console.log('');
|
|
441
|
+
}
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
main().catch((error) => {
|
|
447
|
+
console.error('❌ Unexpected error:', error);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
});
|
package/src/agent/agent.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Agent - Core agent implementation with tool loop and session support
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { LLMProvider, Message, ToolResultContent } from '../providers/types.js';
|
|
6
|
-
import { createProvider, inferProvider } from '../providers/index.js';
|
|
5
|
+
import type { LLMProvider, Message, ToolResultContent, Provider, AuthMethod } from '../providers/types.js';
|
|
6
|
+
import { createProvider, inferProvider, inferAuthMethod } from '../providers/index.js';
|
|
7
7
|
import { ToolRegistry, createDefaultRegistry } from '../tools/index.js';
|
|
8
8
|
import {
|
|
9
9
|
PermissionManager,
|
|
@@ -47,7 +47,10 @@ export class Agent {
|
|
|
47
47
|
...config,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
this.provider = createProvider({
|
|
50
|
+
this.provider = createProvider({
|
|
51
|
+
provider: config.provider,
|
|
52
|
+
authMethod: config.authMethod,
|
|
53
|
+
});
|
|
51
54
|
this.registry = createDefaultRegistry();
|
|
52
55
|
this.permissions = new PermissionManager({
|
|
53
56
|
config: config.permissions,
|
|
@@ -141,7 +144,24 @@ export class Agent {
|
|
|
141
144
|
*/
|
|
142
145
|
async loadMemory(): Promise<LoadedMemory> {
|
|
143
146
|
const cwd = this.config.cwd ?? process.cwd();
|
|
144
|
-
|
|
147
|
+
|
|
148
|
+
// Determine memory merge strategy (priority: env var > config > default)
|
|
149
|
+
const envStrategy = process.env.GEN_MEMORY_STRATEGY as
|
|
150
|
+
| 'fallback'
|
|
151
|
+
| 'both'
|
|
152
|
+
| 'gen-only'
|
|
153
|
+
| 'claude-only'
|
|
154
|
+
| undefined;
|
|
155
|
+
const strategy =
|
|
156
|
+
envStrategy ?? this.config.memoryMergeStrategy ?? 'fallback';
|
|
157
|
+
|
|
158
|
+
this.loadedMemory = await this.memoryManager.load({ cwd, strategy });
|
|
159
|
+
|
|
160
|
+
// Log verbose summary if verbose mode is enabled
|
|
161
|
+
if (this.config.verbose) {
|
|
162
|
+
console.log(this.memoryManager.getVerboseSummary(strategy));
|
|
163
|
+
}
|
|
164
|
+
|
|
145
165
|
return this.loadedMemory;
|
|
146
166
|
}
|
|
147
167
|
|
|
@@ -233,15 +253,30 @@ export class Agent {
|
|
|
233
253
|
|
|
234
254
|
/**
|
|
235
255
|
* Set the model to use (auto-switches provider if needed)
|
|
256
|
+
* @param model Model ID to use
|
|
257
|
+
* @param provider Optional: explicit provider (otherwise inferred from model name)
|
|
258
|
+
* @param authMethod Optional: explicit auth method (otherwise inferred or use current)
|
|
236
259
|
*/
|
|
237
|
-
setModel(model: string): void {
|
|
260
|
+
setModel(model: string, provider?: string, authMethod?: string): void {
|
|
238
261
|
this.config.model = model;
|
|
239
262
|
|
|
240
|
-
//
|
|
241
|
-
const newProvider = inferProvider(model);
|
|
242
|
-
|
|
263
|
+
// Determine new provider and authMethod
|
|
264
|
+
const newProvider = (provider as Provider | undefined) ?? inferProvider(model);
|
|
265
|
+
const newAuthMethod = (authMethod as AuthMethod | undefined) ??
|
|
266
|
+
inferAuthMethod(model) ??
|
|
267
|
+
this.config.authMethod;
|
|
268
|
+
|
|
269
|
+
// Recreate provider if either provider or authMethod changed
|
|
270
|
+
const providerChanged = newProvider !== this.config.provider;
|
|
271
|
+
const authMethodChanged = newAuthMethod !== this.config.authMethod;
|
|
272
|
+
|
|
273
|
+
if (providerChanged || authMethodChanged) {
|
|
243
274
|
this.config.provider = newProvider;
|
|
244
|
-
this.
|
|
275
|
+
this.config.authMethod = newAuthMethod;
|
|
276
|
+
this.provider = createProvider({
|
|
277
|
+
provider: newProvider,
|
|
278
|
+
authMethod: newAuthMethod,
|
|
279
|
+
});
|
|
245
280
|
}
|
|
246
281
|
}
|
|
247
282
|
|
|
@@ -252,6 +287,13 @@ export class Agent {
|
|
|
252
287
|
return this.config.model;
|
|
253
288
|
}
|
|
254
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Get current provider
|
|
292
|
+
*/
|
|
293
|
+
getProvider(): Provider {
|
|
294
|
+
return this.config.provider;
|
|
295
|
+
}
|
|
296
|
+
|
|
255
297
|
/**
|
|
256
298
|
* List available models from the provider API
|
|
257
299
|
*/
|
package/src/agent/types.ts
CHANGED
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import type { PermissionConfig } from '../permissions/types.js';
|
|
6
6
|
import type { CostEstimate } from '../pricing/types.js';
|
|
7
|
+
import type { Provider, AuthMethod } from '../providers/types.js';
|
|
7
8
|
|
|
8
9
|
export interface AgentConfig {
|
|
9
|
-
provider:
|
|
10
|
+
provider: Provider;
|
|
11
|
+
authMethod?: AuthMethod;
|
|
10
12
|
model: string;
|
|
11
13
|
systemPrompt?: string;
|
|
12
14
|
tools?: string[];
|
|
13
15
|
cwd?: string;
|
|
14
16
|
maxTurns?: number;
|
|
15
17
|
permissions?: Partial<PermissionConfig>;
|
|
18
|
+
memoryMergeStrategy?: 'fallback' | 'both' | 'gen-only' | 'claude-only';
|
|
19
|
+
verbose?: boolean;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
// Agent Events
|
|
@@ -133,7 +133,7 @@ function HelpPanel() {
|
|
|
133
133
|
['/new', 'New session'],
|
|
134
134
|
['/save', 'Save session'],
|
|
135
135
|
['/clear', 'Clear chat'],
|
|
136
|
-
['/init', 'Generate
|
|
136
|
+
['/init', 'Generate GEN.md'],
|
|
137
137
|
['/memory', 'Show memory files'],
|
|
138
138
|
['/changes', 'List file changes'],
|
|
139
139
|
['/rewind [n|all]', 'Undo file changes'],
|
|
@@ -266,6 +266,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
266
266
|
const [showModelSelector, setShowModelSelector] = useState(false);
|
|
267
267
|
const [showProviderManager, setShowProviderManager] = useState(false);
|
|
268
268
|
const [currentModel, setCurrentModel] = useState(config.model);
|
|
269
|
+
const [currentProvider, setCurrentProvider] = useState(config.provider);
|
|
269
270
|
const [cmdSuggestionIndex, setCmdSuggestionIndex] = useState(0);
|
|
270
271
|
const [inputKey, setInputKey] = useState(0); // Force cursor to end after autocomplete
|
|
271
272
|
const [pendingTool, setPendingTool] = useState<{ name: string; input: Record<string, unknown> } | null>(null);
|
|
@@ -380,9 +381,14 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
380
381
|
};
|
|
381
382
|
|
|
382
383
|
// Handle model selection
|
|
383
|
-
const handleModelSelect = async (model: string, providerId?: ProviderName) => {
|
|
384
|
-
agent.setModel(model);
|
|
384
|
+
const handleModelSelect = async (model: string, providerId?: ProviderName, authMethod?: string) => {
|
|
385
|
+
agent.setModel(model, providerId, authMethod);
|
|
385
386
|
setCurrentModel(model);
|
|
387
|
+
|
|
388
|
+
// Update provider state to keep UI in sync
|
|
389
|
+
const newProvider = agent.getProvider();
|
|
390
|
+
setCurrentProvider(newProvider);
|
|
391
|
+
|
|
386
392
|
setShowModelSelector(false);
|
|
387
393
|
|
|
388
394
|
if (providerId) {
|
|
@@ -477,6 +483,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
477
483
|
// Direct model switch: /model gpt-4o
|
|
478
484
|
agent.setModel(arg);
|
|
479
485
|
setCurrentModel(arg);
|
|
486
|
+
setCurrentProvider(agent.getProvider());
|
|
480
487
|
addHistory({ type: 'info', content: `Model: ${arg}` });
|
|
481
488
|
} else {
|
|
482
489
|
// Show interactive model selector
|
|
@@ -540,13 +547,13 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
540
547
|
}
|
|
541
548
|
|
|
542
549
|
case 'init': {
|
|
543
|
-
// Gather context files and generate
|
|
550
|
+
// Gather context files and generate GEN.md
|
|
544
551
|
addHistory({ type: 'info', content: 'Analyzing codebase...' });
|
|
545
552
|
|
|
546
553
|
const contextFiles = await gatherContextFiles(cwd);
|
|
547
554
|
addHistory({ type: 'info', content: getContextSummary(contextFiles) });
|
|
548
555
|
|
|
549
|
-
// Check if
|
|
556
|
+
// Check if GEN.md already exists
|
|
550
557
|
const memoryManager = agent.getMemoryManager();
|
|
551
558
|
const existingPath = await memoryManager.getExistingProjectMemoryPath(cwd);
|
|
552
559
|
let existingContent: string | undefined;
|
|
@@ -566,7 +573,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
566
573
|
|
|
567
574
|
// Build init prompt and run through agent
|
|
568
575
|
const initPrompt = buildInitPrompt(contextFiles, existingContent);
|
|
569
|
-
addHistory({ type: 'info', content: 'Generating
|
|
576
|
+
addHistory({ type: 'info', content: 'Generating GEN.md...' });
|
|
570
577
|
addHistory({ type: 'user', content: '/init' });
|
|
571
578
|
await runAgent(initPrompt);
|
|
572
579
|
return true;
|
|
@@ -850,8 +857,8 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
850
857
|
}
|
|
851
858
|
|
|
852
859
|
// Handle # prefix for quick memory adds
|
|
853
|
-
// ## note -> user memory (~/.
|
|
854
|
-
// # note -> project memory (./
|
|
860
|
+
// ## note -> user memory (~/.gen/GEN.md)
|
|
861
|
+
// # note -> project memory (./GEN.md)
|
|
855
862
|
if (trimmed.startsWith('#') && !trimmed.startsWith('#!/')) {
|
|
856
863
|
const memoryManager = agent.getMemoryManager();
|
|
857
864
|
let level: 'user' | 'project';
|
|
@@ -1082,6 +1089,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
1082
1089
|
<Box marginTop={1}>
|
|
1083
1090
|
<ModelSelector
|
|
1084
1091
|
currentModel={currentModel}
|
|
1092
|
+
currentProvider={currentProvider}
|
|
1085
1093
|
onSelect={handleModelSelect}
|
|
1086
1094
|
onCancel={handleModelCancel}
|
|
1087
1095
|
listModels={() => agent.listModels()}
|
|
@@ -1096,6 +1104,7 @@ export function App({ config, settingsManager, resumeLatest, permissionSettings
|
|
|
1096
1104
|
onProviderChange={(providerId, model) => {
|
|
1097
1105
|
agent.setModel(model);
|
|
1098
1106
|
setCurrentModel(model);
|
|
1107
|
+
setCurrentProvider(agent.getProvider());
|
|
1099
1108
|
addHistory({ type: 'info', content: `Switched to ${providerId}: ${model}` });
|
|
1100
1109
|
}}
|
|
1101
1110
|
/>
|
|
@@ -288,7 +288,7 @@ export function InfoMessage({ text, type = 'info' }: InfoMessageProps) {
|
|
|
288
288
|
const { color, icon } = config[type];
|
|
289
289
|
|
|
290
290
|
return (
|
|
291
|
-
<Box>
|
|
291
|
+
<Box marginTop={1}>
|
|
292
292
|
<Text color={color}>{icon} </Text>
|
|
293
293
|
<Text color={colors.textSecondary}>{text}</Text>
|
|
294
294
|
</Box>
|