popeye-cli 1.0.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/.env.example +25 -0
- package/.prettierrc +8 -0
- package/README.md +320 -0
- package/dist/adapters/claude.d.ts +82 -0
- package/dist/adapters/claude.d.ts.map +1 -0
- package/dist/adapters/claude.js +230 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/openai.d.ts +48 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +257 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/auth/claude.d.ts +44 -0
- package/dist/auth/claude.d.ts.map +1 -0
- package/dist/auth/claude.js +139 -0
- package/dist/auth/claude.js.map +1 -0
- package/dist/auth/index.d.ts +61 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +141 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +66 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +125 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/openai-entry.d.ts +9 -0
- package/dist/auth/openai-entry.d.ts.map +1 -0
- package/dist/auth/openai-entry.js +410 -0
- package/dist/auth/openai-entry.js.map +1 -0
- package/dist/auth/openai.d.ts +71 -0
- package/dist/auth/openai.d.ts.map +1 -0
- package/dist/auth/openai.js +212 -0
- package/dist/auth/openai.js.map +1 -0
- package/dist/auth/server.d.ts +32 -0
- package/dist/auth/server.d.ts.map +1 -0
- package/dist/auth/server.js +213 -0
- package/dist/auth/server.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +10 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +162 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/config.d.ts +10 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +215 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/create.d.ts +10 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +240 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/index.d.ts +10 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +10 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +18 -0
- package/dist/cli/commands/resume.d.ts.map +1 -0
- package/dist/cli/commands/resume.js +241 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/status.d.ts +18 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +154 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +71 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +9 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +330 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli/output.d.ts +182 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +355 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/config/defaults.d.ts +57 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +103 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +138 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +244 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/schema.d.ts +220 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +141 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/generators/index.d.ts +101 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +200 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/generators/python.d.ts +48 -0
- package/dist/generators/python.d.ts.map +1 -0
- package/dist/generators/python.js +262 -0
- package/dist/generators/python.js.map +1 -0
- package/dist/generators/templates/index.d.ts +6 -0
- package/dist/generators/templates/index.d.ts.map +1 -0
- package/dist/generators/templates/index.js +6 -0
- package/dist/generators/templates/index.js.map +1 -0
- package/dist/generators/templates/python.d.ts +53 -0
- package/dist/generators/templates/python.d.ts.map +1 -0
- package/dist/generators/templates/python.js +454 -0
- package/dist/generators/templates/python.js.map +1 -0
- package/dist/generators/templates/typescript.d.ts +53 -0
- package/dist/generators/templates/typescript.d.ts.map +1 -0
- package/dist/generators/templates/typescript.js +394 -0
- package/dist/generators/templates/typescript.js.map +1 -0
- package/dist/generators/typescript.d.ts +64 -0
- package/dist/generators/typescript.d.ts.map +1 -0
- package/dist/generators/typescript.js +271 -0
- package/dist/generators/typescript.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/state/index.d.ts +168 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +338 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/persistence.d.ts +91 -0
- package/dist/state/persistence.d.ts.map +1 -0
- package/dist/state/persistence.js +201 -0
- package/dist/state/persistence.js.map +1 -0
- package/dist/types/cli.d.ts +132 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +17 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/consensus.d.ts +111 -0
- package/dist/types/consensus.d.ts.map +1 -0
- package/dist/types/consensus.js +29 -0
- package/dist/types/consensus.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/project.d.ts +73 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +55 -0
- package/dist/types/project.js.map +1 -0
- package/dist/types/workflow.d.ts +236 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +74 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/workflow/consensus.d.ts +89 -0
- package/dist/workflow/consensus.d.ts.map +1 -0
- package/dist/workflow/consensus.js +220 -0
- package/dist/workflow/consensus.js.map +1 -0
- package/dist/workflow/execution-mode.d.ts +82 -0
- package/dist/workflow/execution-mode.d.ts.map +1 -0
- package/dist/workflow/execution-mode.js +346 -0
- package/dist/workflow/execution-mode.js.map +1 -0
- package/dist/workflow/index.d.ts +110 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +283 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +83 -0
- package/dist/workflow/plan-mode.d.ts.map +1 -0
- package/dist/workflow/plan-mode.js +241 -0
- package/dist/workflow/plan-mode.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +87 -0
- package/dist/workflow/test-runner.d.ts.map +1 -0
- package/dist/workflow/test-runner.js +273 -0
- package/dist/workflow/test-runner.js.map +1 -0
- package/eslint.config.js +25 -0
- package/package.json +66 -0
- package/src/adapters/claude.ts +298 -0
- package/src/adapters/openai.ts +300 -0
- package/src/auth/claude.ts +166 -0
- package/src/auth/index.ts +171 -0
- package/src/auth/keychain.ts +138 -0
- package/src/auth/openai-entry.ts +410 -0
- package/src/auth/openai.ts +260 -0
- package/src/auth/server.ts +252 -0
- package/src/cli/commands/auth.ts +194 -0
- package/src/cli/commands/config.ts +241 -0
- package/src/cli/commands/create.ts +308 -0
- package/src/cli/commands/index.ts +10 -0
- package/src/cli/commands/resume.ts +304 -0
- package/src/cli/commands/status.ts +189 -0
- package/src/cli/index.ts +90 -0
- package/src/cli/interactive.ts +418 -0
- package/src/cli/output.ts +410 -0
- package/src/config/defaults.ts +114 -0
- package/src/config/index.ts +315 -0
- package/src/config/schema.ts +164 -0
- package/src/generators/index.ts +251 -0
- package/src/generators/python.ts +318 -0
- package/src/generators/templates/index.ts +6 -0
- package/src/generators/templates/python.ts +465 -0
- package/src/generators/templates/typescript.ts +417 -0
- package/src/generators/typescript.ts +340 -0
- package/src/index.ts +13 -0
- package/src/state/index.ts +454 -0
- package/src/state/persistence.ts +230 -0
- package/src/types/cli.ts +146 -0
- package/src/types/consensus.ts +116 -0
- package/src/types/index.ts +64 -0
- package/src/types/project.ts +85 -0
- package/src/types/workflow.ts +149 -0
- package/src/workflow/consensus.ts +299 -0
- package/src/workflow/execution-mode.ts +517 -0
- package/src/workflow/index.ts +396 -0
- package/src/workflow/plan-mode.ts +356 -0
- package/src/workflow/test-runner.ts +345 -0
- package/tests/adapters/openai.test.ts +145 -0
- package/tests/config/config.test.ts +208 -0
- package/tests/generators/generators.test.ts +185 -0
- package/tests/types/consensus.test.ts +152 -0
- package/tests/types/project.test.ts +134 -0
- package/tests/workflow/consensus.test.ts +221 -0
- package/tests/workflow/test-runner.test.ts +214 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management module
|
|
3
|
+
* Handles loading, merging, and validating configuration from multiple sources
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { cosmiconfig } from 'cosmiconfig';
|
|
7
|
+
import { parse as parseYaml } from 'yaml';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { ConfigSchema, type Config } from './schema.js';
|
|
12
|
+
import {
|
|
13
|
+
DEFAULT_CONFIG,
|
|
14
|
+
GLOBAL_CONFIG_DIR,
|
|
15
|
+
CONFIG_FILE_NAME,
|
|
16
|
+
ENV_VARS,
|
|
17
|
+
} from './defaults.js';
|
|
18
|
+
|
|
19
|
+
// Re-export schema types
|
|
20
|
+
export * from './schema.js';
|
|
21
|
+
export * from './defaults.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Configuration loader using cosmiconfig
|
|
25
|
+
*/
|
|
26
|
+
const explorer = cosmiconfig('popeye', {
|
|
27
|
+
searchPlaces: [
|
|
28
|
+
'popeye.config.yaml',
|
|
29
|
+
'popeye.config.yml',
|
|
30
|
+
'.popeyerc.yaml',
|
|
31
|
+
'.popeyerc.yml',
|
|
32
|
+
'.popeyerc',
|
|
33
|
+
'.popeye/config.yaml',
|
|
34
|
+
'.popeye/config.yml',
|
|
35
|
+
],
|
|
36
|
+
loaders: {
|
|
37
|
+
'.yaml': (_filepath: string, content: string) => parseYaml(content),
|
|
38
|
+
'.yml': (_filepath: string, content: string) => parseYaml(content),
|
|
39
|
+
noExt: (_filepath: string, content: string) => parseYaml(content),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load global configuration from ~/.popeye/config.yaml
|
|
45
|
+
*/
|
|
46
|
+
async function loadGlobalConfig(): Promise<Partial<Config>> {
|
|
47
|
+
const globalConfigPath = path.join(homedir(), GLOBAL_CONFIG_DIR, CONFIG_FILE_NAME);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const content = await fs.readFile(globalConfigPath, 'utf-8');
|
|
51
|
+
const parsed = parseYaml(content);
|
|
52
|
+
return parsed || {};
|
|
53
|
+
} catch {
|
|
54
|
+
// Global config doesn't exist, return empty
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Load project-specific configuration
|
|
61
|
+
*/
|
|
62
|
+
async function loadProjectConfig(cwd?: string): Promise<Partial<Config>> {
|
|
63
|
+
try {
|
|
64
|
+
const result = await explorer.search(cwd);
|
|
65
|
+
if (result && !result.isEmpty) {
|
|
66
|
+
return result.config;
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Project config doesn't exist or is invalid
|
|
70
|
+
}
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Load configuration from environment variables
|
|
76
|
+
*/
|
|
77
|
+
function loadEnvConfig(): Partial<Config> {
|
|
78
|
+
const config: Partial<Config> = {};
|
|
79
|
+
|
|
80
|
+
// OpenAI model
|
|
81
|
+
const openaiModel = process.env[ENV_VARS.OPENAI_MODEL];
|
|
82
|
+
if (openaiModel) {
|
|
83
|
+
config.apis = {
|
|
84
|
+
openai: {
|
|
85
|
+
...DEFAULT_CONFIG.apis.openai,
|
|
86
|
+
model: openaiModel as Config['apis']['openai']['model'],
|
|
87
|
+
},
|
|
88
|
+
claude: DEFAULT_CONFIG.apis.claude,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Default language
|
|
93
|
+
const defaultLanguage = process.env[ENV_VARS.DEFAULT_LANGUAGE];
|
|
94
|
+
if (defaultLanguage && (defaultLanguage === 'python' || defaultLanguage === 'typescript')) {
|
|
95
|
+
config.project = {
|
|
96
|
+
...DEFAULT_CONFIG.project,
|
|
97
|
+
default_language: defaultLanguage,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Consensus threshold
|
|
102
|
+
const threshold = process.env[ENV_VARS.CONSENSUS_THRESHOLD];
|
|
103
|
+
if (threshold) {
|
|
104
|
+
const parsed = parseInt(threshold, 10);
|
|
105
|
+
if (!isNaN(parsed) && parsed >= 0 && parsed <= 100) {
|
|
106
|
+
config.consensus = {
|
|
107
|
+
...DEFAULT_CONFIG.consensus,
|
|
108
|
+
...(config.consensus || {}),
|
|
109
|
+
threshold: parsed,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Max disagreements
|
|
115
|
+
const maxDisagreements = process.env[ENV_VARS.MAX_DISAGREEMENTS];
|
|
116
|
+
if (maxDisagreements) {
|
|
117
|
+
const parsed = parseInt(maxDisagreements, 10);
|
|
118
|
+
if (!isNaN(parsed) && parsed >= 1 && parsed <= 10) {
|
|
119
|
+
config.consensus = {
|
|
120
|
+
...DEFAULT_CONFIG.consensus,
|
|
121
|
+
...(config.consensus || {}),
|
|
122
|
+
max_disagreements: parsed,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Verbose/log level
|
|
128
|
+
const logLevel = process.env[ENV_VARS.LOG_LEVEL];
|
|
129
|
+
if (logLevel === 'debug') {
|
|
130
|
+
config.output = {
|
|
131
|
+
...DEFAULT_CONFIG.output,
|
|
132
|
+
verbose: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return config;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Deep merge configuration objects
|
|
141
|
+
*/
|
|
142
|
+
export function deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {
|
|
143
|
+
const result = { ...target };
|
|
144
|
+
|
|
145
|
+
for (const key in source) {
|
|
146
|
+
const sourceValue = source[key];
|
|
147
|
+
const targetValue = result[key];
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
sourceValue !== undefined &&
|
|
151
|
+
sourceValue !== null &&
|
|
152
|
+
typeof sourceValue === 'object' &&
|
|
153
|
+
!Array.isArray(sourceValue) &&
|
|
154
|
+
typeof targetValue === 'object' &&
|
|
155
|
+
!Array.isArray(targetValue)
|
|
156
|
+
) {
|
|
157
|
+
result[key] = deepMerge(
|
|
158
|
+
targetValue as Record<string, unknown>,
|
|
159
|
+
sourceValue as Record<string, unknown>
|
|
160
|
+
) as T[Extract<keyof T, string>];
|
|
161
|
+
} else if (sourceValue !== undefined) {
|
|
162
|
+
result[key] = sourceValue as T[Extract<keyof T, string>];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Load and merge configuration from all sources
|
|
171
|
+
* Priority: env vars > project config > global config > defaults
|
|
172
|
+
*/
|
|
173
|
+
export async function loadConfig(cwd?: string): Promise<Config> {
|
|
174
|
+
// Load from all sources
|
|
175
|
+
const globalConfig = await loadGlobalConfig();
|
|
176
|
+
const projectConfig = await loadProjectConfig(cwd);
|
|
177
|
+
const envConfig = loadEnvConfig();
|
|
178
|
+
|
|
179
|
+
// Merge in priority order
|
|
180
|
+
let merged = deepMerge(DEFAULT_CONFIG, globalConfig);
|
|
181
|
+
merged = deepMerge(merged, projectConfig);
|
|
182
|
+
merged = deepMerge(merged, envConfig);
|
|
183
|
+
|
|
184
|
+
// Validate final config
|
|
185
|
+
const result = ConfigSchema.safeParse(merged);
|
|
186
|
+
if (!result.success) {
|
|
187
|
+
console.warn('Configuration validation warnings:', result.error.format());
|
|
188
|
+
// Return defaults if validation fails
|
|
189
|
+
return DEFAULT_CONFIG;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return result.data;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Save configuration to file
|
|
197
|
+
*/
|
|
198
|
+
export async function saveConfig(config: Partial<Config>, global = false): Promise<void> {
|
|
199
|
+
const { stringify: stringifyYaml } = await import('yaml');
|
|
200
|
+
|
|
201
|
+
const configPath = global
|
|
202
|
+
? path.join(homedir(), GLOBAL_CONFIG_DIR, CONFIG_FILE_NAME)
|
|
203
|
+
: path.join(process.cwd(), '.popeye', CONFIG_FILE_NAME);
|
|
204
|
+
|
|
205
|
+
// Ensure directory exists
|
|
206
|
+
const dir = path.dirname(configPath);
|
|
207
|
+
await fs.mkdir(dir, { recursive: true });
|
|
208
|
+
|
|
209
|
+
// Write config
|
|
210
|
+
const content = stringifyYaml(config);
|
|
211
|
+
await fs.writeFile(configPath, content, 'utf-8');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Get a specific config value by path
|
|
216
|
+
*/
|
|
217
|
+
export function getConfigValue<T>(config: Config, keyPath: string): T | undefined {
|
|
218
|
+
const keys = keyPath.split('.');
|
|
219
|
+
let current: unknown = config;
|
|
220
|
+
|
|
221
|
+
for (const key of keys) {
|
|
222
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
current = (current as Record<string, unknown>)[key];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return current as T;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Set a config value by path
|
|
233
|
+
*/
|
|
234
|
+
export function setConfigValue(config: Config, keyPath: string, value: unknown): Config {
|
|
235
|
+
const keys = keyPath.split('.');
|
|
236
|
+
const result = JSON.parse(JSON.stringify(config)) as Config;
|
|
237
|
+
let current: Record<string, unknown> = result;
|
|
238
|
+
|
|
239
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
240
|
+
const key = keys[i];
|
|
241
|
+
if (!(key in current) || typeof current[key] !== 'object') {
|
|
242
|
+
current[key] = {};
|
|
243
|
+
}
|
|
244
|
+
current = current[key] as Record<string, unknown>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const lastKey = keys[keys.length - 1];
|
|
248
|
+
current[lastKey] = value;
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Cached config path from last search
|
|
255
|
+
*/
|
|
256
|
+
let cachedConfigPath: string | null = null;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the path to the currently loaded config file (or null if using defaults)
|
|
260
|
+
*/
|
|
261
|
+
export function getConfigPath(): string | null {
|
|
262
|
+
return cachedConfigPath;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Search for and cache the config path
|
|
267
|
+
*/
|
|
268
|
+
export async function findConfigPath(cwd?: string): Promise<string | null> {
|
|
269
|
+
try {
|
|
270
|
+
const result = await explorer.search(cwd);
|
|
271
|
+
if (result && result.filepath) {
|
|
272
|
+
cachedConfigPath = result.filepath;
|
|
273
|
+
return result.filepath;
|
|
274
|
+
}
|
|
275
|
+
} catch {
|
|
276
|
+
// Ignore errors
|
|
277
|
+
}
|
|
278
|
+
cachedConfigPath = null;
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Popeye config type alias for CLI compatibility
|
|
284
|
+
*/
|
|
285
|
+
export interface PopeyeConfig extends Config {
|
|
286
|
+
consensus: Config['consensus'] & {
|
|
287
|
+
threshold: number;
|
|
288
|
+
maxIterations: number;
|
|
289
|
+
temperature: number;
|
|
290
|
+
maxTokens: number;
|
|
291
|
+
};
|
|
292
|
+
apis: Config['apis'] & {
|
|
293
|
+
openai: Config['apis']['openai'] & {
|
|
294
|
+
model: string;
|
|
295
|
+
timeout: number;
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
project: Config['project'] & {
|
|
299
|
+
defaultLanguage: 'python' | 'typescript';
|
|
300
|
+
defaultName: string;
|
|
301
|
+
};
|
|
302
|
+
directories: Config['directories'] & {
|
|
303
|
+
output: string;
|
|
304
|
+
state: string;
|
|
305
|
+
};
|
|
306
|
+
output: Config['output'] & {
|
|
307
|
+
colors: boolean;
|
|
308
|
+
progress: boolean;
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Schema for the Popeye config type used by CLI
|
|
314
|
+
*/
|
|
315
|
+
export const PopeyeConfigSchema = ConfigSchema;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema definitions using Zod
|
|
3
|
+
* Matches popeye-cli-spec.md section 9.1 exactly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Consensus settings schema
|
|
10
|
+
*/
|
|
11
|
+
export const ConsensusSettingsSchema = z.object({
|
|
12
|
+
threshold: z.number().min(0).max(100).default(95),
|
|
13
|
+
max_disagreements: z.number().min(1).max(10).default(5),
|
|
14
|
+
escalation_action: z.enum(['pause', 'continue', 'abort']).default('pause'),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* OpenAI API settings schema
|
|
19
|
+
*/
|
|
20
|
+
export const OpenAISettingsSchema = z.object({
|
|
21
|
+
model: z
|
|
22
|
+
.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'])
|
|
23
|
+
.default('gpt-4o'),
|
|
24
|
+
temperature: z.number().min(0).max(2).default(0.3),
|
|
25
|
+
max_tokens: z.number().min(100).max(32000).default(4096),
|
|
26
|
+
available_models: z
|
|
27
|
+
.array(z.string())
|
|
28
|
+
.default(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini']),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Claude API settings schema
|
|
33
|
+
*/
|
|
34
|
+
export const ClaudeSettingsSchema = z.object({
|
|
35
|
+
model: z.string().default('claude-sonnet-4-20250514'),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* API configuration schema
|
|
40
|
+
*/
|
|
41
|
+
export const APISettingsSchema = z.object({
|
|
42
|
+
openai: OpenAISettingsSchema.default({
|
|
43
|
+
model: 'gpt-4o',
|
|
44
|
+
temperature: 0.3,
|
|
45
|
+
max_tokens: 4096,
|
|
46
|
+
available_models: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'],
|
|
47
|
+
}),
|
|
48
|
+
claude: ClaudeSettingsSchema.default({
|
|
49
|
+
model: 'claude-sonnet-4-20250514',
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Python project settings schema
|
|
55
|
+
*/
|
|
56
|
+
export const PythonSettingsSchema = z.object({
|
|
57
|
+
package_manager: z.enum(['pip', 'poetry', 'pipenv']).default('pip'),
|
|
58
|
+
test_framework: z.string().default('pytest'),
|
|
59
|
+
min_version: z.string().default('3.10'),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* TypeScript project settings schema
|
|
64
|
+
*/
|
|
65
|
+
export const TypeScriptSettingsSchema = z.object({
|
|
66
|
+
package_manager: z.enum(['npm', 'pnpm', 'yarn']).default('npm'),
|
|
67
|
+
test_framework: z.enum(['jest', 'vitest']).default('jest'),
|
|
68
|
+
min_version: z.string().default('18'),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Project defaults schema
|
|
73
|
+
*/
|
|
74
|
+
export const ProjectSettingsSchema = z.object({
|
|
75
|
+
default_language: z.enum(['python', 'typescript']).default('python'),
|
|
76
|
+
python: PythonSettingsSchema.default({
|
|
77
|
+
package_manager: 'pip',
|
|
78
|
+
test_framework: 'pytest',
|
|
79
|
+
min_version: '3.10',
|
|
80
|
+
}),
|
|
81
|
+
typescript: TypeScriptSettingsSchema.default({
|
|
82
|
+
package_manager: 'npm',
|
|
83
|
+
test_framework: 'jest',
|
|
84
|
+
min_version: '18',
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Directory structure settings schema
|
|
90
|
+
*/
|
|
91
|
+
export const DirectorySettingsSchema = z.object({
|
|
92
|
+
docs: z.string().default('docs'),
|
|
93
|
+
tests: z.string().default('docs/tests'),
|
|
94
|
+
plans: z.string().default('docs/plans'),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Output settings schema
|
|
99
|
+
*/
|
|
100
|
+
export const OutputSettingsSchema = z.object({
|
|
101
|
+
format: z.enum(['markdown', 'json']).default('markdown'),
|
|
102
|
+
verbose: z.boolean().default(false),
|
|
103
|
+
timestamps: z.boolean().default(true),
|
|
104
|
+
show_consensus_dialog: z.boolean().default(true),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Complete configuration schema
|
|
109
|
+
*/
|
|
110
|
+
export const ConfigSchema = z.object({
|
|
111
|
+
consensus: ConsensusSettingsSchema.default({
|
|
112
|
+
threshold: 95,
|
|
113
|
+
max_disagreements: 5,
|
|
114
|
+
escalation_action: 'pause',
|
|
115
|
+
}),
|
|
116
|
+
apis: APISettingsSchema.default({
|
|
117
|
+
openai: {
|
|
118
|
+
model: 'gpt-4o',
|
|
119
|
+
temperature: 0.3,
|
|
120
|
+
max_tokens: 4096,
|
|
121
|
+
available_models: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini'],
|
|
122
|
+
},
|
|
123
|
+
claude: {
|
|
124
|
+
model: 'claude-sonnet-4-20250514',
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
project: ProjectSettingsSchema.default({
|
|
128
|
+
default_language: 'python',
|
|
129
|
+
python: {
|
|
130
|
+
package_manager: 'pip',
|
|
131
|
+
test_framework: 'pytest',
|
|
132
|
+
min_version: '3.10',
|
|
133
|
+
},
|
|
134
|
+
typescript: {
|
|
135
|
+
package_manager: 'npm',
|
|
136
|
+
test_framework: 'jest',
|
|
137
|
+
min_version: '18',
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
directories: DirectorySettingsSchema.default({
|
|
141
|
+
docs: 'docs',
|
|
142
|
+
tests: 'docs/tests',
|
|
143
|
+
plans: 'docs/plans',
|
|
144
|
+
}),
|
|
145
|
+
output: OutputSettingsSchema.default({
|
|
146
|
+
format: 'markdown',
|
|
147
|
+
verbose: false,
|
|
148
|
+
timestamps: true,
|
|
149
|
+
show_consensus_dialog: true,
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Configuration type inferred from schema
|
|
155
|
+
*/
|
|
156
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
157
|
+
export type ConsensusSettings = z.infer<typeof ConsensusSettingsSchema>;
|
|
158
|
+
export type OpenAISettings = z.infer<typeof OpenAISettingsSchema>;
|
|
159
|
+
export type ClaudeSettings = z.infer<typeof ClaudeSettingsSchema>;
|
|
160
|
+
export type PythonSettings = z.infer<typeof PythonSettingsSchema>;
|
|
161
|
+
export type TypeScriptSettings = z.infer<typeof TypeScriptSettingsSchema>;
|
|
162
|
+
export type ProjectSettings = z.infer<typeof ProjectSettingsSchema>;
|
|
163
|
+
export type DirectorySettings = z.infer<typeof DirectorySettingsSchema>;
|
|
164
|
+
export type OutputSettings = z.infer<typeof OutputSettingsSchema>;
|