popeye-cli 1.0.0 → 1.1.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/README.md +521 -125
- package/dist/adapters/claude.d.ts +16 -4
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +679 -33
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.d.ts +55 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +318 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +41 -7
- package/dist/adapters/openai.js.map +1 -1
- package/dist/auth/claude.d.ts +11 -9
- package/dist/auth/claude.d.ts.map +1 -1
- package/dist/auth/claude.js +107 -71
- package/dist/auth/claude.js.map +1 -1
- package/dist/auth/gemini.d.ts +58 -0
- package/dist/auth/gemini.d.ts.map +1 -0
- package/dist/auth/gemini.js +172 -0
- package/dist/auth/gemini.js.map +1 -0
- package/dist/auth/index.d.ts +11 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +23 -5
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keychain.d.ts +20 -7
- package/dist/auth/keychain.d.ts.map +1 -1
- package/dist/auth/keychain.js +85 -29
- package/dist/auth/keychain.js.map +1 -1
- package/dist/auth/openai.d.ts +2 -2
- package/dist/auth/openai.d.ts.map +1 -1
- package/dist/auth/openai.js +30 -32
- package/dist/auth/openai.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts +2 -2
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +1380 -183
- package/dist/cli/interactive.js.map +1 -1
- package/dist/config/defaults.d.ts +6 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +10 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/schema.d.ts +20 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +7 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/generators/python.d.ts.map +1 -1
- package/dist/generators/python.js +1 -0
- package/dist/generators/python.js.map +1 -1
- package/dist/generators/typescript.d.ts.map +1 -1
- package/dist/generators/typescript.js +1 -0
- package/dist/generators/typescript.js.map +1 -1
- package/dist/state/index.d.ts +108 -0
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +551 -4
- package/dist/state/index.js.map +1 -1
- package/dist/state/registry.d.ts +52 -0
- package/dist/state/registry.d.ts.map +1 -0
- package/dist/state/registry.js +215 -0
- package/dist/state/registry.js.map +1 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/cli.js.map +1 -1
- package/dist/types/consensus.d.ts +69 -4
- package/dist/types/consensus.d.ts.map +1 -1
- package/dist/types/consensus.js +24 -3
- package/dist/types/consensus.js.map +1 -1
- package/dist/types/workflow.d.ts +55 -0
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/types/workflow.js +16 -0
- package/dist/types/workflow.js.map +1 -1
- package/dist/workflow/auto-fix.d.ts +45 -0
- package/dist/workflow/auto-fix.d.ts.map +1 -0
- package/dist/workflow/auto-fix.js +274 -0
- package/dist/workflow/auto-fix.js.map +1 -0
- package/dist/workflow/consensus.d.ts +44 -2
- package/dist/workflow/consensus.d.ts.map +1 -1
- package/dist/workflow/consensus.js +565 -17
- package/dist/workflow/consensus.js.map +1 -1
- package/dist/workflow/execution-mode.d.ts +10 -4
- package/dist/workflow/execution-mode.d.ts.map +1 -1
- package/dist/workflow/execution-mode.js +547 -58
- package/dist/workflow/execution-mode.js.map +1 -1
- package/dist/workflow/index.d.ts +14 -2
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +69 -6
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/milestone-workflow.d.ts +34 -0
- package/dist/workflow/milestone-workflow.d.ts.map +1 -0
- package/dist/workflow/milestone-workflow.js +414 -0
- package/dist/workflow/milestone-workflow.js.map +1 -0
- package/dist/workflow/plan-mode.d.ts +14 -1
- package/dist/workflow/plan-mode.d.ts.map +1 -1
- package/dist/workflow/plan-mode.js +589 -47
- package/dist/workflow/plan-mode.js.map +1 -1
- package/dist/workflow/plan-storage.d.ts +142 -0
- package/dist/workflow/plan-storage.d.ts.map +1 -0
- package/dist/workflow/plan-storage.js +331 -0
- package/dist/workflow/plan-storage.js.map +1 -0
- package/dist/workflow/project-verification.d.ts +37 -0
- package/dist/workflow/project-verification.d.ts.map +1 -0
- package/dist/workflow/project-verification.js +381 -0
- package/dist/workflow/project-verification.js.map +1 -0
- package/dist/workflow/task-workflow.d.ts +37 -0
- package/dist/workflow/task-workflow.d.ts.map +1 -0
- package/dist/workflow/task-workflow.js +383 -0
- package/dist/workflow/task-workflow.js.map +1 -0
- package/dist/workflow/test-runner.d.ts +1 -0
- package/dist/workflow/test-runner.d.ts.map +1 -1
- package/dist/workflow/test-runner.js +9 -5
- package/dist/workflow/test-runner.js.map +1 -1
- package/dist/workflow/ui-designer.d.ts +82 -0
- package/dist/workflow/ui-designer.d.ts.map +1 -0
- package/dist/workflow/ui-designer.js +234 -0
- package/dist/workflow/ui-designer.js.map +1 -0
- package/dist/workflow/ui-setup.d.ts +58 -0
- package/dist/workflow/ui-setup.d.ts.map +1 -0
- package/dist/workflow/ui-setup.js +685 -0
- package/dist/workflow/ui-setup.js.map +1 -0
- package/dist/workflow/ui-verification.d.ts +114 -0
- package/dist/workflow/ui-verification.d.ts.map +1 -0
- package/dist/workflow/ui-verification.js +258 -0
- package/dist/workflow/ui-verification.js.map +1 -0
- package/dist/workflow/workflow-logger.d.ts +110 -0
- package/dist/workflow/workflow-logger.d.ts.map +1 -0
- package/dist/workflow/workflow-logger.js +267 -0
- package/dist/workflow/workflow-logger.js.map +1 -0
- package/package.json +2 -2
- package/src/adapters/claude.ts +815 -34
- package/src/adapters/gemini.ts +373 -0
- package/src/adapters/openai.ts +40 -7
- package/src/auth/claude.ts +120 -78
- package/src/auth/gemini.ts +207 -0
- package/src/auth/index.ts +28 -8
- package/src/auth/keychain.ts +95 -28
- package/src/auth/openai.ts +29 -36
- package/src/cli/index.ts +4 -7
- package/src/cli/interactive.ts +1641 -216
- package/src/config/defaults.ts +10 -2
- package/src/config/index.ts +21 -0
- package/src/config/schema.ts +7 -0
- package/src/generators/python.ts +1 -0
- package/src/generators/typescript.ts +1 -0
- package/src/state/index.ts +713 -4
- package/src/state/registry.ts +278 -0
- package/src/types/cli.ts +4 -0
- package/src/types/consensus.ts +65 -6
- package/src/types/workflow.ts +35 -0
- package/src/workflow/auto-fix.ts +340 -0
- package/src/workflow/consensus.ts +750 -16
- package/src/workflow/execution-mode.ts +673 -74
- package/src/workflow/index.ts +95 -6
- package/src/workflow/milestone-workflow.ts +576 -0
- package/src/workflow/plan-mode.ts +696 -50
- package/src/workflow/plan-storage.ts +482 -0
- package/src/workflow/project-verification.ts +471 -0
- package/src/workflow/task-workflow.ts +525 -0
- package/src/workflow/test-runner.ts +10 -5
- package/src/workflow/ui-designer.ts +337 -0
- package/src/workflow/ui-setup.ts +797 -0
- package/src/workflow/ui-verification.ts +357 -0
- package/src/workflow/workflow-logger.ts +353 -0
- package/tests/config/config.test.ts +1 -1
- package/tests/types/consensus.test.ts +3 -3
- package/tests/workflow/plan-mode.test.ts +213 -0
- package/tests/workflow/test-runner.test.ts +5 -3
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Registry
|
|
3
|
+
* Tracks all Popeye projects globally and provides discovery functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { promises as fs } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
import { STATE_DIR, STATE_FILE, loadState } from './persistence.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Global registry directory
|
|
13
|
+
*/
|
|
14
|
+
const REGISTRY_DIR = path.join(homedir(), '.popeye');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Registry file name
|
|
18
|
+
*/
|
|
19
|
+
const REGISTRY_FILE = 'projects.json';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Registered project entry
|
|
23
|
+
*/
|
|
24
|
+
export interface RegisteredProject {
|
|
25
|
+
path: string;
|
|
26
|
+
name: string;
|
|
27
|
+
idea?: string;
|
|
28
|
+
language: string;
|
|
29
|
+
phase: string;
|
|
30
|
+
status: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Project registry structure
|
|
37
|
+
*/
|
|
38
|
+
interface ProjectRegistry {
|
|
39
|
+
version: string;
|
|
40
|
+
projects: RegisteredProject[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get registry file path
|
|
45
|
+
*/
|
|
46
|
+
function getRegistryPath(): string {
|
|
47
|
+
return path.join(REGISTRY_DIR, REGISTRY_FILE);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Ensure registry directory exists
|
|
52
|
+
*/
|
|
53
|
+
async function ensureRegistryDir(): Promise<void> {
|
|
54
|
+
await fs.mkdir(REGISTRY_DIR, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Load the project registry
|
|
59
|
+
*/
|
|
60
|
+
async function loadRegistry(): Promise<ProjectRegistry> {
|
|
61
|
+
try {
|
|
62
|
+
const content = await fs.readFile(getRegistryPath(), 'utf-8');
|
|
63
|
+
return JSON.parse(content);
|
|
64
|
+
} catch {
|
|
65
|
+
return { version: '1.0', projects: [] };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Save the project registry
|
|
71
|
+
*/
|
|
72
|
+
async function saveRegistry(registry: ProjectRegistry): Promise<void> {
|
|
73
|
+
await ensureRegistryDir();
|
|
74
|
+
const content = JSON.stringify(registry, null, 2);
|
|
75
|
+
await fs.writeFile(getRegistryPath(), content, 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Register a new project or update existing registration
|
|
80
|
+
*/
|
|
81
|
+
export async function registerProject(projectDir: string): Promise<void> {
|
|
82
|
+
const state = await loadState(projectDir);
|
|
83
|
+
if (!state) return;
|
|
84
|
+
|
|
85
|
+
const registry = await loadRegistry();
|
|
86
|
+
|
|
87
|
+
// Check if project already registered
|
|
88
|
+
const existingIndex = registry.projects.findIndex(p => p.path === projectDir);
|
|
89
|
+
|
|
90
|
+
const entry: RegisteredProject = {
|
|
91
|
+
path: projectDir,
|
|
92
|
+
name: state.name,
|
|
93
|
+
idea: state.idea?.slice(0, 200),
|
|
94
|
+
language: state.language,
|
|
95
|
+
phase: state.phase,
|
|
96
|
+
status: state.status,
|
|
97
|
+
createdAt: state.createdAt,
|
|
98
|
+
updatedAt: state.updatedAt,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (existingIndex >= 0) {
|
|
102
|
+
registry.projects[existingIndex] = entry;
|
|
103
|
+
} else {
|
|
104
|
+
registry.projects.push(entry);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await saveRegistry(registry);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove a project from the registry
|
|
112
|
+
*/
|
|
113
|
+
export async function unregisterProject(projectDir: string): Promise<void> {
|
|
114
|
+
const registry = await loadRegistry();
|
|
115
|
+
registry.projects = registry.projects.filter(p => p.path !== projectDir);
|
|
116
|
+
await saveRegistry(registry);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get all registered projects
|
|
121
|
+
*/
|
|
122
|
+
export async function getRegisteredProjects(): Promise<RegisteredProject[]> {
|
|
123
|
+
const registry = await loadRegistry();
|
|
124
|
+
|
|
125
|
+
// Verify each project still exists and update status
|
|
126
|
+
const validProjects: RegisteredProject[] = [];
|
|
127
|
+
|
|
128
|
+
for (const project of registry.projects) {
|
|
129
|
+
try {
|
|
130
|
+
const state = await loadState(project.path);
|
|
131
|
+
if (state) {
|
|
132
|
+
validProjects.push({
|
|
133
|
+
...project,
|
|
134
|
+
phase: state.phase,
|
|
135
|
+
status: state.status,
|
|
136
|
+
updatedAt: state.updatedAt,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// Project no longer exists, skip it
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Update registry with valid projects only
|
|
145
|
+
if (validProjects.length !== registry.projects.length) {
|
|
146
|
+
await saveRegistry({ ...registry, projects: validProjects });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Sort by updatedAt (most recent first)
|
|
150
|
+
return validProjects.sort((a, b) =>
|
|
151
|
+
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Scan a directory (and subdirectories) for Popeye projects
|
|
157
|
+
*/
|
|
158
|
+
export async function scanForProjects(
|
|
159
|
+
baseDir: string,
|
|
160
|
+
maxDepth: number = 3
|
|
161
|
+
): Promise<RegisteredProject[]> {
|
|
162
|
+
const discovered: RegisteredProject[] = [];
|
|
163
|
+
|
|
164
|
+
async function scanDir(dir: string, depth: number): Promise<void> {
|
|
165
|
+
if (depth > maxDepth) return;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
// Check if this directory has a Popeye project
|
|
169
|
+
const statePath = path.join(dir, STATE_DIR, STATE_FILE);
|
|
170
|
+
try {
|
|
171
|
+
await fs.access(statePath);
|
|
172
|
+
const state = await loadState(dir);
|
|
173
|
+
if (state) {
|
|
174
|
+
discovered.push({
|
|
175
|
+
path: dir,
|
|
176
|
+
name: state.name,
|
|
177
|
+
idea: state.idea?.slice(0, 200),
|
|
178
|
+
language: state.language,
|
|
179
|
+
phase: state.phase,
|
|
180
|
+
status: state.status,
|
|
181
|
+
createdAt: state.createdAt,
|
|
182
|
+
updatedAt: state.updatedAt,
|
|
183
|
+
});
|
|
184
|
+
// Register discovered project
|
|
185
|
+
await registerProject(dir);
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// No state file in this directory
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Scan subdirectories
|
|
192
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
195
|
+
await scanDir(path.join(dir, entry.name), depth + 1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
// Can't read directory, skip it
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
await scanDir(baseDir, 0);
|
|
204
|
+
|
|
205
|
+
// Sort by updatedAt (most recent first)
|
|
206
|
+
return discovered.sort((a, b) =>
|
|
207
|
+
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Find projects in the current directory and registered projects
|
|
213
|
+
*/
|
|
214
|
+
export async function discoverProjects(cwd: string): Promise<{
|
|
215
|
+
registered: RegisteredProject[];
|
|
216
|
+
discovered: RegisteredProject[];
|
|
217
|
+
all: RegisteredProject[];
|
|
218
|
+
}> {
|
|
219
|
+
// Get registered projects
|
|
220
|
+
const registered = await getRegisteredProjects();
|
|
221
|
+
|
|
222
|
+
// Scan current directory for projects
|
|
223
|
+
const scanned = await scanForProjects(cwd, 2);
|
|
224
|
+
|
|
225
|
+
// Merge, avoiding duplicates
|
|
226
|
+
const registeredPaths = new Set(registered.map(p => p.path));
|
|
227
|
+
const discovered = scanned.filter(p => !registeredPaths.has(p.path));
|
|
228
|
+
|
|
229
|
+
// Combine all projects
|
|
230
|
+
const all = [...registered, ...discovered].sort((a, b) =>
|
|
231
|
+
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
return { registered, discovered, all };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Format a project for display
|
|
239
|
+
*/
|
|
240
|
+
export function formatProjectForDisplay(project: RegisteredProject): {
|
|
241
|
+
name: string;
|
|
242
|
+
status: string;
|
|
243
|
+
path: string;
|
|
244
|
+
lastUpdated: string;
|
|
245
|
+
age: string;
|
|
246
|
+
} {
|
|
247
|
+
const now = new Date();
|
|
248
|
+
const updated = new Date(project.updatedAt);
|
|
249
|
+
const diffMs = now.getTime() - updated.getTime();
|
|
250
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
251
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
252
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
253
|
+
|
|
254
|
+
let age: string;
|
|
255
|
+
if (diffMins < 1) {
|
|
256
|
+
age = 'just now';
|
|
257
|
+
} else if (diffMins < 60) {
|
|
258
|
+
age = `${diffMins}m ago`;
|
|
259
|
+
} else if (diffHours < 24) {
|
|
260
|
+
age = `${diffHours}h ago`;
|
|
261
|
+
} else if (diffDays < 7) {
|
|
262
|
+
age = `${diffDays}d ago`;
|
|
263
|
+
} else {
|
|
264
|
+
age = updated.toLocaleDateString();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const statusIcon = project.status === 'complete' ? '✓' :
|
|
268
|
+
project.status === 'failed' ? '✗' :
|
|
269
|
+
project.status === 'in-progress' ? '→' : '○';
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
name: project.name,
|
|
273
|
+
status: `${statusIcon} ${project.phase}/${project.status}`,
|
|
274
|
+
path: project.path,
|
|
275
|
+
lastUpdated: updated.toISOString(),
|
|
276
|
+
age,
|
|
277
|
+
};
|
|
278
|
+
}
|
package/src/types/cli.ts
CHANGED
package/src/types/consensus.ts
CHANGED
|
@@ -7,7 +7,17 @@ import { z } from 'zod';
|
|
|
7
7
|
import type { OpenAIModel } from './project.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Supported AI providers for reviews and arbitration
|
|
11
|
+
*/
|
|
12
|
+
export type AIProvider = 'openai' | 'gemini';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Supported Gemini models
|
|
16
|
+
*/
|
|
17
|
+
export type GeminiModel = 'gemini-2.0-flash' | 'gemini-1.5-pro' | 'gemini-1.5-flash';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Result of a consensus review from OpenAI or Gemini
|
|
11
21
|
*/
|
|
12
22
|
export interface ConsensusResult {
|
|
13
23
|
score: number;
|
|
@@ -19,6 +29,21 @@ export interface ConsensusResult {
|
|
|
19
29
|
rawResponse: string;
|
|
20
30
|
}
|
|
21
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Result of an arbitration decision
|
|
34
|
+
*/
|
|
35
|
+
export interface ArbitrationResult {
|
|
36
|
+
approved: boolean;
|
|
37
|
+
score: number;
|
|
38
|
+
analysis: string;
|
|
39
|
+
criticalConcerns: string[];
|
|
40
|
+
minorConcerns: string[];
|
|
41
|
+
subjectiveConcerns: string[];
|
|
42
|
+
reasoning: string;
|
|
43
|
+
suggestedChanges: string[];
|
|
44
|
+
rawResponse: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
22
47
|
/**
|
|
23
48
|
* Single consensus iteration record
|
|
24
49
|
*/
|
|
@@ -35,33 +60,67 @@ export interface ConsensusIteration {
|
|
|
35
60
|
export interface ConsensusConfig {
|
|
36
61
|
threshold: number;
|
|
37
62
|
maxIterations: number;
|
|
38
|
-
openaiKey
|
|
63
|
+
openaiKey?: string;
|
|
64
|
+
geminiKey?: string;
|
|
39
65
|
openaiModel: OpenAIModel;
|
|
66
|
+
geminiModel: GeminiModel;
|
|
67
|
+
reviewer: AIProvider;
|
|
68
|
+
arbitrator: AIProvider;
|
|
69
|
+
enableArbitration: boolean;
|
|
70
|
+
arbitrationThreshold: number; // Score at which to trigger arbitration (e.g., 85)
|
|
71
|
+
stuckIterations: number; // Number of iterations without improvement before arbitration
|
|
40
72
|
escalationAction: 'pause' | 'continue' | 'abort';
|
|
41
73
|
temperature: number;
|
|
42
74
|
maxTokens: number;
|
|
75
|
+
/** Use optimized consensus with batched feedback and file-based plan storage (default: true) */
|
|
76
|
+
useOptimizedConsensus?: boolean;
|
|
77
|
+
/** Additional reviewers beyond primary (for parallel reviews) */
|
|
78
|
+
additionalReviewers?: AIProvider[];
|
|
43
79
|
}
|
|
44
80
|
|
|
45
81
|
/**
|
|
46
82
|
* Default consensus configuration
|
|
47
83
|
*/
|
|
48
|
-
export const DEFAULT_CONSENSUS_CONFIG: Omit<ConsensusConfig, 'openaiKey'> = {
|
|
84
|
+
export const DEFAULT_CONSENSUS_CONFIG: Omit<ConsensusConfig, 'openaiKey' | 'geminiKey'> = {
|
|
49
85
|
threshold: 95,
|
|
50
|
-
maxIterations:
|
|
86
|
+
maxIterations: 10,
|
|
51
87
|
openaiModel: 'gpt-4o',
|
|
88
|
+
geminiModel: 'gemini-2.0-flash',
|
|
89
|
+
reviewer: 'openai',
|
|
90
|
+
arbitrator: 'gemini',
|
|
91
|
+
enableArbitration: true,
|
|
92
|
+
arbitrationThreshold: 85,
|
|
93
|
+
stuckIterations: 3,
|
|
52
94
|
escalationAction: 'pause',
|
|
53
95
|
temperature: 0.3,
|
|
54
96
|
maxTokens: 4096,
|
|
55
97
|
};
|
|
56
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Zod schema for AI provider
|
|
101
|
+
*/
|
|
102
|
+
export const AIProviderSchema = z.enum(['openai', 'gemini']);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Zod schema for Gemini model
|
|
106
|
+
*/
|
|
107
|
+
export const GeminiModelSchema = z.enum(['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash']);
|
|
108
|
+
|
|
57
109
|
/**
|
|
58
110
|
* Zod schema for consensus config validation
|
|
59
111
|
*/
|
|
60
112
|
export const ConsensusConfigSchema = z.object({
|
|
61
113
|
threshold: z.number().min(0).max(100).default(95),
|
|
62
|
-
maxIterations: z.number().min(1).max(
|
|
63
|
-
openaiKey: z.string().
|
|
114
|
+
maxIterations: z.number().min(1).max(20).default(10),
|
|
115
|
+
openaiKey: z.string().optional(),
|
|
116
|
+
geminiKey: z.string().optional(),
|
|
64
117
|
openaiModel: z.enum(['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'o1-preview', 'o1-mini']),
|
|
118
|
+
geminiModel: GeminiModelSchema.default('gemini-2.0-flash'),
|
|
119
|
+
reviewer: AIProviderSchema.default('openai'),
|
|
120
|
+
arbitrator: AIProviderSchema.default('gemini'),
|
|
121
|
+
enableArbitration: z.boolean().default(true),
|
|
122
|
+
arbitrationThreshold: z.number().min(0).max(100).default(85),
|
|
123
|
+
stuckIterations: z.number().min(1).max(10).default(3),
|
|
65
124
|
escalationAction: z.enum(['pause', 'continue', 'abort']).default('pause'),
|
|
66
125
|
temperature: z.number().min(0).max(2).default(0.3),
|
|
67
126
|
maxTokens: z.number().min(100).max(32000).default(4096),
|
package/src/types/workflow.ts
CHANGED
|
@@ -36,6 +36,14 @@ export interface Task {
|
|
|
36
36
|
testsPassed?: boolean;
|
|
37
37
|
testPlan?: string;
|
|
38
38
|
error?: string;
|
|
39
|
+
// Per-task consensus tracking
|
|
40
|
+
plan?: string; // Detailed task implementation plan
|
|
41
|
+
consensusScore?: number; // Consensus score for task plan (0-100)
|
|
42
|
+
consensusIterations?: number; // Number of iterations to reach consensus
|
|
43
|
+
consensusApproved?: boolean; // Whether task plan was approved
|
|
44
|
+
planDoc?: string; // Path to task plan document
|
|
45
|
+
testResultsDoc?: string; // Path to test results document
|
|
46
|
+
implementationComplete?: boolean; // Whether code implementation finished (for resume)
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
/**
|
|
@@ -49,6 +57,13 @@ export const TaskSchema = z.object({
|
|
|
49
57
|
testsPassed: z.boolean().optional(),
|
|
50
58
|
testPlan: z.string().optional(),
|
|
51
59
|
error: z.string().optional(),
|
|
60
|
+
plan: z.string().optional(),
|
|
61
|
+
consensusScore: z.number().optional(),
|
|
62
|
+
consensusIterations: z.number().optional(),
|
|
63
|
+
consensusApproved: z.boolean().optional(),
|
|
64
|
+
planDoc: z.string().optional(),
|
|
65
|
+
testResultsDoc: z.string().optional(),
|
|
66
|
+
implementationComplete: z.boolean().optional(),
|
|
52
67
|
});
|
|
53
68
|
|
|
54
69
|
/**
|
|
@@ -60,6 +75,17 @@ export interface Milestone {
|
|
|
60
75
|
description: string;
|
|
61
76
|
status: TaskStatus;
|
|
62
77
|
tasks: Task[];
|
|
78
|
+
// Per-milestone consensus tracking
|
|
79
|
+
plan?: string; // Detailed milestone plan
|
|
80
|
+
consensusScore?: number; // Consensus score for milestone plan
|
|
81
|
+
consensusIterations?: number; // Number of iterations to reach consensus
|
|
82
|
+
consensusApproved?: boolean; // Whether milestone plan was approved
|
|
83
|
+
planDoc?: string; // Path: docs/milestone_N_plan.md
|
|
84
|
+
// Milestone completion review
|
|
85
|
+
completionReview?: string; // Code review and summary
|
|
86
|
+
completionScore?: number; // Consensus score for completion
|
|
87
|
+
completionApproved?: boolean; // Whether milestone completion was approved
|
|
88
|
+
completionDoc?: string; // Path: docs/milestone_N_complete.md
|
|
63
89
|
}
|
|
64
90
|
|
|
65
91
|
/**
|
|
@@ -71,6 +97,15 @@ export const MilestoneSchema = z.object({
|
|
|
71
97
|
description: z.string(),
|
|
72
98
|
status: TaskStatusSchema,
|
|
73
99
|
tasks: z.array(TaskSchema),
|
|
100
|
+
plan: z.string().optional(),
|
|
101
|
+
consensusScore: z.number().optional(),
|
|
102
|
+
consensusIterations: z.number().optional(),
|
|
103
|
+
consensusApproved: z.boolean().optional(),
|
|
104
|
+
planDoc: z.string().optional(),
|
|
105
|
+
completionReview: z.string().optional(),
|
|
106
|
+
completionScore: z.number().optional(),
|
|
107
|
+
completionApproved: z.boolean().optional(),
|
|
108
|
+
completionDoc: z.string().optional(),
|
|
74
109
|
});
|
|
75
110
|
|
|
76
111
|
/**
|