archondev 0.1.0 → 1.2.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 +84 -51
- package/dist/auth-2QIFQZTL.js +12 -0
- package/dist/bug-DXLBBW3U.js +10 -0
- package/dist/{chunk-R6IMTNKV.js → chunk-2CFO5GVH.js} +0 -35
- package/dist/chunk-A7QU6JC6.js +119 -0
- package/dist/chunk-CAYCSBNX.js +202 -0
- package/dist/chunk-EDP55FCI.js +485 -0
- package/dist/chunk-I4ZVNLNO.js +4648 -0
- package/dist/chunk-IMZN36GC.js +159 -0
- package/dist/chunk-JBKFAD4M.js +650 -0
- package/dist/chunk-MOZHC2GX.js +351 -0
- package/dist/chunk-PK3OQVBG.js +91 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/chunk-SMR7JQK6.js +399 -0
- package/dist/chunk-UDBFDXJI.js +696 -0
- package/dist/chunk-UG2ZZ7CM.js +737 -0
- package/dist/chunk-VKM3HAHW.js +832 -0
- package/dist/chunk-WCCBJSNI.js +62 -0
- package/dist/code-review-FSTYDHNG.js +16 -0
- package/dist/execute-LYID2ODD.js +13 -0
- package/dist/index.js +1250 -7206
- package/dist/keys-EL3FUM5O.js +15 -0
- package/dist/list-VXMVEIL5.js +13 -0
- package/dist/{parser-D6PBQUJH.js → parser-M4DI7A24.js} +2 -1
- package/dist/plan-7VSFESVD.js +16 -0
- package/dist/preferences-PL2ON5VY.js +17 -0
- package/dist/review-3R6QXAXQ.js +27 -0
- package/package.json +21 -1
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReviewDatabase,
|
|
3
|
+
ReviewService,
|
|
4
|
+
analyzeProject,
|
|
5
|
+
featuresToTasks,
|
|
6
|
+
readArchitectureContext
|
|
7
|
+
} from "./chunk-VKM3HAHW.js";
|
|
8
|
+
|
|
9
|
+
// src/cli/review.ts
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import ora from "ora";
|
|
12
|
+
function getReviewDb(cwd) {
|
|
13
|
+
return new ReviewDatabase(cwd);
|
|
14
|
+
}
|
|
15
|
+
function formatStatus(status) {
|
|
16
|
+
switch (status) {
|
|
17
|
+
case "pending":
|
|
18
|
+
return chalk.yellow("\u23F3 pending");
|
|
19
|
+
case "in_review":
|
|
20
|
+
return chalk.blue("\u{1F50D} in_review");
|
|
21
|
+
case "completed":
|
|
22
|
+
return chalk.green("\u2713 completed");
|
|
23
|
+
case "needs_fix":
|
|
24
|
+
return chalk.red("\u26A0 needs_fix");
|
|
25
|
+
default:
|
|
26
|
+
return status;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function formatPriority(priority) {
|
|
30
|
+
switch (priority) {
|
|
31
|
+
case "critical":
|
|
32
|
+
return chalk.red.bold("CRITICAL");
|
|
33
|
+
case "high":
|
|
34
|
+
return chalk.red("HIGH");
|
|
35
|
+
case "medium":
|
|
36
|
+
return chalk.yellow("MEDIUM");
|
|
37
|
+
case "low":
|
|
38
|
+
return chalk.blue("LOW");
|
|
39
|
+
case "none":
|
|
40
|
+
return chalk.green("NONE");
|
|
41
|
+
default:
|
|
42
|
+
return chalk.dim("-");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function reviewInit() {
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
const db = getReviewDb(cwd);
|
|
48
|
+
if (db.isInitialized()) {
|
|
49
|
+
console.log(chalk.yellow("Code review already initialized in this project."));
|
|
50
|
+
console.log(chalk.dim("Run `archon review status` to see current status."));
|
|
51
|
+
console.log(chalk.dim("Run `archon review analyze` to re-analyze the project."));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const spinner = ora("Initializing code review database...").start();
|
|
55
|
+
try {
|
|
56
|
+
db.open();
|
|
57
|
+
spinner.succeed(chalk.green("Code review initialized!"));
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(chalk.dim("Database created at: docs/code-review/review-tasks.db"));
|
|
60
|
+
console.log();
|
|
61
|
+
console.log("Next steps:");
|
|
62
|
+
console.log(chalk.cyan(" archon review analyze") + " - Analyze project and populate review tasks");
|
|
63
|
+
console.log(chalk.cyan(" archon review status") + " - Show review progress");
|
|
64
|
+
} catch (error) {
|
|
65
|
+
spinner.fail(chalk.red("Failed to initialize database"));
|
|
66
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
} finally {
|
|
69
|
+
db.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function reviewAnalyze() {
|
|
73
|
+
const cwd = process.cwd();
|
|
74
|
+
const spinner = ora("Analyzing project structure...").start();
|
|
75
|
+
try {
|
|
76
|
+
const analysis = await analyzeProject(cwd);
|
|
77
|
+
spinner.text = "Checking documentation...";
|
|
78
|
+
const missingDocs = [];
|
|
79
|
+
if (!analysis.hasArchitecture) missingDocs.push("ARCHITECTURE.md");
|
|
80
|
+
if (!analysis.hasDataDictionary) missingDocs.push("DATA-DICTIONARY.md");
|
|
81
|
+
if (!analysis.hasDependencies) missingDocs.push("DEPENDENCIES.md");
|
|
82
|
+
spinner.text = "Reading architecture context...";
|
|
83
|
+
const archContext = await readArchitectureContext(cwd);
|
|
84
|
+
spinner.text = "Populating review tasks...";
|
|
85
|
+
const db = getReviewDb(cwd);
|
|
86
|
+
db.open();
|
|
87
|
+
db.clearAllTasks();
|
|
88
|
+
const tasks = featuresToTasks(analysis.features, archContext ?? void 0);
|
|
89
|
+
let taskCount = 0;
|
|
90
|
+
for (const task of tasks) {
|
|
91
|
+
db.insertTask(task);
|
|
92
|
+
taskCount++;
|
|
93
|
+
}
|
|
94
|
+
db.close();
|
|
95
|
+
spinner.succeed(chalk.green("Project analyzed!"));
|
|
96
|
+
console.log();
|
|
97
|
+
console.log(chalk.blue("Project:") + ` ${analysis.name}`);
|
|
98
|
+
console.log(chalk.blue("Files scanned:") + ` ${analysis.files.length}`);
|
|
99
|
+
console.log(chalk.blue("Features found:") + ` ${analysis.features.length}`);
|
|
100
|
+
console.log(chalk.blue("Review tasks created:") + ` ${taskCount}`);
|
|
101
|
+
console.log();
|
|
102
|
+
const sourceFiles = analysis.files.filter((f) => f.type === "source");
|
|
103
|
+
const testFiles = analysis.files.filter((f) => f.type === "test");
|
|
104
|
+
console.log(chalk.dim(` Source files: ${sourceFiles.length}`));
|
|
105
|
+
console.log(chalk.dim(` Test files: ${testFiles.length}`));
|
|
106
|
+
console.log();
|
|
107
|
+
if (missingDocs.length > 0) {
|
|
108
|
+
console.log(chalk.yellow("Missing documentation:"));
|
|
109
|
+
for (const doc of missingDocs) {
|
|
110
|
+
console.log(chalk.yellow(` \u25CB ${doc}`));
|
|
111
|
+
}
|
|
112
|
+
console.log(chalk.dim("Consider creating these for better review context."));
|
|
113
|
+
console.log();
|
|
114
|
+
} else {
|
|
115
|
+
console.log(chalk.green("\u2713 All documentation present"));
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
118
|
+
const categories = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const feature of analysis.features) {
|
|
120
|
+
categories.set(feature.category, (categories.get(feature.category) ?? 0) + 1);
|
|
121
|
+
}
|
|
122
|
+
console.log(chalk.blue("Categories:"));
|
|
123
|
+
for (const [category, count] of Array.from(categories.entries()).sort()) {
|
|
124
|
+
console.log(` ${category}: ${count} features`);
|
|
125
|
+
}
|
|
126
|
+
console.log();
|
|
127
|
+
console.log("Next steps:");
|
|
128
|
+
console.log(chalk.cyan(" archon review status") + " - Show review progress");
|
|
129
|
+
console.log(chalk.cyan(" archon review next") + " - Get next task to review");
|
|
130
|
+
} catch (error) {
|
|
131
|
+
spinner.fail(chalk.red("Analysis failed"));
|
|
132
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function reviewStatus() {
|
|
137
|
+
const cwd = process.cwd();
|
|
138
|
+
const db = getReviewDb(cwd);
|
|
139
|
+
if (!db.isInitialized()) {
|
|
140
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
141
|
+
console.log(chalk.dim("Run `archon review init` to get started."));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
db.open();
|
|
145
|
+
const stats = db.getStats();
|
|
146
|
+
db.close();
|
|
147
|
+
console.log(chalk.green("Code Review Status"));
|
|
148
|
+
console.log();
|
|
149
|
+
const completed = stats.completed + stats.needsFix;
|
|
150
|
+
const total = stats.total || 1;
|
|
151
|
+
const progress = Math.round(completed / total * 100);
|
|
152
|
+
const barWidth = 30;
|
|
153
|
+
const filledWidth = Math.round(completed / total * barWidth);
|
|
154
|
+
const bar = "\u2588".repeat(filledWidth) + "\u2591".repeat(barWidth - filledWidth);
|
|
155
|
+
console.log(chalk.blue("Progress:") + ` [${bar}] ${progress}%`);
|
|
156
|
+
console.log();
|
|
157
|
+
console.log(chalk.blue("Tasks:"));
|
|
158
|
+
console.log(` Total: ${stats.total}`);
|
|
159
|
+
console.log(` Pending: ${chalk.yellow(String(stats.pending))}`);
|
|
160
|
+
console.log(` In Review: ${chalk.blue(String(stats.inReview))}`);
|
|
161
|
+
console.log(` Completed: ${chalk.green(String(stats.completed))}`);
|
|
162
|
+
console.log(` Needs Fix: ${chalk.red(String(stats.needsFix))}`);
|
|
163
|
+
console.log();
|
|
164
|
+
if (stats.passing > 0 || stats.failing > 0) {
|
|
165
|
+
console.log(chalk.blue("Results:"));
|
|
166
|
+
console.log(` Passing: ${chalk.green(String(stats.passing))}`);
|
|
167
|
+
console.log(` Failing: ${chalk.red(String(stats.failing))}`);
|
|
168
|
+
console.log();
|
|
169
|
+
}
|
|
170
|
+
if (stats.pending > 0) {
|
|
171
|
+
console.log(chalk.dim("Run `archon review next` to get the next task."));
|
|
172
|
+
} else if (stats.needsFix > 0) {
|
|
173
|
+
console.log(chalk.dim("Run `archon review plan` to generate fix plans."));
|
|
174
|
+
} else if (stats.total > 0) {
|
|
175
|
+
console.log(chalk.green("\u2713 All tasks reviewed!"));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async function reviewNext() {
|
|
179
|
+
const cwd = process.cwd();
|
|
180
|
+
const db = getReviewDb(cwd);
|
|
181
|
+
if (!db.isInitialized()) {
|
|
182
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
183
|
+
console.log(chalk.dim("Run `archon review init` to get started."));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
db.open();
|
|
187
|
+
const task = db.getNextPendingTask();
|
|
188
|
+
db.close();
|
|
189
|
+
if (!task) {
|
|
190
|
+
console.log(chalk.green("No pending tasks!"));
|
|
191
|
+
console.log(chalk.dim("All tasks have been reviewed."));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
printTaskDetails(task);
|
|
195
|
+
console.log();
|
|
196
|
+
console.log(chalk.blue("Instructions:"));
|
|
197
|
+
console.log(" 1. Review the files listed above");
|
|
198
|
+
console.log(" 2. Compare implementation against expected behavior");
|
|
199
|
+
console.log(" 3. Document any issues found");
|
|
200
|
+
console.log(" 4. Update the task with your findings:");
|
|
201
|
+
console.log();
|
|
202
|
+
console.log(chalk.cyan(` archon review update ${task.id} --status completed --passes true`));
|
|
203
|
+
console.log(" or");
|
|
204
|
+
console.log(chalk.cyan(` archon review update ${task.id} --status needs_fix --issues '[{"type":"bug","severity":"high","description":"..."}]' --fix-plan "..."`));
|
|
205
|
+
}
|
|
206
|
+
async function reviewShow(idStr) {
|
|
207
|
+
const id = parseInt(idStr, 10);
|
|
208
|
+
if (isNaN(id)) {
|
|
209
|
+
console.error(chalk.red("Invalid task ID"));
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const cwd = process.cwd();
|
|
213
|
+
const db = getReviewDb(cwd);
|
|
214
|
+
if (!db.isInitialized()) {
|
|
215
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
db.open();
|
|
219
|
+
const task = db.getTask(id);
|
|
220
|
+
db.close();
|
|
221
|
+
if (!task) {
|
|
222
|
+
console.error(chalk.red(`Task ${id} not found`));
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
printTaskDetails(task);
|
|
226
|
+
}
|
|
227
|
+
function printTaskDetails(task) {
|
|
228
|
+
console.log(chalk.green(`Review Task #${task.id}`));
|
|
229
|
+
console.log();
|
|
230
|
+
console.log(chalk.blue("Feature:") + ` ${task.featureName}`);
|
|
231
|
+
console.log(chalk.blue("Category:") + ` ${task.category}${task.subcategory ? `/${task.subcategory}` : ""}`);
|
|
232
|
+
console.log(chalk.blue("Status:") + ` ${formatStatus(task.status)}`);
|
|
233
|
+
console.log();
|
|
234
|
+
if (task.filesToCheck && task.filesToCheck.length > 0) {
|
|
235
|
+
console.log(chalk.blue("Files to check:"));
|
|
236
|
+
for (const file of task.filesToCheck.slice(0, 10)) {
|
|
237
|
+
console.log(` - ${file}`);
|
|
238
|
+
}
|
|
239
|
+
if (task.filesToCheck.length > 10) {
|
|
240
|
+
console.log(chalk.dim(` ... and ${task.filesToCheck.length - 10} more`));
|
|
241
|
+
}
|
|
242
|
+
console.log();
|
|
243
|
+
}
|
|
244
|
+
if (task.expectedBehavior) {
|
|
245
|
+
console.log(chalk.blue("Expected Behavior:"));
|
|
246
|
+
console.log(` ${task.expectedBehavior}`);
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
if (task.acceptanceCriteria && task.acceptanceCriteria.length > 0) {
|
|
250
|
+
console.log(chalk.blue("Acceptance Criteria:"));
|
|
251
|
+
for (const criterion of task.acceptanceCriteria) {
|
|
252
|
+
console.log(` \u2022 ${criterion}`);
|
|
253
|
+
}
|
|
254
|
+
console.log();
|
|
255
|
+
}
|
|
256
|
+
if (task.architectureContext) {
|
|
257
|
+
console.log(chalk.blue("Architecture Context:"));
|
|
258
|
+
console.log(chalk.dim(task.architectureContext.slice(0, 500)));
|
|
259
|
+
if (task.architectureContext.length > 500) {
|
|
260
|
+
console.log(chalk.dim("..."));
|
|
261
|
+
}
|
|
262
|
+
console.log();
|
|
263
|
+
}
|
|
264
|
+
if (task.status === "completed" || task.status === "needs_fix") {
|
|
265
|
+
console.log(chalk.blue("Review Results:"));
|
|
266
|
+
console.log(` Passes: ${task.passesReview ? chalk.green("Yes") : chalk.red("No")}`);
|
|
267
|
+
console.log(` Priority: ${formatPriority(task.fixPriority)}`);
|
|
268
|
+
if (task.issuesFound && task.issuesFound.length > 0) {
|
|
269
|
+
console.log();
|
|
270
|
+
console.log(chalk.blue("Issues Found:"));
|
|
271
|
+
for (const issue of task.issuesFound) {
|
|
272
|
+
console.log(` [${issue.severity.toUpperCase()}] ${issue.type}: ${issue.description}`);
|
|
273
|
+
if (issue.location) {
|
|
274
|
+
console.log(chalk.dim(` Location: ${issue.location}`));
|
|
275
|
+
}
|
|
276
|
+
if (issue.suggestion) {
|
|
277
|
+
console.log(chalk.dim(` Suggestion: ${issue.suggestion}`));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (task.fixPlan) {
|
|
282
|
+
console.log();
|
|
283
|
+
console.log(chalk.blue("Fix Plan:"));
|
|
284
|
+
console.log(` ${task.fixPlan}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (task.reviewedBy) {
|
|
288
|
+
console.log();
|
|
289
|
+
console.log(chalk.dim(`Reviewed by: ${task.reviewedBy}`));
|
|
290
|
+
console.log(chalk.dim(`Reviewed at: ${task.reviewedAt}`));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function reviewUpdate(idStr, options) {
|
|
294
|
+
const id = parseInt(idStr, 10);
|
|
295
|
+
if (isNaN(id)) {
|
|
296
|
+
console.error(chalk.red("Invalid task ID"));
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
const cwd = process.cwd();
|
|
300
|
+
const db = getReviewDb(cwd);
|
|
301
|
+
if (!db.isInitialized()) {
|
|
302
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
db.open();
|
|
306
|
+
const task = db.getTask(id);
|
|
307
|
+
if (!task) {
|
|
308
|
+
db.close();
|
|
309
|
+
console.error(chalk.red(`Task ${id} not found`));
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
const validStatuses = ["pending", "in_review", "completed", "needs_fix"];
|
|
313
|
+
const validPriorities = ["critical", "high", "medium", "low", "none"];
|
|
314
|
+
if (options.status && !validStatuses.includes(options.status)) {
|
|
315
|
+
db.close();
|
|
316
|
+
console.error(chalk.red(`Invalid status. Must be one of: ${validStatuses.join(", ")}`));
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
if (options.priority && !validPriorities.includes(options.priority)) {
|
|
320
|
+
db.close();
|
|
321
|
+
console.error(chalk.red(`Invalid priority. Must be one of: ${validPriorities.join(", ")}`));
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
let parsedIssues;
|
|
325
|
+
if (options.issues) {
|
|
326
|
+
try {
|
|
327
|
+
parsedIssues = JSON.parse(options.issues);
|
|
328
|
+
} catch {
|
|
329
|
+
db.close();
|
|
330
|
+
console.error(chalk.red("Invalid issues JSON format"));
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const success = db.updateTask(id, {
|
|
335
|
+
status: options.status,
|
|
336
|
+
passesReview: options.passes,
|
|
337
|
+
issuesFound: parsedIssues,
|
|
338
|
+
fixPlan: options.fixPlan,
|
|
339
|
+
fixPriority: options.priority,
|
|
340
|
+
architectureImpact: options.impact,
|
|
341
|
+
reviewedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
342
|
+
reviewedBy: "cli-user"
|
|
343
|
+
});
|
|
344
|
+
db.close();
|
|
345
|
+
if (success) {
|
|
346
|
+
console.log(chalk.green(`Task ${id} updated successfully`));
|
|
347
|
+
} else {
|
|
348
|
+
console.error(chalk.red("Failed to update task"));
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
async function reviewList(options) {
|
|
353
|
+
const cwd = process.cwd();
|
|
354
|
+
const db = getReviewDb(cwd);
|
|
355
|
+
if (!db.isInitialized()) {
|
|
356
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
db.open();
|
|
360
|
+
let tasks;
|
|
361
|
+
if (options.status) {
|
|
362
|
+
const validStatuses = ["pending", "in_review", "completed", "needs_fix"];
|
|
363
|
+
if (!validStatuses.includes(options.status)) {
|
|
364
|
+
db.close();
|
|
365
|
+
console.error(chalk.red(`Invalid status. Must be one of: ${validStatuses.join(", ")}`));
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
tasks = db.getTasksByStatus(options.status);
|
|
369
|
+
} else {
|
|
370
|
+
tasks = db.getAllTasks();
|
|
371
|
+
}
|
|
372
|
+
db.close();
|
|
373
|
+
if (options.category) {
|
|
374
|
+
tasks = tasks.filter((t) => t.category === options.category);
|
|
375
|
+
}
|
|
376
|
+
if (tasks.length === 0) {
|
|
377
|
+
console.log(chalk.yellow("No tasks found matching criteria."));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
console.log(chalk.green(`Review Tasks (${tasks.length})`));
|
|
381
|
+
console.log();
|
|
382
|
+
const byCategory = /* @__PURE__ */ new Map();
|
|
383
|
+
for (const task of tasks) {
|
|
384
|
+
if (!byCategory.has(task.category)) {
|
|
385
|
+
byCategory.set(task.category, []);
|
|
386
|
+
}
|
|
387
|
+
byCategory.get(task.category)?.push(task);
|
|
388
|
+
}
|
|
389
|
+
for (const [category, categoryTasks] of byCategory) {
|
|
390
|
+
console.log(chalk.blue(category.toUpperCase()));
|
|
391
|
+
for (const task of categoryTasks) {
|
|
392
|
+
const statusStr = formatStatus(task.status);
|
|
393
|
+
const priorityStr = task.fixPriority ? ` ${formatPriority(task.fixPriority)}` : "";
|
|
394
|
+
console.log(` #${task.id} ${task.featureName} ${statusStr}${priorityStr}`);
|
|
395
|
+
}
|
|
396
|
+
console.log();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
async function reviewPlan() {
|
|
400
|
+
const cwd = process.cwd();
|
|
401
|
+
const db = getReviewDb(cwd);
|
|
402
|
+
if (!db.isInitialized()) {
|
|
403
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
db.open();
|
|
407
|
+
const stats = db.getStats();
|
|
408
|
+
const tasksNeedingFix = db.getTasksNeedingFix();
|
|
409
|
+
db.close();
|
|
410
|
+
if (stats.pending > 0) {
|
|
411
|
+
console.log(chalk.yellow(`${stats.pending} tasks still pending review.`));
|
|
412
|
+
console.log(chalk.dim("Complete all reviews before generating fix plan."));
|
|
413
|
+
console.log(chalk.dim("Run `archon review next` to continue reviewing."));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (tasksNeedingFix.length === 0) {
|
|
417
|
+
console.log(chalk.green("No fixes needed! All tasks passed review."));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
console.log(chalk.green("Fix Plan"));
|
|
421
|
+
console.log();
|
|
422
|
+
console.log(chalk.dim(`Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`));
|
|
423
|
+
console.log();
|
|
424
|
+
const byPriority = /* @__PURE__ */ new Map();
|
|
425
|
+
for (const task of tasksNeedingFix) {
|
|
426
|
+
const priority = task.fixPriority ?? "none";
|
|
427
|
+
if (!byPriority.has(priority)) {
|
|
428
|
+
byPriority.set(priority, []);
|
|
429
|
+
}
|
|
430
|
+
byPriority.get(priority)?.push(task);
|
|
431
|
+
}
|
|
432
|
+
const priorityOrder = ["critical", "high", "medium", "low", "none"];
|
|
433
|
+
for (const priority of priorityOrder) {
|
|
434
|
+
const priorityTasks = byPriority.get(priority);
|
|
435
|
+
if (!priorityTasks || priorityTasks.length === 0) continue;
|
|
436
|
+
console.log(formatPriority(priority) + ` (${priorityTasks.length})`);
|
|
437
|
+
console.log();
|
|
438
|
+
for (const task of priorityTasks) {
|
|
439
|
+
console.log(chalk.blue(` #${task.id} ${task.featureName}`));
|
|
440
|
+
if (task.issuesFound && task.issuesFound.length > 0) {
|
|
441
|
+
console.log(chalk.dim(" Issues:"));
|
|
442
|
+
for (const issue of task.issuesFound) {
|
|
443
|
+
console.log(` - [${issue.severity}] ${issue.description}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (task.fixPlan) {
|
|
447
|
+
console.log(chalk.dim(" Fix Plan:"));
|
|
448
|
+
console.log(` ${task.fixPlan}`);
|
|
449
|
+
}
|
|
450
|
+
if (task.architectureImpact) {
|
|
451
|
+
console.log(chalk.yellow(" \u26A0 Architecture Impact:"));
|
|
452
|
+
console.log(` ${task.architectureImpact}`);
|
|
453
|
+
}
|
|
454
|
+
console.log();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
console.log(chalk.blue("Summary:"));
|
|
458
|
+
console.log(` Total fixes needed: ${tasksNeedingFix.length}`);
|
|
459
|
+
console.log(` Critical: ${byPriority.get("critical")?.length ?? 0}`);
|
|
460
|
+
console.log(` High: ${byPriority.get("high")?.length ?? 0}`);
|
|
461
|
+
console.log(` Medium: ${byPriority.get("medium")?.length ?? 0}`);
|
|
462
|
+
console.log(` Low: ${byPriority.get("low")?.length ?? 0}`);
|
|
463
|
+
console.log();
|
|
464
|
+
console.log(chalk.dim("Address critical and high priority issues first."));
|
|
465
|
+
console.log(chalk.dim("Run `archon review export` to save this report."));
|
|
466
|
+
}
|
|
467
|
+
async function reviewExport(options) {
|
|
468
|
+
const cwd = process.cwd();
|
|
469
|
+
const db = getReviewDb(cwd);
|
|
470
|
+
if (!db.isInitialized()) {
|
|
471
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
db.open();
|
|
475
|
+
const tasks = db.getAllTasks();
|
|
476
|
+
const stats = db.getStats();
|
|
477
|
+
db.close();
|
|
478
|
+
const analysis = await analyzeProject(cwd);
|
|
479
|
+
const format = options.format ?? "markdown";
|
|
480
|
+
if (format === "json") {
|
|
481
|
+
const report = {
|
|
482
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
483
|
+
projectName: analysis.name,
|
|
484
|
+
projectPath: analysis.path,
|
|
485
|
+
stats,
|
|
486
|
+
tasks
|
|
487
|
+
};
|
|
488
|
+
console.log(JSON.stringify(report, null, 2));
|
|
489
|
+
} else {
|
|
490
|
+
console.log(`# Code Review Report`);
|
|
491
|
+
console.log();
|
|
492
|
+
console.log(`**Project:** ${analysis.name}`);
|
|
493
|
+
console.log(`**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
494
|
+
console.log();
|
|
495
|
+
console.log(`## Summary`);
|
|
496
|
+
console.log();
|
|
497
|
+
console.log(`| Metric | Value |`);
|
|
498
|
+
console.log(`|--------|-------|`);
|
|
499
|
+
console.log(`| Total Tasks | ${stats.total} |`);
|
|
500
|
+
console.log(`| Completed | ${stats.completed} |`);
|
|
501
|
+
console.log(`| Needs Fix | ${stats.needsFix} |`);
|
|
502
|
+
console.log(`| Passing | ${stats.passing} |`);
|
|
503
|
+
console.log(`| Failing | ${stats.failing} |`);
|
|
504
|
+
console.log();
|
|
505
|
+
if (tasks.some((t) => t.status === "needs_fix")) {
|
|
506
|
+
console.log(`## Issues Found`);
|
|
507
|
+
console.log();
|
|
508
|
+
for (const task of tasks.filter((t) => t.status === "needs_fix")) {
|
|
509
|
+
console.log(`### ${task.featureName} (${task.category})`);
|
|
510
|
+
console.log();
|
|
511
|
+
console.log(`**Priority:** ${task.fixPriority ?? "not set"}`);
|
|
512
|
+
console.log();
|
|
513
|
+
if (task.issuesFound && task.issuesFound.length > 0) {
|
|
514
|
+
console.log(`**Issues:**`);
|
|
515
|
+
for (const issue of task.issuesFound) {
|
|
516
|
+
console.log(`- [${issue.severity.toUpperCase()}] ${issue.type}: ${issue.description}`);
|
|
517
|
+
}
|
|
518
|
+
console.log();
|
|
519
|
+
}
|
|
520
|
+
if (task.fixPlan) {
|
|
521
|
+
console.log(`**Fix Plan:** ${task.fixPlan}`);
|
|
522
|
+
console.log();
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async function reviewRun(options) {
|
|
529
|
+
const cwd = process.cwd();
|
|
530
|
+
const db = getReviewDb(cwd);
|
|
531
|
+
if (!db.isInitialized()) {
|
|
532
|
+
console.log(chalk.yellow("Code review not initialized."));
|
|
533
|
+
console.log(chalk.dim("Run `archon review init` to get started."));
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
db.open();
|
|
537
|
+
const stats = db.getStats();
|
|
538
|
+
db.close();
|
|
539
|
+
if (stats.pending === 0 && !options.taskId) {
|
|
540
|
+
console.log(chalk.green("No pending tasks to review."));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (!process.env["ANTHROPIC_API_KEY"]) {
|
|
544
|
+
console.log(chalk.yellow("ANTHROPIC_API_KEY not set."));
|
|
545
|
+
console.log(chalk.dim("Set the environment variable or use BYOK with `archon keys add anthropic`."));
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const service = new ReviewService({
|
|
549
|
+
projectPath: cwd,
|
|
550
|
+
apiKey: process.env["ANTHROPIC_API_KEY"],
|
|
551
|
+
maxFilesPerTask: 10,
|
|
552
|
+
onTaskStart: (task) => {
|
|
553
|
+
console.log(chalk.blue(`
|
|
554
|
+
\u{1F50D} Reviewing: ${task.featureName} (#${task.id})`));
|
|
555
|
+
},
|
|
556
|
+
onTaskComplete: (task, result) => {
|
|
557
|
+
if (result.passesReview) {
|
|
558
|
+
console.log(chalk.green(` \u2713 PASSED`));
|
|
559
|
+
} else {
|
|
560
|
+
console.log(chalk.red(` \u2717 NEEDS FIX (${result.issues.length} issues)`));
|
|
561
|
+
for (const issue of result.issues.slice(0, 3)) {
|
|
562
|
+
console.log(chalk.dim(` - [${issue.severity}] ${issue.description.slice(0, 60)}...`));
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
onError: (task, error) => {
|
|
567
|
+
console.log(chalk.red(` \u2717 ERROR: ${error.message}`));
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
try {
|
|
571
|
+
await service.initialize();
|
|
572
|
+
if (options.taskId) {
|
|
573
|
+
const id = parseInt(options.taskId, 10);
|
|
574
|
+
if (isNaN(id)) {
|
|
575
|
+
console.error(chalk.red("Invalid task ID"));
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
const spinner = ora(`Running AI review on task #${id}...`).start();
|
|
579
|
+
try {
|
|
580
|
+
const result = await service.reviewTask(id);
|
|
581
|
+
spinner.stop();
|
|
582
|
+
console.log();
|
|
583
|
+
printReviewResult(id, result);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
spinner.fail(chalk.red("Review failed"));
|
|
586
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
587
|
+
process.exit(1);
|
|
588
|
+
}
|
|
589
|
+
} else if (options.all) {
|
|
590
|
+
const limit = options.limit ? parseInt(options.limit, 10) : void 0;
|
|
591
|
+
const spinner = ora(`Running AI review on ${limit ?? "all"} pending tasks...`).start();
|
|
592
|
+
try {
|
|
593
|
+
spinner.stop();
|
|
594
|
+
console.log(chalk.green(`Starting AI Review`));
|
|
595
|
+
console.log(chalk.dim(`Pending tasks: ${stats.pending}`));
|
|
596
|
+
if (limit) {
|
|
597
|
+
console.log(chalk.dim(`Limit: ${limit}`));
|
|
598
|
+
}
|
|
599
|
+
const result = await service.reviewAllPending(limit);
|
|
600
|
+
console.log();
|
|
601
|
+
console.log(chalk.green("Review Complete"));
|
|
602
|
+
console.log();
|
|
603
|
+
console.log(chalk.blue("Results:"));
|
|
604
|
+
console.log(` Reviewed: ${result.tasksReviewed}`);
|
|
605
|
+
console.log(` Passed: ${chalk.green(String(result.tasksPassed))}`);
|
|
606
|
+
console.log(` Failed: ${chalk.red(String(result.tasksFailed))}`);
|
|
607
|
+
if (result.tasksErrored > 0) {
|
|
608
|
+
console.log(` Errors: ${chalk.yellow(String(result.tasksErrored))}`);
|
|
609
|
+
}
|
|
610
|
+
console.log();
|
|
611
|
+
console.log(chalk.blue("Token Usage:"));
|
|
612
|
+
console.log(` Input: ${result.totalUsage.inputTokens.toLocaleString()}`);
|
|
613
|
+
console.log(` Output: ${result.totalUsage.outputTokens.toLocaleString()}`);
|
|
614
|
+
console.log(` Cost: $${result.totalUsage.baseCost.toFixed(4)}`);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
spinner.fail(chalk.red("Review failed"));
|
|
617
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
} else {
|
|
621
|
+
const spinner = ora("Running AI review on next pending task...").start();
|
|
622
|
+
try {
|
|
623
|
+
const reviewResult = await service.reviewNext();
|
|
624
|
+
spinner.stop();
|
|
625
|
+
if (!reviewResult) {
|
|
626
|
+
console.log(chalk.green("No pending tasks."));
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
console.log();
|
|
630
|
+
printReviewResult(reviewResult.task.id, reviewResult.result);
|
|
631
|
+
} catch (error) {
|
|
632
|
+
spinner.fail(chalk.red("Review failed"));
|
|
633
|
+
console.error(error instanceof Error ? error.message : "Unknown error");
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const finalStats = service.getStats();
|
|
638
|
+
if (finalStats.pending > 0) {
|
|
639
|
+
console.log();
|
|
640
|
+
console.log(chalk.dim(`${finalStats.pending} tasks still pending.`));
|
|
641
|
+
console.log(chalk.dim("Run `archon review run --all` to review all."));
|
|
642
|
+
} else if (finalStats.needsFix > 0) {
|
|
643
|
+
console.log();
|
|
644
|
+
console.log(chalk.dim(`${finalStats.needsFix} tasks need fixes.`));
|
|
645
|
+
console.log(chalk.dim("Run `archon review plan` to see the fix plan."));
|
|
646
|
+
}
|
|
647
|
+
} finally {
|
|
648
|
+
service.close();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function printReviewResult(taskId, result) {
|
|
652
|
+
console.log(chalk.green(`Review Result for Task #${taskId}`));
|
|
653
|
+
console.log();
|
|
654
|
+
if (result.passesReview) {
|
|
655
|
+
console.log(chalk.green.bold(" \u2713 PASSED"));
|
|
656
|
+
} else {
|
|
657
|
+
console.log(chalk.red.bold(" \u2717 NEEDS FIX"));
|
|
658
|
+
console.log();
|
|
659
|
+
console.log(chalk.blue("Issues Found:"));
|
|
660
|
+
for (const issue of result.issues) {
|
|
661
|
+
const severityColor = issue.severity === "critical" ? chalk.red.bold : issue.severity === "high" ? chalk.red : issue.severity === "medium" ? chalk.yellow : chalk.blue;
|
|
662
|
+
console.log(` ${severityColor(`[${issue.severity.toUpperCase()}]`)} ${issue.type}: ${issue.description}`);
|
|
663
|
+
if (issue.location) {
|
|
664
|
+
console.log(chalk.dim(` at ${issue.location}`));
|
|
665
|
+
}
|
|
666
|
+
if (issue.suggestion) {
|
|
667
|
+
console.log(chalk.dim(` \u2192 ${issue.suggestion}`));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
if (result.fixPlan) {
|
|
672
|
+
console.log();
|
|
673
|
+
console.log(chalk.blue("Fix Plan:"));
|
|
674
|
+
console.log(` ${result.fixPlan}`);
|
|
675
|
+
}
|
|
676
|
+
if (result.architectureImpact) {
|
|
677
|
+
console.log();
|
|
678
|
+
console.log(chalk.yellow("Architecture Impact:"));
|
|
679
|
+
console.log(` ${result.architectureImpact}`);
|
|
680
|
+
}
|
|
681
|
+
console.log();
|
|
682
|
+
console.log(chalk.dim(`Tokens: ${result.usage.totalTokens.toLocaleString()} | Cost: $${result.usage.baseCost.toFixed(4)}`));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export {
|
|
686
|
+
reviewInit,
|
|
687
|
+
reviewAnalyze,
|
|
688
|
+
reviewStatus,
|
|
689
|
+
reviewNext,
|
|
690
|
+
reviewShow,
|
|
691
|
+
reviewUpdate,
|
|
692
|
+
reviewList,
|
|
693
|
+
reviewPlan,
|
|
694
|
+
reviewExport,
|
|
695
|
+
reviewRun
|
|
696
|
+
};
|