archondev 2.18.8 → 2.19.1
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 +20 -1
- package/dist/{bug-DCFTT2AF.js → bug-IJCK43FK.js} +4 -4
- package/dist/{chunk-AFC2XMZS.js → chunk-ACFMKTDL.js} +3 -3
- package/dist/{chunk-2BCITFWP.js → chunk-DCIIYVJW.js} +2 -2
- package/dist/{chunk-HJARQDQR.js → chunk-EIEU3IIY.js} +9 -0
- package/dist/{chunk-HGLPIM7J.js → chunk-KG35EHZY.js} +1 -1
- package/dist/{chunk-BFPWDOMA.js → chunk-M6GNIN64.js} +1 -1
- package/dist/{chunk-OAHFRSDS.js → chunk-O3B6BE5D.js} +1 -1
- package/dist/{chunk-SLUOXPMX.js → chunk-OREGEFTF.js} +4 -4
- package/dist/{chunk-5CFGPXQ3.js → chunk-PCTP3LKJ.js} +1 -1
- package/dist/{chunk-5BYCJAFM.js → chunk-PJRQI5UN.js} +1 -1
- package/dist/{chunk-BPYTLJOS.js → chunk-QTBRLNZQ.js} +1 -1
- package/dist/{chunk-HANCO7VL.js → chunk-YA562WHL.js} +1 -1
- package/dist/{code-review-6MU4UE5M.js → code-review-ODLXGXNZ.js} +2 -2
- package/dist/{execute-VGBCOD5W.js → execute-ZTJGSRBW.js} +5 -5
- package/dist/{geo-GEWH777F.js → geo-HRG7M7YX.js} +3 -3
- package/dist/index.js +1140 -98
- package/dist/{interviewer-NQHNMVH4.js → interviewer-BWM5SNOE.js} +1 -1
- package/dist/{list-QWQ4QBKM.js → list-REPLUXJF.js} +5 -5
- package/dist/{parallel-NXSICGS5.js → parallel-23VQYK7H.js} +5 -5
- package/dist/{plan-6AZ3U2DA.js → plan-HCYXLSSD.js} +4 -4
- package/dist/{review-AUG6GIL6.js → review-F6DHAGDF.js} +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-JF7JCK6H.js";
|
|
5
5
|
import {
|
|
6
6
|
createGeoCommand
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O3B6BE5D.js";
|
|
8
8
|
import {
|
|
9
9
|
bugReport
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DCIIYVJW.js";
|
|
11
11
|
import {
|
|
12
12
|
reviewAnalyze,
|
|
13
13
|
reviewExport,
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
reviewShow,
|
|
20
20
|
reviewStatus,
|
|
21
21
|
reviewUpdate
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import "./chunk-
|
|
22
|
+
} from "./chunk-M6GNIN64.js";
|
|
23
|
+
import "./chunk-KG35EHZY.js";
|
|
24
24
|
import {
|
|
25
25
|
resetPreferences,
|
|
26
26
|
setExecutionPreference,
|
|
@@ -46,13 +46,13 @@ import {
|
|
|
46
46
|
parallelRunWaves,
|
|
47
47
|
parallelSchedule,
|
|
48
48
|
parallelStatus
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-QTBRLNZQ.js";
|
|
50
50
|
import {
|
|
51
51
|
DependencyParser,
|
|
52
52
|
EnvironmentConfigLoader,
|
|
53
53
|
EnvironmentValidator,
|
|
54
54
|
execute
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-OREGEFTF.js";
|
|
56
56
|
import {
|
|
57
57
|
cloudCancel,
|
|
58
58
|
cloudLogs,
|
|
@@ -60,15 +60,19 @@ import {
|
|
|
60
60
|
} from "./chunk-EBHHIUCB.js";
|
|
61
61
|
import {
|
|
62
62
|
list
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-YA562WHL.js";
|
|
64
64
|
import {
|
|
65
65
|
listLocalAtoms,
|
|
66
66
|
loadAtom,
|
|
67
67
|
plan
|
|
68
|
-
} from "./chunk-
|
|
69
|
-
import "./chunk-
|
|
70
|
-
import "./chunk-
|
|
71
|
-
import
|
|
68
|
+
} from "./chunk-ACFMKTDL.js";
|
|
69
|
+
import "./chunk-PCTP3LKJ.js";
|
|
70
|
+
import "./chunk-PJRQI5UN.js";
|
|
71
|
+
import {
|
|
72
|
+
err,
|
|
73
|
+
ok,
|
|
74
|
+
sleep
|
|
75
|
+
} from "./chunk-EIEU3IIY.js";
|
|
72
76
|
import {
|
|
73
77
|
createAuthedSupabaseClient
|
|
74
78
|
} from "./chunk-Q3GIFHIQ.js";
|
|
@@ -107,8 +111,8 @@ import {
|
|
|
107
111
|
import "./chunk-4VNS5WPM.js";
|
|
108
112
|
|
|
109
113
|
// src/cli/index.ts
|
|
110
|
-
import { Command as
|
|
111
|
-
import
|
|
114
|
+
import { Command as Command3 } from "commander";
|
|
115
|
+
import chalk18 from "chalk";
|
|
112
116
|
import "dotenv/config";
|
|
113
117
|
|
|
114
118
|
// src/cli/promote.ts
|
|
@@ -427,8 +431,8 @@ var ContextManager = class _ContextManager {
|
|
|
427
431
|
async clearPendingAtoms(cwd) {
|
|
428
432
|
const filePath = join2(cwd, _ContextManager.PENDING_ATOMS_FILE);
|
|
429
433
|
if (existsSync2(filePath)) {
|
|
430
|
-
const { unlink:
|
|
431
|
-
await
|
|
434
|
+
const { unlink: unlink4 } = await import("fs/promises");
|
|
435
|
+
await unlink4(filePath);
|
|
432
436
|
}
|
|
433
437
|
}
|
|
434
438
|
};
|
|
@@ -2738,7 +2742,7 @@ async function start(options = {}) {
|
|
|
2738
2742
|
}
|
|
2739
2743
|
}
|
|
2740
2744
|
if (!config.tier || config.tier === "FREE") {
|
|
2741
|
-
const isFirstRun = !existsSync6(join6(cwd, ".archon")) &&
|
|
2745
|
+
const isFirstRun = !existsSync6(join6(cwd, ".archon")) && resolveArchitecturePath(cwd).path === null;
|
|
2742
2746
|
if (isFirstRun && !config.tierConfirmed) {
|
|
2743
2747
|
console.log(chalk6.bold("How would you like to use ArchonDev?\n"));
|
|
2744
2748
|
const selection = await promptTierSelection();
|
|
@@ -3030,6 +3034,18 @@ function displayGovernanceStatus(status2) {
|
|
|
3030
3034
|
}
|
|
3031
3035
|
console.log();
|
|
3032
3036
|
}
|
|
3037
|
+
var ACTIVE_ARCH_PATH = join6(".archon", "active", "architecture.md");
|
|
3038
|
+
function resolveArchitecturePath(cwd) {
|
|
3039
|
+
const agdPath = join6(cwd, ACTIVE_ARCH_PATH);
|
|
3040
|
+
if (existsSync6(agdPath)) {
|
|
3041
|
+
return { path: agdPath, source: "agd" };
|
|
3042
|
+
}
|
|
3043
|
+
const legacyPath = join6(cwd, "ARCHITECTURE.md");
|
|
3044
|
+
if (existsSync6(legacyPath)) {
|
|
3045
|
+
return { path: legacyPath, source: "legacy" };
|
|
3046
|
+
}
|
|
3047
|
+
return { path: null, source: null };
|
|
3048
|
+
}
|
|
3033
3049
|
function detectProjectState(cwd) {
|
|
3034
3050
|
const sourceDirs = ["src", "lib", "app", "packages", "components", "pages", "api"];
|
|
3035
3051
|
const sourceExtensions = [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java", ".rb", ".php"];
|
|
@@ -3053,7 +3069,7 @@ function detectProjectState(cwd) {
|
|
|
3053
3069
|
if (!hasSourceFiles) {
|
|
3054
3070
|
hasSourceFiles = projectMarkers.some((marker) => existsSync6(join6(cwd, marker)));
|
|
3055
3071
|
}
|
|
3056
|
-
const hasArchitecture =
|
|
3072
|
+
const hasArchitecture = resolveArchitecturePath(cwd).path !== null;
|
|
3057
3073
|
const hasProgress = existsSync6(join6(cwd, "progress.txt"));
|
|
3058
3074
|
const hasReviewDb = existsSync6(join6(cwd, "docs", "code-review", "review-tasks.db"));
|
|
3059
3075
|
let hasProgressEntries = false;
|
|
@@ -3102,9 +3118,9 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
3102
3118
|
dependencyRulesCount: 0,
|
|
3103
3119
|
pendingAtomsCount: 0
|
|
3104
3120
|
};
|
|
3105
|
-
const
|
|
3106
|
-
if (
|
|
3107
|
-
const parser = new ArchitectureParser(
|
|
3121
|
+
const archInfo = resolveArchitecturePath(cwd);
|
|
3122
|
+
if (archInfo.path) {
|
|
3123
|
+
const parser = new ArchitectureParser(archInfo.path);
|
|
3108
3124
|
const result = await parser.parse();
|
|
3109
3125
|
if (result.success && result.schema) {
|
|
3110
3126
|
status2.hasArchitecture = true;
|
|
@@ -3168,7 +3184,7 @@ async function handleNewProject(cwd, _state) {
|
|
|
3168
3184
|
}
|
|
3169
3185
|
if (intent.mode === "ad_hoc" && intent.confidence >= 0.7) {
|
|
3170
3186
|
console.log(chalk6.dim("\n> Got it! Creating a task for this...\n"));
|
|
3171
|
-
const { plan: plan2 } = await import("./plan-
|
|
3187
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3172
3188
|
await plan2(initialResponse, {});
|
|
3173
3189
|
return;
|
|
3174
3190
|
}
|
|
@@ -3192,7 +3208,7 @@ async function handleNewProject(cwd, _state) {
|
|
|
3192
3208
|
break;
|
|
3193
3209
|
case "2":
|
|
3194
3210
|
console.log(chalk6.dim("\n> Creating a task for this...\n"));
|
|
3195
|
-
const { plan: plan2 } = await import("./plan-
|
|
3211
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3196
3212
|
await plan2(initialResponse, {});
|
|
3197
3213
|
break;
|
|
3198
3214
|
case "3":
|
|
@@ -3220,9 +3236,9 @@ async function showNewProjectMenu(cwd) {
|
|
|
3220
3236
|
await quickStart(cwd);
|
|
3221
3237
|
break;
|
|
3222
3238
|
case "3": {
|
|
3223
|
-
const description = await
|
|
3239
|
+
const description = await promptWithCommands("Describe what you want to do", { allowMultiline: true });
|
|
3224
3240
|
if (description.trim()) {
|
|
3225
|
-
const { plan: plan2 } = await import("./plan-
|
|
3241
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3226
3242
|
await plan2(description, {});
|
|
3227
3243
|
}
|
|
3228
3244
|
break;
|
|
@@ -3286,9 +3302,9 @@ async function runExploreFlow(cwd) {
|
|
|
3286
3302
|
const choice = await promptWithCommands("Enter choice");
|
|
3287
3303
|
switch (choice.toLowerCase()) {
|
|
3288
3304
|
case "1": {
|
|
3289
|
-
const description = await
|
|
3305
|
+
const description = await promptWithCommands("Describe what you want to do", { allowMultiline: true });
|
|
3290
3306
|
if (description.trim()) {
|
|
3291
|
-
const { plan: plan2 } = await import("./plan-
|
|
3307
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3292
3308
|
await plan2(description, {});
|
|
3293
3309
|
}
|
|
3294
3310
|
break;
|
|
@@ -3381,11 +3397,14 @@ async function gatherProjectInfo(cwd) {
|
|
|
3381
3397
|
}
|
|
3382
3398
|
} catch {
|
|
3383
3399
|
}
|
|
3384
|
-
info.hasArchitecture =
|
|
3400
|
+
info.hasArchitecture = resolveArchitecturePath(cwd).path !== null;
|
|
3385
3401
|
info.hasProgress = existsSync6(join6(cwd, "progress.txt"));
|
|
3386
3402
|
if (info.hasArchitecture) {
|
|
3387
|
-
const
|
|
3388
|
-
|
|
3403
|
+
const archInfo = resolveArchitecturePath(cwd);
|
|
3404
|
+
if (!archInfo.path) {
|
|
3405
|
+
return info;
|
|
3406
|
+
}
|
|
3407
|
+
const parser = new ArchitectureParser(archInfo.path);
|
|
3389
3408
|
const result = await parser.parse();
|
|
3390
3409
|
if (result.success && result.schema) {
|
|
3391
3410
|
info.posture = result.schema.qualityLevel?.posture;
|
|
@@ -3396,7 +3415,7 @@ async function gatherProjectInfo(cwd) {
|
|
|
3396
3415
|
return info;
|
|
3397
3416
|
}
|
|
3398
3417
|
async function runConversationalInterview(cwd, initialMessage) {
|
|
3399
|
-
const { InterviewerAgent, createInterviewerAgent } = await import("./interviewer-
|
|
3418
|
+
const { InterviewerAgent, createInterviewerAgent } = await import("./interviewer-BWM5SNOE.js");
|
|
3400
3419
|
const agent = await createInterviewerAgent();
|
|
3401
3420
|
if (agent && agent.isAvailable()) {
|
|
3402
3421
|
await runAIInterview(cwd, initialMessage, agent);
|
|
@@ -3545,9 +3564,9 @@ ${state.forbiddenPatterns?.length ? `- **Forbidden patterns:** ${state.forbidden
|
|
|
3545
3564
|
console.log();
|
|
3546
3565
|
const continueChoice = await promptYesNo("Would you like to plan your first task now?", true);
|
|
3547
3566
|
if (continueChoice) {
|
|
3548
|
-
const description = await
|
|
3567
|
+
const description = await promptWithCommands("Describe what you want to build first", { allowMultiline: true });
|
|
3549
3568
|
if (description.trim()) {
|
|
3550
|
-
const { plan: plan2 } = await import("./plan-
|
|
3569
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3551
3570
|
await plan2(description, {});
|
|
3552
3571
|
}
|
|
3553
3572
|
}
|
|
@@ -3601,7 +3620,7 @@ async function handleAdaptExisting(cwd, state) {
|
|
|
3601
3620
|
}
|
|
3602
3621
|
if (intent.mode === "ad_hoc" && intent.confidence >= 0.7) {
|
|
3603
3622
|
console.log(chalk6.dim("\n> Got it! Creating a task for this...\n"));
|
|
3604
|
-
const { plan: plan2 } = await import("./plan-
|
|
3623
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3605
3624
|
await plan2(response, {});
|
|
3606
3625
|
return;
|
|
3607
3626
|
}
|
|
@@ -3630,9 +3649,9 @@ async function showAdaptExistingMenu(cwd, state) {
|
|
|
3630
3649
|
await runExploreFlow(cwd);
|
|
3631
3650
|
break;
|
|
3632
3651
|
case "2": {
|
|
3633
|
-
const description = await
|
|
3652
|
+
const description = await promptWithCommands("Describe what you want to do", { allowMultiline: true });
|
|
3634
3653
|
if (description.trim()) {
|
|
3635
|
-
const { plan: plan2 } = await import("./plan-
|
|
3654
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3636
3655
|
await plan2(description, {});
|
|
3637
3656
|
}
|
|
3638
3657
|
break;
|
|
@@ -3684,7 +3703,7 @@ async function analyzeAndAdapt(cwd) {
|
|
|
3684
3703
|
async function codeReviewFirst(cwd) {
|
|
3685
3704
|
console.log(chalk6.blue("\n-- Code Review Mode --\n"));
|
|
3686
3705
|
console.log(chalk6.dim("I'll analyze your code for issues without making any changes.\n"));
|
|
3687
|
-
const { reviewInit: reviewInit2, reviewAnalyze: reviewAnalyze2, reviewRun: reviewRun2 } = await import("./review-
|
|
3706
|
+
const { reviewInit: reviewInit2, reviewAnalyze: reviewAnalyze2, reviewRun: reviewRun2 } = await import("./review-F6DHAGDF.js");
|
|
3688
3707
|
const reviewDbPath = join6(cwd, "docs", "code-review", "review-tasks.db");
|
|
3689
3708
|
if (!existsSync6(reviewDbPath)) {
|
|
3690
3709
|
await reviewInit2();
|
|
@@ -3720,6 +3739,14 @@ async function handleContinueSession(cwd, state) {
|
|
|
3720
3739
|
}
|
|
3721
3740
|
function checkForHandoff(cwd) {
|
|
3722
3741
|
try {
|
|
3742
|
+
const currentContextPath = join6(cwd, ".archon", "current_context.md");
|
|
3743
|
+
if (existsSync6(currentContextPath)) {
|
|
3744
|
+
const content2 = readFileSync3(currentContextPath, "utf-8");
|
|
3745
|
+
const nextStepsMatch = content2.match(/## Next Actions[^\n]*\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
3746
|
+
if (nextStepsMatch && nextStepsMatch[1]) {
|
|
3747
|
+
return { nextSteps: nextStepsMatch[1].trim() };
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3723
3750
|
const progressPath = join6(cwd, "progress.txt");
|
|
3724
3751
|
if (!existsSync6(progressPath)) return null;
|
|
3725
3752
|
const content = readFileSync3(progressPath, "utf-8");
|
|
@@ -3783,7 +3810,7 @@ async function showMainMenu() {
|
|
|
3783
3810
|
}
|
|
3784
3811
|
async function showReviewProgress(cwd) {
|
|
3785
3812
|
try {
|
|
3786
|
-
const { ReviewDatabase } = await import("./code-review-
|
|
3813
|
+
const { ReviewDatabase } = await import("./code-review-ODLXGXNZ.js");
|
|
3787
3814
|
const db = new ReviewDatabase(cwd);
|
|
3788
3815
|
db.open();
|
|
3789
3816
|
const stats = db.getStats();
|
|
@@ -3800,18 +3827,18 @@ async function showReviewProgress(cwd) {
|
|
|
3800
3827
|
}
|
|
3801
3828
|
}
|
|
3802
3829
|
async function planTask() {
|
|
3803
|
-
const { plan: plan2 } = await import("./plan-
|
|
3804
|
-
const description = await
|
|
3830
|
+
const { plan: plan2 } = await import("./plan-HCYXLSSD.js");
|
|
3831
|
+
const description = await promptWithCommands("Describe what you want to build", { allowMultiline: true });
|
|
3805
3832
|
if (description.trim()) {
|
|
3806
3833
|
await plan2(description, {});
|
|
3807
3834
|
}
|
|
3808
3835
|
}
|
|
3809
3836
|
async function listAtoms() {
|
|
3810
|
-
const { list: list2 } = await import("./list-
|
|
3837
|
+
const { list: list2 } = await import("./list-REPLUXJF.js");
|
|
3811
3838
|
await list2({});
|
|
3812
3839
|
}
|
|
3813
3840
|
async function executeNext() {
|
|
3814
|
-
const { listLocalAtoms: listLocalAtoms2 } = await import("./plan-
|
|
3841
|
+
const { listLocalAtoms: listLocalAtoms2 } = await import("./plan-HCYXLSSD.js");
|
|
3815
3842
|
const { analyzeProject, getComplexityDescription, getModeDescription } = await import("./orchestration-HIF3KP25.js");
|
|
3816
3843
|
const { loadExecutionPreferences } = await import("./preferences-I6WETXOI.js");
|
|
3817
3844
|
const cwd = process.cwd();
|
|
@@ -3882,11 +3909,11 @@ async function executeNext() {
|
|
|
3882
3909
|
}
|
|
3883
3910
|
}
|
|
3884
3911
|
if (selectedMode === "parallel-cloud") {
|
|
3885
|
-
const { parallelExecuteCloud: parallelExecuteCloud2 } = await import("./parallel-
|
|
3912
|
+
const { parallelExecuteCloud: parallelExecuteCloud2 } = await import("./parallel-23VQYK7H.js");
|
|
3886
3913
|
await parallelExecuteCloud2(runIds);
|
|
3887
3914
|
return;
|
|
3888
3915
|
}
|
|
3889
|
-
const { parallelExecute } = await import("./parallel-
|
|
3916
|
+
const { parallelExecute } = await import("./parallel-23VQYK7H.js");
|
|
3890
3917
|
await parallelExecute(runIds);
|
|
3891
3918
|
return;
|
|
3892
3919
|
}
|
|
@@ -3894,14 +3921,14 @@ async function executeNext() {
|
|
|
3894
3921
|
const atomId = await prompt("Enter atom ID to execute (or press Enter for first pending)");
|
|
3895
3922
|
const targetId = atomId.trim() || pendingAtoms[0]?.id;
|
|
3896
3923
|
if (targetId) {
|
|
3897
|
-
const { execute: execute2 } = await import("./execute-
|
|
3924
|
+
const { execute: execute2 } = await import("./execute-ZTJGSRBW.js");
|
|
3898
3925
|
await execute2(targetId, {});
|
|
3899
3926
|
} else {
|
|
3900
3927
|
console.log(chalk6.yellow("No atom to execute."));
|
|
3901
3928
|
}
|
|
3902
3929
|
}
|
|
3903
3930
|
async function reportBug() {
|
|
3904
|
-
const { bugReport: bugReport2 } = await import("./bug-
|
|
3931
|
+
const { bugReport: bugReport2 } = await import("./bug-IJCK43FK.js");
|
|
3905
3932
|
const title = await prompt("Bug title");
|
|
3906
3933
|
if (title.trim()) {
|
|
3907
3934
|
await bugReport2(title, {});
|
|
@@ -3921,7 +3948,7 @@ async function reviewCode() {
|
|
|
3921
3948
|
const reviewDbPath = join6(cwd, "docs", "code-review", "review-tasks.db");
|
|
3922
3949
|
if (!existsSync6(reviewDbPath)) {
|
|
3923
3950
|
console.log(chalk6.dim("Code review not initialized. Starting setup...\n"));
|
|
3924
|
-
const { reviewInit: reviewInit2 } = await import("./review-
|
|
3951
|
+
const { reviewInit: reviewInit2 } = await import("./review-F6DHAGDF.js");
|
|
3925
3952
|
await reviewInit2();
|
|
3926
3953
|
console.log();
|
|
3927
3954
|
}
|
|
@@ -3936,27 +3963,27 @@ async function reviewCode() {
|
|
|
3936
3963
|
const choice = await prompt("Enter choice");
|
|
3937
3964
|
switch (choice.toLowerCase()) {
|
|
3938
3965
|
case "1": {
|
|
3939
|
-
const { reviewAnalyze: reviewAnalyze2 } = await import("./review-
|
|
3966
|
+
const { reviewAnalyze: reviewAnalyze2 } = await import("./review-F6DHAGDF.js");
|
|
3940
3967
|
await reviewAnalyze2();
|
|
3941
3968
|
break;
|
|
3942
3969
|
}
|
|
3943
3970
|
case "2": {
|
|
3944
|
-
const { reviewStatus: reviewStatus2 } = await import("./review-
|
|
3971
|
+
const { reviewStatus: reviewStatus2 } = await import("./review-F6DHAGDF.js");
|
|
3945
3972
|
await reviewStatus2();
|
|
3946
3973
|
break;
|
|
3947
3974
|
}
|
|
3948
3975
|
case "3": {
|
|
3949
|
-
const { reviewNext: reviewNext2 } = await import("./review-
|
|
3976
|
+
const { reviewNext: reviewNext2 } = await import("./review-F6DHAGDF.js");
|
|
3950
3977
|
await reviewNext2();
|
|
3951
3978
|
break;
|
|
3952
3979
|
}
|
|
3953
3980
|
case "4": {
|
|
3954
|
-
const { reviewList: reviewList2 } = await import("./review-
|
|
3981
|
+
const { reviewList: reviewList2 } = await import("./review-F6DHAGDF.js");
|
|
3955
3982
|
await reviewList2({});
|
|
3956
3983
|
break;
|
|
3957
3984
|
}
|
|
3958
3985
|
case "5": {
|
|
3959
|
-
const { reviewRun: reviewRun2 } = await import("./review-
|
|
3986
|
+
const { reviewRun: reviewRun2 } = await import("./review-F6DHAGDF.js");
|
|
3960
3987
|
await reviewRun2({ all: true });
|
|
3961
3988
|
break;
|
|
3962
3989
|
}
|
|
@@ -4014,32 +4041,28 @@ function prompt(question) {
|
|
|
4014
4041
|
});
|
|
4015
4042
|
});
|
|
4016
4043
|
}
|
|
4017
|
-
function promptMultiline(question
|
|
4044
|
+
function promptMultiline(question) {
|
|
4018
4045
|
return new Promise((resolve) => {
|
|
4019
4046
|
const rl = readline.createInterface({
|
|
4020
4047
|
input: process.stdin,
|
|
4021
4048
|
output: process.stdout
|
|
4022
4049
|
});
|
|
4023
4050
|
const lines = [];
|
|
4024
|
-
let timer = null;
|
|
4025
|
-
const finish = () => {
|
|
4026
|
-
if (timer) clearTimeout(timer);
|
|
4027
|
-
rl.close();
|
|
4028
|
-
resolve(lines.join("\n").trimEnd());
|
|
4029
|
-
};
|
|
4030
|
-
const scheduleFinish = () => {
|
|
4031
|
-
if (timer) clearTimeout(timer);
|
|
4032
|
-
timer = setTimeout(finish, idleMs);
|
|
4033
|
-
};
|
|
4034
4051
|
rl.on("line", (line) => {
|
|
4052
|
+
if (line.trim() === ".") {
|
|
4053
|
+
rl.close();
|
|
4054
|
+
return;
|
|
4055
|
+
}
|
|
4035
4056
|
lines.push(line);
|
|
4036
|
-
scheduleFinish();
|
|
4037
4057
|
});
|
|
4038
4058
|
rl.on("SIGINT", () => {
|
|
4039
4059
|
rl.close();
|
|
4040
4060
|
process.exit(0);
|
|
4041
4061
|
});
|
|
4042
|
-
rl.
|
|
4062
|
+
rl.on("close", () => {
|
|
4063
|
+
resolve(lines.join("\n").trimEnd());
|
|
4064
|
+
});
|
|
4065
|
+
rl.setPrompt(`${chalk6.cyan("?")} ${question} (multi-line: end with a single '.' line or Ctrl+D): `);
|
|
4043
4066
|
rl.prompt();
|
|
4044
4067
|
});
|
|
4045
4068
|
}
|
|
@@ -4059,7 +4082,7 @@ async function promptWithCommands(question, options = {}) {
|
|
|
4059
4082
|
async function runWebChecksSuite() {
|
|
4060
4083
|
const { a11yCheck: a11yCheck2 } = await import("./a11y-O35BAA25.js");
|
|
4061
4084
|
const { seoCheck } = await import("./seo-PMI42KRZ.js");
|
|
4062
|
-
const { geoAudit } = await import("./geo-
|
|
4085
|
+
const { geoAudit } = await import("./geo-HRG7M7YX.js");
|
|
4063
4086
|
console.log(chalk6.blue("\nRunning web checks (A11y, SEO, GEO)...\n"));
|
|
4064
4087
|
await a11yCheck2({});
|
|
4065
4088
|
await seoCheck({});
|
|
@@ -4099,17 +4122,17 @@ async function showWebChecksMenu() {
|
|
|
4099
4122
|
break;
|
|
4100
4123
|
}
|
|
4101
4124
|
case "4": {
|
|
4102
|
-
const { geoAudit } = await import("./geo-
|
|
4125
|
+
const { geoAudit } = await import("./geo-HRG7M7YX.js");
|
|
4103
4126
|
await geoAudit();
|
|
4104
4127
|
break;
|
|
4105
4128
|
}
|
|
4106
4129
|
case "5": {
|
|
4107
|
-
const { geoIdentity } = await import("./geo-
|
|
4130
|
+
const { geoIdentity } = await import("./geo-HRG7M7YX.js");
|
|
4108
4131
|
await geoIdentity();
|
|
4109
4132
|
break;
|
|
4110
4133
|
}
|
|
4111
4134
|
case "6": {
|
|
4112
|
-
const { geoSchema } = await import("./geo-
|
|
4135
|
+
const { geoSchema } = await import("./geo-HRG7M7YX.js");
|
|
4113
4136
|
await geoSchema({});
|
|
4114
4137
|
break;
|
|
4115
4138
|
}
|
|
@@ -4245,9 +4268,9 @@ async function showCredits() {
|
|
|
4245
4268
|
console.log(chalk7.dim(" Using your own API keys - no credit charges"));
|
|
4246
4269
|
}
|
|
4247
4270
|
console.log();
|
|
4248
|
-
} catch (
|
|
4271
|
+
} catch (err2) {
|
|
4249
4272
|
spinner.fail("Error fetching credits");
|
|
4250
|
-
console.error(
|
|
4273
|
+
console.error(err2);
|
|
4251
4274
|
}
|
|
4252
4275
|
}
|
|
4253
4276
|
async function addCredits(options = {}) {
|
|
@@ -4301,9 +4324,9 @@ async function addCredits(options = {}) {
|
|
|
4301
4324
|
} catch {
|
|
4302
4325
|
console.log(chalk7.yellow(" Could not open browser. Please visit the URL above."));
|
|
4303
4326
|
}
|
|
4304
|
-
} catch (
|
|
4327
|
+
} catch (err2) {
|
|
4305
4328
|
spinner.fail("Error preparing checkout");
|
|
4306
|
-
console.error(
|
|
4329
|
+
console.error(err2);
|
|
4307
4330
|
}
|
|
4308
4331
|
}
|
|
4309
4332
|
async function showHistory(options = {}) {
|
|
@@ -4347,9 +4370,9 @@ async function showHistory(options = {}) {
|
|
|
4347
4370
|
` ${"Total".padEnd(30)} ${totalTokens.toString().padStart(8)} ${chalk7.green(`$${totalCost.toFixed(4)}`.padStart(10))}`
|
|
4348
4371
|
);
|
|
4349
4372
|
console.log();
|
|
4350
|
-
} catch (
|
|
4373
|
+
} catch (err2) {
|
|
4351
4374
|
spinner.fail("Error fetching history");
|
|
4352
|
-
console.error(
|
|
4375
|
+
console.error(err2);
|
|
4353
4376
|
}
|
|
4354
4377
|
}
|
|
4355
4378
|
async function manageBudget(options = {}) {
|
|
@@ -4429,9 +4452,9 @@ async function manageBudget(options = {}) {
|
|
|
4429
4452
|
console.log(chalk7.dim(" Clear budget: archon credits budget --clear"));
|
|
4430
4453
|
console.log(chalk7.dim(" Set alert: archon credits budget --alert 80"));
|
|
4431
4454
|
console.log();
|
|
4432
|
-
} catch (
|
|
4455
|
+
} catch (err2) {
|
|
4433
4456
|
spinner.fail("Error managing budget");
|
|
4434
|
-
console.error(
|
|
4457
|
+
console.error(err2);
|
|
4435
4458
|
}
|
|
4436
4459
|
}
|
|
4437
4460
|
async function manageAutoRecharge(options = {}) {
|
|
@@ -4507,9 +4530,9 @@ async function manageAutoRecharge(options = {}) {
|
|
|
4507
4530
|
console.log(chalk7.dim(" Enable: archon credits auto-recharge --enable --threshold 5 --amount 20"));
|
|
4508
4531
|
console.log(chalk7.dim(" Disable: archon credits auto-recharge --disable"));
|
|
4509
4532
|
console.log();
|
|
4510
|
-
} catch (
|
|
4533
|
+
} catch (err2) {
|
|
4511
4534
|
spinner.fail("Error managing auto-recharge");
|
|
4512
|
-
console.error(
|
|
4535
|
+
console.error(err2);
|
|
4513
4536
|
}
|
|
4514
4537
|
}
|
|
4515
4538
|
async function showAuditHistory(options = {}) {
|
|
@@ -4562,9 +4585,9 @@ async function showAuditHistory(options = {}) {
|
|
|
4562
4585
|
console.log(chalk7.dim(` Showing ${history.length} most recent events`));
|
|
4563
4586
|
console.log(chalk7.dim(" Use --limit N to show more"));
|
|
4564
4587
|
console.log();
|
|
4565
|
-
} catch (
|
|
4588
|
+
} catch (err2) {
|
|
4566
4589
|
spinner.fail("Error fetching audit history");
|
|
4567
|
-
console.error(
|
|
4590
|
+
console.error(err2);
|
|
4568
4591
|
}
|
|
4569
4592
|
}
|
|
4570
4593
|
function formatEventType(eventType) {
|
|
@@ -5222,9 +5245,9 @@ async function saveSession(name) {
|
|
|
5222
5245
|
console.log();
|
|
5223
5246
|
console.log(chalk9.dim(" Resume on another device: archon session resume " + session.id));
|
|
5224
5247
|
console.log();
|
|
5225
|
-
} catch (
|
|
5248
|
+
} catch (err2) {
|
|
5226
5249
|
spinner.fail("Error saving session");
|
|
5227
|
-
console.error(
|
|
5250
|
+
console.error(err2);
|
|
5228
5251
|
}
|
|
5229
5252
|
}
|
|
5230
5253
|
async function listSessions() {
|
|
@@ -5265,9 +5288,9 @@ async function listSessions() {
|
|
|
5265
5288
|
console.log();
|
|
5266
5289
|
}
|
|
5267
5290
|
console.log(chalk9.dim(" Resume: archon session resume <id>\n"));
|
|
5268
|
-
} catch (
|
|
5291
|
+
} catch (err2) {
|
|
5269
5292
|
spinner.fail("Error fetching sessions");
|
|
5270
|
-
console.error(
|
|
5293
|
+
console.error(err2);
|
|
5271
5294
|
}
|
|
5272
5295
|
}
|
|
5273
5296
|
async function resumeSession(sessionId) {
|
|
@@ -5316,9 +5339,9 @@ async function resumeSession(sessionId) {
|
|
|
5316
5339
|
console.log();
|
|
5317
5340
|
console.log(chalk9.dim(" Continue working: archon start"));
|
|
5318
5341
|
console.log();
|
|
5319
|
-
} catch (
|
|
5342
|
+
} catch (err2) {
|
|
5320
5343
|
spinner.fail("Error resuming session");
|
|
5321
|
-
console.error(
|
|
5344
|
+
console.error(err2);
|
|
5322
5345
|
}
|
|
5323
5346
|
}
|
|
5324
5347
|
async function syncSession() {
|
|
@@ -5358,9 +5381,9 @@ async function syncSession() {
|
|
|
5358
5381
|
return;
|
|
5359
5382
|
}
|
|
5360
5383
|
spinner.succeed(chalk9.green("Session synced to cloud"));
|
|
5361
|
-
} catch (
|
|
5384
|
+
} catch (err2) {
|
|
5362
5385
|
spinner.fail("Error syncing session");
|
|
5363
|
-
console.error(
|
|
5386
|
+
console.error(err2);
|
|
5364
5387
|
}
|
|
5365
5388
|
}
|
|
5366
5389
|
|
|
@@ -6394,9 +6417,9 @@ async function interview(options = {}) {
|
|
|
6394
6417
|
console.log(` ${chalk13.cyan("2.")} Run ${chalk13.bold("archon list")} to see generated atoms`);
|
|
6395
6418
|
console.log(` ${chalk13.cyan("3.")} Run ${chalk13.bold("archon execute <atom-id>")} to start building`);
|
|
6396
6419
|
console.log();
|
|
6397
|
-
} catch (
|
|
6420
|
+
} catch (err2) {
|
|
6398
6421
|
spinner.fail("Failed to freeze Constitution");
|
|
6399
|
-
console.error(
|
|
6422
|
+
console.error(err2);
|
|
6400
6423
|
}
|
|
6401
6424
|
}
|
|
6402
6425
|
async function runPhase(phase, constitution, cwd) {
|
|
@@ -7075,13 +7098,13 @@ async function modelsSync(options) {
|
|
|
7075
7098
|
if (result.parseErrors.length > 0) {
|
|
7076
7099
|
console.log();
|
|
7077
7100
|
console.log(chalk16.red(`\u26A0\uFE0F Warnings/Errors (${result.parseErrors.length}):`));
|
|
7078
|
-
for (const
|
|
7079
|
-
console.log(chalk16.red(` - ${
|
|
7101
|
+
for (const err2 of result.parseErrors) {
|
|
7102
|
+
console.log(chalk16.red(` - ${err2}`));
|
|
7080
7103
|
}
|
|
7081
7104
|
}
|
|
7082
7105
|
console.log();
|
|
7083
|
-
} catch (
|
|
7084
|
-
console.log(chalk16.red(`Failed to run sync: ${
|
|
7106
|
+
} catch (err2) {
|
|
7107
|
+
console.log(chalk16.red(`Failed to run sync: ${err2}`));
|
|
7085
7108
|
}
|
|
7086
7109
|
}
|
|
7087
7110
|
async function modelsList() {
|
|
@@ -7108,13 +7131,1031 @@ async function modelsList() {
|
|
|
7108
7131
|
console.log();
|
|
7109
7132
|
}
|
|
7110
7133
|
|
|
7134
|
+
// src/cli/governance.ts
|
|
7135
|
+
import { Command as Command2 } from "commander";
|
|
7136
|
+
import chalk17 from "chalk";
|
|
7137
|
+
import { existsSync as existsSync19, readFileSync as readFileSync7 } from "fs";
|
|
7138
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
7139
|
+
import { join as join19 } from "path";
|
|
7140
|
+
import matter4 from "gray-matter";
|
|
7141
|
+
|
|
7142
|
+
// src/core/governance/store.ts
|
|
7143
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync3 } from "fs";
|
|
7144
|
+
import { readFile as readFile10, writeFile as writeFile8, appendFile, unlink as unlink2, stat as stat2, rename } from "fs/promises";
|
|
7145
|
+
import { join as join16 } from "path";
|
|
7146
|
+
import matter from "gray-matter";
|
|
7147
|
+
var ARCHON_DIR2 = ".archon";
|
|
7148
|
+
var ACTIVE_DIR = join16(ARCHON_DIR2, "active");
|
|
7149
|
+
var HISTORY_DIR = join16(ARCHON_DIR2, "history");
|
|
7150
|
+
var ARCHIVE_DIR2 = join16(ARCHON_DIR2, "archive");
|
|
7151
|
+
var LOCK_FILE = join16(ARCHON_DIR2, "governance.lock");
|
|
7152
|
+
var CURRENT_CONTEXT = join16(ARCHON_DIR2, "current_context.md");
|
|
7153
|
+
var ARCHITECTURE_FILE = join16(ACTIVE_DIR, "architecture.md");
|
|
7154
|
+
var TASKS_FILE = join16(ACTIVE_DIR, "tasks.json");
|
|
7155
|
+
var HANDOFFS_FILE = join16(HISTORY_DIR, "handoffs.jsonl");
|
|
7156
|
+
var DECISION_LOG_FILE = join16(HISTORY_DIR, "decision_log.jsonl");
|
|
7157
|
+
var COMPLETED_TASKS_FILE = join16(ARCHIVE_DIR2, "completed_tasks.jsonl");
|
|
7158
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 5e3;
|
|
7159
|
+
var DEFAULT_LOCK_RETRY_MS = 100;
|
|
7160
|
+
var DEFAULT_STALE_LOCK_MS = 2 * 60 * 1e3;
|
|
7161
|
+
var GovernanceStore = class {
|
|
7162
|
+
cwd;
|
|
7163
|
+
constructor(cwd = process.cwd()) {
|
|
7164
|
+
this.cwd = cwd;
|
|
7165
|
+
}
|
|
7166
|
+
/**
|
|
7167
|
+
* Ensure governance directories exist
|
|
7168
|
+
*/
|
|
7169
|
+
ensureStructure() {
|
|
7170
|
+
this.ensureDir(join16(this.cwd, ARCHON_DIR2));
|
|
7171
|
+
this.ensureDir(join16(this.cwd, ACTIVE_DIR));
|
|
7172
|
+
this.ensureDir(join16(this.cwd, HISTORY_DIR));
|
|
7173
|
+
this.ensureDir(join16(this.cwd, ARCHIVE_DIR2));
|
|
7174
|
+
}
|
|
7175
|
+
/**
|
|
7176
|
+
* Read current architecture content (without frontmatter)
|
|
7177
|
+
*/
|
|
7178
|
+
async readArchitecture() {
|
|
7179
|
+
const filePath = join16(this.cwd, ARCHITECTURE_FILE);
|
|
7180
|
+
if (!existsSync16(filePath)) {
|
|
7181
|
+
return ok("");
|
|
7182
|
+
}
|
|
7183
|
+
try {
|
|
7184
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
7185
|
+
const parsed = matter(raw);
|
|
7186
|
+
return ok(parsed.content.trim());
|
|
7187
|
+
} catch (error) {
|
|
7188
|
+
return err(error instanceof Error ? error.message : "Failed to read architecture");
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
/**
|
|
7192
|
+
* Update architecture with required change_reason and optional updated_by
|
|
7193
|
+
*/
|
|
7194
|
+
async updateArchitecture(content, changeReason, updatedBy) {
|
|
7195
|
+
if (!changeReason.trim()) {
|
|
7196
|
+
return err("change_reason is required for architecture updates");
|
|
7197
|
+
}
|
|
7198
|
+
this.ensureStructure();
|
|
7199
|
+
const release = await this.acquireLock();
|
|
7200
|
+
if (!release.ok) return release;
|
|
7201
|
+
try {
|
|
7202
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7203
|
+
const filePath = join16(this.cwd, ARCHITECTURE_FILE);
|
|
7204
|
+
const current = await this.readArchitectureFrontmatter(filePath);
|
|
7205
|
+
const nextVersion = this.nextVersion(current?.version);
|
|
7206
|
+
const frontmatter = {
|
|
7207
|
+
version: nextVersion,
|
|
7208
|
+
last_updated: now,
|
|
7209
|
+
change_reason: changeReason
|
|
7210
|
+
};
|
|
7211
|
+
if (updatedBy) {
|
|
7212
|
+
frontmatter.updated_by = updatedBy;
|
|
7213
|
+
}
|
|
7214
|
+
const output = matter.stringify(content.trim() + "\n", frontmatter);
|
|
7215
|
+
await this.writeFileAtomic(filePath, output);
|
|
7216
|
+
const decision = {
|
|
7217
|
+
timestamp: now,
|
|
7218
|
+
category: "architecture",
|
|
7219
|
+
change_reason: changeReason,
|
|
7220
|
+
updated_by: updatedBy
|
|
7221
|
+
};
|
|
7222
|
+
await this.appendJsonLine(join16(this.cwd, DECISION_LOG_FILE), decision);
|
|
7223
|
+
return ok(void 0);
|
|
7224
|
+
} catch (error) {
|
|
7225
|
+
return err(error instanceof Error ? error.message : "Failed to update architecture");
|
|
7226
|
+
} finally {
|
|
7227
|
+
await release.value();
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
/**
|
|
7231
|
+
* Update a task's status and optional notes. Moves to archive when done.
|
|
7232
|
+
*/
|
|
7233
|
+
async updateTask(taskId, status2, notes) {
|
|
7234
|
+
this.ensureStructure();
|
|
7235
|
+
const release = await this.acquireLock();
|
|
7236
|
+
if (!release.ok) return release;
|
|
7237
|
+
try {
|
|
7238
|
+
const tasksPath = join16(this.cwd, TASKS_FILE);
|
|
7239
|
+
const tasks = await this.loadTasks(tasksPath);
|
|
7240
|
+
if (!tasks.ok) return tasks;
|
|
7241
|
+
const index = tasks.value.findIndex((task2) => task2.id === taskId);
|
|
7242
|
+
if (index === -1) {
|
|
7243
|
+
return err(`Task not found: ${taskId}`);
|
|
7244
|
+
}
|
|
7245
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7246
|
+
const task = { ...tasks.value[index] };
|
|
7247
|
+
task.status = status2;
|
|
7248
|
+
task.notes = notes ?? task.notes;
|
|
7249
|
+
task.updated_at = now;
|
|
7250
|
+
if (status2 === "done") {
|
|
7251
|
+
tasks.value.splice(index, 1);
|
|
7252
|
+
await this.appendJsonLine(join16(this.cwd, COMPLETED_TASKS_FILE), task);
|
|
7253
|
+
} else {
|
|
7254
|
+
tasks.value[index] = task;
|
|
7255
|
+
}
|
|
7256
|
+
await this.writeJson(tasksPath, tasks.value);
|
|
7257
|
+
return ok(void 0);
|
|
7258
|
+
} catch (error) {
|
|
7259
|
+
return err(error instanceof Error ? error.message : "Failed to update task");
|
|
7260
|
+
} finally {
|
|
7261
|
+
await release.value();
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
/**
|
|
7265
|
+
* Append handoff entry and update current_context.md
|
|
7266
|
+
*/
|
|
7267
|
+
async logHandoff(entry) {
|
|
7268
|
+
if (!entry.reason.trim()) {
|
|
7269
|
+
return err("reason is required for handoff");
|
|
7270
|
+
}
|
|
7271
|
+
if (!entry.summary.trim()) {
|
|
7272
|
+
return err("summary is required for handoff");
|
|
7273
|
+
}
|
|
7274
|
+
if (!entry.next_actions || entry.next_actions.length === 0) {
|
|
7275
|
+
return err("next_actions are required for handoff");
|
|
7276
|
+
}
|
|
7277
|
+
this.ensureStructure();
|
|
7278
|
+
const release = await this.acquireLock();
|
|
7279
|
+
if (!release.ok) return release;
|
|
7280
|
+
try {
|
|
7281
|
+
await this.appendJsonLine(join16(this.cwd, HANDOFFS_FILE), entry);
|
|
7282
|
+
const contextLines = [
|
|
7283
|
+
"# Current Context",
|
|
7284
|
+
"",
|
|
7285
|
+
`Timestamp: ${entry.timestamp}`,
|
|
7286
|
+
entry.from_agent ? `From: ${entry.from_agent}` : void 0,
|
|
7287
|
+
`Reason: ${entry.reason}`,
|
|
7288
|
+
"",
|
|
7289
|
+
"## Summary",
|
|
7290
|
+
entry.summary.trim(),
|
|
7291
|
+
"",
|
|
7292
|
+
"## Next Actions",
|
|
7293
|
+
...entry.next_actions.map((action) => `- ${action}`),
|
|
7294
|
+
""
|
|
7295
|
+
].filter((line) => typeof line === "string");
|
|
7296
|
+
await this.writeFileAtomic(join16(this.cwd, CURRENT_CONTEXT), contextLines.join("\n"));
|
|
7297
|
+
return ok(void 0);
|
|
7298
|
+
} catch (error) {
|
|
7299
|
+
return err(error instanceof Error ? error.message : "Failed to log handoff");
|
|
7300
|
+
} finally {
|
|
7301
|
+
await release.value();
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
ensureDir(path2) {
|
|
7305
|
+
if (!existsSync16(path2)) {
|
|
7306
|
+
mkdirSync3(path2, { recursive: true });
|
|
7307
|
+
}
|
|
7308
|
+
}
|
|
7309
|
+
async acquireLock(timeoutMs = DEFAULT_LOCK_TIMEOUT_MS, retryMs = DEFAULT_LOCK_RETRY_MS, staleMs = DEFAULT_STALE_LOCK_MS) {
|
|
7310
|
+
const lockPath = join16(this.cwd, LOCK_FILE);
|
|
7311
|
+
const deadline = Date.now() + timeoutMs;
|
|
7312
|
+
while (Date.now() < deadline) {
|
|
7313
|
+
try {
|
|
7314
|
+
await this.tryCreateLock(lockPath);
|
|
7315
|
+
return ok(async () => {
|
|
7316
|
+
await this.safeUnlink(lockPath);
|
|
7317
|
+
});
|
|
7318
|
+
} catch (error) {
|
|
7319
|
+
if (error instanceof Error && error.message === "LOCK_EXISTS") {
|
|
7320
|
+
const staleCleared = await this.clearStaleLock(lockPath, staleMs);
|
|
7321
|
+
if (staleCleared) {
|
|
7322
|
+
continue;
|
|
7323
|
+
}
|
|
7324
|
+
await sleep(retryMs);
|
|
7325
|
+
continue;
|
|
7326
|
+
}
|
|
7327
|
+
return err("Failed to acquire governance lock");
|
|
7328
|
+
}
|
|
7329
|
+
}
|
|
7330
|
+
return err("Timed out waiting for governance lock");
|
|
7331
|
+
}
|
|
7332
|
+
async tryCreateLock(lockPath) {
|
|
7333
|
+
if (existsSync16(lockPath)) {
|
|
7334
|
+
throw new Error("LOCK_EXISTS");
|
|
7335
|
+
}
|
|
7336
|
+
const payload = JSON.stringify({ pid: process.pid, created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7337
|
+
await writeFile8(lockPath, payload, { flag: "wx" });
|
|
7338
|
+
}
|
|
7339
|
+
async clearStaleLock(lockPath, staleMs) {
|
|
7340
|
+
try {
|
|
7341
|
+
const info = await stat2(lockPath);
|
|
7342
|
+
const ageByMtime = Date.now() - info.mtimeMs;
|
|
7343
|
+
let ageByCreatedAt = null;
|
|
7344
|
+
try {
|
|
7345
|
+
const raw = await readFile10(lockPath, "utf-8");
|
|
7346
|
+
const parsed = JSON.parse(raw);
|
|
7347
|
+
if (parsed.created_at) {
|
|
7348
|
+
const createdAt = Date.parse(parsed.created_at);
|
|
7349
|
+
if (!Number.isNaN(createdAt)) {
|
|
7350
|
+
ageByCreatedAt = Date.now() - createdAt;
|
|
7351
|
+
}
|
|
7352
|
+
}
|
|
7353
|
+
} catch {
|
|
7354
|
+
}
|
|
7355
|
+
const ageMs = Math.max(ageByMtime, ageByCreatedAt ?? -Infinity);
|
|
7356
|
+
if (ageMs > staleMs) {
|
|
7357
|
+
await this.safeUnlink(lockPath);
|
|
7358
|
+
return true;
|
|
7359
|
+
}
|
|
7360
|
+
} catch {
|
|
7361
|
+
return false;
|
|
7362
|
+
}
|
|
7363
|
+
return false;
|
|
7364
|
+
}
|
|
7365
|
+
async safeUnlink(path2) {
|
|
7366
|
+
try {
|
|
7367
|
+
await unlink2(path2);
|
|
7368
|
+
} catch {
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
async readArchitectureFrontmatter(filePath) {
|
|
7372
|
+
if (!existsSync16(filePath)) return null;
|
|
7373
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
7374
|
+
const parsed = matter(raw);
|
|
7375
|
+
const data = parsed.data;
|
|
7376
|
+
const versionRaw = data["version"];
|
|
7377
|
+
const version = typeof versionRaw === "number" ? versionRaw : typeof versionRaw === "string" ? Number.parseFloat(versionRaw) : 1;
|
|
7378
|
+
return {
|
|
7379
|
+
version: Number.isFinite(version) ? version : 1,
|
|
7380
|
+
last_updated: typeof data["last_updated"] === "string" ? data["last_updated"] : "",
|
|
7381
|
+
updated_by: typeof data["updated_by"] === "string" ? data["updated_by"] : void 0,
|
|
7382
|
+
change_reason: typeof data["change_reason"] === "string" ? data["change_reason"] : ""
|
|
7383
|
+
};
|
|
7384
|
+
}
|
|
7385
|
+
nextVersion(current) {
|
|
7386
|
+
if (!current || !Number.isFinite(current)) return 1;
|
|
7387
|
+
return Math.round((current + 0.1) * 10) / 10;
|
|
7388
|
+
}
|
|
7389
|
+
async loadTasks(path2) {
|
|
7390
|
+
if (!existsSync16(path2)) return ok([]);
|
|
7391
|
+
try {
|
|
7392
|
+
const raw = await readFile10(path2, "utf-8");
|
|
7393
|
+
const data = JSON.parse(raw);
|
|
7394
|
+
if (!Array.isArray(data)) {
|
|
7395
|
+
return err("tasks.json must be an array");
|
|
7396
|
+
}
|
|
7397
|
+
const parsed = [];
|
|
7398
|
+
for (const item of data) {
|
|
7399
|
+
const task = this.parseTask(item);
|
|
7400
|
+
if (!task) {
|
|
7401
|
+
return err("tasks.json contains invalid entries");
|
|
7402
|
+
}
|
|
7403
|
+
parsed.push(task);
|
|
7404
|
+
}
|
|
7405
|
+
return ok(parsed);
|
|
7406
|
+
} catch (error) {
|
|
7407
|
+
return err(error instanceof Error ? error.message : "Failed to parse tasks.json");
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
parseTask(value) {
|
|
7411
|
+
if (typeof value !== "object" || value === null) return null;
|
|
7412
|
+
const raw = value;
|
|
7413
|
+
const id = typeof raw["id"] === "string" ? raw["id"] : null;
|
|
7414
|
+
const description = typeof raw["description"] === "string" ? raw["description"] : null;
|
|
7415
|
+
const status2 = this.parseStatus(raw["status"]);
|
|
7416
|
+
const createdAt = typeof raw["created_at"] === "string" ? raw["created_at"] : null;
|
|
7417
|
+
if (!id || !description || !status2 || !createdAt) return null;
|
|
7418
|
+
return {
|
|
7419
|
+
id,
|
|
7420
|
+
parent_id: typeof raw["parent_id"] === "string" ? raw["parent_id"] : void 0,
|
|
7421
|
+
description,
|
|
7422
|
+
status: status2,
|
|
7423
|
+
assigned_to: typeof raw["assigned_to"] === "string" ? raw["assigned_to"] : void 0,
|
|
7424
|
+
created_at: createdAt,
|
|
7425
|
+
updated_at: typeof raw["updated_at"] === "string" ? raw["updated_at"] : void 0,
|
|
7426
|
+
notes: typeof raw["notes"] === "string" ? raw["notes"] : void 0
|
|
7427
|
+
};
|
|
7428
|
+
}
|
|
7429
|
+
parseStatus(value) {
|
|
7430
|
+
if (value === "pending" || value === "in_progress" || value === "verification" || value === "done") {
|
|
7431
|
+
return value;
|
|
7432
|
+
}
|
|
7433
|
+
return null;
|
|
7434
|
+
}
|
|
7435
|
+
async writeJson(path2, payload) {
|
|
7436
|
+
const content = JSON.stringify(payload, null, 2) + "\n";
|
|
7437
|
+
await this.writeFileAtomic(path2, content);
|
|
7438
|
+
}
|
|
7439
|
+
async writeFileAtomic(path2, content) {
|
|
7440
|
+
const tmpPath = `${path2}.tmp`;
|
|
7441
|
+
await writeFile8(tmpPath, content, "utf-8");
|
|
7442
|
+
try {
|
|
7443
|
+
await rename(tmpPath, path2);
|
|
7444
|
+
} catch {
|
|
7445
|
+
await this.safeUnlink(path2);
|
|
7446
|
+
await rename(tmpPath, path2);
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
async appendJsonLine(path2, payload) {
|
|
7450
|
+
const line = JSON.stringify(payload) + "\n";
|
|
7451
|
+
await appendFile(path2, line, "utf-8");
|
|
7452
|
+
}
|
|
7453
|
+
};
|
|
7454
|
+
|
|
7455
|
+
// src/core/governance/migrate.ts
|
|
7456
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync4 } from "fs";
|
|
7457
|
+
import { readFile as readFile11, writeFile as writeFile9, unlink as unlink3 } from "fs/promises";
|
|
7458
|
+
import { join as join17 } from "path";
|
|
7459
|
+
import matter2 from "gray-matter";
|
|
7460
|
+
var LEGACY_ARCH_PATH = "ARCHITECTURE.md";
|
|
7461
|
+
var LEGACY_TASKS_PATH = join17(".archon", "current-tasks.md");
|
|
7462
|
+
var ACTIVE_ARCH_PATH2 = join17(".archon", "active", "architecture.md");
|
|
7463
|
+
var ACTIVE_TASKS_PATH = join17(".archon", "active", "tasks.json");
|
|
7464
|
+
var ARCHIVE_COMPLETED_PATH = join17(".archon", "archive", "completed_tasks.jsonl");
|
|
7465
|
+
var HISTORY_LEGACY_DIR = join17(".archon", "history", "legacy");
|
|
7466
|
+
function parseLegacyTasks(content) {
|
|
7467
|
+
const result = { active: [], paused: [] };
|
|
7468
|
+
const activeHeaderMatch = content.match(/##\s+Active\s*\((\d{4}-\d{2}-\d{2})\)/i);
|
|
7469
|
+
if (activeHeaderMatch?.[1]) {
|
|
7470
|
+
result.referenceDate = activeHeaderMatch[1];
|
|
7471
|
+
}
|
|
7472
|
+
const sections = content.split(/\n##\s+/).map((block) => block.trim());
|
|
7473
|
+
for (const section of sections) {
|
|
7474
|
+
const normalized = section.toLowerCase();
|
|
7475
|
+
const lines = section.split("\n").slice(1);
|
|
7476
|
+
if (normalized.startsWith("active")) {
|
|
7477
|
+
for (const line of lines) {
|
|
7478
|
+
const match = line.match(/^\d+\.\s+(.*)$/);
|
|
7479
|
+
if (!match?.[1]) continue;
|
|
7480
|
+
const raw = match[1].trim();
|
|
7481
|
+
if (!raw) continue;
|
|
7482
|
+
const done = /\(done\)\s*$/i.test(raw);
|
|
7483
|
+
const cleaned = raw.replace(/\s*\(done\)\s*$/i, "").trim();
|
|
7484
|
+
result.active.push({ description: cleaned, done });
|
|
7485
|
+
}
|
|
7486
|
+
}
|
|
7487
|
+
if (normalized.startsWith("paused")) {
|
|
7488
|
+
for (const line of lines) {
|
|
7489
|
+
const match = line.match(/^\d+\.\s+(.*)$/);
|
|
7490
|
+
if (!match?.[1]) continue;
|
|
7491
|
+
const raw = match[1].trim();
|
|
7492
|
+
if (!raw) continue;
|
|
7493
|
+
result.paused.push(raw);
|
|
7494
|
+
}
|
|
7495
|
+
}
|
|
7496
|
+
}
|
|
7497
|
+
return result;
|
|
7498
|
+
}
|
|
7499
|
+
function buildTaskEntries(parsed, nowIso) {
|
|
7500
|
+
const active = [];
|
|
7501
|
+
const completed = [];
|
|
7502
|
+
let counter = 1;
|
|
7503
|
+
const referenceDate = parsed.referenceDate ? `${parsed.referenceDate}T00:00:00.000Z` : nowIso;
|
|
7504
|
+
const makeTask = (description, status2, notes) => {
|
|
7505
|
+
const task = {
|
|
7506
|
+
id: `TASK-${String(counter).padStart(3, "0")}`,
|
|
7507
|
+
description,
|
|
7508
|
+
status: status2,
|
|
7509
|
+
created_at: referenceDate,
|
|
7510
|
+
updated_at: nowIso,
|
|
7511
|
+
notes
|
|
7512
|
+
};
|
|
7513
|
+
counter += 1;
|
|
7514
|
+
return task;
|
|
7515
|
+
};
|
|
7516
|
+
for (const item of parsed.active) {
|
|
7517
|
+
if (item.done) {
|
|
7518
|
+
completed.push(makeTask(item.description, "done", "Migrated from current-tasks.md (done)"));
|
|
7519
|
+
} else {
|
|
7520
|
+
active.push(makeTask(item.description, "pending"));
|
|
7521
|
+
}
|
|
7522
|
+
}
|
|
7523
|
+
for (const paused of parsed.paused) {
|
|
7524
|
+
active.push(makeTask(paused, "pending", "Migrated from current-tasks.md (paused)"));
|
|
7525
|
+
}
|
|
7526
|
+
return { active, completed };
|
|
7527
|
+
}
|
|
7528
|
+
async function writeJson(path2, payload, dryRun) {
|
|
7529
|
+
if (dryRun) return;
|
|
7530
|
+
const content = JSON.stringify(payload, null, 2) + "\n";
|
|
7531
|
+
await writeFile9(path2, content, "utf-8");
|
|
7532
|
+
}
|
|
7533
|
+
async function appendJsonLines(path2, items, dryRun) {
|
|
7534
|
+
if (dryRun || items.length === 0) return;
|
|
7535
|
+
const lines = items.map((item) => JSON.stringify(item)).join("\n") + "\n";
|
|
7536
|
+
await writeFile9(path2, lines, { encoding: "utf-8", flag: "a" });
|
|
7537
|
+
}
|
|
7538
|
+
async function archiveLegacyFile(source, destDir, dryRun) {
|
|
7539
|
+
const fileName = source.split("/").pop() ?? source.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
7540
|
+
const target = join17(destDir, fileName);
|
|
7541
|
+
if (dryRun) return target;
|
|
7542
|
+
if (!existsSync17(destDir)) {
|
|
7543
|
+
mkdirSync4(destDir, { recursive: true });
|
|
7544
|
+
}
|
|
7545
|
+
const content = await readFile11(source, "utf-8");
|
|
7546
|
+
await writeFile9(target, content, "utf-8");
|
|
7547
|
+
return target;
|
|
7548
|
+
}
|
|
7549
|
+
async function migrateLegacyGovernance(options = {}) {
|
|
7550
|
+
const cwd = options.cwd ?? process.cwd();
|
|
7551
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
7552
|
+
const nowIso = now.toISOString();
|
|
7553
|
+
const result = {
|
|
7554
|
+
migratedArchitecture: false,
|
|
7555
|
+
migratedTasks: false,
|
|
7556
|
+
archivedLegacyFiles: [],
|
|
7557
|
+
warnings: []
|
|
7558
|
+
};
|
|
7559
|
+
const store = new GovernanceStore(cwd);
|
|
7560
|
+
store.ensureStructure();
|
|
7561
|
+
const archPath = join17(cwd, LEGACY_ARCH_PATH);
|
|
7562
|
+
const activeArchPath = join17(cwd, ACTIVE_ARCH_PATH2);
|
|
7563
|
+
if (existsSync17(archPath) && !existsSync17(activeArchPath)) {
|
|
7564
|
+
const raw = await readFile11(archPath, "utf-8");
|
|
7565
|
+
const parsed = matter2(raw);
|
|
7566
|
+
const content = parsed.content.trim();
|
|
7567
|
+
if (!content) {
|
|
7568
|
+
result.warnings.push("Legacy ARCHITECTURE.md was empty; skipping architecture migration.");
|
|
7569
|
+
} else if (!options.dryRun) {
|
|
7570
|
+
const update = await store.updateArchitecture(
|
|
7571
|
+
content,
|
|
7572
|
+
"Migrated from legacy ARCHITECTURE.md",
|
|
7573
|
+
options.updatedBy ?? "archon migration"
|
|
7574
|
+
);
|
|
7575
|
+
if (!update.ok) {
|
|
7576
|
+
result.warnings.push(`Failed to migrate architecture: ${update.error}`);
|
|
7577
|
+
} else {
|
|
7578
|
+
result.migratedArchitecture = true;
|
|
7579
|
+
}
|
|
7580
|
+
} else {
|
|
7581
|
+
result.migratedArchitecture = true;
|
|
7582
|
+
}
|
|
7583
|
+
if (options.removeLegacy) {
|
|
7584
|
+
const archived = await archiveLegacyFile(archPath, join17(cwd, HISTORY_LEGACY_DIR), !!options.dryRun);
|
|
7585
|
+
result.archivedLegacyFiles.push(archived);
|
|
7586
|
+
if (!options.dryRun) {
|
|
7587
|
+
await unlink3(archPath);
|
|
7588
|
+
}
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
const legacyTasksPath = join17(cwd, LEGACY_TASKS_PATH);
|
|
7592
|
+
const activeTasksPath = join17(cwd, ACTIVE_TASKS_PATH);
|
|
7593
|
+
if (existsSync17(legacyTasksPath) && !existsSync17(activeTasksPath)) {
|
|
7594
|
+
const rawTasks = await readFile11(legacyTasksPath, "utf-8");
|
|
7595
|
+
const parsedTasks = parseLegacyTasks(rawTasks);
|
|
7596
|
+
const { active, completed } = buildTaskEntries(parsedTasks, nowIso);
|
|
7597
|
+
if (active.length === 0 && completed.length === 0) {
|
|
7598
|
+
result.warnings.push("Legacy current-tasks.md did not contain actionable items.");
|
|
7599
|
+
} else {
|
|
7600
|
+
await writeJson(activeTasksPath, active, !!options.dryRun);
|
|
7601
|
+
await appendJsonLines(join17(cwd, ARCHIVE_COMPLETED_PATH), completed, !!options.dryRun);
|
|
7602
|
+
result.migratedTasks = true;
|
|
7603
|
+
}
|
|
7604
|
+
if (options.removeLegacy) {
|
|
7605
|
+
const archived = await archiveLegacyFile(legacyTasksPath, join17(cwd, HISTORY_LEGACY_DIR), !!options.dryRun);
|
|
7606
|
+
result.archivedLegacyFiles.push(archived);
|
|
7607
|
+
if (!options.dryRun) {
|
|
7608
|
+
await unlink3(legacyTasksPath);
|
|
7609
|
+
}
|
|
7610
|
+
}
|
|
7611
|
+
}
|
|
7612
|
+
if (existsSync17(join17(cwd, ACTIVE_ARCH_PATH2)) && existsSync17(join17(cwd, LEGACY_ARCH_PATH))) {
|
|
7613
|
+
result.warnings.push("Active architecture already exists; legacy ARCHITECTURE.md left untouched.");
|
|
7614
|
+
}
|
|
7615
|
+
if (existsSync17(join17(cwd, ACTIVE_TASKS_PATH)) && existsSync17(join17(cwd, LEGACY_TASKS_PATH))) {
|
|
7616
|
+
result.warnings.push("Active tasks already exist; legacy current-tasks.md left untouched.");
|
|
7617
|
+
}
|
|
7618
|
+
return result;
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
// src/core/governance/sqlite.ts
|
|
7622
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync5, readFileSync as readFileSync6 } from "fs";
|
|
7623
|
+
import { join as join18 } from "path";
|
|
7624
|
+
import matter3 from "gray-matter";
|
|
7625
|
+
import Database2 from "better-sqlite3";
|
|
7626
|
+
var ARCH_PATH = join18(".archon", "active", "architecture.md");
|
|
7627
|
+
var TASKS_PATH = join18(".archon", "active", "tasks.json");
|
|
7628
|
+
var HANDOFFS_PATH = join18(".archon", "history", "handoffs.jsonl");
|
|
7629
|
+
var DECISIONS_PATH = join18(".archon", "history", "decision_log.jsonl");
|
|
7630
|
+
var COMPLETED_PATH = join18(".archon", "archive", "completed_tasks.jsonl");
|
|
7631
|
+
var GovernanceSqliteView = class {
|
|
7632
|
+
db = null;
|
|
7633
|
+
config;
|
|
7634
|
+
constructor(config = {}) {
|
|
7635
|
+
this.config = config;
|
|
7636
|
+
}
|
|
7637
|
+
init(cwd) {
|
|
7638
|
+
const archonDir = join18(cwd, ".archon");
|
|
7639
|
+
if (!existsSync18(archonDir)) {
|
|
7640
|
+
mkdirSync5(archonDir, { recursive: true });
|
|
7641
|
+
}
|
|
7642
|
+
const dbPath = this.config.inMemory ? ":memory:" : join18(cwd, this.config.dbPath ?? ".archon/governance.db");
|
|
7643
|
+
this.db = new Database2(dbPath);
|
|
7644
|
+
this.db.exec(`
|
|
7645
|
+
PRAGMA journal_mode = WAL;
|
|
7646
|
+
CREATE TABLE IF NOT EXISTS architecture (
|
|
7647
|
+
id TEXT PRIMARY KEY,
|
|
7648
|
+
version REAL,
|
|
7649
|
+
last_updated TEXT,
|
|
7650
|
+
updated_by TEXT,
|
|
7651
|
+
change_reason TEXT,
|
|
7652
|
+
content TEXT
|
|
7653
|
+
);
|
|
7654
|
+
CREATE TABLE IF NOT EXISTS tasks (
|
|
7655
|
+
id TEXT PRIMARY KEY,
|
|
7656
|
+
description TEXT NOT NULL,
|
|
7657
|
+
status TEXT NOT NULL,
|
|
7658
|
+
created_at TEXT NOT NULL,
|
|
7659
|
+
updated_at TEXT,
|
|
7660
|
+
notes TEXT
|
|
7661
|
+
);
|
|
7662
|
+
CREATE TABLE IF NOT EXISTS completed_tasks (
|
|
7663
|
+
id TEXT PRIMARY KEY,
|
|
7664
|
+
description TEXT NOT NULL,
|
|
7665
|
+
status TEXT NOT NULL,
|
|
7666
|
+
created_at TEXT NOT NULL,
|
|
7667
|
+
updated_at TEXT,
|
|
7668
|
+
notes TEXT
|
|
7669
|
+
);
|
|
7670
|
+
CREATE TABLE IF NOT EXISTS handoffs (
|
|
7671
|
+
id TEXT PRIMARY KEY,
|
|
7672
|
+
timestamp TEXT NOT NULL,
|
|
7673
|
+
from_agent TEXT,
|
|
7674
|
+
reason TEXT NOT NULL,
|
|
7675
|
+
summary TEXT NOT NULL,
|
|
7676
|
+
next_actions TEXT NOT NULL
|
|
7677
|
+
);
|
|
7678
|
+
CREATE TABLE IF NOT EXISTS decisions (
|
|
7679
|
+
id TEXT PRIMARY KEY,
|
|
7680
|
+
timestamp TEXT NOT NULL,
|
|
7681
|
+
category TEXT NOT NULL,
|
|
7682
|
+
change_reason TEXT NOT NULL,
|
|
7683
|
+
updated_by TEXT,
|
|
7684
|
+
diff_summary TEXT
|
|
7685
|
+
);
|
|
7686
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS governance_fts USING fts5(
|
|
7687
|
+
source,
|
|
7688
|
+
record_id,
|
|
7689
|
+
text,
|
|
7690
|
+
timestamp
|
|
7691
|
+
);
|
|
7692
|
+
`);
|
|
7693
|
+
}
|
|
7694
|
+
close() {
|
|
7695
|
+
if (this.db) {
|
|
7696
|
+
this.db.close();
|
|
7697
|
+
this.db = null;
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
async loadFromDisk(cwd) {
|
|
7701
|
+
if (!this.db) {
|
|
7702
|
+
throw new Error("GovernanceSqliteView not initialized. Call init() first.");
|
|
7703
|
+
}
|
|
7704
|
+
const stats = {
|
|
7705
|
+
tasks: 0,
|
|
7706
|
+
completedTasks: 0,
|
|
7707
|
+
handoffs: 0,
|
|
7708
|
+
decisions: 0,
|
|
7709
|
+
architecture: false
|
|
7710
|
+
};
|
|
7711
|
+
this.db.exec("DELETE FROM governance_fts; DELETE FROM architecture; DELETE FROM tasks; DELETE FROM completed_tasks; DELETE FROM handoffs; DELETE FROM decisions;");
|
|
7712
|
+
const archPath = join18(cwd, ARCH_PATH);
|
|
7713
|
+
if (existsSync18(archPath)) {
|
|
7714
|
+
const raw = readFileSync6(archPath, "utf-8");
|
|
7715
|
+
const parsed = matter3(raw);
|
|
7716
|
+
const data = parsed.data;
|
|
7717
|
+
const versionRaw = data["version"];
|
|
7718
|
+
const version = typeof versionRaw === "number" ? versionRaw : typeof versionRaw === "string" ? Number.parseFloat(versionRaw) : null;
|
|
7719
|
+
const archId = "architecture";
|
|
7720
|
+
const content = parsed.content.trim();
|
|
7721
|
+
this.db.prepare(
|
|
7722
|
+
`INSERT OR REPLACE INTO architecture (id, version, last_updated, updated_by, change_reason, content)
|
|
7723
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
7724
|
+
).run(
|
|
7725
|
+
archId,
|
|
7726
|
+
Number.isFinite(version) ? version : null,
|
|
7727
|
+
typeof data["last_updated"] === "string" ? data["last_updated"] : null,
|
|
7728
|
+
typeof data["updated_by"] === "string" ? data["updated_by"] : null,
|
|
7729
|
+
typeof data["change_reason"] === "string" ? data["change_reason"] : null,
|
|
7730
|
+
content
|
|
7731
|
+
);
|
|
7732
|
+
this.db.prepare(
|
|
7733
|
+
`INSERT INTO governance_fts (source, record_id, text, timestamp)
|
|
7734
|
+
VALUES (?, ?, ?, ?)`
|
|
7735
|
+
).run("architecture", archId, content, typeof data["last_updated"] === "string" ? data["last_updated"] : null);
|
|
7736
|
+
stats.architecture = true;
|
|
7737
|
+
}
|
|
7738
|
+
const tasksPath = join18(cwd, TASKS_PATH);
|
|
7739
|
+
if (existsSync18(tasksPath)) {
|
|
7740
|
+
const raw = readFileSync6(tasksPath, "utf-8");
|
|
7741
|
+
const tasks = JSON.parse(raw);
|
|
7742
|
+
const insertTask = this.db.prepare(
|
|
7743
|
+
`INSERT OR REPLACE INTO tasks (id, description, status, created_at, updated_at, notes)
|
|
7744
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
7745
|
+
);
|
|
7746
|
+
const insertFts = this.db.prepare(
|
|
7747
|
+
`INSERT INTO governance_fts (source, record_id, text, timestamp)
|
|
7748
|
+
VALUES (?, ?, ?, ?)`
|
|
7749
|
+
);
|
|
7750
|
+
for (const task of tasks) {
|
|
7751
|
+
insertTask.run(task.id, task.description, task.status, task.created_at, task.updated_at ?? null, task.notes ?? null);
|
|
7752
|
+
insertFts.run("task", task.id, task.description + (task.notes ? `
|
|
7753
|
+
${task.notes}` : ""), task.updated_at ?? task.created_at);
|
|
7754
|
+
stats.tasks += 1;
|
|
7755
|
+
}
|
|
7756
|
+
}
|
|
7757
|
+
const completedPath = join18(cwd, COMPLETED_PATH);
|
|
7758
|
+
if (existsSync18(completedPath)) {
|
|
7759
|
+
const lines = readFileSync6(completedPath, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
7760
|
+
const insertCompleted = this.db.prepare(
|
|
7761
|
+
`INSERT OR REPLACE INTO completed_tasks (id, description, status, created_at, updated_at, notes)
|
|
7762
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
7763
|
+
);
|
|
7764
|
+
const insertFts = this.db.prepare(
|
|
7765
|
+
`INSERT INTO governance_fts (source, record_id, text, timestamp)
|
|
7766
|
+
VALUES (?, ?, ?, ?)`
|
|
7767
|
+
);
|
|
7768
|
+
for (const line of lines) {
|
|
7769
|
+
const task = JSON.parse(line);
|
|
7770
|
+
insertCompleted.run(task.id, task.description, task.status, task.created_at, task.updated_at ?? null, task.notes ?? null);
|
|
7771
|
+
insertFts.run("completed_task", task.id, task.description + (task.notes ? `
|
|
7772
|
+
${task.notes}` : ""), task.updated_at ?? task.created_at);
|
|
7773
|
+
stats.completedTasks += 1;
|
|
7774
|
+
}
|
|
7775
|
+
}
|
|
7776
|
+
const handoffsPath = join18(cwd, HANDOFFS_PATH);
|
|
7777
|
+
if (existsSync18(handoffsPath)) {
|
|
7778
|
+
const lines = readFileSync6(handoffsPath, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
7779
|
+
const insertHandoff = this.db.prepare(
|
|
7780
|
+
`INSERT OR REPLACE INTO handoffs (id, timestamp, from_agent, reason, summary, next_actions)
|
|
7781
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
7782
|
+
);
|
|
7783
|
+
const insertFts = this.db.prepare(
|
|
7784
|
+
`INSERT INTO governance_fts (source, record_id, text, timestamp)
|
|
7785
|
+
VALUES (?, ?, ?, ?)`
|
|
7786
|
+
);
|
|
7787
|
+
for (const line of lines) {
|
|
7788
|
+
const entry = JSON.parse(line);
|
|
7789
|
+
const id = `${entry.timestamp}-${entry.from_agent ?? "unknown"}`;
|
|
7790
|
+
const nextActions = entry.next_actions.join("\n");
|
|
7791
|
+
insertHandoff.run(id, entry.timestamp, entry.from_agent ?? null, entry.reason, entry.summary, nextActions);
|
|
7792
|
+
insertFts.run("handoff", id, `${entry.summary}
|
|
7793
|
+
${nextActions}`, entry.timestamp);
|
|
7794
|
+
stats.handoffs += 1;
|
|
7795
|
+
}
|
|
7796
|
+
}
|
|
7797
|
+
const decisionsPath = join18(cwd, DECISIONS_PATH);
|
|
7798
|
+
if (existsSync18(decisionsPath)) {
|
|
7799
|
+
const lines = readFileSync6(decisionsPath, "utf-8").split("\n").filter((line) => line.trim().length > 0);
|
|
7800
|
+
const insertDecision = this.db.prepare(
|
|
7801
|
+
`INSERT OR REPLACE INTO decisions (id, timestamp, category, change_reason, updated_by, diff_summary)
|
|
7802
|
+
VALUES (?, ?, ?, ?, ?, ?)`
|
|
7803
|
+
);
|
|
7804
|
+
const insertFts = this.db.prepare(
|
|
7805
|
+
`INSERT INTO governance_fts (source, record_id, text, timestamp)
|
|
7806
|
+
VALUES (?, ?, ?, ?)`
|
|
7807
|
+
);
|
|
7808
|
+
for (const line of lines) {
|
|
7809
|
+
const entry = JSON.parse(line);
|
|
7810
|
+
const id = `${entry.timestamp}-${entry.category}`;
|
|
7811
|
+
insertDecision.run(
|
|
7812
|
+
id,
|
|
7813
|
+
entry.timestamp,
|
|
7814
|
+
entry.category,
|
|
7815
|
+
entry.change_reason,
|
|
7816
|
+
entry.updated_by ?? null,
|
|
7817
|
+
entry.diff_summary ?? null
|
|
7818
|
+
);
|
|
7819
|
+
insertFts.run("decision", id, `${entry.change_reason}${entry.diff_summary ? `
|
|
7820
|
+
${entry.diff_summary}` : ""}`, entry.timestamp);
|
|
7821
|
+
stats.decisions += 1;
|
|
7822
|
+
}
|
|
7823
|
+
}
|
|
7824
|
+
return stats;
|
|
7825
|
+
}
|
|
7826
|
+
search(query, limit = 10) {
|
|
7827
|
+
if (!this.db) {
|
|
7828
|
+
throw new Error("GovernanceSqliteView not initialized. Call init() first.");
|
|
7829
|
+
}
|
|
7830
|
+
const rows = this.db.prepare(
|
|
7831
|
+
`SELECT source, record_id as recordId, text, timestamp, rank
|
|
7832
|
+
FROM governance_fts
|
|
7833
|
+
WHERE governance_fts MATCH ?
|
|
7834
|
+
ORDER BY rank
|
|
7835
|
+
LIMIT ?`
|
|
7836
|
+
).all(query, limit);
|
|
7837
|
+
return rows;
|
|
7838
|
+
}
|
|
7839
|
+
listTasks(status2) {
|
|
7840
|
+
if (!this.db) {
|
|
7841
|
+
throw new Error("GovernanceSqliteView not initialized. Call init() first.");
|
|
7842
|
+
}
|
|
7843
|
+
if (status2) {
|
|
7844
|
+
return this.db.prepare(
|
|
7845
|
+
"SELECT id, description, status, created_at, updated_at, notes FROM tasks WHERE status = ? ORDER BY created_at"
|
|
7846
|
+
).all(status2);
|
|
7847
|
+
}
|
|
7848
|
+
return this.db.prepare(
|
|
7849
|
+
"SELECT id, description, status, created_at, updated_at, notes FROM tasks ORDER BY created_at"
|
|
7850
|
+
).all();
|
|
7851
|
+
}
|
|
7852
|
+
getLatestHandoff() {
|
|
7853
|
+
if (!this.db) {
|
|
7854
|
+
throw new Error("GovernanceSqliteView not initialized. Call init() first.");
|
|
7855
|
+
}
|
|
7856
|
+
const row = this.db.prepare(
|
|
7857
|
+
"SELECT timestamp, from_agent as fromAgent, reason, summary, next_actions as nextActions FROM handoffs ORDER BY timestamp DESC LIMIT 1"
|
|
7858
|
+
).get();
|
|
7859
|
+
if (!row) return null;
|
|
7860
|
+
return {
|
|
7861
|
+
timestamp: row.timestamp,
|
|
7862
|
+
from_agent: row.fromAgent ?? void 0,
|
|
7863
|
+
reason: row.reason,
|
|
7864
|
+
summary: row.summary,
|
|
7865
|
+
next_actions: row.nextActions.split("\n").filter((item) => item.trim().length > 0)
|
|
7866
|
+
};
|
|
7867
|
+
}
|
|
7868
|
+
getArchitecture() {
|
|
7869
|
+
if (!this.db) {
|
|
7870
|
+
throw new Error("GovernanceSqliteView not initialized. Call init() first.");
|
|
7871
|
+
}
|
|
7872
|
+
const row = this.db.prepare(
|
|
7873
|
+
"SELECT version, last_updated, updated_by, change_reason, content FROM architecture LIMIT 1"
|
|
7874
|
+
).get();
|
|
7875
|
+
if (!row) return null;
|
|
7876
|
+
return {
|
|
7877
|
+
content: row.content,
|
|
7878
|
+
meta: {
|
|
7879
|
+
version: row.version,
|
|
7880
|
+
last_updated: row.last_updated,
|
|
7881
|
+
updated_by: row.updated_by,
|
|
7882
|
+
change_reason: row.change_reason
|
|
7883
|
+
}
|
|
7884
|
+
};
|
|
7885
|
+
}
|
|
7886
|
+
};
|
|
7887
|
+
|
|
7888
|
+
// src/cli/governance.ts
|
|
7889
|
+
var ACTIVE_ARCH_PATH3 = join19(".archon", "active", "architecture.md");
|
|
7890
|
+
var ACTIVE_TASKS_PATH2 = join19(".archon", "active", "tasks.json");
|
|
7891
|
+
var CURRENT_CONTEXT_PATH = join19(".archon", "current_context.md");
|
|
7892
|
+
var HANDOFF_HISTORY_PATH = join19(".archon", "history", "handoffs.jsonl");
|
|
7893
|
+
var COMPLETED_TASKS_PATH = join19(".archon", "archive", "completed_tasks.jsonl");
|
|
7894
|
+
var TASK_STATUSES = ["pending", "in_progress", "verification", "done"];
|
|
7895
|
+
function isTaskStatus(value) {
|
|
7896
|
+
return typeof value === "string" && TASK_STATUSES.includes(value);
|
|
7897
|
+
}
|
|
7898
|
+
function resolveArchitecturePath2(cwd) {
|
|
7899
|
+
const agdPath = join19(cwd, ACTIVE_ARCH_PATH3);
|
|
7900
|
+
if (existsSync19(agdPath)) {
|
|
7901
|
+
return { path: agdPath, source: "agd" };
|
|
7902
|
+
}
|
|
7903
|
+
const legacyPath = join19(cwd, "ARCHITECTURE.md");
|
|
7904
|
+
if (existsSync19(legacyPath)) {
|
|
7905
|
+
return { path: legacyPath, source: "legacy" };
|
|
7906
|
+
}
|
|
7907
|
+
return { path: null, source: null };
|
|
7908
|
+
}
|
|
7909
|
+
function parseTasks(raw) {
|
|
7910
|
+
if (!Array.isArray(raw)) return null;
|
|
7911
|
+
const tasks = [];
|
|
7912
|
+
for (const item of raw) {
|
|
7913
|
+
if (typeof item !== "object" || item === null) return null;
|
|
7914
|
+
const entry = item;
|
|
7915
|
+
if (typeof entry["id"] !== "string") return null;
|
|
7916
|
+
if (typeof entry["description"] !== "string") return null;
|
|
7917
|
+
if (!isTaskStatus(entry["status"])) return null;
|
|
7918
|
+
if (typeof entry["created_at"] !== "string") return null;
|
|
7919
|
+
tasks.push({
|
|
7920
|
+
id: entry["id"],
|
|
7921
|
+
description: entry["description"],
|
|
7922
|
+
status: entry["status"],
|
|
7923
|
+
created_at: entry["created_at"],
|
|
7924
|
+
parent_id: typeof entry["parent_id"] === "string" ? entry["parent_id"] : void 0,
|
|
7925
|
+
assigned_to: typeof entry["assigned_to"] === "string" ? entry["assigned_to"] : void 0,
|
|
7926
|
+
updated_at: typeof entry["updated_at"] === "string" ? entry["updated_at"] : void 0,
|
|
7927
|
+
notes: typeof entry["notes"] === "string" ? entry["notes"] : void 0
|
|
7928
|
+
});
|
|
7929
|
+
}
|
|
7930
|
+
return tasks;
|
|
7931
|
+
}
|
|
7932
|
+
function countTaskStatuses(tasks) {
|
|
7933
|
+
const counts = {
|
|
7934
|
+
pending: 0,
|
|
7935
|
+
in_progress: 0,
|
|
7936
|
+
verification: 0,
|
|
7937
|
+
done: 0
|
|
7938
|
+
};
|
|
7939
|
+
for (const task of tasks) {
|
|
7940
|
+
if (counts[task.status] === void 0) continue;
|
|
7941
|
+
counts[task.status] += 1;
|
|
7942
|
+
}
|
|
7943
|
+
return counts;
|
|
7944
|
+
}
|
|
7945
|
+
function parseContextMeta(content) {
|
|
7946
|
+
const timestampMatch = content.match(/^Timestamp:\s*(.+)$/m);
|
|
7947
|
+
const reasonMatch = content.match(/^Reason:\s*(.+)$/m);
|
|
7948
|
+
return {
|
|
7949
|
+
timestamp: timestampMatch?.[1]?.trim(),
|
|
7950
|
+
reason: reasonMatch?.[1]?.trim()
|
|
7951
|
+
};
|
|
7952
|
+
}
|
|
7953
|
+
function renderStatusCounts(counts) {
|
|
7954
|
+
const total = Object.values(counts).reduce((sum, value) => sum + value, 0);
|
|
7955
|
+
if (total === 0) return;
|
|
7956
|
+
console.log(chalk17.dim(` Tasks: ${total} total`));
|
|
7957
|
+
console.log(
|
|
7958
|
+
chalk17.dim(
|
|
7959
|
+
` pending: ${counts.pending} | in_progress: ${counts.in_progress} | verification: ${counts.verification} | done: ${counts.done}`
|
|
7960
|
+
)
|
|
7961
|
+
);
|
|
7962
|
+
}
|
|
7963
|
+
function lineCount(content) {
|
|
7964
|
+
const trimmed = content.trim();
|
|
7965
|
+
if (!trimmed) return 0;
|
|
7966
|
+
return trimmed.split("\n").length;
|
|
7967
|
+
}
|
|
7968
|
+
function createGovernanceCommand() {
|
|
7969
|
+
const governance = new Command2("governance").alias("gov").description("Manage governance store (AGD)");
|
|
7970
|
+
governance.command("status").description("Show governance status from AGD store").action(async () => {
|
|
7971
|
+
const cwd = process.cwd();
|
|
7972
|
+
const store = new GovernanceStore(cwd);
|
|
7973
|
+
store.ensureStructure();
|
|
7974
|
+
console.log(chalk17.bold("\nGovernance Status\n"));
|
|
7975
|
+
const archInfo = resolveArchitecturePath2(cwd);
|
|
7976
|
+
if (archInfo.path) {
|
|
7977
|
+
const parser = new ArchitectureParser(archInfo.path);
|
|
7978
|
+
const parsed = await parser.parse();
|
|
7979
|
+
if (parsed.success && parsed.schema) {
|
|
7980
|
+
const posture = parsed.schema.qualityLevel?.posture ?? "unknown";
|
|
7981
|
+
console.log(chalk17.green(" \u2713") + ` Architecture (${archInfo.source})`);
|
|
7982
|
+
console.log(chalk17.dim(` posture: ${posture}`));
|
|
7983
|
+
console.log(chalk17.dim(` invariants: ${parsed.schema.invariants.length}`));
|
|
7984
|
+
console.log(chalk17.dim(` protected paths: ${parsed.schema.protectedPaths.length}`));
|
|
7985
|
+
} else {
|
|
7986
|
+
console.log(chalk17.yellow(" ! Architecture present but failed to parse"));
|
|
7987
|
+
}
|
|
7988
|
+
} else {
|
|
7989
|
+
console.log(chalk17.yellow(" \u25CB No architecture file found"));
|
|
7990
|
+
}
|
|
7991
|
+
const tasksPath = join19(cwd, ACTIVE_TASKS_PATH2);
|
|
7992
|
+
if (existsSync19(tasksPath)) {
|
|
7993
|
+
try {
|
|
7994
|
+
const tasksRaw = JSON.parse(readFileSync7(tasksPath, "utf-8"));
|
|
7995
|
+
const tasks2 = parseTasks(tasksRaw);
|
|
7996
|
+
if (tasks2) {
|
|
7997
|
+
renderStatusCounts(countTaskStatuses(tasks2));
|
|
7998
|
+
} else {
|
|
7999
|
+
console.log(chalk17.yellow(" ! tasks.json present but invalid"));
|
|
8000
|
+
}
|
|
8001
|
+
} catch {
|
|
8002
|
+
console.log(chalk17.yellow(" ! tasks.json present but unreadable"));
|
|
8003
|
+
}
|
|
8004
|
+
}
|
|
8005
|
+
const contextPath = join19(cwd, CURRENT_CONTEXT_PATH);
|
|
8006
|
+
if (existsSync19(contextPath)) {
|
|
8007
|
+
const content = readFileSync7(contextPath, "utf-8");
|
|
8008
|
+
const meta = parseContextMeta(content);
|
|
8009
|
+
console.log(chalk17.dim(` Handoff: ${meta.timestamp ?? "present"}`));
|
|
8010
|
+
if (meta.reason) {
|
|
8011
|
+
console.log(chalk17.dim(` reason: ${meta.reason}`));
|
|
8012
|
+
}
|
|
8013
|
+
}
|
|
8014
|
+
const handoffHistoryPath = join19(cwd, HANDOFF_HISTORY_PATH);
|
|
8015
|
+
if (existsSync19(handoffHistoryPath)) {
|
|
8016
|
+
const count = lineCount(readFileSync7(handoffHistoryPath, "utf-8"));
|
|
8017
|
+
if (count > 0) {
|
|
8018
|
+
console.log(chalk17.dim(` Handoff entries: ${count}`));
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
const completedPath = join19(cwd, COMPLETED_TASKS_PATH);
|
|
8022
|
+
if (existsSync19(completedPath)) {
|
|
8023
|
+
const count = lineCount(readFileSync7(completedPath, "utf-8"));
|
|
8024
|
+
if (count > 0) {
|
|
8025
|
+
console.log(chalk17.dim(` Completed tasks archived: ${count}`));
|
|
8026
|
+
}
|
|
8027
|
+
}
|
|
8028
|
+
console.log("");
|
|
8029
|
+
});
|
|
8030
|
+
const architecture = governance.command("architecture").description("Read or update governance architecture");
|
|
8031
|
+
architecture.command("show").description("Show active architecture content").option("--raw", "Show full file including frontmatter").action(async (options) => {
|
|
8032
|
+
const cwd = process.cwd();
|
|
8033
|
+
const store = new GovernanceStore(cwd);
|
|
8034
|
+
const archInfo = resolveArchitecturePath2(cwd);
|
|
8035
|
+
if (!archInfo.path) {
|
|
8036
|
+
console.log(chalk17.yellow("No architecture file found."));
|
|
8037
|
+
return;
|
|
8038
|
+
}
|
|
8039
|
+
if (options.raw) {
|
|
8040
|
+
const raw = readFileSync7(archInfo.path, "utf-8");
|
|
8041
|
+
console.log(raw.trimEnd());
|
|
8042
|
+
return;
|
|
8043
|
+
}
|
|
8044
|
+
if (archInfo.source === "agd") {
|
|
8045
|
+
const result = await store.readArchitecture();
|
|
8046
|
+
if (!result.ok) {
|
|
8047
|
+
console.log(chalk17.red(`Failed to read architecture: ${result.error}`));
|
|
8048
|
+
return;
|
|
8049
|
+
}
|
|
8050
|
+
if (!result.value) {
|
|
8051
|
+
console.log(chalk17.yellow("Architecture file is empty."));
|
|
8052
|
+
return;
|
|
8053
|
+
}
|
|
8054
|
+
console.log(result.value);
|
|
8055
|
+
return;
|
|
8056
|
+
}
|
|
8057
|
+
const legacyRaw = readFileSync7(archInfo.path, "utf-8");
|
|
8058
|
+
const legacyParsed = matter4(legacyRaw);
|
|
8059
|
+
const legacyContent = legacyParsed.content.trim();
|
|
8060
|
+
if (!legacyContent) {
|
|
8061
|
+
console.log(chalk17.yellow("Architecture file is empty."));
|
|
8062
|
+
return;
|
|
8063
|
+
}
|
|
8064
|
+
console.log(legacyContent);
|
|
8065
|
+
});
|
|
8066
|
+
architecture.command("update").description("Update active architecture with change reason").requiredOption("-f, --file <path>", "Path to new architecture content").requiredOption("-r, --reason <text>", "Reason for the change (required)").option("-b, --by <name>", "Updated by (optional)").action(async (options) => {
|
|
8067
|
+
const cwd = process.cwd();
|
|
8068
|
+
const filePath = options.file;
|
|
8069
|
+
if (!existsSync19(filePath)) {
|
|
8070
|
+
console.log(chalk17.red(`File not found: ${filePath}`));
|
|
8071
|
+
process.exit(1);
|
|
8072
|
+
}
|
|
8073
|
+
const content = await readFile12(filePath, "utf-8");
|
|
8074
|
+
const store = new GovernanceStore(cwd);
|
|
8075
|
+
const result = await store.updateArchitecture(content, options.reason, options.by);
|
|
8076
|
+
if (!result.ok) {
|
|
8077
|
+
console.log(chalk17.red(`Failed to update architecture: ${result.error}`));
|
|
8078
|
+
process.exit(1);
|
|
8079
|
+
}
|
|
8080
|
+
console.log(chalk17.green("\u2713 Architecture updated"));
|
|
8081
|
+
});
|
|
8082
|
+
const tasks = governance.command("task").description("Update tasks in governance store");
|
|
8083
|
+
tasks.command("update <taskId>").description("Update task status and notes").requiredOption("-s, --status <status>", "Status: pending, in_progress, verification, done").option("-n, --notes <text>", "Optional notes").action(async (taskId, options) => {
|
|
8084
|
+
const status2 = options.status;
|
|
8085
|
+
if (!isTaskStatus(status2)) {
|
|
8086
|
+
console.log(chalk17.red(`Invalid status: ${options.status}`));
|
|
8087
|
+
process.exit(1);
|
|
8088
|
+
}
|
|
8089
|
+
const store = new GovernanceStore(process.cwd());
|
|
8090
|
+
const result = await store.updateTask(taskId, status2, options.notes);
|
|
8091
|
+
if (!result.ok) {
|
|
8092
|
+
console.log(chalk17.red(`Failed to update task: ${result.error}`));
|
|
8093
|
+
process.exit(1);
|
|
8094
|
+
}
|
|
8095
|
+
console.log(chalk17.green(`\u2713 Task ${taskId} updated to ${status2}`));
|
|
8096
|
+
});
|
|
8097
|
+
governance.command("handoff").description("Log a handoff entry and update current context").requiredOption("-r, --reason <text>", "Reason for handoff").requiredOption("-s, --summary <text>", "Summary (single paragraph)").requiredOption("-n, --next <actions...>", "Next actions (space-separated)").option("-f, --from <agent>", "Agent or source name").option("-t, --timestamp <iso>", "Timestamp override (ISO 8601)").action(async (options) => {
|
|
8098
|
+
const store = new GovernanceStore(process.cwd());
|
|
8099
|
+
const entry = {
|
|
8100
|
+
timestamp: options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
8101
|
+
from_agent: options.from,
|
|
8102
|
+
reason: options.reason,
|
|
8103
|
+
summary: options.summary,
|
|
8104
|
+
next_actions: options.next
|
|
8105
|
+
};
|
|
8106
|
+
const result = await store.logHandoff(entry);
|
|
8107
|
+
if (!result.ok) {
|
|
8108
|
+
console.log(chalk17.red(`Failed to log handoff: ${result.error}`));
|
|
8109
|
+
process.exit(1);
|
|
8110
|
+
}
|
|
8111
|
+
console.log(chalk17.green("\u2713 Handoff logged"));
|
|
8112
|
+
});
|
|
8113
|
+
governance.command("migrate").description("Migrate legacy governance files into AGD structure").option("--remove-legacy", "Archive legacy files after migration").option("--dry-run", "Show what would change without writing files").option("-b, --by <name>", "Updated by label for architecture migration").action(async (options) => {
|
|
8114
|
+
const result = await migrateLegacyGovernance({
|
|
8115
|
+
cwd: process.cwd(),
|
|
8116
|
+
removeLegacy: options.removeLegacy,
|
|
8117
|
+
dryRun: options.dryRun,
|
|
8118
|
+
updatedBy: options.by
|
|
8119
|
+
});
|
|
8120
|
+
console.log(chalk17.bold("\nMigration Summary"));
|
|
8121
|
+
console.log(
|
|
8122
|
+
result.migratedArchitecture ? chalk17.green(" \u2713 Architecture migrated") : chalk17.dim(" \u25CB Architecture migration skipped")
|
|
8123
|
+
);
|
|
8124
|
+
console.log(
|
|
8125
|
+
result.migratedTasks ? chalk17.green(" \u2713 Tasks migrated") : chalk17.dim(" \u25CB Tasks migration skipped")
|
|
8126
|
+
);
|
|
8127
|
+
if (result.archivedLegacyFiles.length > 0) {
|
|
8128
|
+
console.log(chalk17.dim(" Archived legacy files:"));
|
|
8129
|
+
for (const file of result.archivedLegacyFiles) {
|
|
8130
|
+
console.log(chalk17.dim(` - ${file}`));
|
|
8131
|
+
}
|
|
8132
|
+
}
|
|
8133
|
+
if (result.warnings.length > 0) {
|
|
8134
|
+
console.log(chalk17.yellow("\nWarnings:"));
|
|
8135
|
+
for (const warning of result.warnings) {
|
|
8136
|
+
console.log(chalk17.yellow(` - ${warning}`));
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
console.log("");
|
|
8140
|
+
});
|
|
8141
|
+
governance.command("sqlite-init").description("Initialize or refresh local governance SQLite DB").option("--db <path>", "Override DB path (default: .archon/governance.db)").action(async (options) => {
|
|
8142
|
+
const cwd = process.cwd();
|
|
8143
|
+
const view = new GovernanceSqliteView({ dbPath: options.db ?? ".archon/governance.db" });
|
|
8144
|
+
view.init(cwd);
|
|
8145
|
+
await view.loadFromDisk(cwd);
|
|
8146
|
+
view.close();
|
|
8147
|
+
console.log(chalk17.green("\u2713 Governance SQLite DB initialized"));
|
|
8148
|
+
});
|
|
8149
|
+
return governance;
|
|
8150
|
+
}
|
|
8151
|
+
|
|
7111
8152
|
// src/cli/index.ts
|
|
7112
|
-
var program = new
|
|
8153
|
+
var program = new Command3();
|
|
7113
8154
|
program.name("archon").description("Local-first AI-powered development governance").version(getCurrentVersion()).action(async () => {
|
|
7114
8155
|
const cwd = process.cwd();
|
|
7115
8156
|
const wasInitialized = isInitialized(cwd);
|
|
7116
8157
|
if (!wasInitialized) {
|
|
7117
|
-
console.log(
|
|
8158
|
+
console.log(chalk18.blue("\nArchonDev is not initialized in this folder.\n"));
|
|
7118
8159
|
await init({ analyze: true, git: true });
|
|
7119
8160
|
}
|
|
7120
8161
|
await start({ skipGovernanceBanner: !wasInitialized });
|
|
@@ -7122,7 +8163,7 @@ program.name("archon").description("Local-first AI-powered development governanc
|
|
|
7122
8163
|
program.command("login").description("Authenticate with ArchonDev").option("-p, --provider <provider>", "OAuth provider (github or google)", "github").action(async (options) => {
|
|
7123
8164
|
const provider = options.provider;
|
|
7124
8165
|
if (provider !== "github" && provider !== "google") {
|
|
7125
|
-
console.error(
|
|
8166
|
+
console.error(chalk18.red('Invalid provider. Use "github" or "google"'));
|
|
7126
8167
|
process.exit(1);
|
|
7127
8168
|
}
|
|
7128
8169
|
await login(provider);
|
|
@@ -7256,6 +8297,7 @@ reviewCommand.action(async () => {
|
|
|
7256
8297
|
await reviewStatus();
|
|
7257
8298
|
});
|
|
7258
8299
|
program.addCommand(createDepsCommand());
|
|
8300
|
+
program.addCommand(createGovernanceCommand());
|
|
7259
8301
|
var a11yCommand = program.command("a11y").description("Accessibility checking and compliance for web deployments");
|
|
7260
8302
|
a11yCommand.command("check").description("Run WCAG 2.2 AA accessibility audit").option("-v, --verbose", "Show all issues including minor ones").option("-f, --format <format>", "Output format (text or json)", "text").action(async (options) => {
|
|
7261
8303
|
await a11yCheck(options);
|
|
@@ -7316,7 +8358,7 @@ cleanupCmd.command("check").description("Analyze workspace for bloat and mainten
|
|
|
7316
8358
|
cleanupCmd.command("run").description("Execute cleanup (archive old entries, remove stale files)").action(cleanupRun);
|
|
7317
8359
|
cleanupCmd.command("auto").description("Enable/disable automatic cleanup checks").argument("[action]", "enable, disable, or status", "status").action(async (action) => {
|
|
7318
8360
|
if (action !== "enable" && action !== "disable" && action !== "status") {
|
|
7319
|
-
console.error(
|
|
8361
|
+
console.error(chalk18.red("Invalid action. Use: enable, disable, or status"));
|
|
7320
8362
|
process.exit(1);
|
|
7321
8363
|
}
|
|
7322
8364
|
await cleanupAuto(action);
|