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.
Files changed (209) hide show
  1. package/.env.example +25 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +320 -0
  4. package/dist/adapters/claude.d.ts +82 -0
  5. package/dist/adapters/claude.d.ts.map +1 -0
  6. package/dist/adapters/claude.js +230 -0
  7. package/dist/adapters/claude.js.map +1 -0
  8. package/dist/adapters/openai.d.ts +48 -0
  9. package/dist/adapters/openai.d.ts.map +1 -0
  10. package/dist/adapters/openai.js +257 -0
  11. package/dist/adapters/openai.js.map +1 -0
  12. package/dist/auth/claude.d.ts +44 -0
  13. package/dist/auth/claude.d.ts.map +1 -0
  14. package/dist/auth/claude.js +139 -0
  15. package/dist/auth/claude.js.map +1 -0
  16. package/dist/auth/index.d.ts +61 -0
  17. package/dist/auth/index.d.ts.map +1 -0
  18. package/dist/auth/index.js +141 -0
  19. package/dist/auth/index.js.map +1 -0
  20. package/dist/auth/keychain.d.ts +66 -0
  21. package/dist/auth/keychain.d.ts.map +1 -0
  22. package/dist/auth/keychain.js +125 -0
  23. package/dist/auth/keychain.js.map +1 -0
  24. package/dist/auth/openai-entry.d.ts +9 -0
  25. package/dist/auth/openai-entry.d.ts.map +1 -0
  26. package/dist/auth/openai-entry.js +410 -0
  27. package/dist/auth/openai-entry.js.map +1 -0
  28. package/dist/auth/openai.d.ts +71 -0
  29. package/dist/auth/openai.d.ts.map +1 -0
  30. package/dist/auth/openai.js +212 -0
  31. package/dist/auth/openai.js.map +1 -0
  32. package/dist/auth/server.d.ts +32 -0
  33. package/dist/auth/server.d.ts.map +1 -0
  34. package/dist/auth/server.js +213 -0
  35. package/dist/auth/server.js.map +1 -0
  36. package/dist/cli/commands/auth.d.ts +10 -0
  37. package/dist/cli/commands/auth.d.ts.map +1 -0
  38. package/dist/cli/commands/auth.js +162 -0
  39. package/dist/cli/commands/auth.js.map +1 -0
  40. package/dist/cli/commands/config.d.ts +10 -0
  41. package/dist/cli/commands/config.d.ts.map +1 -0
  42. package/dist/cli/commands/config.js +215 -0
  43. package/dist/cli/commands/config.js.map +1 -0
  44. package/dist/cli/commands/create.d.ts +10 -0
  45. package/dist/cli/commands/create.d.ts.map +1 -0
  46. package/dist/cli/commands/create.js +240 -0
  47. package/dist/cli/commands/create.js.map +1 -0
  48. package/dist/cli/commands/index.d.ts +10 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -0
  50. package/dist/cli/commands/index.js +10 -0
  51. package/dist/cli/commands/index.js.map +1 -0
  52. package/dist/cli/commands/resume.d.ts +18 -0
  53. package/dist/cli/commands/resume.d.ts.map +1 -0
  54. package/dist/cli/commands/resume.js +241 -0
  55. package/dist/cli/commands/resume.js.map +1 -0
  56. package/dist/cli/commands/status.d.ts +18 -0
  57. package/dist/cli/commands/status.d.ts.map +1 -0
  58. package/dist/cli/commands/status.js +154 -0
  59. package/dist/cli/commands/status.js.map +1 -0
  60. package/dist/cli/index.d.ts +17 -0
  61. package/dist/cli/index.d.ts.map +1 -0
  62. package/dist/cli/index.js +71 -0
  63. package/dist/cli/index.js.map +1 -0
  64. package/dist/cli/interactive.d.ts +9 -0
  65. package/dist/cli/interactive.d.ts.map +1 -0
  66. package/dist/cli/interactive.js +330 -0
  67. package/dist/cli/interactive.js.map +1 -0
  68. package/dist/cli/output.d.ts +182 -0
  69. package/dist/cli/output.d.ts.map +1 -0
  70. package/dist/cli/output.js +355 -0
  71. package/dist/cli/output.js.map +1 -0
  72. package/dist/config/defaults.d.ts +57 -0
  73. package/dist/config/defaults.d.ts.map +1 -0
  74. package/dist/config/defaults.js +103 -0
  75. package/dist/config/defaults.js.map +1 -0
  76. package/dist/config/index.d.ts +138 -0
  77. package/dist/config/index.d.ts.map +1 -0
  78. package/dist/config/index.js +244 -0
  79. package/dist/config/index.js.map +1 -0
  80. package/dist/config/schema.d.ts +220 -0
  81. package/dist/config/schema.d.ts.map +1 -0
  82. package/dist/config/schema.js +141 -0
  83. package/dist/config/schema.js.map +1 -0
  84. package/dist/generators/index.d.ts +101 -0
  85. package/dist/generators/index.d.ts.map +1 -0
  86. package/dist/generators/index.js +200 -0
  87. package/dist/generators/index.js.map +1 -0
  88. package/dist/generators/python.d.ts +48 -0
  89. package/dist/generators/python.d.ts.map +1 -0
  90. package/dist/generators/python.js +262 -0
  91. package/dist/generators/python.js.map +1 -0
  92. package/dist/generators/templates/index.d.ts +6 -0
  93. package/dist/generators/templates/index.d.ts.map +1 -0
  94. package/dist/generators/templates/index.js +6 -0
  95. package/dist/generators/templates/index.js.map +1 -0
  96. package/dist/generators/templates/python.d.ts +53 -0
  97. package/dist/generators/templates/python.d.ts.map +1 -0
  98. package/dist/generators/templates/python.js +454 -0
  99. package/dist/generators/templates/python.js.map +1 -0
  100. package/dist/generators/templates/typescript.d.ts +53 -0
  101. package/dist/generators/templates/typescript.d.ts.map +1 -0
  102. package/dist/generators/templates/typescript.js +394 -0
  103. package/dist/generators/templates/typescript.js.map +1 -0
  104. package/dist/generators/typescript.d.ts +64 -0
  105. package/dist/generators/typescript.d.ts.map +1 -0
  106. package/dist/generators/typescript.js +271 -0
  107. package/dist/generators/typescript.js.map +1 -0
  108. package/dist/index.d.ts +7 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +12 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/state/index.d.ts +168 -0
  113. package/dist/state/index.d.ts.map +1 -0
  114. package/dist/state/index.js +338 -0
  115. package/dist/state/index.js.map +1 -0
  116. package/dist/state/persistence.d.ts +91 -0
  117. package/dist/state/persistence.d.ts.map +1 -0
  118. package/dist/state/persistence.js +201 -0
  119. package/dist/state/persistence.js.map +1 -0
  120. package/dist/types/cli.d.ts +132 -0
  121. package/dist/types/cli.d.ts.map +1 -0
  122. package/dist/types/cli.js +17 -0
  123. package/dist/types/cli.js.map +1 -0
  124. package/dist/types/consensus.d.ts +111 -0
  125. package/dist/types/consensus.d.ts.map +1 -0
  126. package/dist/types/consensus.js +29 -0
  127. package/dist/types/consensus.js.map +1 -0
  128. package/dist/types/index.d.ts +9 -0
  129. package/dist/types/index.d.ts.map +1 -0
  130. package/dist/types/index.js +13 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/project.d.ts +73 -0
  133. package/dist/types/project.d.ts.map +1 -0
  134. package/dist/types/project.js +55 -0
  135. package/dist/types/project.js.map +1 -0
  136. package/dist/types/workflow.d.ts +236 -0
  137. package/dist/types/workflow.d.ts.map +1 -0
  138. package/dist/types/workflow.js +74 -0
  139. package/dist/types/workflow.js.map +1 -0
  140. package/dist/workflow/consensus.d.ts +89 -0
  141. package/dist/workflow/consensus.d.ts.map +1 -0
  142. package/dist/workflow/consensus.js +220 -0
  143. package/dist/workflow/consensus.js.map +1 -0
  144. package/dist/workflow/execution-mode.d.ts +82 -0
  145. package/dist/workflow/execution-mode.d.ts.map +1 -0
  146. package/dist/workflow/execution-mode.js +346 -0
  147. package/dist/workflow/execution-mode.js.map +1 -0
  148. package/dist/workflow/index.d.ts +110 -0
  149. package/dist/workflow/index.d.ts.map +1 -0
  150. package/dist/workflow/index.js +283 -0
  151. package/dist/workflow/index.js.map +1 -0
  152. package/dist/workflow/plan-mode.d.ts +83 -0
  153. package/dist/workflow/plan-mode.d.ts.map +1 -0
  154. package/dist/workflow/plan-mode.js +241 -0
  155. package/dist/workflow/plan-mode.js.map +1 -0
  156. package/dist/workflow/test-runner.d.ts +87 -0
  157. package/dist/workflow/test-runner.d.ts.map +1 -0
  158. package/dist/workflow/test-runner.js +273 -0
  159. package/dist/workflow/test-runner.js.map +1 -0
  160. package/eslint.config.js +25 -0
  161. package/package.json +66 -0
  162. package/src/adapters/claude.ts +298 -0
  163. package/src/adapters/openai.ts +300 -0
  164. package/src/auth/claude.ts +166 -0
  165. package/src/auth/index.ts +171 -0
  166. package/src/auth/keychain.ts +138 -0
  167. package/src/auth/openai-entry.ts +410 -0
  168. package/src/auth/openai.ts +260 -0
  169. package/src/auth/server.ts +252 -0
  170. package/src/cli/commands/auth.ts +194 -0
  171. package/src/cli/commands/config.ts +241 -0
  172. package/src/cli/commands/create.ts +308 -0
  173. package/src/cli/commands/index.ts +10 -0
  174. package/src/cli/commands/resume.ts +304 -0
  175. package/src/cli/commands/status.ts +189 -0
  176. package/src/cli/index.ts +90 -0
  177. package/src/cli/interactive.ts +418 -0
  178. package/src/cli/output.ts +410 -0
  179. package/src/config/defaults.ts +114 -0
  180. package/src/config/index.ts +315 -0
  181. package/src/config/schema.ts +164 -0
  182. package/src/generators/index.ts +251 -0
  183. package/src/generators/python.ts +318 -0
  184. package/src/generators/templates/index.ts +6 -0
  185. package/src/generators/templates/python.ts +465 -0
  186. package/src/generators/templates/typescript.ts +417 -0
  187. package/src/generators/typescript.ts +340 -0
  188. package/src/index.ts +13 -0
  189. package/src/state/index.ts +454 -0
  190. package/src/state/persistence.ts +230 -0
  191. package/src/types/cli.ts +146 -0
  192. package/src/types/consensus.ts +116 -0
  193. package/src/types/index.ts +64 -0
  194. package/src/types/project.ts +85 -0
  195. package/src/types/workflow.ts +149 -0
  196. package/src/workflow/consensus.ts +299 -0
  197. package/src/workflow/execution-mode.ts +517 -0
  198. package/src/workflow/index.ts +396 -0
  199. package/src/workflow/plan-mode.ts +356 -0
  200. package/src/workflow/test-runner.ts +345 -0
  201. package/tests/adapters/openai.test.ts +145 -0
  202. package/tests/config/config.test.ts +208 -0
  203. package/tests/generators/generators.test.ts +185 -0
  204. package/tests/types/consensus.test.ts +152 -0
  205. package/tests/types/project.test.ts +134 -0
  206. package/tests/workflow/consensus.test.ts +221 -0
  207. package/tests/workflow/test-runner.test.ts +214 -0
  208. package/tsconfig.json +25 -0
  209. 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>;