archondev 2.17.0 → 2.18.2
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 +1 -0
- package/dist/a11y-O35BAA25.js +14 -0
- package/dist/auth-HQBAG4CR.js +14 -0
- package/dist/bug-5WRBCFRT.js +12 -0
- package/dist/{chunk-ER4ADSWH.js → chunk-2NSWZDP7.js} +1 -156
- package/dist/chunk-3ASILTFB.js +73 -0
- package/dist/{chunk-LHCXE6UL.js → chunk-3PZ7WU5I.js} +26 -73
- package/dist/{chunk-QGM4M3NI.js → chunk-4VNS5WPM.js} +5 -0
- package/dist/chunk-4YEJ26F7.js +183 -0
- package/dist/chunk-5CFGPXQ3.js +160 -0
- package/dist/{chunk-TRLP7RMZ.js → chunk-75J2JMU3.js} +2 -2
- package/dist/{chunk-FGH2UX3E.js → chunk-BFPWDOMA.js} +1 -1
- package/dist/{chunk-FA2GAZ7L.js → chunk-CZCY63EY.js} +12 -8
- package/dist/{chunk-E7ZTIAQM.js → chunk-EKU62MGC.js} +160 -286
- package/dist/chunk-FWLLGLD5.js +353 -0
- package/dist/{chunk-BDPGWWQC.js → chunk-HGLPIM7J.js} +1 -1
- package/dist/{chunk-NIKN37AY.js → chunk-HJARQDQR.js} +1 -1
- package/dist/chunk-JF7JCK6H.js +485 -0
- package/dist/{chunk-DUJOT5B6.js → chunk-LBBHM4I5.js} +8 -8
- package/dist/{chunk-LPSS2U5V.js → chunk-NCPHO54C.js} +65 -2
- package/dist/{chunk-WZIRUPMP.js → chunk-NJF6MRTR.js} +1 -1
- package/dist/{chunk-54ATBLYE.js → chunk-P666JE3G.js} +1 -1
- package/dist/{chunk-SUGIWSCB.js → chunk-SVU7MLG6.js} +5 -24
- package/dist/chunk-UD45ZLV3.js +495 -0
- package/dist/{chunk-LXXTCZ2Q.js → chunk-UFR2LX6G.js} +216 -14
- package/dist/{chunk-2QIXLBAC.js → chunk-VF2OTDMS.js} +6 -4
- package/dist/{chunk-HTJOCKVV.js → chunk-ZJXIOETW.js} +6 -6
- package/dist/{code-review-ORCNXANW.js → code-review-6MU4UE5M.js} +4 -4
- package/dist/{config-USLUSE4N.js → config-JBPM2YAB.js} +2 -3
- package/dist/{constants-AHP5F7HW.js → constants-XDIWFFPN.js} +1 -1
- package/dist/execute-MB4ZF3HQ.js +18 -0
- package/dist/geo-KV4IRGKN.js +20 -0
- package/dist/index.js +928 -1831
- package/dist/{init-PSMJLDEZ.js → init-FINETS3Q.js} +2 -2
- package/dist/{interviewer-Q6PFSAGT.js → interviewer-NQHNMVH4.js} +5 -6
- package/dist/{keys-SXJ6MKPY.js → keys-THCHXIFD.js} +1 -1
- package/dist/{keys-VLK3EWSN.js → keys-U4QZE5YB.js} +3 -4
- package/dist/list-CNKAH354.js +17 -0
- package/dist/{manager-32P6ZZBP.js → manager-YSNTH2DG.js} +1 -1
- package/dist/models-UTFGCHAY.js +33 -0
- package/dist/{orchestration-X6LHSHBJ.js → orchestration-HIF3KP25.js} +1 -1
- package/dist/{parallel-WHFKRBPR.js → parallel-W2ETYYAL.js} +11 -7
- package/dist/{parser-4KJH2PT5.js → parser-BFHETZ5B.js} +1 -1
- package/dist/{plan-X77BUKNE.js → plan-XALA4SLH.js} +7 -6
- package/dist/{preferences-TWEK2RWY.js → preferences-I6WETXOI.js} +6 -6
- package/dist/{review-T4ID2QUF.js → review-AUG6GIL6.js} +5 -5
- package/dist/seo-PMI42KRZ.js +10 -0
- package/dist/{tier-selection-Z2RFHZUX.js → tier-selection-426HA765.js} +3 -4
- package/dist/web-checks-4BSYXWDF.js +13 -0
- package/package.json +1 -1
- package/dist/auth-KUFS3PBS.js +0 -14
- package/dist/bug-BJH4X5LI.js +0 -12
- package/dist/execute-73QW4ZEZ.js +0 -16
- package/dist/list-MMKB5TGX.js +0 -16
package/dist/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createGeoCommand
|
|
4
|
+
} from "./chunk-UD45ZLV3.js";
|
|
2
5
|
import {
|
|
3
6
|
reviewAnalyze,
|
|
4
7
|
reviewExport,
|
|
@@ -10,8 +13,8 @@ import {
|
|
|
10
13
|
reviewShow,
|
|
11
14
|
reviewStatus,
|
|
12
15
|
reviewUpdate
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-BFPWDOMA.js";
|
|
17
|
+
import "./chunk-HGLPIM7J.js";
|
|
15
18
|
import {
|
|
16
19
|
listModels,
|
|
17
20
|
resetPreferences,
|
|
@@ -19,80 +22,92 @@ import {
|
|
|
19
22
|
setPreference,
|
|
20
23
|
showExecutionPreferences,
|
|
21
24
|
showPreferences
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-ZJXIOETW.js";
|
|
26
|
+
import {
|
|
27
|
+
a11yBadge,
|
|
28
|
+
a11yCheck,
|
|
29
|
+
a11yFix,
|
|
30
|
+
a11yPreDeploy
|
|
31
|
+
} from "./chunk-FWLLGLD5.js";
|
|
32
|
+
import {
|
|
33
|
+
createSeoCommand
|
|
34
|
+
} from "./chunk-JF7JCK6H.js";
|
|
35
|
+
import "./chunk-3ASILTFB.js";
|
|
23
36
|
import {
|
|
24
37
|
init,
|
|
25
38
|
isInitialized
|
|
26
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-P666JE3G.js";
|
|
27
40
|
import {
|
|
28
41
|
parallelClean,
|
|
42
|
+
parallelExecuteCloud,
|
|
29
43
|
parallelMerge,
|
|
30
44
|
parallelRunWaves,
|
|
31
45
|
parallelSchedule,
|
|
32
46
|
parallelStatus
|
|
33
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-NCPHO54C.js";
|
|
34
48
|
import {
|
|
35
49
|
DependencyParser,
|
|
36
50
|
EnvironmentConfigLoader,
|
|
37
51
|
EnvironmentValidator,
|
|
52
|
+
execute
|
|
53
|
+
} from "./chunk-EKU62MGC.js";
|
|
54
|
+
import {
|
|
38
55
|
cloudCancel,
|
|
39
56
|
cloudLogs,
|
|
40
57
|
cloudStatus,
|
|
41
|
-
|
|
42
|
-
} from "./chunk-
|
|
58
|
+
createAuthedSupabaseClient
|
|
59
|
+
} from "./chunk-4YEJ26F7.js";
|
|
43
60
|
import {
|
|
44
61
|
list
|
|
45
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-NJF6MRTR.js";
|
|
46
63
|
import {
|
|
47
64
|
listLocalAtoms,
|
|
48
65
|
loadAtom,
|
|
49
66
|
plan
|
|
50
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-3PZ7WU5I.js";
|
|
51
68
|
import {
|
|
52
69
|
ArchitectureParser
|
|
53
70
|
} from "./chunk-5EVHUDQX.js";
|
|
54
71
|
import {
|
|
55
72
|
bugReport
|
|
56
|
-
} from "./chunk-
|
|
57
|
-
import
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
import "./chunk-
|
|
61
|
-
import "./chunk-LXXTCZ2Q.js";
|
|
73
|
+
} from "./chunk-VF2OTDMS.js";
|
|
74
|
+
import "./chunk-5CFGPXQ3.js";
|
|
75
|
+
import "./chunk-2NSWZDP7.js";
|
|
76
|
+
import "./chunk-HJARQDQR.js";
|
|
77
|
+
import "./chunk-UFR2LX6G.js";
|
|
62
78
|
import {
|
|
63
79
|
addKey,
|
|
64
80
|
listKeys,
|
|
65
81
|
removeKey,
|
|
66
82
|
setPrimaryKey
|
|
67
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-75J2JMU3.js";
|
|
68
84
|
import "./chunk-TFSHS7EN.js";
|
|
69
85
|
import "./chunk-RDG5BUED.js";
|
|
70
86
|
import {
|
|
71
87
|
login,
|
|
72
88
|
logout,
|
|
73
89
|
status
|
|
74
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-CZCY63EY.js";
|
|
91
|
+
import {
|
|
92
|
+
API_URL,
|
|
93
|
+
SUPABASE_ANON_KEY,
|
|
94
|
+
SUPABASE_URL
|
|
95
|
+
} from "./chunk-M4LGRTLC.js";
|
|
75
96
|
import {
|
|
76
97
|
handleTierSetup,
|
|
77
98
|
promptTierSelection,
|
|
78
99
|
updateUserTier
|
|
79
|
-
} from "./chunk-
|
|
100
|
+
} from "./chunk-LBBHM4I5.js";
|
|
80
101
|
import {
|
|
81
|
-
createAuthedSupabaseClient,
|
|
82
102
|
getAuthToken,
|
|
83
103
|
loadConfig,
|
|
84
104
|
saveConfig
|
|
85
|
-
} from "./chunk-
|
|
86
|
-
import
|
|
87
|
-
API_URL,
|
|
88
|
-
SUPABASE_ANON_KEY,
|
|
89
|
-
SUPABASE_URL
|
|
90
|
-
} from "./chunk-M4LGRTLC.js";
|
|
91
|
-
import "./chunk-QGM4M3NI.js";
|
|
105
|
+
} from "./chunk-SVU7MLG6.js";
|
|
106
|
+
import "./chunk-4VNS5WPM.js";
|
|
92
107
|
|
|
93
108
|
// src/cli/index.ts
|
|
94
|
-
import { Command as
|
|
95
|
-
import
|
|
109
|
+
import { Command as Command2 } from "commander";
|
|
110
|
+
import chalk17 from "chalk";
|
|
96
111
|
import "dotenv/config";
|
|
97
112
|
|
|
98
113
|
// src/cli/promote.ts
|
|
@@ -313,8 +328,8 @@ function formatDateTime(date) {
|
|
|
313
328
|
// src/cli/start.ts
|
|
314
329
|
import chalk6 from "chalk";
|
|
315
330
|
import readline from "readline";
|
|
316
|
-
import { existsSync as
|
|
317
|
-
import { join as
|
|
331
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as readdirSync3, appendFileSync } from "fs";
|
|
332
|
+
import { join as join6 } from "path";
|
|
318
333
|
|
|
319
334
|
// src/core/context/manager.ts
|
|
320
335
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -2617,6 +2632,87 @@ function summarizeGeneration(result) {
|
|
|
2617
2632
|
return lines.join("\n");
|
|
2618
2633
|
}
|
|
2619
2634
|
|
|
2635
|
+
// src/core/web/detect.ts
|
|
2636
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2, readFileSync as readFileSync2 } from "fs";
|
|
2637
|
+
import { join as join5 } from "path";
|
|
2638
|
+
var FRAMEWORK_MARKERS = [
|
|
2639
|
+
{ name: "Next.js", files: ["next.config.js", "next.config.mjs"], deps: ["next"] },
|
|
2640
|
+
{ name: "Astro", files: ["astro.config.mjs", "astro.config.ts"], deps: ["astro"] },
|
|
2641
|
+
{ name: "Vite", files: ["vite.config.ts", "vite.config.js", "vite.config.mjs"], deps: ["vite"] },
|
|
2642
|
+
{ name: "Svelte", files: ["svelte.config.js", "svelte.config.ts"], deps: ["svelte"] },
|
|
2643
|
+
{ name: "Vue", files: ["vue.config.js"], deps: ["vue", "nuxt", "vitepress"] },
|
|
2644
|
+
{ name: "Nuxt", files: ["nuxt.config.ts", "nuxt.config.js"], deps: ["nuxt"] },
|
|
2645
|
+
{ name: "Gatsby", files: ["gatsby-config.js", "gatsby-config.ts"], deps: ["gatsby"] },
|
|
2646
|
+
{ name: "Remix", files: ["remix.config.js", "remix.config.ts"], deps: ["@remix-run/react"] },
|
|
2647
|
+
{ name: "Angular", files: ["angular.json"], deps: ["@angular/core"] },
|
|
2648
|
+
{ name: "React", deps: ["react", "react-dom"] },
|
|
2649
|
+
{ name: "Solid", deps: ["solid-js"] }
|
|
2650
|
+
];
|
|
2651
|
+
var HTML_MARKERS = [
|
|
2652
|
+
"index.html",
|
|
2653
|
+
join5("public", "index.html"),
|
|
2654
|
+
join5("src", "index.html")
|
|
2655
|
+
];
|
|
2656
|
+
function readPackageJson(cwd) {
|
|
2657
|
+
const pkgPath = join5(cwd, "package.json");
|
|
2658
|
+
if (!existsSync5(pkgPath)) {
|
|
2659
|
+
return null;
|
|
2660
|
+
}
|
|
2661
|
+
try {
|
|
2662
|
+
const content = readFileSync2(pkgPath, "utf-8");
|
|
2663
|
+
return JSON.parse(content);
|
|
2664
|
+
} catch {
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
function getDeps(pkg) {
|
|
2669
|
+
if (!pkg) return /* @__PURE__ */ new Set();
|
|
2670
|
+
const deps = pkg["dependencies"] ?? {};
|
|
2671
|
+
const devDeps = pkg["devDependencies"] ?? {};
|
|
2672
|
+
return /* @__PURE__ */ new Set([...Object.keys(deps), ...Object.keys(devDeps)]);
|
|
2673
|
+
}
|
|
2674
|
+
function hasHtmlLikeFiles(cwd, dir) {
|
|
2675
|
+
const target = join5(cwd, dir);
|
|
2676
|
+
if (!existsSync5(target)) return false;
|
|
2677
|
+
try {
|
|
2678
|
+
const entries = readdirSync2(target, { withFileTypes: true });
|
|
2679
|
+
return entries.some((entry) => {
|
|
2680
|
+
if (!entry.isFile()) return false;
|
|
2681
|
+
return entry.name.endsWith(".html") || entry.name.endsWith(".astro") || entry.name.endsWith(".svelte") || entry.name.endsWith(".vue");
|
|
2682
|
+
});
|
|
2683
|
+
} catch {
|
|
2684
|
+
return false;
|
|
2685
|
+
}
|
|
2686
|
+
}
|
|
2687
|
+
function detectWebProject(cwd) {
|
|
2688
|
+
const frameworks = [];
|
|
2689
|
+
const reasons = [];
|
|
2690
|
+
const pkg = readPackageJson(cwd);
|
|
2691
|
+
const deps = getDeps(pkg);
|
|
2692
|
+
for (const marker of FRAMEWORK_MARKERS) {
|
|
2693
|
+
const hasMarkerFile = marker.files?.some((file) => existsSync5(join5(cwd, file))) ?? false;
|
|
2694
|
+
const hasDep = marker.deps?.some((dep) => deps.has(dep)) ?? false;
|
|
2695
|
+
if (hasMarkerFile || hasDep) {
|
|
2696
|
+
frameworks.push(marker.name);
|
|
2697
|
+
reasons.push(`${marker.name} detected`);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
const hasHtml = HTML_MARKERS.some((file) => existsSync5(join5(cwd, file)));
|
|
2701
|
+
if (hasHtml) {
|
|
2702
|
+
reasons.push("HTML entrypoint detected");
|
|
2703
|
+
}
|
|
2704
|
+
const hasWebDirs = hasHtmlLikeFiles(cwd, "src") || hasHtmlLikeFiles(cwd, "public") || hasHtmlLikeFiles(cwd, "pages") || hasHtmlLikeFiles(cwd, "app");
|
|
2705
|
+
if (hasWebDirs) {
|
|
2706
|
+
reasons.push("Web template files detected");
|
|
2707
|
+
}
|
|
2708
|
+
const isWebProject = reasons.length > 0;
|
|
2709
|
+
return {
|
|
2710
|
+
isWebProject,
|
|
2711
|
+
frameworks: Array.from(new Set(frameworks)),
|
|
2712
|
+
reasons
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2620
2716
|
// src/cli/start.ts
|
|
2621
2717
|
async function start(options = {}) {
|
|
2622
2718
|
const cwd = process.cwd();
|
|
@@ -2637,7 +2733,7 @@ async function start(options = {}) {
|
|
|
2637
2733
|
}
|
|
2638
2734
|
}
|
|
2639
2735
|
if (!config.tier || config.tier === "FREE") {
|
|
2640
|
-
const isFirstRun = !
|
|
2736
|
+
const isFirstRun = !existsSync6(join6(cwd, ".archon")) && !existsSync6(join6(cwd, "ARCHITECTURE.md"));
|
|
2641
2737
|
if (isFirstRun && !config.tierConfirmed) {
|
|
2642
2738
|
console.log(chalk6.bold("How would you like to use ArchonDev?\n"));
|
|
2643
2739
|
const selection = await promptTierSelection();
|
|
@@ -2708,7 +2804,7 @@ async function start(options = {}) {
|
|
|
2708
2804
|
}
|
|
2709
2805
|
}
|
|
2710
2806
|
if (currentTier === "BYOK") {
|
|
2711
|
-
const { keyManager } = await import("./keys-
|
|
2807
|
+
const { keyManager } = await import("./keys-THCHXIFD.js");
|
|
2712
2808
|
const hasKeys = await keyManager.hasAnyKey();
|
|
2713
2809
|
if (!hasKeys) {
|
|
2714
2810
|
console.log();
|
|
@@ -2721,22 +2817,22 @@ async function start(options = {}) {
|
|
|
2721
2817
|
const choice = await promptWithCommands("What would you like to do?");
|
|
2722
2818
|
switch (choice) {
|
|
2723
2819
|
case "1":
|
|
2724
|
-
const { handleTierSetup: handleTierSetup2 } = await import("./tier-selection-
|
|
2820
|
+
const { handleTierSetup: handleTierSetup2 } = await import("./tier-selection-426HA765.js");
|
|
2725
2821
|
await handleTierSetup2("BYOK");
|
|
2726
2822
|
break;
|
|
2727
2823
|
case "2":
|
|
2728
|
-
const { updateUserTier: updateUserTier2 } = await import("./tier-selection-
|
|
2824
|
+
const { updateUserTier: updateUserTier2 } = await import("./tier-selection-426HA765.js");
|
|
2729
2825
|
const result = await updateUserTier2("CREDITS");
|
|
2730
2826
|
if (result.success) {
|
|
2731
2827
|
config.tier = "CREDITS";
|
|
2732
2828
|
await saveConfig(config);
|
|
2733
2829
|
console.log(chalk6.green("\u2713 Switched to Managed Plan"));
|
|
2734
|
-
const { handleTierSetup: setupCredits } = await import("./tier-selection-
|
|
2830
|
+
const { handleTierSetup: setupCredits } = await import("./tier-selection-426HA765.js");
|
|
2735
2831
|
await setupCredits("CREDITS");
|
|
2736
2832
|
}
|
|
2737
2833
|
break;
|
|
2738
2834
|
case "3":
|
|
2739
|
-
const { updateUserTier: downgrade } = await import("./tier-selection-
|
|
2835
|
+
const { updateUserTier: downgrade } = await import("./tier-selection-426HA765.js");
|
|
2740
2836
|
const downgradeResult = await downgrade("FREE");
|
|
2741
2837
|
if (downgradeResult.success) {
|
|
2742
2838
|
config.tier = "FREE";
|
|
@@ -2757,7 +2853,7 @@ async function start(options = {}) {
|
|
|
2757
2853
|
const projectState = detectProjectState(cwd);
|
|
2758
2854
|
if (!projectState.hasArchitecture) {
|
|
2759
2855
|
console.log(chalk6.dim("No project initialized in this folder.\n"));
|
|
2760
|
-
const { init: init2 } = await import("./init-
|
|
2856
|
+
const { init: init2 } = await import("./init-FINETS3Q.js");
|
|
2761
2857
|
await init2({ analyze: true, git: true });
|
|
2762
2858
|
console.log();
|
|
2763
2859
|
const newProjectState = detectProjectState(cwd);
|
|
@@ -2766,6 +2862,7 @@ async function start(options = {}) {
|
|
|
2766
2862
|
const governanceStatus = await gatherGovernanceStatus(cwd);
|
|
2767
2863
|
displayGovernanceStatus(governanceStatus);
|
|
2768
2864
|
}
|
|
2865
|
+
await maybePromptWebChecks(cwd, projectState);
|
|
2769
2866
|
const contextManager = new ContextManager();
|
|
2770
2867
|
const pendingAtomsData = await contextManager.getPendingAtomsData(cwd);
|
|
2771
2868
|
if (pendingAtomsData && pendingAtomsData.atoms.length > 0) {
|
|
@@ -2815,7 +2912,7 @@ function formatTierName(tier) {
|
|
|
2815
2912
|
}
|
|
2816
2913
|
async function fetchCreditsUsageStats(accessToken) {
|
|
2817
2914
|
try {
|
|
2818
|
-
const { API_URL: API_URL3 } = await import("./constants-
|
|
2915
|
+
const { API_URL: API_URL3 } = await import("./constants-XDIWFFPN.js");
|
|
2819
2916
|
const response = await fetch(`${API_URL3}/api/usage`, {
|
|
2820
2917
|
headers: {
|
|
2821
2918
|
"Authorization": `Bearer ${accessToken}`
|
|
@@ -2855,14 +2952,14 @@ function detectProjectState(cwd) {
|
|
|
2855
2952
|
const sourceExtensions = [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java", ".rb", ".php"];
|
|
2856
2953
|
let hasSourceFiles = false;
|
|
2857
2954
|
for (const dir of sourceDirs) {
|
|
2858
|
-
if (
|
|
2955
|
+
if (existsSync6(join6(cwd, dir))) {
|
|
2859
2956
|
hasSourceFiles = true;
|
|
2860
2957
|
break;
|
|
2861
2958
|
}
|
|
2862
2959
|
}
|
|
2863
2960
|
if (!hasSourceFiles) {
|
|
2864
2961
|
try {
|
|
2865
|
-
const files =
|
|
2962
|
+
const files = readdirSync3(cwd);
|
|
2866
2963
|
hasSourceFiles = files.some(
|
|
2867
2964
|
(f) => sourceExtensions.some((ext) => f.endsWith(ext))
|
|
2868
2965
|
);
|
|
@@ -2871,16 +2968,16 @@ function detectProjectState(cwd) {
|
|
|
2871
2968
|
}
|
|
2872
2969
|
const projectMarkers = ["package.json", "Cargo.toml", "pyproject.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
2873
2970
|
if (!hasSourceFiles) {
|
|
2874
|
-
hasSourceFiles = projectMarkers.some((marker) =>
|
|
2971
|
+
hasSourceFiles = projectMarkers.some((marker) => existsSync6(join6(cwd, marker)));
|
|
2875
2972
|
}
|
|
2876
|
-
const hasArchitecture =
|
|
2877
|
-
const hasProgress =
|
|
2878
|
-
const hasReviewDb =
|
|
2973
|
+
const hasArchitecture = existsSync6(join6(cwd, "ARCHITECTURE.md"));
|
|
2974
|
+
const hasProgress = existsSync6(join6(cwd, "progress.txt"));
|
|
2975
|
+
const hasReviewDb = existsSync6(join6(cwd, "docs", "code-review", "review-tasks.db"));
|
|
2879
2976
|
let hasProgressEntries = false;
|
|
2880
2977
|
let lastProgressEntry;
|
|
2881
2978
|
if (hasProgress) {
|
|
2882
2979
|
try {
|
|
2883
|
-
const progressContent =
|
|
2980
|
+
const progressContent = readFileSync3(join6(cwd, "progress.txt"), "utf-8");
|
|
2884
2981
|
const entries = progressContent.match(/^## \d{4}-\d{2}-\d{2}/gm);
|
|
2885
2982
|
hasProgressEntries = entries !== null && entries.length > 0;
|
|
2886
2983
|
if (hasProgressEntries) {
|
|
@@ -2901,6 +2998,7 @@ function detectProjectState(cwd) {
|
|
|
2901
2998
|
} else {
|
|
2902
2999
|
scenario = "NEW_PROJECT";
|
|
2903
3000
|
}
|
|
3001
|
+
const webDetection = detectWebProject(cwd);
|
|
2904
3002
|
return {
|
|
2905
3003
|
scenario,
|
|
2906
3004
|
hasSourceFiles,
|
|
@@ -2908,7 +3006,9 @@ function detectProjectState(cwd) {
|
|
|
2908
3006
|
hasProgress,
|
|
2909
3007
|
hasProgressEntries,
|
|
2910
3008
|
hasReviewDb,
|
|
2911
|
-
lastProgressEntry
|
|
3009
|
+
lastProgressEntry,
|
|
3010
|
+
isWebProject: webDetection.isWebProject,
|
|
3011
|
+
webFrameworks: webDetection.frameworks
|
|
2912
3012
|
};
|
|
2913
3013
|
}
|
|
2914
3014
|
async function gatherGovernanceStatus(cwd) {
|
|
@@ -2919,8 +3019,8 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
2919
3019
|
dependencyRulesCount: 0,
|
|
2920
3020
|
pendingAtomsCount: 0
|
|
2921
3021
|
};
|
|
2922
|
-
const archPath =
|
|
2923
|
-
if (
|
|
3022
|
+
const archPath = join6(cwd, "ARCHITECTURE.md");
|
|
3023
|
+
if (existsSync6(archPath)) {
|
|
2924
3024
|
const parser = new ArchitectureParser(archPath);
|
|
2925
3025
|
const result = await parser.parse();
|
|
2926
3026
|
if (result.success && result.schema) {
|
|
@@ -2937,10 +3037,10 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
2937
3037
|
status2.dependencyRulesCount = depResult.document.rules.length;
|
|
2938
3038
|
}
|
|
2939
3039
|
}
|
|
2940
|
-
const progressPath =
|
|
2941
|
-
if (
|
|
3040
|
+
const progressPath = join6(cwd, "progress.txt");
|
|
3041
|
+
if (existsSync6(progressPath)) {
|
|
2942
3042
|
try {
|
|
2943
|
-
const content =
|
|
3043
|
+
const content = readFileSync3(progressPath, "utf-8");
|
|
2944
3044
|
const dateMatches = content.match(/^## (\d{4}-\d{2}-\d{2})/gm);
|
|
2945
3045
|
if (dateMatches && dateMatches.length > 0) {
|
|
2946
3046
|
const lastMatch = dateMatches[dateMatches.length - 1];
|
|
@@ -2952,10 +3052,10 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
2952
3052
|
} catch {
|
|
2953
3053
|
}
|
|
2954
3054
|
}
|
|
2955
|
-
const atomsDir =
|
|
2956
|
-
if (
|
|
3055
|
+
const atomsDir = join6(cwd, ".archon", "atoms");
|
|
3056
|
+
if (existsSync6(atomsDir)) {
|
|
2957
3057
|
try {
|
|
2958
|
-
const files =
|
|
3058
|
+
const files = readdirSync3(atomsDir);
|
|
2959
3059
|
status2.pendingAtomsCount = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).length;
|
|
2960
3060
|
} catch {
|
|
2961
3061
|
}
|
|
@@ -2985,7 +3085,7 @@ async function handleNewProject(cwd, _state) {
|
|
|
2985
3085
|
}
|
|
2986
3086
|
if (intent.mode === "ad_hoc" && intent.confidence >= 0.7) {
|
|
2987
3087
|
console.log(chalk6.dim("\n> Got it! Creating a task for this...\n"));
|
|
2988
|
-
const { plan: plan2 } = await import("./plan-
|
|
3088
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
2989
3089
|
await plan2(initialResponse, {});
|
|
2990
3090
|
return;
|
|
2991
3091
|
}
|
|
@@ -3009,7 +3109,7 @@ async function handleNewProject(cwd, _state) {
|
|
|
3009
3109
|
break;
|
|
3010
3110
|
case "2":
|
|
3011
3111
|
console.log(chalk6.dim("\n> Creating a task for this...\n"));
|
|
3012
|
-
const { plan: plan2 } = await import("./plan-
|
|
3112
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3013
3113
|
await plan2(initialResponse, {});
|
|
3014
3114
|
break;
|
|
3015
3115
|
case "3":
|
|
@@ -3039,7 +3139,7 @@ async function showNewProjectMenu(cwd) {
|
|
|
3039
3139
|
case "3": {
|
|
3040
3140
|
const description = await prompt("Describe what you want to do");
|
|
3041
3141
|
if (description.trim()) {
|
|
3042
|
-
const { plan: plan2 } = await import("./plan-
|
|
3142
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3043
3143
|
await plan2(description, {});
|
|
3044
3144
|
}
|
|
3045
3145
|
break;
|
|
@@ -3105,7 +3205,7 @@ async function runExploreFlow(cwd) {
|
|
|
3105
3205
|
case "1": {
|
|
3106
3206
|
const description = await prompt("Describe what you want to do");
|
|
3107
3207
|
if (description.trim()) {
|
|
3108
|
-
const { plan: plan2 } = await import("./plan-
|
|
3208
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3109
3209
|
await plan2(description, {});
|
|
3110
3210
|
}
|
|
3111
3211
|
break;
|
|
@@ -3132,12 +3232,12 @@ async function gatherProjectInfo(cwd) {
|
|
|
3132
3232
|
invariantsCount: 0,
|
|
3133
3233
|
protectedPathsCount: 0
|
|
3134
3234
|
};
|
|
3135
|
-
const packageJsonPath =
|
|
3136
|
-
if (
|
|
3235
|
+
const packageJsonPath = join6(cwd, "package.json");
|
|
3236
|
+
if (existsSync6(packageJsonPath)) {
|
|
3137
3237
|
try {
|
|
3138
|
-
const packageJson = JSON.parse(
|
|
3238
|
+
const packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
3139
3239
|
info.name = packageJson.name;
|
|
3140
|
-
info.packageManager =
|
|
3240
|
+
info.packageManager = existsSync6(join6(cwd, "pnpm-lock.yaml")) ? "pnpm" : existsSync6(join6(cwd, "yarn.lock")) ? "yarn" : "npm";
|
|
3141
3241
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
3142
3242
|
if (deps["next"]) info.framework = "Next.js";
|
|
3143
3243
|
else if (deps["react"]) info.framework = "React";
|
|
@@ -3145,7 +3245,7 @@ async function gatherProjectInfo(cwd) {
|
|
|
3145
3245
|
else if (deps["svelte"]) info.framework = "Svelte";
|
|
3146
3246
|
else if (deps["express"]) info.framework = "Express";
|
|
3147
3247
|
else if (deps["fastify"]) info.framework = "Fastify";
|
|
3148
|
-
if (deps["typescript"] ||
|
|
3248
|
+
if (deps["typescript"] || existsSync6(join6(cwd, "tsconfig.json"))) {
|
|
3149
3249
|
info.language = "TypeScript";
|
|
3150
3250
|
} else {
|
|
3151
3251
|
info.language = "JavaScript";
|
|
@@ -3153,25 +3253,25 @@ async function gatherProjectInfo(cwd) {
|
|
|
3153
3253
|
} catch {
|
|
3154
3254
|
}
|
|
3155
3255
|
}
|
|
3156
|
-
if (
|
|
3256
|
+
if (existsSync6(join6(cwd, "Cargo.toml"))) {
|
|
3157
3257
|
info.language = "Rust";
|
|
3158
3258
|
info.packageManager = "cargo";
|
|
3159
|
-
} else if (
|
|
3259
|
+
} else if (existsSync6(join6(cwd, "go.mod"))) {
|
|
3160
3260
|
info.language = "Go";
|
|
3161
3261
|
info.packageManager = "go";
|
|
3162
|
-
} else if (
|
|
3262
|
+
} else if (existsSync6(join6(cwd, "pyproject.toml")) || existsSync6(join6(cwd, "requirements.txt"))) {
|
|
3163
3263
|
info.language = "Python";
|
|
3164
|
-
info.packageManager =
|
|
3264
|
+
info.packageManager = existsSync6(join6(cwd, "pyproject.toml")) ? "poetry/pip" : "pip";
|
|
3165
3265
|
}
|
|
3166
3266
|
const sourceDirNames = ["src", "lib", "app", "packages", "components", "pages", "api"];
|
|
3167
3267
|
const sourceExtensions = [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs"];
|
|
3168
3268
|
for (const dir of sourceDirNames) {
|
|
3169
|
-
if (
|
|
3269
|
+
if (existsSync6(join6(cwd, dir))) {
|
|
3170
3270
|
info.sourceDirs.push(dir);
|
|
3171
3271
|
}
|
|
3172
3272
|
}
|
|
3173
3273
|
try {
|
|
3174
|
-
const files =
|
|
3274
|
+
const files = readdirSync3(cwd);
|
|
3175
3275
|
for (const file of files) {
|
|
3176
3276
|
if (sourceExtensions.some((ext) => file.endsWith(ext))) {
|
|
3177
3277
|
if (file.includes(".test.") || file.includes(".spec.") || file.includes("_test.")) {
|
|
@@ -3183,7 +3283,7 @@ async function gatherProjectInfo(cwd) {
|
|
|
3183
3283
|
}
|
|
3184
3284
|
for (const dir of info.sourceDirs) {
|
|
3185
3285
|
try {
|
|
3186
|
-
const dirFiles =
|
|
3286
|
+
const dirFiles = readdirSync3(join6(cwd, dir));
|
|
3187
3287
|
for (const file of dirFiles) {
|
|
3188
3288
|
if (sourceExtensions.some((ext) => file.endsWith(ext))) {
|
|
3189
3289
|
if (file.includes(".test.") || file.includes(".spec.") || file.includes("_test.")) {
|
|
@@ -3198,10 +3298,10 @@ async function gatherProjectInfo(cwd) {
|
|
|
3198
3298
|
}
|
|
3199
3299
|
} catch {
|
|
3200
3300
|
}
|
|
3201
|
-
info.hasArchitecture =
|
|
3202
|
-
info.hasProgress =
|
|
3301
|
+
info.hasArchitecture = existsSync6(join6(cwd, "ARCHITECTURE.md"));
|
|
3302
|
+
info.hasProgress = existsSync6(join6(cwd, "progress.txt"));
|
|
3203
3303
|
if (info.hasArchitecture) {
|
|
3204
|
-
const archPath =
|
|
3304
|
+
const archPath = join6(cwd, "ARCHITECTURE.md");
|
|
3205
3305
|
const parser = new ArchitectureParser(archPath);
|
|
3206
3306
|
const result = await parser.parse();
|
|
3207
3307
|
if (result.success && result.schema) {
|
|
@@ -3213,7 +3313,7 @@ async function gatherProjectInfo(cwd) {
|
|
|
3213
3313
|
return info;
|
|
3214
3314
|
}
|
|
3215
3315
|
async function runConversationalInterview(cwd, initialMessage) {
|
|
3216
|
-
const { InterviewerAgent, createInterviewerAgent } = await import("./interviewer-
|
|
3316
|
+
const { InterviewerAgent, createInterviewerAgent } = await import("./interviewer-NQHNMVH4.js");
|
|
3217
3317
|
const agent = await createInterviewerAgent();
|
|
3218
3318
|
if (agent && agent.isAvailable()) {
|
|
3219
3319
|
await runAIInterview(cwd, initialMessage, agent);
|
|
@@ -3341,8 +3441,8 @@ ${state.forbiddenPatterns?.length ? `- **Forbidden patterns:** ${state.forbidden
|
|
|
3341
3441
|
- Vision and stack recorded in progress.txt
|
|
3342
3442
|
- Ready for first atom
|
|
3343
3443
|
`;
|
|
3344
|
-
const progressPath =
|
|
3345
|
-
if (!
|
|
3444
|
+
const progressPath = join6(cwd, "progress.txt");
|
|
3445
|
+
if (!existsSync6(progressPath)) {
|
|
3346
3446
|
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
3347
3447
|
writeFileSync2(progressPath, "# ArchonDev Progress Log\n\nThis file tracks learnings and decisions across sessions.\n");
|
|
3348
3448
|
}
|
|
@@ -3364,15 +3464,15 @@ ${state.forbiddenPatterns?.length ? `- **Forbidden patterns:** ${state.forbidden
|
|
|
3364
3464
|
if (continueChoice) {
|
|
3365
3465
|
const description = await prompt("Describe what you want to build first");
|
|
3366
3466
|
if (description.trim()) {
|
|
3367
|
-
const { plan: plan2 } = await import("./plan-
|
|
3467
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3368
3468
|
await plan2(description, {});
|
|
3369
3469
|
}
|
|
3370
3470
|
}
|
|
3371
3471
|
}
|
|
3372
3472
|
async function quickStart(cwd) {
|
|
3373
3473
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3374
|
-
const progressPath =
|
|
3375
|
-
if (!
|
|
3474
|
+
const progressPath = join6(cwd, "progress.txt");
|
|
3475
|
+
if (!existsSync6(progressPath)) {
|
|
3376
3476
|
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
3377
3477
|
writeFileSync2(progressPath, `# ArchonDev Progress Log
|
|
3378
3478
|
|
|
@@ -3418,7 +3518,7 @@ async function handleAdaptExisting(cwd, state) {
|
|
|
3418
3518
|
}
|
|
3419
3519
|
if (intent.mode === "ad_hoc" && intent.confidence >= 0.7) {
|
|
3420
3520
|
console.log(chalk6.dim("\n> Got it! Creating a task for this...\n"));
|
|
3421
|
-
const { plan: plan2 } = await import("./plan-
|
|
3521
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3422
3522
|
await plan2(response, {});
|
|
3423
3523
|
return;
|
|
3424
3524
|
}
|
|
@@ -3449,7 +3549,7 @@ async function showAdaptExistingMenu(cwd, state) {
|
|
|
3449
3549
|
case "2": {
|
|
3450
3550
|
const description = await prompt("Describe what you want to do");
|
|
3451
3551
|
if (description.trim()) {
|
|
3452
|
-
const { plan: plan2 } = await import("./plan-
|
|
3552
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3453
3553
|
await plan2(description, {});
|
|
3454
3554
|
}
|
|
3455
3555
|
break;
|
|
@@ -3474,11 +3574,11 @@ async function showAdaptExistingMenu(cwd, state) {
|
|
|
3474
3574
|
}
|
|
3475
3575
|
async function analyzeAndAdapt(cwd) {
|
|
3476
3576
|
console.log(chalk6.blue("\n-- Analyzing Project --\n"));
|
|
3477
|
-
const { init: init2 } = await import("./init-
|
|
3577
|
+
const { init: init2 } = await import("./init-FINETS3Q.js");
|
|
3478
3578
|
await init2({ analyze: true, git: true });
|
|
3479
3579
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3480
|
-
const progressPath =
|
|
3481
|
-
if (!
|
|
3580
|
+
const progressPath = join6(cwd, "progress.txt");
|
|
3581
|
+
if (!existsSync6(progressPath)) {
|
|
3482
3582
|
const { writeFileSync: writeFileSync2 } = await import("fs");
|
|
3483
3583
|
writeFileSync2(progressPath, "# ArchonDev Progress Log\n\nThis file tracks learnings and decisions across sessions.\n");
|
|
3484
3584
|
}
|
|
@@ -3501,9 +3601,9 @@ async function analyzeAndAdapt(cwd) {
|
|
|
3501
3601
|
async function codeReviewFirst(cwd) {
|
|
3502
3602
|
console.log(chalk6.blue("\n-- Code Review Mode --\n"));
|
|
3503
3603
|
console.log(chalk6.dim("I'll analyze your code for issues without making any changes.\n"));
|
|
3504
|
-
const { reviewInit: reviewInit2, reviewAnalyze: reviewAnalyze2, reviewRun: reviewRun2 } = await import("./review-
|
|
3505
|
-
const reviewDbPath =
|
|
3506
|
-
if (!
|
|
3604
|
+
const { reviewInit: reviewInit2, reviewAnalyze: reviewAnalyze2, reviewRun: reviewRun2 } = await import("./review-AUG6GIL6.js");
|
|
3605
|
+
const reviewDbPath = join6(cwd, "docs", "code-review", "review-tasks.db");
|
|
3606
|
+
if (!existsSync6(reviewDbPath)) {
|
|
3507
3607
|
await reviewInit2();
|
|
3508
3608
|
}
|
|
3509
3609
|
await reviewAnalyze2();
|
|
@@ -3537,9 +3637,9 @@ async function handleContinueSession(cwd, state) {
|
|
|
3537
3637
|
}
|
|
3538
3638
|
function checkForHandoff(cwd) {
|
|
3539
3639
|
try {
|
|
3540
|
-
const progressPath =
|
|
3541
|
-
if (!
|
|
3542
|
-
const content =
|
|
3640
|
+
const progressPath = join6(cwd, "progress.txt");
|
|
3641
|
+
if (!existsSync6(progressPath)) return null;
|
|
3642
|
+
const content = readFileSync3(progressPath, "utf-8");
|
|
3543
3643
|
const handoffMatch = content.match(/## Context Handoff[^\n]*\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
3544
3644
|
if (handoffMatch && handoffMatch[1]) {
|
|
3545
3645
|
const handoffContent = handoffMatch[1];
|
|
@@ -3556,6 +3656,15 @@ async function showMainMenu() {
|
|
|
3556
3656
|
const cwd = process.cwd();
|
|
3557
3657
|
const state = detectProjectState(cwd);
|
|
3558
3658
|
console.log(chalk6.bold("What would you like to do?\n"));
|
|
3659
|
+
if (state.isWebProject) {
|
|
3660
|
+
const { loadWebChecks, formatWebCheckStatus } = await import("./web-checks-4BSYXWDF.js");
|
|
3661
|
+
const prefs = await loadWebChecks(cwd);
|
|
3662
|
+
console.log(chalk6.dim("Web checks status:"));
|
|
3663
|
+
console.log(chalk6.dim(` A11y: ${formatWebCheckStatus(prefs.lastRun?.a11y)}`));
|
|
3664
|
+
console.log(chalk6.dim(` SEO: ${formatWebCheckStatus(prefs.lastRun?.seo)}`));
|
|
3665
|
+
console.log(chalk6.dim(` GEO: ${formatWebCheckStatus(prefs.lastRun?.geo)}`));
|
|
3666
|
+
console.log();
|
|
3667
|
+
}
|
|
3559
3668
|
const choices = [
|
|
3560
3669
|
{ key: "1", label: "Plan a new task", action: () => planTask() },
|
|
3561
3670
|
{ key: "2", label: "List atoms", action: () => listAtoms() },
|
|
@@ -3563,9 +3672,13 @@ async function showMainMenu() {
|
|
|
3563
3672
|
{ key: "4", label: "Report a bug", action: () => reportBug() },
|
|
3564
3673
|
{ key: "5", label: "View status", action: () => viewStatus() },
|
|
3565
3674
|
{ key: "6", label: "Code Review", action: () => reviewCode() },
|
|
3675
|
+
...state.isWebProject ? [{ key: "w", label: "Web checks (SEO/GEO/A11y)", action: async () => {
|
|
3676
|
+
await showWebChecksMenu();
|
|
3677
|
+
await showMainMenu();
|
|
3678
|
+
} }] : [],
|
|
3566
3679
|
{ key: "7", label: "Settings & Preferences", action: () => settingsMenu() },
|
|
3567
3680
|
{ key: "8", label: "Upgrade tier", action: async () => {
|
|
3568
|
-
const { showUpgradeMenu } = await import("./tier-selection-
|
|
3681
|
+
const { showUpgradeMenu } = await import("./tier-selection-426HA765.js");
|
|
3569
3682
|
await showUpgradeMenu();
|
|
3570
3683
|
await showMainMenu();
|
|
3571
3684
|
} },
|
|
@@ -3587,7 +3700,7 @@ async function showMainMenu() {
|
|
|
3587
3700
|
}
|
|
3588
3701
|
async function showReviewProgress(cwd) {
|
|
3589
3702
|
try {
|
|
3590
|
-
const { ReviewDatabase } = await import("./code-review-
|
|
3703
|
+
const { ReviewDatabase } = await import("./code-review-6MU4UE5M.js");
|
|
3591
3704
|
const db = new ReviewDatabase(cwd);
|
|
3592
3705
|
db.open();
|
|
3593
3706
|
const stats = db.getStats();
|
|
@@ -3604,23 +3717,24 @@ async function showReviewProgress(cwd) {
|
|
|
3604
3717
|
}
|
|
3605
3718
|
}
|
|
3606
3719
|
async function planTask() {
|
|
3607
|
-
const { plan: plan2 } = await import("./plan-
|
|
3720
|
+
const { plan: plan2 } = await import("./plan-XALA4SLH.js");
|
|
3608
3721
|
const description = await prompt("Describe what you want to build");
|
|
3609
3722
|
if (description.trim()) {
|
|
3610
3723
|
await plan2(description, {});
|
|
3611
3724
|
}
|
|
3612
3725
|
}
|
|
3613
3726
|
async function listAtoms() {
|
|
3614
|
-
const { list: list2 } = await import("./list-
|
|
3727
|
+
const { list: list2 } = await import("./list-CNKAH354.js");
|
|
3615
3728
|
await list2({});
|
|
3616
3729
|
}
|
|
3617
3730
|
async function executeNext() {
|
|
3618
|
-
const { listLocalAtoms: listLocalAtoms2 } = await import("./plan-
|
|
3619
|
-
const { analyzeProject, getComplexityDescription, getModeDescription } = await import("./orchestration-
|
|
3620
|
-
const { loadExecutionPreferences } = await import("./preferences-
|
|
3731
|
+
const { listLocalAtoms: listLocalAtoms2 } = await import("./plan-XALA4SLH.js");
|
|
3732
|
+
const { analyzeProject, getComplexityDescription, getModeDescription } = await import("./orchestration-HIF3KP25.js");
|
|
3733
|
+
const { loadExecutionPreferences } = await import("./preferences-I6WETXOI.js");
|
|
3621
3734
|
const cwd = process.cwd();
|
|
3622
3735
|
const atoms = await listLocalAtoms2();
|
|
3623
3736
|
const pendingAtoms = atoms.filter((a) => a.status === "READY" || a.status === "IN_PROGRESS");
|
|
3737
|
+
const readyAtoms = atoms.filter((a) => a.status === "READY");
|
|
3624
3738
|
if (pendingAtoms.length === 0) {
|
|
3625
3739
|
console.log(chalk6.yellow('No pending atoms. Use "archon plan" to create one.'));
|
|
3626
3740
|
return;
|
|
@@ -3628,56 +3742,103 @@ async function executeNext() {
|
|
|
3628
3742
|
if (pendingAtoms.length > 1) {
|
|
3629
3743
|
const analysis = analyzeProject(pendingAtoms);
|
|
3630
3744
|
const prefs = await loadExecutionPreferences(cwd);
|
|
3745
|
+
const config = await loadConfig();
|
|
3746
|
+
const canUseCloud = (config.tier ?? "FREE") === "CREDITS";
|
|
3631
3747
|
console.log(chalk6.blue("\n-- Project Analysis --\n"));
|
|
3632
3748
|
console.log(` ${chalk6.bold("Atoms:")} ${analysis.atomCount}`);
|
|
3633
3749
|
console.log(` ${chalk6.bold("Estimated:")} ${analysis.estimatedMinutes} minutes`);
|
|
3634
3750
|
console.log(` ${chalk6.bold("Complexity:")} ${analysis.complexity} - ${getComplexityDescription(analysis.complexity)}`);
|
|
3635
3751
|
console.log(` ${chalk6.bold("Suggested:")} ${analysis.suggestedMode} - ${getModeDescription(analysis.suggestedMode)}`);
|
|
3636
3752
|
console.log();
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
\u2713
|
|
3642
|
-
console.log(chalk6.dim("(Parallel/cloud execution coming soon - running sequentially for now)\n"));
|
|
3643
|
-
}
|
|
3753
|
+
let selectedMode = "sequential";
|
|
3754
|
+
if (canUseCloud && prefs.cloudMode === "always" && analysis.suggestedMode === "parallel-cloud") {
|
|
3755
|
+
selectedMode = "parallel-cloud";
|
|
3756
|
+
console.log(chalk6.green(`
|
|
3757
|
+
\u2713 Auto-selected parallel cloud execution (preference: always)`));
|
|
3644
3758
|
} else if (prefs.parallelMode === "always" && analysis.suggestedMode !== "sequential") {
|
|
3759
|
+
selectedMode = "parallel-local";
|
|
3645
3760
|
console.log(chalk6.green(`
|
|
3646
|
-
\u2713 Auto-selected
|
|
3647
|
-
|
|
3761
|
+
\u2713 Auto-selected parallel local execution (preference: always)`));
|
|
3762
|
+
} else if (prefs.parallelMode === "ask" || canUseCloud && prefs.cloudMode === "ask") {
|
|
3763
|
+
const options = [
|
|
3764
|
+
{ key: "1", label: "Sequential (local)" },
|
|
3765
|
+
{ key: "2", label: "Parallel (local)" }
|
|
3766
|
+
];
|
|
3767
|
+
if (canUseCloud) {
|
|
3768
|
+
options.push({ key: "3", label: "Parallel (cloud)" });
|
|
3769
|
+
}
|
|
3770
|
+
const choice = await promptChoice("Select execution mode", options);
|
|
3771
|
+
if (choice === "2") selectedMode = "parallel-local";
|
|
3772
|
+
if (choice === "3" && canUseCloud) selectedMode = "parallel-cloud";
|
|
3773
|
+
}
|
|
3774
|
+
if (selectedMode !== "sequential") {
|
|
3775
|
+
if (readyAtoms.length === 0) {
|
|
3776
|
+
console.log(chalk6.yellow("No READY atoms available for parallel execution."));
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
console.log(chalk6.dim("\nPending atoms:"));
|
|
3780
|
+
for (const atom of readyAtoms) {
|
|
3781
|
+
console.log(chalk6.dim(` - ${atom.id}: ${atom.title}`));
|
|
3782
|
+
}
|
|
3783
|
+
console.log();
|
|
3784
|
+
const raw = await prompt("Enter atom IDs to run in parallel (comma-separated, or press Enter for all)");
|
|
3785
|
+
const selectedIds = raw.trim() ? raw.split(",").map((s) => s.trim()).filter(Boolean) : readyAtoms.map((a) => a.id);
|
|
3786
|
+
if (selectedIds.length === 0) {
|
|
3787
|
+
console.log(chalk6.yellow("No atoms selected."));
|
|
3788
|
+
return;
|
|
3789
|
+
}
|
|
3790
|
+
let runIds = selectedIds;
|
|
3791
|
+
if (selectedIds.length > prefs.maxParallelAgents) {
|
|
3792
|
+
const override = await promptYesNo(
|
|
3793
|
+
`Selected ${selectedIds.length} atoms, but max parallel agents is ${prefs.maxParallelAgents}. Override for this run?`,
|
|
3794
|
+
false
|
|
3795
|
+
);
|
|
3796
|
+
if (!override) {
|
|
3797
|
+
runIds = selectedIds.slice(0, prefs.maxParallelAgents);
|
|
3798
|
+
console.log(chalk6.dim(`Running first ${runIds.length} atoms.`));
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
if (selectedMode === "parallel-cloud") {
|
|
3802
|
+
const { parallelExecuteCloud: parallelExecuteCloud2 } = await import("./parallel-W2ETYYAL.js");
|
|
3803
|
+
await parallelExecuteCloud2(runIds);
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
const { parallelExecute } = await import("./parallel-W2ETYYAL.js");
|
|
3807
|
+
await parallelExecute(runIds);
|
|
3808
|
+
return;
|
|
3648
3809
|
}
|
|
3649
3810
|
}
|
|
3650
3811
|
const atomId = await prompt("Enter atom ID to execute (or press Enter for first pending)");
|
|
3651
3812
|
const targetId = atomId.trim() || pendingAtoms[0]?.id;
|
|
3652
3813
|
if (targetId) {
|
|
3653
|
-
const { execute: execute2 } = await import("./execute-
|
|
3814
|
+
const { execute: execute2 } = await import("./execute-MB4ZF3HQ.js");
|
|
3654
3815
|
await execute2(targetId, {});
|
|
3655
3816
|
} else {
|
|
3656
3817
|
console.log(chalk6.yellow("No atom to execute."));
|
|
3657
3818
|
}
|
|
3658
3819
|
}
|
|
3659
3820
|
async function reportBug() {
|
|
3660
|
-
const { bugReport: bugReport2 } = await import("./bug-
|
|
3821
|
+
const { bugReport: bugReport2 } = await import("./bug-5WRBCFRT.js");
|
|
3661
3822
|
const title = await prompt("Bug title");
|
|
3662
3823
|
if (title.trim()) {
|
|
3663
3824
|
await bugReport2(title, {});
|
|
3664
3825
|
}
|
|
3665
3826
|
}
|
|
3666
3827
|
async function viewStatus() {
|
|
3667
|
-
const { status: status2 } = await import("./auth-
|
|
3828
|
+
const { status: status2 } = await import("./auth-HQBAG4CR.js");
|
|
3668
3829
|
await status2();
|
|
3669
3830
|
}
|
|
3670
3831
|
async function settingsMenu() {
|
|
3671
|
-
const { interactiveSettings } = await import("./preferences-
|
|
3832
|
+
const { interactiveSettings } = await import("./preferences-I6WETXOI.js");
|
|
3672
3833
|
await interactiveSettings();
|
|
3673
3834
|
await showMainMenu();
|
|
3674
3835
|
}
|
|
3675
3836
|
async function reviewCode() {
|
|
3676
3837
|
const cwd = process.cwd();
|
|
3677
|
-
const reviewDbPath =
|
|
3678
|
-
if (!
|
|
3838
|
+
const reviewDbPath = join6(cwd, "docs", "code-review", "review-tasks.db");
|
|
3839
|
+
if (!existsSync6(reviewDbPath)) {
|
|
3679
3840
|
console.log(chalk6.dim("Code review not initialized. Starting setup...\n"));
|
|
3680
|
-
const { reviewInit: reviewInit2 } = await import("./review-
|
|
3841
|
+
const { reviewInit: reviewInit2 } = await import("./review-AUG6GIL6.js");
|
|
3681
3842
|
await reviewInit2();
|
|
3682
3843
|
console.log();
|
|
3683
3844
|
}
|
|
@@ -3692,27 +3853,27 @@ async function reviewCode() {
|
|
|
3692
3853
|
const choice = await prompt("Enter choice");
|
|
3693
3854
|
switch (choice.toLowerCase()) {
|
|
3694
3855
|
case "1": {
|
|
3695
|
-
const { reviewAnalyze: reviewAnalyze2 } = await import("./review-
|
|
3856
|
+
const { reviewAnalyze: reviewAnalyze2 } = await import("./review-AUG6GIL6.js");
|
|
3696
3857
|
await reviewAnalyze2();
|
|
3697
3858
|
break;
|
|
3698
3859
|
}
|
|
3699
3860
|
case "2": {
|
|
3700
|
-
const { reviewStatus: reviewStatus2 } = await import("./review-
|
|
3861
|
+
const { reviewStatus: reviewStatus2 } = await import("./review-AUG6GIL6.js");
|
|
3701
3862
|
await reviewStatus2();
|
|
3702
3863
|
break;
|
|
3703
3864
|
}
|
|
3704
3865
|
case "3": {
|
|
3705
|
-
const { reviewNext: reviewNext2 } = await import("./review-
|
|
3866
|
+
const { reviewNext: reviewNext2 } = await import("./review-AUG6GIL6.js");
|
|
3706
3867
|
await reviewNext2();
|
|
3707
3868
|
break;
|
|
3708
3869
|
}
|
|
3709
3870
|
case "4": {
|
|
3710
|
-
const { reviewList: reviewList2 } = await import("./review-
|
|
3871
|
+
const { reviewList: reviewList2 } = await import("./review-AUG6GIL6.js");
|
|
3711
3872
|
await reviewList2({});
|
|
3712
3873
|
break;
|
|
3713
3874
|
}
|
|
3714
3875
|
case "5": {
|
|
3715
|
-
const { reviewRun: reviewRun2 } = await import("./review-
|
|
3876
|
+
const { reviewRun: reviewRun2 } = await import("./review-AUG6GIL6.js");
|
|
3716
3877
|
await reviewRun2({ all: true });
|
|
3717
3878
|
break;
|
|
3718
3879
|
}
|
|
@@ -3727,12 +3888,12 @@ async function reviewCode() {
|
|
|
3727
3888
|
async function handleInSessionCommand(input) {
|
|
3728
3889
|
const normalized = input.toLowerCase().trim();
|
|
3729
3890
|
if (normalized === "upgrade" || normalized === "archon upgrade" || normalized === "pricing" || normalized === "archon pricing") {
|
|
3730
|
-
const { showUpgradeMenu } = await import("./tier-selection-
|
|
3891
|
+
const { showUpgradeMenu } = await import("./tier-selection-426HA765.js");
|
|
3731
3892
|
await showUpgradeMenu();
|
|
3732
3893
|
return true;
|
|
3733
3894
|
}
|
|
3734
3895
|
if (normalized === "status" || normalized === "archon status") {
|
|
3735
|
-
const { status: status2 } = await import("./auth-
|
|
3896
|
+
const { status: status2 } = await import("./auth-HQBAG4CR.js");
|
|
3736
3897
|
await status2();
|
|
3737
3898
|
return true;
|
|
3738
3899
|
}
|
|
@@ -3748,7 +3909,7 @@ async function handleInSessionCommand(input) {
|
|
|
3748
3909
|
return true;
|
|
3749
3910
|
}
|
|
3750
3911
|
if (normalized === "keys" || normalized === "archon keys") {
|
|
3751
|
-
const { listKeys: listKeys2 } = await import("./keys-
|
|
3912
|
+
const { listKeys: listKeys2 } = await import("./keys-U4QZE5YB.js");
|
|
3752
3913
|
await listKeys2();
|
|
3753
3914
|
return true;
|
|
3754
3915
|
}
|
|
@@ -3780,6 +3941,111 @@ async function promptWithCommands(question) {
|
|
|
3780
3941
|
console.log(chalk6.dim("(Returning to previous question...)\n"));
|
|
3781
3942
|
}
|
|
3782
3943
|
}
|
|
3944
|
+
async function runWebChecksSuite() {
|
|
3945
|
+
const { a11yCheck: a11yCheck2 } = await import("./a11y-O35BAA25.js");
|
|
3946
|
+
const { seoCheck } = await import("./seo-PMI42KRZ.js");
|
|
3947
|
+
const { geoAudit } = await import("./geo-KV4IRGKN.js");
|
|
3948
|
+
console.log(chalk6.blue("\nRunning web checks (A11y, SEO, GEO)...\n"));
|
|
3949
|
+
await a11yCheck2({});
|
|
3950
|
+
await seoCheck({});
|
|
3951
|
+
await geoAudit();
|
|
3952
|
+
}
|
|
3953
|
+
async function showWebChecksMenu() {
|
|
3954
|
+
const cwd = process.cwd();
|
|
3955
|
+
const { loadWebChecks, formatWebCheckStatus, setWebPromptMode } = await import("./web-checks-4BSYXWDF.js");
|
|
3956
|
+
const prefs = await loadWebChecks(cwd);
|
|
3957
|
+
console.log(chalk6.bold("\nWeb Checks (SEO / GEO / A11y)\n"));
|
|
3958
|
+
console.log(chalk6.dim(`A11y: ${formatWebCheckStatus(prefs.lastRun?.a11y)}`));
|
|
3959
|
+
console.log(chalk6.dim(`SEO: ${formatWebCheckStatus(prefs.lastRun?.seo)}`));
|
|
3960
|
+
console.log(chalk6.dim(`GEO: ${formatWebCheckStatus(prefs.lastRun?.geo)}`));
|
|
3961
|
+
console.log();
|
|
3962
|
+
console.log(` ${chalk6.cyan("1")}) Run all checks`);
|
|
3963
|
+
console.log(` ${chalk6.cyan("2")}) Run accessibility check`);
|
|
3964
|
+
console.log(` ${chalk6.cyan("3")}) Run SEO check`);
|
|
3965
|
+
console.log(` ${chalk6.cyan("4")}) Run GEO audit`);
|
|
3966
|
+
console.log(` ${chalk6.cyan("5")}) Generate GEO identity`);
|
|
3967
|
+
console.log(` ${chalk6.cyan("6")}) Generate GEO schema`);
|
|
3968
|
+
console.log(` ${chalk6.cyan("7")}) Set prompt mode (ask/always/never)`);
|
|
3969
|
+
console.log(` ${chalk6.cyan("b")}) Back to main menu`);
|
|
3970
|
+
console.log();
|
|
3971
|
+
const choice = await promptWithCommands("Enter choice");
|
|
3972
|
+
switch (choice.toLowerCase()) {
|
|
3973
|
+
case "1":
|
|
3974
|
+
await runWebChecksSuite();
|
|
3975
|
+
break;
|
|
3976
|
+
case "2": {
|
|
3977
|
+
const { a11yCheck: a11yCheck2 } = await import("./a11y-O35BAA25.js");
|
|
3978
|
+
await a11yCheck2({});
|
|
3979
|
+
break;
|
|
3980
|
+
}
|
|
3981
|
+
case "3": {
|
|
3982
|
+
const { seoCheck } = await import("./seo-PMI42KRZ.js");
|
|
3983
|
+
await seoCheck({});
|
|
3984
|
+
break;
|
|
3985
|
+
}
|
|
3986
|
+
case "4": {
|
|
3987
|
+
const { geoAudit } = await import("./geo-KV4IRGKN.js");
|
|
3988
|
+
await geoAudit();
|
|
3989
|
+
break;
|
|
3990
|
+
}
|
|
3991
|
+
case "5": {
|
|
3992
|
+
const { geoIdentity } = await import("./geo-KV4IRGKN.js");
|
|
3993
|
+
await geoIdentity();
|
|
3994
|
+
break;
|
|
3995
|
+
}
|
|
3996
|
+
case "6": {
|
|
3997
|
+
const { geoSchema } = await import("./geo-KV4IRGKN.js");
|
|
3998
|
+
await geoSchema({});
|
|
3999
|
+
break;
|
|
4000
|
+
}
|
|
4001
|
+
case "7": {
|
|
4002
|
+
console.log();
|
|
4003
|
+
console.log(` ${chalk6.cyan("1")}) Ask each session`);
|
|
4004
|
+
console.log(` ${chalk6.cyan("2")}) Always run automatically`);
|
|
4005
|
+
console.log(` ${chalk6.cyan("3")}) Never prompt`);
|
|
4006
|
+
console.log();
|
|
4007
|
+
const modeChoice = await promptWithCommands("Select prompt mode");
|
|
4008
|
+
if (modeChoice === "1") await setWebPromptMode(cwd, "ask");
|
|
4009
|
+
if (modeChoice === "2") await setWebPromptMode(cwd, "always");
|
|
4010
|
+
if (modeChoice === "3") await setWebPromptMode(cwd, "never");
|
|
4011
|
+
break;
|
|
4012
|
+
}
|
|
4013
|
+
default:
|
|
4014
|
+
break;
|
|
4015
|
+
}
|
|
4016
|
+
if (choice.toLowerCase() !== "b") {
|
|
4017
|
+
await showWebChecksMenu();
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
async function maybePromptWebChecks(cwd, projectState) {
|
|
4021
|
+
if (!projectState.isWebProject) {
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
const { loadWebChecks, setWebPromptMode } = await import("./web-checks-4BSYXWDF.js");
|
|
4025
|
+
const prefs = await loadWebChecks(cwd);
|
|
4026
|
+
if (prefs.promptMode === "never") {
|
|
4027
|
+
return;
|
|
4028
|
+
}
|
|
4029
|
+
if (prefs.promptMode === "always") {
|
|
4030
|
+
console.log(chalk6.blue("\nWeb project detected. Running web checks automatically...\n"));
|
|
4031
|
+
await runWebChecksSuite();
|
|
4032
|
+
return;
|
|
4033
|
+
}
|
|
4034
|
+
console.log(chalk6.blue("\nWeb project detected."));
|
|
4035
|
+
const choice = await promptChoice("Run web checks now?", [
|
|
4036
|
+
{ key: "1", label: "Run all checks (A11y + SEO + GEO)" },
|
|
4037
|
+
{ key: "2", label: "Choose checks" },
|
|
4038
|
+
{ key: "3", label: "Not now" },
|
|
4039
|
+
{ key: "4", label: "Never ask again" }
|
|
4040
|
+
]);
|
|
4041
|
+
if (choice === "1") {
|
|
4042
|
+
await runWebChecksSuite();
|
|
4043
|
+
} else if (choice === "2") {
|
|
4044
|
+
await showWebChecksMenu();
|
|
4045
|
+
} else if (choice === "4") {
|
|
4046
|
+
await setWebPromptMode(cwd, "never");
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
3783
4049
|
function promptYesNo(question, defaultValue) {
|
|
3784
4050
|
return new Promise((resolve) => {
|
|
3785
4051
|
const rl = readline.createInterface({
|
|
@@ -3797,6 +4063,22 @@ function promptYesNo(question, defaultValue) {
|
|
|
3797
4063
|
});
|
|
3798
4064
|
});
|
|
3799
4065
|
}
|
|
4066
|
+
function promptChoice(question, options) {
|
|
4067
|
+
return new Promise((resolve) => {
|
|
4068
|
+
console.log(`${chalk6.cyan("?")} ${question}`);
|
|
4069
|
+
for (const opt of options) {
|
|
4070
|
+
console.log(` ${chalk6.dim(opt.key)}) ${opt.label}`);
|
|
4071
|
+
}
|
|
4072
|
+
const rl = readline.createInterface({
|
|
4073
|
+
input: process.stdin,
|
|
4074
|
+
output: process.stdout
|
|
4075
|
+
});
|
|
4076
|
+
rl.question(` ${chalk6.dim("Enter choice")}: `, (answer) => {
|
|
4077
|
+
rl.close();
|
|
4078
|
+
resolve(answer.trim() || "1");
|
|
4079
|
+
});
|
|
4080
|
+
});
|
|
4081
|
+
}
|
|
3800
4082
|
|
|
3801
4083
|
// src/cli/credits.ts
|
|
3802
4084
|
import chalk7 from "chalk";
|
|
@@ -4476,7 +4758,7 @@ async function watch() {
|
|
|
4476
4758
|
import { Command } from "commander";
|
|
4477
4759
|
import chalk8 from "chalk";
|
|
4478
4760
|
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
4479
|
-
import { existsSync as
|
|
4761
|
+
import { existsSync as existsSync8 } from "fs";
|
|
4480
4762
|
var DEPENDENCIES_FILENAME = "DEPENDENCIES.md";
|
|
4481
4763
|
function createDepsCommand() {
|
|
4482
4764
|
const deps = new Command("deps").description("Manage file-level dependencies (DEPENDENCIES.md)").addHelpText(
|
|
@@ -4602,9 +4884,9 @@ Examples:
|
|
|
4602
4884
|
reason
|
|
4603
4885
|
};
|
|
4604
4886
|
existingRules.push(newRule);
|
|
4605
|
-
const
|
|
4887
|
+
const yaml2 = generateYamlFrontmatter(existingRules);
|
|
4606
4888
|
await writeFile4(DEPENDENCIES_FILENAME, `---
|
|
4607
|
-
${
|
|
4889
|
+
${yaml2}---${markdownBody}`, "utf-8");
|
|
4608
4890
|
console.log(chalk8.green(`\u2705 Added dependency rule: ${nextId}`));
|
|
4609
4891
|
console.log(` Source: ${chalk8.cyan(source)}`);
|
|
4610
4892
|
console.log(` Dependent: ${chalk8.dim(dependent)}`);
|
|
@@ -4626,7 +4908,7 @@ ${yaml3}---${markdownBody}`, "utf-8");
|
|
|
4626
4908
|
}
|
|
4627
4909
|
});
|
|
4628
4910
|
deps.command("init").description("Create a starter DEPENDENCIES.md file").action(async () => {
|
|
4629
|
-
if (
|
|
4911
|
+
if (existsSync8(DEPENDENCIES_FILENAME)) {
|
|
4630
4912
|
console.log(chalk8.yellow("DEPENDENCIES.md already exists."));
|
|
4631
4913
|
return;
|
|
4632
4914
|
}
|
|
@@ -4711,1379 +4993,87 @@ function generateYamlFrontmatter(rules) {
|
|
|
4711
4993
|
return lines.join("\n") + "\n";
|
|
4712
4994
|
}
|
|
4713
4995
|
|
|
4714
|
-
// src/cli/
|
|
4996
|
+
// src/cli/session.ts
|
|
4715
4997
|
import chalk9 from "chalk";
|
|
4716
|
-
import
|
|
4717
|
-
import
|
|
4718
|
-
import {
|
|
4719
|
-
import {
|
|
4720
|
-
import {
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
patterns: [
|
|
4727
|
-
{ regex: /text-primary\/[0-9]+/g, issue: "Opacity-based text colors may have insufficient contrast" },
|
|
4728
|
-
{ regex: /opacity-[0-9]+/g, issue: "Low opacity text may not meet contrast requirements" },
|
|
4729
|
-
{ regex: /text-gray-[3-4]00/g, issue: "Light gray text often fails contrast requirements" }
|
|
4730
|
-
]
|
|
4731
|
-
},
|
|
4732
|
-
images: {
|
|
4733
|
-
name: "Image Alt Text",
|
|
4734
|
-
wcag: "1.1.1",
|
|
4735
|
-
description: "All meaningful images have alt text",
|
|
4736
|
-
patterns: [
|
|
4737
|
-
{ regex: /<img(?![^>]*alt=)[^>]*>/gi, issue: "Image missing alt attribute" },
|
|
4738
|
-
{ regex: /<img[^>]*alt=["']\s*["'][^>]*>/gi, issue: "Image has empty alt text (verify if decorative)" }
|
|
4739
|
-
]
|
|
4740
|
-
},
|
|
4741
|
-
forms: {
|
|
4742
|
-
name: "Form Labels",
|
|
4743
|
-
wcag: "1.3.1, 4.1.2",
|
|
4744
|
-
description: "Form inputs have associated labels",
|
|
4745
|
-
patterns: [
|
|
4746
|
-
{ regex: /<input(?![^>]*aria-label)[^>]*(?<![^>]*id=)[^>]*>/gi, issue: "Input without label association" }
|
|
4747
|
-
]
|
|
4748
|
-
},
|
|
4749
|
-
keyboard: {
|
|
4750
|
-
name: "Keyboard Navigation",
|
|
4751
|
-
wcag: "2.1.1, 2.4.7",
|
|
4752
|
-
description: "All interactive elements are keyboard accessible with visible focus",
|
|
4753
|
-
patterns: [
|
|
4754
|
-
{ regex: /outline:\s*none|outline:\s*0/gi, issue: "Focus outline removed - may break keyboard navigation" },
|
|
4755
|
-
{ regex: /tabindex=["']-1["']/gi, issue: "Negative tabindex removes element from tab order" }
|
|
4756
|
-
]
|
|
4757
|
-
},
|
|
4758
|
-
structure: {
|
|
4759
|
-
name: "Document Structure",
|
|
4760
|
-
wcag: "1.3.1, 2.4.1",
|
|
4761
|
-
description: "Proper heading hierarchy and landmarks",
|
|
4762
|
-
patterns: [
|
|
4763
|
-
{ regex: /<html(?![^>]*lang=)[^>]*>/gi, issue: "HTML element missing lang attribute" }
|
|
4764
|
-
]
|
|
4765
|
-
},
|
|
4766
|
-
aria: {
|
|
4767
|
-
name: "ARIA Usage",
|
|
4768
|
-
wcag: "4.1.2",
|
|
4769
|
-
description: "ARIA attributes are used correctly",
|
|
4770
|
-
patterns: [
|
|
4771
|
-
{ regex: /role=["']button["'](?![^>]*tabindex)/gi, issue: "Custom button missing tabindex" }
|
|
4772
|
-
]
|
|
4773
|
-
}
|
|
4774
|
-
};
|
|
4775
|
-
function createPrompt() {
|
|
4776
|
-
const rl = createInterface({
|
|
4777
|
-
input: process.stdin,
|
|
4778
|
-
output: process.stdout
|
|
4779
|
-
});
|
|
4780
|
-
return {
|
|
4781
|
-
ask: (question) => new Promise((resolve) => {
|
|
4782
|
-
rl.question(question, resolve);
|
|
4783
|
-
}),
|
|
4784
|
-
close: () => rl.close()
|
|
4785
|
-
};
|
|
4998
|
+
import ora2 from "ora";
|
|
4999
|
+
import os from "os";
|
|
5000
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
5001
|
+
import { existsSync as existsSync9 } from "fs";
|
|
5002
|
+
import { join as join8 } from "path";
|
|
5003
|
+
function getSupabaseClient2(accessToken) {
|
|
5004
|
+
return createAuthedSupabaseClient(SUPABASE_URL, SUPABASE_ANON_KEY, accessToken);
|
|
5005
|
+
}
|
|
5006
|
+
function getDeviceName() {
|
|
5007
|
+
return `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
4786
5008
|
}
|
|
4787
|
-
async function
|
|
4788
|
-
const
|
|
4789
|
-
|
|
5009
|
+
async function getProjectInfo(cwd) {
|
|
5010
|
+
const archonConfigPath = join8(cwd, ".archon", "config.yaml");
|
|
5011
|
+
const packageJsonPath = join8(cwd, "package.json");
|
|
5012
|
+
let projectName = "Unknown Project";
|
|
5013
|
+
if (existsSync9(packageJsonPath)) {
|
|
4790
5014
|
try {
|
|
4791
|
-
const
|
|
4792
|
-
|
|
4793
|
-
for (const [checkId, check] of Object.entries(ACCESSIBILITY_CHECKS)) {
|
|
4794
|
-
for (const pattern of check.patterns) {
|
|
4795
|
-
for (let i = 0; i < lines.length; i++) {
|
|
4796
|
-
const line = lines[i];
|
|
4797
|
-
if (line && pattern.regex.test(line)) {
|
|
4798
|
-
pattern.regex.lastIndex = 0;
|
|
4799
|
-
issues.push({
|
|
4800
|
-
file: file.replace(process.cwd() + "/", ""),
|
|
4801
|
-
line: i + 1,
|
|
4802
|
-
check: check.name,
|
|
4803
|
-
wcag: check.wcag,
|
|
4804
|
-
severity: checkId === "contrast" || checkId === "keyboard" ? "critical" : "major",
|
|
4805
|
-
message: pattern.issue
|
|
4806
|
-
});
|
|
4807
|
-
}
|
|
4808
|
-
pattern.regex.lastIndex = 0;
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
}
|
|
5015
|
+
const pkg = JSON.parse(await readFile5(packageJsonPath, "utf-8"));
|
|
5016
|
+
projectName = pkg.name || projectName;
|
|
4812
5017
|
} catch {
|
|
4813
5018
|
}
|
|
4814
5019
|
}
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
async function a11yCheck(options) {
|
|
4818
|
-
console.log(chalk9.blue("\n\u267F Pre-Deploy Accessibility Check\n"));
|
|
4819
|
-
console.log(chalk9.dim("Scanning for WCAG 2.2 AA compliance issues...\n"));
|
|
4820
|
-
const patterns = [
|
|
4821
|
-
"**/*.html",
|
|
4822
|
-
"**/*.htm",
|
|
4823
|
-
"**/*.jsx",
|
|
4824
|
-
"**/*.tsx",
|
|
4825
|
-
"**/*.astro",
|
|
4826
|
-
"**/*.svelte",
|
|
4827
|
-
"**/*.vue"
|
|
4828
|
-
];
|
|
4829
|
-
const ignorePatterns = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.next/**"];
|
|
4830
|
-
let allFiles = [];
|
|
4831
|
-
for (const pattern of patterns) {
|
|
4832
|
-
const files = await glob(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
4833
|
-
allFiles = allFiles.concat(files.map((f) => join7(process.cwd(), f)));
|
|
4834
|
-
}
|
|
4835
|
-
if (allFiles.length === 0) {
|
|
4836
|
-
console.log(chalk9.yellow("No web files found to check."));
|
|
4837
|
-
return;
|
|
4838
|
-
}
|
|
4839
|
-
console.log(chalk9.dim(`Scanning ${allFiles.length} files...
|
|
4840
|
-
`));
|
|
4841
|
-
const issues = await scanForIssues(allFiles);
|
|
4842
|
-
const report = {
|
|
4843
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4844
|
-
totalFiles: allFiles.length,
|
|
4845
|
-
issuesFound: issues.length,
|
|
4846
|
-
issues,
|
|
4847
|
-
passed: issues.length === 0
|
|
4848
|
-
};
|
|
4849
|
-
const archonDir = join7(process.cwd(), ".archon");
|
|
4850
|
-
if (!existsSync8(archonDir)) {
|
|
4851
|
-
await mkdir3(archonDir, { recursive: true });
|
|
4852
|
-
}
|
|
4853
|
-
await writeFile5(join7(archonDir, "a11y-report.json"), JSON.stringify(report, null, 2));
|
|
4854
|
-
if (issues.length === 0) {
|
|
4855
|
-
console.log(chalk9.green("\u2705 Accessibility Audit Passed\n"));
|
|
4856
|
-
console.log(chalk9.dim("Your site meets WCAG 2.2 AA requirements based on static analysis."));
|
|
4857
|
-
console.log(chalk9.dim("Note: Run manual testing with screen readers for full compliance.\n"));
|
|
4858
|
-
return;
|
|
4859
|
-
}
|
|
4860
|
-
console.log(chalk9.red(`[!] ${issues.length} Accessibility Issues Found
|
|
4861
|
-
`));
|
|
4862
|
-
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
4863
|
-
const majorCount = issues.filter((i) => i.severity === "major").length;
|
|
4864
|
-
const minorCount = issues.filter((i) => i.severity === "minor").length;
|
|
4865
|
-
console.log(chalk9.dim("Summary:"));
|
|
4866
|
-
if (criticalCount > 0) console.log(chalk9.red(` \u2022 ${criticalCount} critical`));
|
|
4867
|
-
if (majorCount > 0) console.log(chalk9.yellow(` \u2022 ${majorCount} major`));
|
|
4868
|
-
if (minorCount > 0) console.log(chalk9.dim(` \u2022 ${minorCount} minor`));
|
|
4869
|
-
console.log();
|
|
4870
|
-
console.log(chalk9.bold("Issues:\n"));
|
|
4871
|
-
const issuesByFile = /* @__PURE__ */ new Map();
|
|
4872
|
-
for (const issue of issues) {
|
|
4873
|
-
const existing = issuesByFile.get(issue.file) || [];
|
|
4874
|
-
existing.push(issue);
|
|
4875
|
-
issuesByFile.set(issue.file, existing);
|
|
4876
|
-
}
|
|
4877
|
-
for (const [file, fileIssues] of issuesByFile) {
|
|
4878
|
-
console.log(chalk9.cyan(` ${file}`));
|
|
4879
|
-
for (const issue of fileIssues.slice(0, 5)) {
|
|
4880
|
-
const color = issue.severity === "critical" ? chalk9.red : issue.severity === "major" ? chalk9.yellow : chalk9.dim;
|
|
4881
|
-
console.log(color(` Line ${issue.line}: ${issue.message} (WCAG ${issue.wcag})`));
|
|
4882
|
-
}
|
|
4883
|
-
if (fileIssues.length > 5) {
|
|
4884
|
-
console.log(chalk9.dim(` ... and ${fileIssues.length - 5} more issues`));
|
|
4885
|
-
}
|
|
4886
|
-
console.log();
|
|
5020
|
+
if (!existsSync9(archonConfigPath)) {
|
|
5021
|
+
return null;
|
|
4887
5022
|
}
|
|
4888
|
-
|
|
4889
|
-
console.log(chalk9.dim("Websites that don't meet accessibility standards may violate:"));
|
|
4890
|
-
console.log(chalk9.dim(" \u2022 ADA (Americans with Disabilities Act) \u2014 US"));
|
|
4891
|
-
console.log(chalk9.dim(" \u2022 EAA (European Accessibility Act) \u2014 EU, effective June 2025"));
|
|
4892
|
-
console.log(chalk9.dim(" \u2022 Section 508 \u2014 US federal agencies and contractors"));
|
|
4893
|
-
console.log(chalk9.dim(" \u2022 AODA (Accessibility for Ontarians) \u2014 Ontario, Canada"));
|
|
4894
|
-
console.log();
|
|
4895
|
-
console.log(chalk9.dim("Non-compliance can result in lawsuits, fines, and reputational damage."));
|
|
4896
|
-
console.log(chalk9.dim("In 2023, over 4,600 ADA web accessibility lawsuits were filed in the US.\n"));
|
|
4897
|
-
console.log(chalk9.dim(`Full report saved to: .archon/a11y-report.json`));
|
|
5023
|
+
return { name: projectName, path: cwd };
|
|
4898
5024
|
}
|
|
4899
|
-
async function
|
|
4900
|
-
const
|
|
5025
|
+
async function getCurrentAtomId(cwd) {
|
|
5026
|
+
const stateFile = join8(cwd, ".archon", "state.json");
|
|
5027
|
+
if (!existsSync9(stateFile)) return null;
|
|
4901
5028
|
try {
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
await a11yCheck({});
|
|
4907
|
-
}
|
|
4908
|
-
const reportContent = await readFile5(reportPath, "utf-8");
|
|
4909
|
-
const report = JSON.parse(reportContent);
|
|
4910
|
-
if (report.passed || report.issues.length === 0) {
|
|
4911
|
-
console.log(chalk9.green("No issues to fix!"));
|
|
4912
|
-
return;
|
|
4913
|
-
}
|
|
4914
|
-
console.log(chalk9.dim(`Found ${report.issues.length} issues from last check.
|
|
4915
|
-
`));
|
|
4916
|
-
const fixablePatterns = [
|
|
4917
|
-
{
|
|
4918
|
-
pattern: /text-primary\/60/g,
|
|
4919
|
-
replacement: "text-text-muted",
|
|
4920
|
-
description: "Replace low-opacity text with accessible color"
|
|
4921
|
-
},
|
|
4922
|
-
{
|
|
4923
|
-
pattern: /text-primary\/70/g,
|
|
4924
|
-
replacement: "text-text-muted",
|
|
4925
|
-
description: "Replace low-opacity text with accessible color"
|
|
4926
|
-
},
|
|
4927
|
-
{
|
|
4928
|
-
pattern: /outline:\s*none/gi,
|
|
4929
|
-
replacement: "outline: 2px solid transparent",
|
|
4930
|
-
description: "Replace outline:none with accessible alternative"
|
|
4931
|
-
},
|
|
4932
|
-
{
|
|
4933
|
-
pattern: /<html>/gi,
|
|
4934
|
-
replacement: '<html lang="en">',
|
|
4935
|
-
description: "Add lang attribute to html element"
|
|
4936
|
-
}
|
|
4937
|
-
];
|
|
4938
|
-
let totalFixes = 0;
|
|
4939
|
-
for (const fix of fixablePatterns) {
|
|
4940
|
-
const patterns = ["**/*.html", "**/*.jsx", "**/*.tsx", "**/*.astro", "**/*.svelte", "**/*.vue", "**/*.css"];
|
|
4941
|
-
const ignorePatterns = ["**/node_modules/**", "**/dist/**", "**/build/**"];
|
|
4942
|
-
for (const pattern of patterns) {
|
|
4943
|
-
const files = await glob(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
4944
|
-
for (const file of files) {
|
|
4945
|
-
const filePath = join7(process.cwd(), file);
|
|
4946
|
-
try {
|
|
4947
|
-
const content = await readFile5(filePath, "utf-8");
|
|
4948
|
-
if (fix.pattern.test(content)) {
|
|
4949
|
-
fix.pattern.lastIndex = 0;
|
|
4950
|
-
const matches = content.match(fix.pattern);
|
|
4951
|
-
const count = matches?.length || 0;
|
|
4952
|
-
if (count > 0) {
|
|
4953
|
-
console.log(chalk9.cyan(` ${file}: ${count} fixes (${fix.description})`));
|
|
4954
|
-
totalFixes += count;
|
|
4955
|
-
if (!options.dryRun) {
|
|
4956
|
-
const newContent = content.replace(fix.pattern, fix.replacement);
|
|
4957
|
-
await writeFile5(filePath, newContent);
|
|
4958
|
-
}
|
|
4959
|
-
}
|
|
4960
|
-
}
|
|
4961
|
-
} catch {
|
|
4962
|
-
}
|
|
4963
|
-
}
|
|
4964
|
-
}
|
|
4965
|
-
}
|
|
4966
|
-
if (totalFixes === 0) {
|
|
4967
|
-
console.log(chalk9.dim("No auto-fixable issues found. Some issues require manual fixes."));
|
|
4968
|
-
} else if (options.dryRun) {
|
|
4969
|
-
console.log(chalk9.yellow(`
|
|
4970
|
-
${totalFixes} fixes would be applied. Run without --dry-run to apply.`));
|
|
4971
|
-
} else {
|
|
4972
|
-
console.log(chalk9.green(`
|
|
4973
|
-
\u2705 Applied ${totalFixes} fixes.`));
|
|
4974
|
-
console.log(chalk9.dim('Run "archon a11y check" to verify fixes.'));
|
|
4975
|
-
}
|
|
4976
|
-
} finally {
|
|
4977
|
-
prompt3.close();
|
|
4978
|
-
}
|
|
4979
|
-
}
|
|
4980
|
-
async function a11yBadge(options) {
|
|
4981
|
-
console.log(chalk9.blue("\n\u267F Accessibility Badge\n"));
|
|
4982
|
-
const badgeHtml = `<!-- WCAG 2.2 AA Compliance Badge -->
|
|
4983
|
-
<div class="flex items-center gap-2 text-xs text-text-muted dark:text-cream-300">
|
|
4984
|
-
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
|
4985
|
-
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
|
|
4986
|
-
</svg>
|
|
4987
|
-
<span>WCAG 2.2 AA Accessible</span>
|
|
4988
|
-
</div>`;
|
|
4989
|
-
const footerPatterns = ["**/Footer.{jsx,tsx,astro,svelte,vue}", "**/footer.{jsx,tsx,astro,svelte,vue,html}"];
|
|
4990
|
-
let footerFiles = [];
|
|
4991
|
-
for (const pattern of footerPatterns) {
|
|
4992
|
-
const files = await glob(pattern, { cwd: process.cwd(), ignore: ["**/node_modules/**"] });
|
|
4993
|
-
footerFiles = footerFiles.concat(files);
|
|
4994
|
-
}
|
|
4995
|
-
if (footerFiles.length === 0) {
|
|
4996
|
-
console.log(chalk9.yellow("No footer files found."));
|
|
4997
|
-
console.log(chalk9.dim("\nManually add this badge to your footer:\n"));
|
|
4998
|
-
console.log(badgeHtml);
|
|
4999
|
-
return;
|
|
5000
|
-
}
|
|
5001
|
-
console.log(chalk9.dim(`Found ${footerFiles.length} footer file(s):
|
|
5002
|
-
`));
|
|
5003
|
-
for (const file of footerFiles) {
|
|
5004
|
-
console.log(chalk9.cyan(` ${file}`));
|
|
5005
|
-
}
|
|
5006
|
-
if (options.remove) {
|
|
5007
|
-
console.log(chalk9.yellow("\nRemoving accessibility badge..."));
|
|
5008
|
-
console.log(chalk9.dim("Badge removal not yet implemented. Manually remove the WCAG badge comment block."));
|
|
5009
|
-
} else {
|
|
5010
|
-
console.log(chalk9.dim("\nTo add the badge, insert this code before the closing </footer> tag:\n"));
|
|
5011
|
-
console.log(chalk9.green(badgeHtml));
|
|
5029
|
+
const state = JSON.parse(await readFile5(stateFile, "utf-8"));
|
|
5030
|
+
return state.currentAtomId || null;
|
|
5031
|
+
} catch {
|
|
5032
|
+
return null;
|
|
5012
5033
|
}
|
|
5013
5034
|
}
|
|
5014
|
-
async function
|
|
5015
|
-
const
|
|
5035
|
+
async function getPendingAtoms(cwd) {
|
|
5036
|
+
const stateFile = join8(cwd, ".archon", "state.json");
|
|
5037
|
+
if (!existsSync9(stateFile)) return [];
|
|
5016
5038
|
try {
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
if (!existsSync8(reportPath)) {
|
|
5022
|
-
return false;
|
|
5023
|
-
}
|
|
5024
|
-
const reportContent = await readFile5(reportPath, "utf-8");
|
|
5025
|
-
const report = JSON.parse(reportContent);
|
|
5026
|
-
if (report.passed) {
|
|
5027
|
-
const addBadge = await prompt3.ask("\nWould you like to add a WCAG 2.2 AA badge to your footer? (y/N): ");
|
|
5028
|
-
if (addBadge.toLowerCase() === "y") {
|
|
5029
|
-
await a11yBadge({});
|
|
5030
|
-
}
|
|
5031
|
-
return true;
|
|
5032
|
-
}
|
|
5033
|
-
console.log(chalk9.bold("\nOptions:\n"));
|
|
5034
|
-
console.log(" 1) Fix issues now (recommended)");
|
|
5035
|
-
console.log(" 2) Deploy anyway (not recommended)");
|
|
5036
|
-
console.log(" 3) Cancel deployment\n");
|
|
5037
|
-
const choice = await prompt3.ask("Which would you like to do? (1/2/3): ");
|
|
5038
|
-
if (choice === "1") {
|
|
5039
|
-
await a11yFix({});
|
|
5040
|
-
await a11yCheck({});
|
|
5041
|
-
return true;
|
|
5042
|
-
} else if (choice === "2") {
|
|
5043
|
-
console.log(chalk9.yellow("\n[!] Acknowledged. Proceeding without full accessibility compliance."));
|
|
5044
|
-
console.log(chalk9.dim("Consider addressing these issues in a future session.\n"));
|
|
5045
|
-
return true;
|
|
5046
|
-
} else {
|
|
5047
|
-
console.log(chalk9.dim("\nDeployment cancelled."));
|
|
5048
|
-
return false;
|
|
5049
|
-
}
|
|
5050
|
-
} finally {
|
|
5051
|
-
prompt3.close();
|
|
5039
|
+
const state = JSON.parse(await readFile5(stateFile, "utf-8"));
|
|
5040
|
+
return state.pendingAtoms || [];
|
|
5041
|
+
} catch {
|
|
5042
|
+
return [];
|
|
5052
5043
|
}
|
|
5053
5044
|
}
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
import { Command as Command2 } from "commander";
|
|
5057
|
-
import chalk10 from "chalk";
|
|
5058
|
-
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
|
|
5059
|
-
import { existsSync as existsSync9 } from "fs";
|
|
5060
|
-
import { join as join8 } from "path";
|
|
5061
|
-
import { createInterface as createInterface2 } from "readline";
|
|
5062
|
-
import { glob as glob2 } from "glob";
|
|
5063
|
-
import * as yaml2 from "yaml";
|
|
5064
|
-
var CONFIG_PATH2 = ".archon/config.yaml";
|
|
5065
|
-
function createPrompt2() {
|
|
5066
|
-
const rl = createInterface2({
|
|
5067
|
-
input: process.stdin,
|
|
5068
|
-
output: process.stdout
|
|
5069
|
-
});
|
|
5070
|
-
return {
|
|
5071
|
-
ask: (question) => new Promise((resolve) => {
|
|
5072
|
-
rl.question(question, resolve);
|
|
5073
|
-
}),
|
|
5074
|
-
close: () => rl.close()
|
|
5075
|
-
};
|
|
5076
|
-
}
|
|
5077
|
-
async function loadGeoConfig(cwd) {
|
|
5078
|
-
const configPath = join8(cwd, CONFIG_PATH2);
|
|
5079
|
-
if (!existsSync9(configPath)) {
|
|
5080
|
-
return {};
|
|
5081
|
-
}
|
|
5045
|
+
async function getFileContent(path2) {
|
|
5046
|
+
if (!existsSync9(path2)) return null;
|
|
5082
5047
|
try {
|
|
5083
|
-
|
|
5084
|
-
return yaml2.parse(content);
|
|
5048
|
+
return await readFile5(path2, "utf-8");
|
|
5085
5049
|
} catch {
|
|
5086
|
-
return
|
|
5087
|
-
}
|
|
5088
|
-
}
|
|
5089
|
-
async function saveGeoConfig(cwd, config) {
|
|
5090
|
-
const configPath = join8(cwd, CONFIG_PATH2);
|
|
5091
|
-
const archonDir = join8(cwd, ".archon");
|
|
5092
|
-
if (!existsSync9(archonDir)) {
|
|
5093
|
-
await mkdir4(archonDir, { recursive: true });
|
|
5094
|
-
}
|
|
5095
|
-
let existing = {};
|
|
5096
|
-
if (existsSync9(configPath)) {
|
|
5097
|
-
try {
|
|
5098
|
-
const content = await readFile6(configPath, "utf-8");
|
|
5099
|
-
existing = yaml2.parse(content);
|
|
5100
|
-
} catch {
|
|
5101
|
-
}
|
|
5102
|
-
}
|
|
5103
|
-
const merged = { ...existing, ...config };
|
|
5104
|
-
await writeFile6(configPath, yaml2.stringify(merged), "utf-8");
|
|
5105
|
-
}
|
|
5106
|
-
async function readPageContent(patterns) {
|
|
5107
|
-
const cwd = process.cwd();
|
|
5108
|
-
let content = "";
|
|
5109
|
-
for (const pattern of patterns) {
|
|
5110
|
-
const files = await glob2(pattern, { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
5111
|
-
for (const file of files.slice(0, 3)) {
|
|
5112
|
-
try {
|
|
5113
|
-
const fileContent = await readFile6(join8(cwd, file), "utf-8");
|
|
5114
|
-
content += `
|
|
5115
|
-
--- File: ${file} ---
|
|
5116
|
-
${fileContent.slice(0, 5e3)}
|
|
5117
|
-
`;
|
|
5118
|
-
} catch {
|
|
5119
|
-
}
|
|
5120
|
-
}
|
|
5121
|
-
}
|
|
5122
|
-
return content || "No content found.";
|
|
5123
|
-
}
|
|
5124
|
-
async function checkStrongModelAccess() {
|
|
5125
|
-
const config = await loadConfig();
|
|
5126
|
-
const tier = config.tier ?? "FREE";
|
|
5127
|
-
if (tier === "CREDITS" || tier === "BYOK") {
|
|
5128
|
-
return { allowed: true, tier };
|
|
5129
|
-
}
|
|
5130
|
-
return { allowed: false, tier };
|
|
5131
|
-
}
|
|
5132
|
-
var IDENTITY_SYSTEM_PROMPT = `You are a brand strategist and SEO expert. Your job is to create concise, memorable brand identity phrases and descriptions.
|
|
5133
|
-
|
|
5134
|
-
Guidelines:
|
|
5135
|
-
- Identity phrases should be EXACTLY 7 words
|
|
5136
|
-
- Identity phrases should be unique, memorable, and capture the essence of the brand
|
|
5137
|
-
- Short descriptions should be EXACTLY 50 words
|
|
5138
|
-
- Descriptions should explain what the product/service does and why it matters
|
|
5139
|
-
- Both should be optimized for search engines and AI assistants (GEO)
|
|
5140
|
-
- Avoid jargon unless it's industry-standard terminology
|
|
5141
|
-
- Focus on value proposition and differentiation
|
|
5142
|
-
|
|
5143
|
-
Output your response as valid JSON.`;
|
|
5144
|
-
async function generateIdentityCandidates(pageContent) {
|
|
5145
|
-
const agent = new ArchitectAgent({ temperature: 0.8 });
|
|
5146
|
-
const prompt3 = `Based on the following website content, generate brand identity options.
|
|
5147
|
-
|
|
5148
|
-
${pageContent}
|
|
5149
|
-
|
|
5150
|
-
Generate EXACTLY 3 candidate 7-word brand phrases and 3 candidate 50-word descriptions.
|
|
5151
|
-
|
|
5152
|
-
Output as JSON:
|
|
5153
|
-
{
|
|
5154
|
-
"phrases": [
|
|
5155
|
-
{ "phrase": "exactly seven words go right here", "rationale": "why this phrase works" },
|
|
5156
|
-
{ "phrase": "another seven word phrase goes here", "rationale": "why this phrase works" },
|
|
5157
|
-
{ "phrase": "third seven word phrase goes here", "rationale": "why this phrase works" }
|
|
5158
|
-
],
|
|
5159
|
-
"descriptions": [
|
|
5160
|
-
{ "description": "exactly 50 words description...", "rationale": "why this works" },
|
|
5161
|
-
{ "description": "another 50 words description...", "rationale": "why this works" },
|
|
5162
|
-
{ "description": "third 50 words description...", "rationale": "why this works" }
|
|
5163
|
-
]
|
|
5164
|
-
}`;
|
|
5165
|
-
const response = await agent.client.chat(
|
|
5166
|
-
IDENTITY_SYSTEM_PROMPT,
|
|
5167
|
-
prompt3,
|
|
5168
|
-
{ temperature: 0.8, maxTokens: 2e3 }
|
|
5169
|
-
);
|
|
5170
|
-
const jsonMatch = response.content.match(/\{[\s\S]*\}/);
|
|
5171
|
-
if (!jsonMatch) {
|
|
5172
|
-
throw new Error("Failed to parse AI response");
|
|
5050
|
+
return null;
|
|
5173
5051
|
}
|
|
5174
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
5175
|
-
return parsed;
|
|
5176
5052
|
}
|
|
5177
|
-
async function
|
|
5053
|
+
async function saveSession(name) {
|
|
5054
|
+
const spinner = ora2("Saving session...").start();
|
|
5178
5055
|
const cwd = process.cwd();
|
|
5179
|
-
const prompt3 = createPrompt2();
|
|
5180
5056
|
try {
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
console.log(chalk10.yellow(`[!] Your tier (${tier}) uses basic models.`));
|
|
5185
|
-
console.log(chalk10.dim("For better results, add credits or use BYOK mode.\n"));
|
|
5186
|
-
}
|
|
5187
|
-
console.log(chalk10.dim("Reading homepage and about page content...\n"));
|
|
5188
|
-
const pageContent = await readPageContent([
|
|
5189
|
-
"**/index.html",
|
|
5190
|
-
"**/index.{jsx,tsx,astro,svelte,vue}",
|
|
5191
|
-
"**/about.{html,jsx,tsx,astro,svelte,vue,md}",
|
|
5192
|
-
"**/about/index.{jsx,tsx,astro,svelte,vue}",
|
|
5193
|
-
"**/pages/index.{jsx,tsx,astro,svelte,vue}",
|
|
5194
|
-
"**/app/page.{jsx,tsx}",
|
|
5195
|
-
"README.md"
|
|
5196
|
-
]);
|
|
5197
|
-
if (pageContent === "No content found.") {
|
|
5198
|
-
console.log(chalk10.yellow("No homepage or about page content found."));
|
|
5199
|
-
console.log(chalk10.dim("Create an index file or README.md first.\n"));
|
|
5057
|
+
const config = await loadConfig();
|
|
5058
|
+
if (!config.accessToken || !config.userId) {
|
|
5059
|
+
spinner.fail("Not logged in. Run: archon login");
|
|
5200
5060
|
return;
|
|
5201
5061
|
}
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
candidates.phrases.forEach((p, i) => {
|
|
5206
|
-
console.log(` ${chalk10.cyan(`${i + 1})`)} ${chalk10.bold(p.phrase)}`);
|
|
5207
|
-
console.log(` ${chalk10.dim(p.rationale)}
|
|
5208
|
-
`);
|
|
5209
|
-
});
|
|
5210
|
-
const phraseChoice = await prompt3.ask('Select a phrase (1-3) or "r" to regenerate: ');
|
|
5211
|
-
if (phraseChoice.toLowerCase() === "r") {
|
|
5212
|
-
console.log(chalk10.dim("\nRegenerating... Run the command again.\n"));
|
|
5062
|
+
const projectInfo = await getProjectInfo(cwd);
|
|
5063
|
+
if (!projectInfo) {
|
|
5064
|
+
spinner.fail("Not an ArchonDev project. Run: archon init");
|
|
5213
5065
|
return;
|
|
5214
5066
|
}
|
|
5215
|
-
const
|
|
5216
|
-
const
|
|
5217
|
-
if (
|
|
5218
|
-
|
|
5219
|
-
return;
|
|
5220
|
-
}
|
|
5221
|
-
console.log(chalk10.bold("\n\u{1F4DD} 50-Word Descriptions:\n"));
|
|
5222
|
-
candidates.descriptions.forEach((d, i) => {
|
|
5223
|
-
console.log(` ${chalk10.cyan(`${i + 1})`)} ${d.description}`);
|
|
5224
|
-
console.log(` ${chalk10.dim(d.rationale)}
|
|
5225
|
-
`);
|
|
5226
|
-
});
|
|
5227
|
-
const descChoice = await prompt3.ask('Select a description (1-3) or "r" to regenerate: ');
|
|
5228
|
-
if (descChoice.toLowerCase() === "r") {
|
|
5229
|
-
console.log(chalk10.dim("\nRegenerating... Run the command again.\n"));
|
|
5230
|
-
return;
|
|
5231
|
-
}
|
|
5232
|
-
const descIndex = parseInt(descChoice, 10) - 1;
|
|
5233
|
-
const selectedDescription = candidates.descriptions[descIndex];
|
|
5234
|
-
if (isNaN(descIndex) || descIndex < 0 || descIndex >= candidates.descriptions.length || !selectedDescription) {
|
|
5235
|
-
console.log(chalk10.red("Invalid selection."));
|
|
5236
|
-
return;
|
|
5237
|
-
}
|
|
5238
|
-
const geoConfig = {
|
|
5239
|
-
geo: {
|
|
5240
|
-
identityPhrase: selectedPhrase.phrase,
|
|
5241
|
-
shortDescription: selectedDescription.description,
|
|
5242
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5243
|
-
}
|
|
5244
|
-
};
|
|
5245
|
-
await saveGeoConfig(cwd, geoConfig);
|
|
5246
|
-
console.log(chalk10.green("\n\u2705 Identity saved!\n"));
|
|
5247
|
-
console.log(chalk10.dim(` Phrase: ${selectedPhrase.phrase}`));
|
|
5248
|
-
console.log(chalk10.dim(` Description: ${selectedDescription.description.slice(0, 60)}...`));
|
|
5249
|
-
console.log();
|
|
5250
|
-
console.log(chalk10.cyan(`Use 'archon geo schema' to generate JSON-LD.`));
|
|
5251
|
-
} finally {
|
|
5252
|
-
prompt3.close();
|
|
5253
|
-
}
|
|
5254
|
-
}
|
|
5255
|
-
async function geoSchema(options) {
|
|
5256
|
-
const cwd = process.cwd();
|
|
5257
|
-
console.log(chalk10.blue("\n\u{1F4E6} JSON-LD Schema Generator\n"));
|
|
5258
|
-
const config = await loadGeoConfig(cwd);
|
|
5259
|
-
if (!config.geo?.identityPhrase || !config.geo?.shortDescription) {
|
|
5260
|
-
console.log(chalk10.yellow("No identity defined."));
|
|
5261
|
-
console.log(chalk10.dim(`Run 'archon geo identity' first.
|
|
5262
|
-
`));
|
|
5263
|
-
return;
|
|
5264
|
-
}
|
|
5265
|
-
const { identityPhrase, shortDescription } = config.geo;
|
|
5266
|
-
let orgName = identityPhrase.split(" ").slice(0, 2).join(" ");
|
|
5267
|
-
try {
|
|
5268
|
-
const pkgPath = join8(cwd, "package.json");
|
|
5269
|
-
if (existsSync9(pkgPath)) {
|
|
5270
|
-
const pkg = JSON.parse(await readFile6(pkgPath, "utf-8"));
|
|
5271
|
-
if (pkg.name) {
|
|
5272
|
-
orgName = pkg.name.replace(/-/g, " ").replace(/^\w/, (c) => c.toUpperCase());
|
|
5273
|
-
}
|
|
5274
|
-
}
|
|
5275
|
-
} catch {
|
|
5276
|
-
}
|
|
5277
|
-
const schema = {
|
|
5278
|
-
"@context": "https://schema.org",
|
|
5279
|
-
"@graph": [
|
|
5280
|
-
{
|
|
5281
|
-
"@type": "Organization",
|
|
5282
|
-
"@id": "#organization",
|
|
5283
|
-
name: orgName,
|
|
5284
|
-
description: shortDescription,
|
|
5285
|
-
slogan: identityPhrase
|
|
5286
|
-
},
|
|
5287
|
-
{
|
|
5288
|
-
"@type": "WebSite",
|
|
5289
|
-
"@id": "#website",
|
|
5290
|
-
name: orgName,
|
|
5291
|
-
description: shortDescription,
|
|
5292
|
-
publisher: { "@id": "#organization" }
|
|
5293
|
-
},
|
|
5294
|
-
{
|
|
5295
|
-
"@type": "Service",
|
|
5296
|
-
name: orgName,
|
|
5297
|
-
description: shortDescription,
|
|
5298
|
-
provider: { "@id": "#organization" }
|
|
5299
|
-
}
|
|
5300
|
-
]
|
|
5301
|
-
};
|
|
5302
|
-
const jsonLd = JSON.stringify(schema, null, 2);
|
|
5303
|
-
if (options.output) {
|
|
5304
|
-
await writeFile6(options.output, jsonLd, "utf-8");
|
|
5305
|
-
console.log(chalk10.green(`\u2705 Schema written to ${options.output}`));
|
|
5306
|
-
} else {
|
|
5307
|
-
console.log(chalk10.bold("Generated JSON-LD:\n"));
|
|
5308
|
-
console.log(chalk10.cyan(jsonLd));
|
|
5309
|
-
}
|
|
5310
|
-
if (options.apply) {
|
|
5311
|
-
console.log(chalk10.dim("\nAttempting to insert into homepage <head>...\n"));
|
|
5312
|
-
const indexFiles = await glob2("**/index.html", { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
5313
|
-
if (indexFiles.length === 0) {
|
|
5314
|
-
console.log(chalk10.yellow("No index.html found. Manually add to your HTML <head>:"));
|
|
5315
|
-
console.log(chalk10.dim(`<script type="application/ld+json">
|
|
5316
|
-
${jsonLd}
|
|
5317
|
-
</script>`));
|
|
5318
|
-
return;
|
|
5319
|
-
}
|
|
5320
|
-
const firstIndexFile = indexFiles[0];
|
|
5321
|
-
if (!firstIndexFile) {
|
|
5322
|
-
console.log(chalk10.yellow("No index.html found."));
|
|
5323
|
-
return;
|
|
5324
|
-
}
|
|
5325
|
-
const indexPath = join8(cwd, firstIndexFile);
|
|
5326
|
-
let indexContent = await readFile6(indexPath, "utf-8");
|
|
5327
|
-
const scriptTag = `<script type="application/ld+json">
|
|
5328
|
-
${jsonLd}
|
|
5329
|
-
</script>`;
|
|
5330
|
-
if (indexContent.includes("application/ld+json")) {
|
|
5331
|
-
console.log(chalk10.yellow("JSON-LD already exists in index.html. Remove it first or update manually."));
|
|
5332
|
-
return;
|
|
5333
|
-
}
|
|
5334
|
-
indexContent = indexContent.replace("</head>", `${scriptTag}
|
|
5335
|
-
</head>`);
|
|
5336
|
-
await writeFile6(indexPath, indexContent, "utf-8");
|
|
5337
|
-
console.log(chalk10.green(`\u2705 JSON-LD inserted into ${firstIndexFile}`));
|
|
5338
|
-
}
|
|
5339
|
-
console.log();
|
|
5340
|
-
}
|
|
5341
|
-
var FAQ_SYSTEM_PROMPT = `You are an SEO expert creating FAQ content for schema markup.
|
|
5342
|
-
|
|
5343
|
-
Guidelines:
|
|
5344
|
-
- Generate 6-8 questions that potential customers would ask
|
|
5345
|
-
- Answers should be concise but informative (2-4 sentences)
|
|
5346
|
-
- Questions should naturally include keywords from the brand identity
|
|
5347
|
-
- Answers should demonstrate expertise and build trust
|
|
5348
|
-
- Format for FAQPage schema compatibility
|
|
5349
|
-
|
|
5350
|
-
Output your response as valid JSON.`;
|
|
5351
|
-
async function geoFaq(options) {
|
|
5352
|
-
const cwd = process.cwd();
|
|
5353
|
-
console.log(chalk10.blue("\n\u2753 FAQ Schema Generator\n"));
|
|
5354
|
-
const config = await loadGeoConfig(cwd);
|
|
5355
|
-
if (!config.geo?.identityPhrase || !config.geo?.shortDescription) {
|
|
5356
|
-
console.log(chalk10.yellow("No identity defined."));
|
|
5357
|
-
console.log(chalk10.dim(`Run 'archon geo identity' first.
|
|
5358
|
-
`));
|
|
5359
|
-
return;
|
|
5360
|
-
}
|
|
5361
|
-
const { identityPhrase, shortDescription } = config.geo;
|
|
5362
|
-
const { allowed, tier } = await checkStrongModelAccess();
|
|
5363
|
-
if (!allowed) {
|
|
5364
|
-
console.log(chalk10.yellow(`[!] Your tier (${tier}) uses basic models.`));
|
|
5365
|
-
console.log(chalk10.dim("For better results, add credits or use BYOK mode.\n"));
|
|
5366
|
-
}
|
|
5367
|
-
console.log(chalk10.dim("Generating FAQ content with AI...\n"));
|
|
5368
|
-
const agent = new ArchitectAgent({ temperature: 0.7 });
|
|
5369
|
-
const prompt3 = `Generate FAQ content for a product/service with:
|
|
5370
|
-
- Brand phrase: "${identityPhrase}"
|
|
5371
|
-
- Description: "${shortDescription}"
|
|
5372
|
-
|
|
5373
|
-
Generate 6-8 FAQs as JSON:
|
|
5374
|
-
{
|
|
5375
|
-
"faqs": [
|
|
5376
|
-
{ "question": "What is...?", "answer": "..." },
|
|
5377
|
-
...
|
|
5378
|
-
]
|
|
5379
|
-
}`;
|
|
5380
|
-
const response = await agent.client.chat(
|
|
5381
|
-
FAQ_SYSTEM_PROMPT,
|
|
5382
|
-
prompt3,
|
|
5383
|
-
{ temperature: 0.7, maxTokens: 2e3 }
|
|
5384
|
-
);
|
|
5385
|
-
const jsonMatch = response.content.match(/\{[\s\S]*\}/);
|
|
5386
|
-
if (!jsonMatch) {
|
|
5387
|
-
console.log(chalk10.red("Failed to generate FAQ content."));
|
|
5388
|
-
return;
|
|
5389
|
-
}
|
|
5390
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
5391
|
-
const faqSchema = {
|
|
5392
|
-
"@context": "https://schema.org",
|
|
5393
|
-
"@type": "FAQPage",
|
|
5394
|
-
mainEntity: parsed.faqs.map((faq) => ({
|
|
5395
|
-
"@type": "Question",
|
|
5396
|
-
name: faq.question,
|
|
5397
|
-
acceptedAnswer: {
|
|
5398
|
-
"@type": "Answer",
|
|
5399
|
-
text: faq.answer
|
|
5400
|
-
}
|
|
5401
|
-
}))
|
|
5402
|
-
};
|
|
5403
|
-
const jsonLd = JSON.stringify(faqSchema, null, 2);
|
|
5404
|
-
if (options.output) {
|
|
5405
|
-
await writeFile6(options.output, jsonLd, "utf-8");
|
|
5406
|
-
console.log(chalk10.green(`\u2705 FAQ schema written to ${options.output}`));
|
|
5407
|
-
} else {
|
|
5408
|
-
console.log(chalk10.bold("Generated FAQPage JSON-LD:\n"));
|
|
5409
|
-
console.log(chalk10.cyan(jsonLd));
|
|
5410
|
-
}
|
|
5411
|
-
console.log();
|
|
5412
|
-
}
|
|
5413
|
-
async function geoAudit() {
|
|
5414
|
-
const cwd = process.cwd();
|
|
5415
|
-
console.log(chalk10.blue("\n\u{1F50D} GEO Audit\n"));
|
|
5416
|
-
const result = {
|
|
5417
|
-
identityDefined: false,
|
|
5418
|
-
phraseInH1: false,
|
|
5419
|
-
phraseInMetaDescription: false,
|
|
5420
|
-
hasOrganizationSchema: false,
|
|
5421
|
-
hasServiceSchema: false,
|
|
5422
|
-
hasFAQSchema: false,
|
|
5423
|
-
issues: []
|
|
5424
|
-
};
|
|
5425
|
-
const config = await loadGeoConfig(cwd);
|
|
5426
|
-
if (config.geo?.identityPhrase && config.geo?.shortDescription) {
|
|
5427
|
-
result.identityDefined = true;
|
|
5428
|
-
console.log(chalk10.green("\u2705 Identity defined"));
|
|
5429
|
-
console.log(chalk10.dim(` Phrase: ${config.geo.identityPhrase}`));
|
|
5430
|
-
} else {
|
|
5431
|
-
result.issues.push('Identity not defined. Run "archon geo identity"');
|
|
5432
|
-
console.log(chalk10.red("\u274C Identity not defined"));
|
|
5433
|
-
}
|
|
5434
|
-
const htmlFiles = await glob2("**/index.html", { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
5435
|
-
const firstHtmlFile = htmlFiles[0];
|
|
5436
|
-
if (firstHtmlFile && config.geo?.identityPhrase) {
|
|
5437
|
-
const indexPath = join8(cwd, firstHtmlFile);
|
|
5438
|
-
const content = await readFile6(indexPath, "utf-8");
|
|
5439
|
-
const identityPhrase = config.geo.identityPhrase;
|
|
5440
|
-
const firstKeyword = identityPhrase.toLowerCase().split(" ")[0] ?? "";
|
|
5441
|
-
const h1Match = content.match(/<h1[^>]*>(.*?)<\/h1>/is);
|
|
5442
|
-
if (h1Match?.[1] && h1Match[1].toLowerCase().includes(firstKeyword)) {
|
|
5443
|
-
result.phraseInH1 = true;
|
|
5444
|
-
console.log(chalk10.green("\u2705 Brand keyword in H1"));
|
|
5445
|
-
} else {
|
|
5446
|
-
result.issues.push("Brand phrase keyword not found in H1");
|
|
5447
|
-
console.log(chalk10.yellow("[!] Brand keyword not in H1"));
|
|
5448
|
-
}
|
|
5449
|
-
const metaMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i);
|
|
5450
|
-
if (metaMatch?.[1] && metaMatch[1].toLowerCase().includes(firstKeyword)) {
|
|
5451
|
-
result.phraseInMetaDescription = true;
|
|
5452
|
-
console.log(chalk10.green("\u2705 Brand keyword in meta description"));
|
|
5453
|
-
} else {
|
|
5454
|
-
result.issues.push("Brand phrase keyword not found in meta description");
|
|
5455
|
-
console.log(chalk10.yellow("[!] Brand keyword not in meta description"));
|
|
5456
|
-
}
|
|
5457
|
-
if (content.includes("application/ld+json")) {
|
|
5458
|
-
if (content.includes('"@type":"Organization"') || content.includes('"@type": "Organization"')) {
|
|
5459
|
-
result.hasOrganizationSchema = true;
|
|
5460
|
-
console.log(chalk10.green("\u2705 Organization schema present"));
|
|
5461
|
-
} else {
|
|
5462
|
-
result.issues.push("Organization schema not found");
|
|
5463
|
-
console.log(chalk10.yellow("[!] Organization schema missing"));
|
|
5464
|
-
}
|
|
5465
|
-
if (content.includes('"@type":"Service"') || content.includes('"@type": "Service"')) {
|
|
5466
|
-
result.hasServiceSchema = true;
|
|
5467
|
-
console.log(chalk10.green("\u2705 Service schema present"));
|
|
5468
|
-
} else {
|
|
5469
|
-
result.issues.push("Service schema not found");
|
|
5470
|
-
console.log(chalk10.yellow("[!] Service schema missing"));
|
|
5471
|
-
}
|
|
5472
|
-
if (content.includes('"@type":"FAQPage"') || content.includes('"@type": "FAQPage"')) {
|
|
5473
|
-
result.hasFAQSchema = true;
|
|
5474
|
-
console.log(chalk10.green("\u2705 FAQPage schema present"));
|
|
5475
|
-
} else {
|
|
5476
|
-
result.issues.push("FAQPage schema not found");
|
|
5477
|
-
console.log(chalk10.yellow("[!] FAQPage schema missing"));
|
|
5478
|
-
}
|
|
5479
|
-
} else {
|
|
5480
|
-
result.issues.push("No JSON-LD schemas found");
|
|
5481
|
-
console.log(chalk10.red("\u274C No JSON-LD schemas found"));
|
|
5482
|
-
}
|
|
5483
|
-
} else if (htmlFiles.length === 0) {
|
|
5484
|
-
result.issues.push("No index.html found");
|
|
5485
|
-
console.log(chalk10.yellow("[!] No index.html found to audit"));
|
|
5486
|
-
}
|
|
5487
|
-
console.log();
|
|
5488
|
-
const passed = result.issues.length === 0;
|
|
5489
|
-
if (passed) {
|
|
5490
|
-
console.log(chalk10.green.bold("\u2705 GEO Audit Passed"));
|
|
5491
|
-
} else {
|
|
5492
|
-
console.log(chalk10.yellow.bold(`[!] ${result.issues.length} issue(s) found`));
|
|
5493
|
-
console.log();
|
|
5494
|
-
console.log(chalk10.bold("Recommendations:"));
|
|
5495
|
-
result.issues.forEach((issue, i) => {
|
|
5496
|
-
console.log(chalk10.dim(` ${i + 1}. ${issue}`));
|
|
5497
|
-
});
|
|
5498
|
-
}
|
|
5499
|
-
console.log();
|
|
5500
|
-
}
|
|
5501
|
-
function createGeoCommand() {
|
|
5502
|
-
const geo = new Command2("geo").description("GEO (Generative Engine Optimization) for AI-powered SEO").addHelpText(
|
|
5503
|
-
"after",
|
|
5504
|
-
`
|
|
5505
|
-
Examples:
|
|
5506
|
-
archon geo identity Generate brand identity phrases
|
|
5507
|
-
archon geo schema Generate JSON-LD Organization/Service schemas
|
|
5508
|
-
archon geo schema --apply Insert JSON-LD into homepage
|
|
5509
|
-
archon geo faq Generate FAQPage schema
|
|
5510
|
-
archon geo audit Check GEO compliance status
|
|
5511
|
-
`
|
|
5512
|
-
);
|
|
5513
|
-
geo.command("identity").description("Generate and save brand identity (7-word phrase + 50-word description)").action(async () => {
|
|
5514
|
-
await geoIdentity();
|
|
5515
|
-
});
|
|
5516
|
-
geo.command("schema").description("Generate JSON-LD Organization and Service schemas").option("-o, --output <file>", "Write schema to file").option("-a, --apply", "Insert schema into homepage <head>").action(async (options) => {
|
|
5517
|
-
await geoSchema(options);
|
|
5518
|
-
});
|
|
5519
|
-
geo.command("faq").description("Generate FAQPage JSON-LD schema").option("-o, --output <file>", "Write FAQ schema to file").action(async (options) => {
|
|
5520
|
-
await geoFaq(options);
|
|
5521
|
-
});
|
|
5522
|
-
geo.command("audit").description("Audit GEO compliance (identity, schemas, placement)").action(async () => {
|
|
5523
|
-
await geoAudit();
|
|
5524
|
-
});
|
|
5525
|
-
geo.action(async () => {
|
|
5526
|
-
await geoAudit();
|
|
5527
|
-
});
|
|
5528
|
-
return geo;
|
|
5529
|
-
}
|
|
5530
|
-
|
|
5531
|
-
// src/cli/seo.ts
|
|
5532
|
-
import { Command as Command3 } from "commander";
|
|
5533
|
-
import chalk11 from "chalk";
|
|
5534
|
-
import { readFile as readFile7, writeFile as writeFile7, mkdir as mkdir5 } from "fs/promises";
|
|
5535
|
-
import { existsSync as existsSync10 } from "fs";
|
|
5536
|
-
import { join as join9 } from "path";
|
|
5537
|
-
import { createInterface as createInterface3 } from "readline";
|
|
5538
|
-
import { glob as glob3 } from "glob";
|
|
5539
|
-
var SEO_CHECKS = {
|
|
5540
|
-
title: {
|
|
5541
|
-
name: "Title Tag",
|
|
5542
|
-
regex: /<title[^>]*>.*?<\/title>/is,
|
|
5543
|
-
required: true,
|
|
5544
|
-
recommendation: "Add a <title> tag with 50-60 characters describing the page"
|
|
5545
|
-
},
|
|
5546
|
-
metaDescription: {
|
|
5547
|
-
name: "Meta Description",
|
|
5548
|
-
regex: /<meta[^>]*name=["']description["'][^>]*>/i,
|
|
5549
|
-
required: true,
|
|
5550
|
-
recommendation: 'Add <meta name="description" content="..."> with 150-160 characters'
|
|
5551
|
-
},
|
|
5552
|
-
viewport: {
|
|
5553
|
-
name: "Viewport Meta",
|
|
5554
|
-
regex: /<meta[^>]*name=["']viewport["'][^>]*>/i,
|
|
5555
|
-
required: true,
|
|
5556
|
-
recommendation: 'Add <meta name="viewport" content="width=device-width, initial-scale=1">'
|
|
5557
|
-
},
|
|
5558
|
-
canonical: {
|
|
5559
|
-
name: "Canonical URL",
|
|
5560
|
-
regex: /<link[^>]*rel=["']canonical["'][^>]*>/i,
|
|
5561
|
-
required: false,
|
|
5562
|
-
recommendation: 'Add <link rel="canonical" href="https://..."> to specify preferred URL'
|
|
5563
|
-
},
|
|
5564
|
-
robots: {
|
|
5565
|
-
name: "Robots Meta",
|
|
5566
|
-
regex: /<meta[^>]*name=["']robots["'][^>]*>/i,
|
|
5567
|
-
required: false,
|
|
5568
|
-
recommendation: 'Add <meta name="robots" content="index, follow"> for crawl directives'
|
|
5569
|
-
},
|
|
5570
|
-
ogTitle: {
|
|
5571
|
-
name: "Open Graph Title",
|
|
5572
|
-
regex: /<meta[^>]*property=["']og:title["'][^>]*>/i,
|
|
5573
|
-
required: false,
|
|
5574
|
-
recommendation: 'Add <meta property="og:title" content="..."> for social sharing'
|
|
5575
|
-
},
|
|
5576
|
-
ogDescription: {
|
|
5577
|
-
name: "Open Graph Description",
|
|
5578
|
-
regex: /<meta[^>]*property=["']og:description["'][^>]*>/i,
|
|
5579
|
-
required: false,
|
|
5580
|
-
recommendation: 'Add <meta property="og:description" content="..."> for social sharing'
|
|
5581
|
-
},
|
|
5582
|
-
ogImage: {
|
|
5583
|
-
name: "Open Graph Image",
|
|
5584
|
-
regex: /<meta[^>]*property=["']og:image["'][^>]*>/i,
|
|
5585
|
-
required: false,
|
|
5586
|
-
recommendation: 'Add <meta property="og:image" content="https://..."> (1200x630px recommended)'
|
|
5587
|
-
},
|
|
5588
|
-
ogUrl: {
|
|
5589
|
-
name: "Open Graph URL",
|
|
5590
|
-
regex: /<meta[^>]*property=["']og:url["'][^>]*>/i,
|
|
5591
|
-
required: false,
|
|
5592
|
-
recommendation: 'Add <meta property="og:url" content="https://..."> for canonical social URL'
|
|
5593
|
-
},
|
|
5594
|
-
twitterCard: {
|
|
5595
|
-
name: "Twitter Card",
|
|
5596
|
-
regex: /<meta[^>]*name=["']twitter:card["'][^>]*>/i,
|
|
5597
|
-
required: false,
|
|
5598
|
-
recommendation: 'Add <meta name="twitter:card" content="summary_large_image">'
|
|
5599
|
-
},
|
|
5600
|
-
twitterTitle: {
|
|
5601
|
-
name: "Twitter Title",
|
|
5602
|
-
regex: /<meta[^>]*name=["']twitter:title["'][^>]*>/i,
|
|
5603
|
-
required: false,
|
|
5604
|
-
recommendation: 'Add <meta name="twitter:title" content="...">'
|
|
5605
|
-
},
|
|
5606
|
-
twitterDescription: {
|
|
5607
|
-
name: "Twitter Description",
|
|
5608
|
-
regex: /<meta[^>]*name=["']twitter:description["'][^>]*>/i,
|
|
5609
|
-
required: false,
|
|
5610
|
-
recommendation: 'Add <meta name="twitter:description" content="...">'
|
|
5611
|
-
},
|
|
5612
|
-
twitterImage: {
|
|
5613
|
-
name: "Twitter Image",
|
|
5614
|
-
regex: /<meta[^>]*name=["']twitter:image["'][^>]*>/i,
|
|
5615
|
-
required: false,
|
|
5616
|
-
recommendation: 'Add <meta name="twitter:image" content="https://...">'
|
|
5617
|
-
}
|
|
5618
|
-
};
|
|
5619
|
-
function createPrompt3() {
|
|
5620
|
-
const rl = createInterface3({
|
|
5621
|
-
input: process.stdin,
|
|
5622
|
-
output: process.stdout
|
|
5623
|
-
});
|
|
5624
|
-
return {
|
|
5625
|
-
ask: (question) => new Promise((resolve) => {
|
|
5626
|
-
rl.question(question, resolve);
|
|
5627
|
-
}),
|
|
5628
|
-
close: () => rl.close()
|
|
5629
|
-
};
|
|
5630
|
-
}
|
|
5631
|
-
async function getWebFiles() {
|
|
5632
|
-
const patterns = [
|
|
5633
|
-
"**/*.html",
|
|
5634
|
-
"**/*.htm",
|
|
5635
|
-
"**/*.astro",
|
|
5636
|
-
"**/*.jsx",
|
|
5637
|
-
"**/*.tsx",
|
|
5638
|
-
"**/*.vue",
|
|
5639
|
-
"**/*.svelte"
|
|
5640
|
-
];
|
|
5641
|
-
const ignorePatterns = ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.next/**"];
|
|
5642
|
-
let allFiles = [];
|
|
5643
|
-
for (const pattern of patterns) {
|
|
5644
|
-
const files = await glob3(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
5645
|
-
allFiles = allFiles.concat(files.map((f) => join9(process.cwd(), f)));
|
|
5646
|
-
}
|
|
5647
|
-
return allFiles;
|
|
5648
|
-
}
|
|
5649
|
-
async function scanForSeoIssues(files) {
|
|
5650
|
-
const issues = [];
|
|
5651
|
-
for (const file of files) {
|
|
5652
|
-
try {
|
|
5653
|
-
const content = await readFile7(file, "utf-8");
|
|
5654
|
-
const relativePath = file.replace(process.cwd() + "/", "");
|
|
5655
|
-
for (const [checkId, check] of Object.entries(SEO_CHECKS)) {
|
|
5656
|
-
const hasTag = check.regex.test(content);
|
|
5657
|
-
if (!hasTag) {
|
|
5658
|
-
const severity = check.required ? "critical" : checkId.startsWith("og") || checkId.startsWith("twitter") ? "info" : "warning";
|
|
5659
|
-
issues.push({
|
|
5660
|
-
file: relativePath,
|
|
5661
|
-
issue: `Missing ${check.name}`,
|
|
5662
|
-
recommendation: check.recommendation,
|
|
5663
|
-
severity
|
|
5664
|
-
});
|
|
5665
|
-
}
|
|
5666
|
-
}
|
|
5667
|
-
} catch {
|
|
5668
|
-
}
|
|
5669
|
-
}
|
|
5670
|
-
return issues;
|
|
5671
|
-
}
|
|
5672
|
-
function findHeadInsertionPoint(content) {
|
|
5673
|
-
const headMatch = content.match(/<head[^>]*>/i);
|
|
5674
|
-
if (headMatch && headMatch.index !== void 0) {
|
|
5675
|
-
const afterHead = headMatch.index + headMatch[0].length;
|
|
5676
|
-
const nextLineMatch = content.slice(afterHead).match(/\n(\s*)/);
|
|
5677
|
-
const indent = nextLineMatch?.[1] ?? " ";
|
|
5678
|
-
return { index: afterHead, indent };
|
|
5679
|
-
}
|
|
5680
|
-
return null;
|
|
5681
|
-
}
|
|
5682
|
-
async function seoCheck(options) {
|
|
5683
|
-
console.log(chalk11.blue("\n\u{1F50D} SEO Check\n"));
|
|
5684
|
-
console.log(chalk11.dim("Scanning for SEO issues...\n"));
|
|
5685
|
-
const files = await getWebFiles();
|
|
5686
|
-
if (files.length === 0) {
|
|
5687
|
-
console.log(chalk11.yellow("No web files found to check."));
|
|
5688
|
-
return;
|
|
5689
|
-
}
|
|
5690
|
-
console.log(chalk11.dim(`Scanning ${files.length} files...
|
|
5691
|
-
`));
|
|
5692
|
-
const issues = await scanForSeoIssues(files);
|
|
5693
|
-
const archonDir = join9(process.cwd(), ".archon");
|
|
5694
|
-
if (!existsSync10(archonDir)) {
|
|
5695
|
-
await mkdir5(archonDir, { recursive: true });
|
|
5696
|
-
}
|
|
5697
|
-
const report = {
|
|
5698
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5699
|
-
totalFiles: files.length,
|
|
5700
|
-
issuesFound: issues.length,
|
|
5701
|
-
issues,
|
|
5702
|
-
passed: issues.length === 0
|
|
5703
|
-
};
|
|
5704
|
-
await writeFile7(join9(archonDir, "seo-report.json"), JSON.stringify(report, null, 2));
|
|
5705
|
-
if (issues.length === 0) {
|
|
5706
|
-
console.log(chalk11.green("\u2705 SEO Check Passed\n"));
|
|
5707
|
-
console.log(chalk11.dim("All essential meta tags are present."));
|
|
5708
|
-
return;
|
|
5709
|
-
}
|
|
5710
|
-
console.log(chalk11.yellow(`[!] ${issues.length} SEO Issues Found
|
|
5711
|
-
`));
|
|
5712
|
-
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
5713
|
-
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
5714
|
-
const infoCount = issues.filter((i) => i.severity === "info").length;
|
|
5715
|
-
console.log(chalk11.dim("Summary:"));
|
|
5716
|
-
if (criticalCount > 0) console.log(chalk11.red(` \u2022 ${criticalCount} critical`));
|
|
5717
|
-
if (warningCount > 0) console.log(chalk11.yellow(` \u2022 ${warningCount} warning`));
|
|
5718
|
-
if (infoCount > 0) console.log(chalk11.dim(` \u2022 ${infoCount} info`));
|
|
5719
|
-
console.log();
|
|
5720
|
-
console.log(chalk11.bold("File | Issue | Recommendation"));
|
|
5721
|
-
console.log(chalk11.dim("\u2500".repeat(100)));
|
|
5722
|
-
const issuesByFile = /* @__PURE__ */ new Map();
|
|
5723
|
-
for (const issue of issues) {
|
|
5724
|
-
const existing = issuesByFile.get(issue.file) || [];
|
|
5725
|
-
existing.push(issue);
|
|
5726
|
-
issuesByFile.set(issue.file, existing);
|
|
5727
|
-
}
|
|
5728
|
-
for (const [file, fileIssues] of issuesByFile) {
|
|
5729
|
-
const displayFile = file.length > 30 ? "..." + file.slice(-27) : file.padEnd(30);
|
|
5730
|
-
for (const issue of fileIssues.slice(0, options.verbose ? 20 : 5)) {
|
|
5731
|
-
const color = issue.severity === "critical" ? chalk11.red : issue.severity === "warning" ? chalk11.yellow : chalk11.dim;
|
|
5732
|
-
const displayIssue = issue.issue.padEnd(24).slice(0, 24);
|
|
5733
|
-
const displayRec = issue.recommendation.slice(0, 50);
|
|
5734
|
-
console.log(color(`${displayFile} | ${displayIssue} | ${displayRec}`));
|
|
5735
|
-
}
|
|
5736
|
-
if (!options.verbose && fileIssues.length > 5) {
|
|
5737
|
-
console.log(chalk11.dim(`${"".padEnd(30)} | ... and ${fileIssues.length - 5} more issues`));
|
|
5738
|
-
}
|
|
5739
|
-
}
|
|
5740
|
-
console.log(chalk11.dim(`
|
|
5741
|
-
Full report saved to: .archon/seo-report.json`));
|
|
5742
|
-
}
|
|
5743
|
-
async function seoFix(options) {
|
|
5744
|
-
const prompt3 = createPrompt3();
|
|
5745
|
-
try {
|
|
5746
|
-
console.log(chalk11.blue("\n\u{1F527} SEO Auto-Fix\n"));
|
|
5747
|
-
const reportPath = join9(process.cwd(), ".archon/seo-report.json");
|
|
5748
|
-
if (!existsSync10(reportPath)) {
|
|
5749
|
-
console.log(chalk11.yellow("No SEO report found. Running check first...\n"));
|
|
5750
|
-
await seoCheck({});
|
|
5751
|
-
}
|
|
5752
|
-
const reportContent = await readFile7(reportPath, "utf-8");
|
|
5753
|
-
const report = JSON.parse(reportContent);
|
|
5754
|
-
if (report.passed || report.issues.length === 0) {
|
|
5755
|
-
console.log(chalk11.green("No issues to fix!"));
|
|
5756
|
-
return;
|
|
5757
|
-
}
|
|
5758
|
-
const issuesByFile = /* @__PURE__ */ new Map();
|
|
5759
|
-
for (const issue of report.issues) {
|
|
5760
|
-
const existing = issuesByFile.get(issue.file) || [];
|
|
5761
|
-
existing.push(issue);
|
|
5762
|
-
issuesByFile.set(issue.file, existing);
|
|
5763
|
-
}
|
|
5764
|
-
let totalFixes = 0;
|
|
5765
|
-
for (const [file, fileIssues] of issuesByFile) {
|
|
5766
|
-
const filePath = join9(process.cwd(), file);
|
|
5767
|
-
let content;
|
|
5768
|
-
try {
|
|
5769
|
-
content = await readFile7(filePath, "utf-8");
|
|
5770
|
-
} catch {
|
|
5771
|
-
continue;
|
|
5772
|
-
}
|
|
5773
|
-
const insertPoint = findHeadInsertionPoint(content);
|
|
5774
|
-
if (!insertPoint) {
|
|
5775
|
-
console.log(chalk11.yellow(` ${file}: No <head> tag found, skipping`));
|
|
5776
|
-
continue;
|
|
5777
|
-
}
|
|
5778
|
-
const tagsToAdd = [];
|
|
5779
|
-
for (const issue of fileIssues) {
|
|
5780
|
-
if (issue.issue === "Missing Title Tag") {
|
|
5781
|
-
tagsToAdd.push("<title>Page Title - Site Name</title>");
|
|
5782
|
-
} else if (issue.issue === "Missing Meta Description") {
|
|
5783
|
-
tagsToAdd.push('<meta name="description" content="Add your page description here (150-160 characters recommended)">');
|
|
5784
|
-
} else if (issue.issue === "Missing Viewport Meta") {
|
|
5785
|
-
tagsToAdd.push('<meta name="viewport" content="width=device-width, initial-scale=1">');
|
|
5786
|
-
} else if (issue.issue === "Missing Canonical URL") {
|
|
5787
|
-
tagsToAdd.push('<link rel="canonical" href="https://example.com/page">');
|
|
5788
|
-
} else if (issue.issue === "Missing Robots Meta") {
|
|
5789
|
-
tagsToAdd.push('<meta name="robots" content="index, follow">');
|
|
5790
|
-
}
|
|
5791
|
-
}
|
|
5792
|
-
if (tagsToAdd.length === 0) continue;
|
|
5793
|
-
const newContent = content.slice(0, insertPoint.index) + "\n" + tagsToAdd.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
5794
|
-
console.log(chalk11.cyan(`
|
|
5795
|
-
${file}:`));
|
|
5796
|
-
for (const tag of tagsToAdd) {
|
|
5797
|
-
console.log(chalk11.green(` + ${tag.slice(0, 70)}${tag.length > 70 ? "..." : ""}`));
|
|
5798
|
-
}
|
|
5799
|
-
if (!options.dryRun) {
|
|
5800
|
-
const confirm = await prompt3.ask(chalk11.dim(" Apply these changes? (y/N): "));
|
|
5801
|
-
if (confirm.toLowerCase() === "y") {
|
|
5802
|
-
await writeFile7(filePath, newContent);
|
|
5803
|
-
totalFixes += tagsToAdd.length;
|
|
5804
|
-
console.log(chalk11.green(" \u2713 Applied"));
|
|
5805
|
-
} else {
|
|
5806
|
-
console.log(chalk11.dim(" Skipped"));
|
|
5807
|
-
}
|
|
5808
|
-
} else {
|
|
5809
|
-
totalFixes += tagsToAdd.length;
|
|
5810
|
-
}
|
|
5811
|
-
}
|
|
5812
|
-
if (totalFixes === 0) {
|
|
5813
|
-
console.log(chalk11.dim("\nNo auto-fixable issues found. Some issues require manual configuration."));
|
|
5814
|
-
} else if (options.dryRun) {
|
|
5815
|
-
console.log(chalk11.yellow(`
|
|
5816
|
-
${totalFixes} fixes would be applied. Run without --dry-run to apply.`));
|
|
5817
|
-
} else {
|
|
5818
|
-
console.log(chalk11.green(`
|
|
5819
|
-
\u2705 Applied ${totalFixes} fixes.`));
|
|
5820
|
-
console.log(chalk11.dim('Run "archon seo check" to verify fixes.'));
|
|
5821
|
-
}
|
|
5822
|
-
} finally {
|
|
5823
|
-
prompt3.close();
|
|
5824
|
-
}
|
|
5825
|
-
}
|
|
5826
|
-
async function seoOpenGraph(options) {
|
|
5827
|
-
const prompt3 = createPrompt3();
|
|
5828
|
-
try {
|
|
5829
|
-
console.log(chalk11.blue("\n\u{1F4F1} Add Open Graph Tags\n"));
|
|
5830
|
-
let targetFile;
|
|
5831
|
-
if (options.file) {
|
|
5832
|
-
targetFile = options.file.startsWith("/") ? options.file : join9(process.cwd(), options.file);
|
|
5833
|
-
} else {
|
|
5834
|
-
const files = await getWebFiles();
|
|
5835
|
-
if (files.length === 0) {
|
|
5836
|
-
console.log(chalk11.yellow("No web files found."));
|
|
5837
|
-
return;
|
|
5838
|
-
}
|
|
5839
|
-
console.log(chalk11.dim("Available files:"));
|
|
5840
|
-
files.slice(0, 10).forEach((f, i) => {
|
|
5841
|
-
console.log(` ${i + 1}) ${f.replace(process.cwd() + "/", "")}`);
|
|
5842
|
-
});
|
|
5843
|
-
if (files.length > 10) {
|
|
5844
|
-
console.log(chalk11.dim(` ... and ${files.length - 10} more`));
|
|
5845
|
-
}
|
|
5846
|
-
const fileChoice = await prompt3.ask("\nEnter file path or number: ");
|
|
5847
|
-
const num = parseInt(fileChoice, 10);
|
|
5848
|
-
if (num > 0 && num <= files.length) {
|
|
5849
|
-
targetFile = files[num - 1] ?? "";
|
|
5850
|
-
} else {
|
|
5851
|
-
targetFile = fileChoice.startsWith("/") ? fileChoice : join9(process.cwd(), fileChoice);
|
|
5852
|
-
}
|
|
5853
|
-
}
|
|
5854
|
-
if (!existsSync10(targetFile)) {
|
|
5855
|
-
console.log(chalk11.red(`File not found: ${targetFile}`));
|
|
5856
|
-
return;
|
|
5857
|
-
}
|
|
5858
|
-
const ogTitle = await prompt3.ask("og:title (page title for social): ");
|
|
5859
|
-
const ogDescription = await prompt3.ask("og:description (page description): ");
|
|
5860
|
-
const ogImage = await prompt3.ask("og:image (full URL to image): ");
|
|
5861
|
-
const ogUrl = await prompt3.ask("og:url (canonical page URL): ");
|
|
5862
|
-
const tags = [
|
|
5863
|
-
`<meta property="og:type" content="website">`,
|
|
5864
|
-
`<meta property="og:title" content="${ogTitle}">`,
|
|
5865
|
-
`<meta property="og:description" content="${ogDescription}">`,
|
|
5866
|
-
`<meta property="og:image" content="${ogImage}">`,
|
|
5867
|
-
`<meta property="og:url" content="${ogUrl}">`
|
|
5868
|
-
];
|
|
5869
|
-
const content = await readFile7(targetFile, "utf-8");
|
|
5870
|
-
const insertPoint = findHeadInsertionPoint(content);
|
|
5871
|
-
if (!insertPoint) {
|
|
5872
|
-
console.log(chalk11.yellow("No <head> tag found. Add these tags manually:"));
|
|
5873
|
-
tags.forEach((tag) => console.log(chalk11.cyan(` ${tag}`)));
|
|
5874
|
-
return;
|
|
5875
|
-
}
|
|
5876
|
-
console.log(chalk11.dim("\nTags to add:"));
|
|
5877
|
-
tags.forEach((tag) => console.log(chalk11.green(` + ${tag}`)));
|
|
5878
|
-
const confirm = await prompt3.ask("\nApply changes? (y/N): ");
|
|
5879
|
-
if (confirm.toLowerCase() === "y") {
|
|
5880
|
-
const newContent = content.slice(0, insertPoint.index) + "\n" + tags.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
5881
|
-
await writeFile7(targetFile, newContent);
|
|
5882
|
-
console.log(chalk11.green("\n\u2705 Open Graph tags added."));
|
|
5883
|
-
} else {
|
|
5884
|
-
console.log(chalk11.dim("Cancelled."));
|
|
5885
|
-
}
|
|
5886
|
-
} finally {
|
|
5887
|
-
prompt3.close();
|
|
5888
|
-
}
|
|
5889
|
-
}
|
|
5890
|
-
async function seoTwitter(options) {
|
|
5891
|
-
const prompt3 = createPrompt3();
|
|
5892
|
-
try {
|
|
5893
|
-
console.log(chalk11.blue("\n\u{1F426} Add Twitter Card Tags\n"));
|
|
5894
|
-
let targetFile;
|
|
5895
|
-
if (options.file) {
|
|
5896
|
-
targetFile = options.file.startsWith("/") ? options.file : join9(process.cwd(), options.file);
|
|
5897
|
-
} else {
|
|
5898
|
-
const files = await getWebFiles();
|
|
5899
|
-
if (files.length === 0) {
|
|
5900
|
-
console.log(chalk11.yellow("No web files found."));
|
|
5901
|
-
return;
|
|
5902
|
-
}
|
|
5903
|
-
console.log(chalk11.dim("Available files:"));
|
|
5904
|
-
files.slice(0, 10).forEach((f, i) => {
|
|
5905
|
-
console.log(` ${i + 1}) ${f.replace(process.cwd() + "/", "")}`);
|
|
5906
|
-
});
|
|
5907
|
-
if (files.length > 10) {
|
|
5908
|
-
console.log(chalk11.dim(` ... and ${files.length - 10} more`));
|
|
5909
|
-
}
|
|
5910
|
-
const fileChoice = await prompt3.ask("\nEnter file path or number: ");
|
|
5911
|
-
const num = parseInt(fileChoice, 10);
|
|
5912
|
-
if (num > 0 && num <= files.length) {
|
|
5913
|
-
targetFile = files[num - 1] ?? "";
|
|
5914
|
-
} else {
|
|
5915
|
-
targetFile = fileChoice.startsWith("/") ? fileChoice : join9(process.cwd(), fileChoice);
|
|
5916
|
-
}
|
|
5917
|
-
}
|
|
5918
|
-
if (!existsSync10(targetFile)) {
|
|
5919
|
-
console.log(chalk11.red(`File not found: ${targetFile}`));
|
|
5920
|
-
return;
|
|
5921
|
-
}
|
|
5922
|
-
console.log(chalk11.dim("Card types: summary, summary_large_image, app, player"));
|
|
5923
|
-
const cardType = await prompt3.ask("twitter:card type (default: summary_large_image): ") || "summary_large_image";
|
|
5924
|
-
const twitterTitle = await prompt3.ask("twitter:title: ");
|
|
5925
|
-
const twitterDescription = await prompt3.ask("twitter:description: ");
|
|
5926
|
-
const twitterImage = await prompt3.ask("twitter:image (full URL): ");
|
|
5927
|
-
const tags = [
|
|
5928
|
-
`<meta name="twitter:card" content="${cardType}">`,
|
|
5929
|
-
`<meta name="twitter:title" content="${twitterTitle}">`,
|
|
5930
|
-
`<meta name="twitter:description" content="${twitterDescription}">`,
|
|
5931
|
-
`<meta name="twitter:image" content="${twitterImage}">`
|
|
5932
|
-
];
|
|
5933
|
-
const content = await readFile7(targetFile, "utf-8");
|
|
5934
|
-
const insertPoint = findHeadInsertionPoint(content);
|
|
5935
|
-
if (!insertPoint) {
|
|
5936
|
-
console.log(chalk11.yellow("No <head> tag found. Add these tags manually:"));
|
|
5937
|
-
tags.forEach((tag) => console.log(chalk11.cyan(` ${tag}`)));
|
|
5938
|
-
return;
|
|
5939
|
-
}
|
|
5940
|
-
console.log(chalk11.dim("\nTags to add:"));
|
|
5941
|
-
tags.forEach((tag) => console.log(chalk11.green(` + ${tag}`)));
|
|
5942
|
-
const confirm = await prompt3.ask("\nApply changes? (y/N): ");
|
|
5943
|
-
if (confirm.toLowerCase() === "y") {
|
|
5944
|
-
const newContent = content.slice(0, insertPoint.index) + "\n" + tags.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
5945
|
-
await writeFile7(targetFile, newContent);
|
|
5946
|
-
console.log(chalk11.green("\n\u2705 Twitter Card tags added."));
|
|
5947
|
-
} else {
|
|
5948
|
-
console.log(chalk11.dim("Cancelled."));
|
|
5949
|
-
}
|
|
5950
|
-
} finally {
|
|
5951
|
-
prompt3.close();
|
|
5952
|
-
}
|
|
5953
|
-
}
|
|
5954
|
-
function createSeoCommand() {
|
|
5955
|
-
const seo = new Command3("seo").description("SEO checking and meta tag management").addHelpText(
|
|
5956
|
-
"after",
|
|
5957
|
-
`
|
|
5958
|
-
Examples:
|
|
5959
|
-
archon seo check Scan files for missing SEO meta tags
|
|
5960
|
-
archon seo fix Auto-fix missing essential meta tags
|
|
5961
|
-
archon seo fix --dry-run Preview fixes without applying
|
|
5962
|
-
archon seo og Add Open Graph tags interactively
|
|
5963
|
-
archon seo og --file index.html
|
|
5964
|
-
archon seo twitter Add Twitter Card tags interactively
|
|
5965
|
-
`
|
|
5966
|
-
);
|
|
5967
|
-
seo.command("check").description("Scan files for missing SEO meta tags").option("-v, --verbose", "Show all issues (not truncated)").action(async (options) => {
|
|
5968
|
-
try {
|
|
5969
|
-
await seoCheck(options);
|
|
5970
|
-
process.exit(0);
|
|
5971
|
-
} catch (error) {
|
|
5972
|
-
console.error(chalk11.red("Error:"), error instanceof Error ? error.message : error);
|
|
5973
|
-
process.exit(1);
|
|
5974
|
-
}
|
|
5975
|
-
});
|
|
5976
|
-
seo.command("fix").description("Auto-fix missing essential meta tags").option("--dry-run", "Preview changes without applying").action(async (options) => {
|
|
5977
|
-
try {
|
|
5978
|
-
await seoFix(options);
|
|
5979
|
-
process.exit(0);
|
|
5980
|
-
} catch (error) {
|
|
5981
|
-
console.error(chalk11.red("Error:"), error instanceof Error ? error.message : error);
|
|
5982
|
-
process.exit(1);
|
|
5983
|
-
}
|
|
5984
|
-
});
|
|
5985
|
-
seo.command("og").description("Add Open Graph meta tags to a file").option("-f, --file <path>", "Target file path").action(async (options) => {
|
|
5986
|
-
try {
|
|
5987
|
-
await seoOpenGraph(options);
|
|
5988
|
-
process.exit(0);
|
|
5989
|
-
} catch (error) {
|
|
5990
|
-
console.error(chalk11.red("Error:"), error instanceof Error ? error.message : error);
|
|
5991
|
-
process.exit(1);
|
|
5992
|
-
}
|
|
5993
|
-
});
|
|
5994
|
-
seo.command("twitter").description("Add Twitter Card meta tags to a file").option("-f, --file <path>", "Target file path").action(async (options) => {
|
|
5995
|
-
try {
|
|
5996
|
-
await seoTwitter(options);
|
|
5997
|
-
process.exit(0);
|
|
5998
|
-
} catch (error) {
|
|
5999
|
-
console.error(chalk11.red("Error:"), error instanceof Error ? error.message : error);
|
|
6000
|
-
process.exit(1);
|
|
6001
|
-
}
|
|
6002
|
-
});
|
|
6003
|
-
return seo;
|
|
6004
|
-
}
|
|
6005
|
-
|
|
6006
|
-
// src/cli/session.ts
|
|
6007
|
-
import chalk12 from "chalk";
|
|
6008
|
-
import ora2 from "ora";
|
|
6009
|
-
import os from "os";
|
|
6010
|
-
import { readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
6011
|
-
import { existsSync as existsSync11 } from "fs";
|
|
6012
|
-
import { join as join10 } from "path";
|
|
6013
|
-
function getSupabaseClient2(accessToken) {
|
|
6014
|
-
return createAuthedSupabaseClient(SUPABASE_URL, SUPABASE_ANON_KEY, accessToken);
|
|
6015
|
-
}
|
|
6016
|
-
function getDeviceName() {
|
|
6017
|
-
return `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
6018
|
-
}
|
|
6019
|
-
async function getProjectInfo(cwd) {
|
|
6020
|
-
const archonConfigPath = join10(cwd, ".archon", "config.yaml");
|
|
6021
|
-
const packageJsonPath = join10(cwd, "package.json");
|
|
6022
|
-
let projectName = "Unknown Project";
|
|
6023
|
-
if (existsSync11(packageJsonPath)) {
|
|
6024
|
-
try {
|
|
6025
|
-
const pkg = JSON.parse(await readFile8(packageJsonPath, "utf-8"));
|
|
6026
|
-
projectName = pkg.name || projectName;
|
|
6027
|
-
} catch {
|
|
6028
|
-
}
|
|
6029
|
-
}
|
|
6030
|
-
if (!existsSync11(archonConfigPath)) {
|
|
6031
|
-
return null;
|
|
6032
|
-
}
|
|
6033
|
-
return { name: projectName, path: cwd };
|
|
6034
|
-
}
|
|
6035
|
-
async function getCurrentAtomId(cwd) {
|
|
6036
|
-
const stateFile = join10(cwd, ".archon", "state.json");
|
|
6037
|
-
if (!existsSync11(stateFile)) return null;
|
|
6038
|
-
try {
|
|
6039
|
-
const state = JSON.parse(await readFile8(stateFile, "utf-8"));
|
|
6040
|
-
return state.currentAtomId || null;
|
|
6041
|
-
} catch {
|
|
6042
|
-
return null;
|
|
6043
|
-
}
|
|
6044
|
-
}
|
|
6045
|
-
async function getPendingAtoms(cwd) {
|
|
6046
|
-
const stateFile = join10(cwd, ".archon", "state.json");
|
|
6047
|
-
if (!existsSync11(stateFile)) return [];
|
|
6048
|
-
try {
|
|
6049
|
-
const state = JSON.parse(await readFile8(stateFile, "utf-8"));
|
|
6050
|
-
return state.pendingAtoms || [];
|
|
6051
|
-
} catch {
|
|
6052
|
-
return [];
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
async function getFileContent(path2) {
|
|
6056
|
-
if (!existsSync11(path2)) return null;
|
|
6057
|
-
try {
|
|
6058
|
-
return await readFile8(path2, "utf-8");
|
|
6059
|
-
} catch {
|
|
6060
|
-
return null;
|
|
6061
|
-
}
|
|
6062
|
-
}
|
|
6063
|
-
async function saveSession(name) {
|
|
6064
|
-
const spinner = ora2("Saving session...").start();
|
|
6065
|
-
const cwd = process.cwd();
|
|
6066
|
-
try {
|
|
6067
|
-
const config = await loadConfig();
|
|
6068
|
-
if (!config.accessToken || !config.userId) {
|
|
6069
|
-
spinner.fail("Not logged in. Run: archon login");
|
|
6070
|
-
return;
|
|
6071
|
-
}
|
|
6072
|
-
const projectInfo = await getProjectInfo(cwd);
|
|
6073
|
-
if (!projectInfo) {
|
|
6074
|
-
spinner.fail("Not an ArchonDev project. Run: archon init");
|
|
6075
|
-
return;
|
|
6076
|
-
}
|
|
6077
|
-
const supabase = getSupabaseClient2(config.accessToken);
|
|
6078
|
-
const { data: profile } = await supabase.from("user_profiles").select("id").eq("auth_id", config.userId).single();
|
|
6079
|
-
if (!profile) {
|
|
6080
|
-
spinner.fail("Could not find user profile");
|
|
5067
|
+
const supabase = getSupabaseClient2(config.accessToken);
|
|
5068
|
+
const { data: profile } = await supabase.from("user_profiles").select("id").eq("auth_id", config.userId).single();
|
|
5069
|
+
if (!profile) {
|
|
5070
|
+
spinner.fail("Could not find user profile");
|
|
6081
5071
|
return;
|
|
6082
5072
|
}
|
|
6083
5073
|
const currentAtomId = await getCurrentAtomId(cwd);
|
|
6084
5074
|
const pendingAtoms = await getPendingAtoms(cwd);
|
|
6085
|
-
const progressSnapshot = await getFileContent(
|
|
6086
|
-
const architectureSnapshot = await getFileContent(
|
|
5075
|
+
const progressSnapshot = await getFileContent(join8(cwd, "progress.txt"));
|
|
5076
|
+
const architectureSnapshot = await getFileContent(join8(cwd, "ARCHITECTURE.md"));
|
|
6087
5077
|
const sessionData = {
|
|
6088
5078
|
user_id: profile.id,
|
|
6089
5079
|
project_name: name || projectInfo.name,
|
|
@@ -6100,13 +5090,13 @@ async function saveSession(name) {
|
|
|
6100
5090
|
spinner.fail(`Failed to save session: ${error.message}`);
|
|
6101
5091
|
return;
|
|
6102
5092
|
}
|
|
6103
|
-
spinner.succeed(
|
|
5093
|
+
spinner.succeed(chalk9.green("Session saved!"));
|
|
6104
5094
|
console.log();
|
|
6105
|
-
console.log(` ID: ${
|
|
5095
|
+
console.log(` ID: ${chalk9.cyan(session.id)}`);
|
|
6106
5096
|
console.log(` Project: ${session.project_name}`);
|
|
6107
5097
|
console.log(` Device: ${session.last_device}`);
|
|
6108
5098
|
console.log();
|
|
6109
|
-
console.log(
|
|
5099
|
+
console.log(chalk9.dim(" Resume on another device: archon session resume " + session.id));
|
|
6110
5100
|
console.log();
|
|
6111
5101
|
} catch (err) {
|
|
6112
5102
|
spinner.fail("Error saving session");
|
|
@@ -6134,23 +5124,23 @@ async function listSessions() {
|
|
|
6134
5124
|
}
|
|
6135
5125
|
spinner.stop();
|
|
6136
5126
|
if (!sessions || sessions.length === 0) {
|
|
6137
|
-
console.log(
|
|
6138
|
-
console.log(
|
|
5127
|
+
console.log(chalk9.yellow("\nNo saved sessions found.\n"));
|
|
5128
|
+
console.log(chalk9.dim(" Save a session: archon session save [name]\n"));
|
|
6139
5129
|
return;
|
|
6140
5130
|
}
|
|
6141
5131
|
console.log();
|
|
6142
|
-
console.log(
|
|
5132
|
+
console.log(chalk9.bold("\u{1F4C2} Saved Sessions"));
|
|
6143
5133
|
console.log();
|
|
6144
5134
|
for (const session of sessions) {
|
|
6145
5135
|
const date = new Date(session.updated_at).toLocaleDateString();
|
|
6146
|
-
const atomInfo = session.current_atom_id ?
|
|
5136
|
+
const atomInfo = session.current_atom_id ? chalk9.dim(` (atom: ${session.current_atom_id})`) : "";
|
|
6147
5137
|
console.log(
|
|
6148
|
-
` ${
|
|
5138
|
+
` ${chalk9.cyan(session.id.slice(0, 8))} ${session.project_name}${atomInfo}`
|
|
6149
5139
|
);
|
|
6150
|
-
console.log(
|
|
5140
|
+
console.log(chalk9.dim(` ${date} from ${session.last_device || "unknown device"}`));
|
|
6151
5141
|
console.log();
|
|
6152
5142
|
}
|
|
6153
|
-
console.log(
|
|
5143
|
+
console.log(chalk9.dim(" Resume: archon session resume <id>\n"));
|
|
6154
5144
|
} catch (err) {
|
|
6155
5145
|
spinner.fail("Error fetching sessions");
|
|
6156
5146
|
console.error(err);
|
|
@@ -6177,30 +5167,30 @@ async function resumeSession(sessionId) {
|
|
|
6177
5167
|
return;
|
|
6178
5168
|
}
|
|
6179
5169
|
const session = sessions[0];
|
|
6180
|
-
const stateFile =
|
|
5170
|
+
const stateFile = join8(cwd, ".archon", "state.json");
|
|
6181
5171
|
const state = {
|
|
6182
5172
|
currentAtomId: session.current_atom_id,
|
|
6183
5173
|
pendingAtoms: session.pending_atoms,
|
|
6184
5174
|
resumedFrom: session.id,
|
|
6185
5175
|
resumedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6186
5176
|
};
|
|
6187
|
-
await
|
|
5177
|
+
await writeFile5(stateFile, JSON.stringify(state, null, 2));
|
|
6188
5178
|
if (session.progress_snapshot) {
|
|
6189
|
-
const progressPath =
|
|
6190
|
-
await
|
|
5179
|
+
const progressPath = join8(cwd, "progress.txt");
|
|
5180
|
+
await writeFile5(progressPath, session.progress_snapshot);
|
|
6191
5181
|
}
|
|
6192
5182
|
if (session.architecture_snapshot) {
|
|
6193
|
-
const archPath =
|
|
6194
|
-
await
|
|
5183
|
+
const archPath = join8(cwd, "ARCHITECTURE.md");
|
|
5184
|
+
await writeFile5(archPath, session.architecture_snapshot);
|
|
6195
5185
|
}
|
|
6196
5186
|
await supabase.from("sessions").update({ last_device: getDeviceName(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", session.id);
|
|
6197
|
-
spinner.succeed(
|
|
5187
|
+
spinner.succeed(chalk9.green("Session resumed!"));
|
|
6198
5188
|
console.log();
|
|
6199
5189
|
console.log(` Project: ${session.project_name}`);
|
|
6200
5190
|
console.log(` Current Atom: ${session.current_atom_id || "none"}`);
|
|
6201
5191
|
console.log(` Pending: ${session.pending_atoms.length} atoms`);
|
|
6202
5192
|
console.log();
|
|
6203
|
-
console.log(
|
|
5193
|
+
console.log(chalk9.dim(" Continue working: archon start"));
|
|
6204
5194
|
console.log();
|
|
6205
5195
|
} catch (err) {
|
|
6206
5196
|
spinner.fail("Error resuming session");
|
|
@@ -6216,12 +5206,12 @@ async function syncSession() {
|
|
|
6216
5206
|
spinner.fail("Not logged in. Run: archon login");
|
|
6217
5207
|
return;
|
|
6218
5208
|
}
|
|
6219
|
-
const stateFile =
|
|
6220
|
-
if (!
|
|
5209
|
+
const stateFile = join8(cwd, ".archon", "state.json");
|
|
5210
|
+
if (!existsSync9(stateFile)) {
|
|
6221
5211
|
spinner.info("No active session to sync");
|
|
6222
5212
|
return;
|
|
6223
5213
|
}
|
|
6224
|
-
const state = JSON.parse(await
|
|
5214
|
+
const state = JSON.parse(await readFile5(stateFile, "utf-8"));
|
|
6225
5215
|
if (!state.resumedFrom) {
|
|
6226
5216
|
spinner.info('Session was not resumed from cloud - use "archon session save" to create one');
|
|
6227
5217
|
return;
|
|
@@ -6229,8 +5219,8 @@ async function syncSession() {
|
|
|
6229
5219
|
const supabase = getSupabaseClient2(config.accessToken);
|
|
6230
5220
|
const currentAtomId = await getCurrentAtomId(cwd);
|
|
6231
5221
|
const pendingAtoms = await getPendingAtoms(cwd);
|
|
6232
|
-
const progressSnapshot = await getFileContent(
|
|
6233
|
-
const architectureSnapshot = await getFileContent(
|
|
5222
|
+
const progressSnapshot = await getFileContent(join8(cwd, "progress.txt"));
|
|
5223
|
+
const architectureSnapshot = await getFileContent(join8(cwd, "ARCHITECTURE.md"));
|
|
6234
5224
|
const { error } = await supabase.from("sessions").update({
|
|
6235
5225
|
current_atom_id: currentAtomId,
|
|
6236
5226
|
pending_atoms: pendingAtoms,
|
|
@@ -6243,7 +5233,7 @@ async function syncSession() {
|
|
|
6243
5233
|
spinner.fail(`Failed to sync: ${error.message}`);
|
|
6244
5234
|
return;
|
|
6245
5235
|
}
|
|
6246
|
-
spinner.succeed(
|
|
5236
|
+
spinner.succeed(chalk9.green("Session synced to cloud"));
|
|
6247
5237
|
} catch (err) {
|
|
6248
5238
|
spinner.fail("Error syncing session");
|
|
6249
5239
|
console.error(err);
|
|
@@ -6251,66 +5241,66 @@ async function syncSession() {
|
|
|
6251
5241
|
}
|
|
6252
5242
|
|
|
6253
5243
|
// src/cli/deploy.ts
|
|
6254
|
-
import
|
|
6255
|
-
import { existsSync as
|
|
6256
|
-
import { join as
|
|
5244
|
+
import chalk10 from "chalk";
|
|
5245
|
+
import { existsSync as existsSync10 } from "fs";
|
|
5246
|
+
import { join as join9 } from "path";
|
|
6257
5247
|
import { execSync as execSync3 } from "child_process";
|
|
6258
5248
|
function detectPlatform(cwd) {
|
|
6259
|
-
if (
|
|
6260
|
-
if (
|
|
6261
|
-
if (
|
|
6262
|
-
if (
|
|
6263
|
-
if (
|
|
6264
|
-
if (
|
|
5249
|
+
if (existsSync10(join9(cwd, "fly.toml"))) return "fly";
|
|
5250
|
+
if (existsSync10(join9(cwd, "vercel.json"))) return "vercel";
|
|
5251
|
+
if (existsSync10(join9(cwd, "netlify.toml"))) return "netlify";
|
|
5252
|
+
if (existsSync10(join9(cwd, "railway.json"))) return "railway";
|
|
5253
|
+
if (existsSync10(join9(cwd, "render.yaml"))) return "render";
|
|
5254
|
+
if (existsSync10(join9(cwd, "Dockerfile"))) return "fly";
|
|
6265
5255
|
return "unknown";
|
|
6266
5256
|
}
|
|
6267
5257
|
async function deploy(options) {
|
|
6268
5258
|
const cwd = process.cwd();
|
|
6269
|
-
console.log(
|
|
5259
|
+
console.log(chalk10.blue("Running pre-deploy checks..."));
|
|
6270
5260
|
const platform = options.platform ?? detectPlatform(cwd);
|
|
6271
|
-
console.log(
|
|
5261
|
+
console.log(chalk10.dim(`Detected platform: ${platform}`));
|
|
6272
5262
|
if (options.dryRun) {
|
|
6273
|
-
console.log(
|
|
5263
|
+
console.log(chalk10.dim("Dry run mode - would deploy to:"), platform);
|
|
6274
5264
|
return;
|
|
6275
5265
|
}
|
|
6276
5266
|
switch (platform) {
|
|
6277
5267
|
case "fly":
|
|
6278
|
-
console.log(
|
|
5268
|
+
console.log(chalk10.blue("Deploying to Fly.io..."));
|
|
6279
5269
|
execSync3("fly deploy", { cwd, stdio: "inherit" });
|
|
6280
5270
|
break;
|
|
6281
5271
|
case "vercel": {
|
|
6282
|
-
console.log(
|
|
5272
|
+
console.log(chalk10.blue("Deploying to Vercel..."));
|
|
6283
5273
|
const cmd = options.preview ? "vercel" : "vercel --prod";
|
|
6284
5274
|
execSync3(cmd, { cwd, stdio: "inherit" });
|
|
6285
5275
|
break;
|
|
6286
5276
|
}
|
|
6287
5277
|
case "netlify": {
|
|
6288
|
-
console.log(
|
|
5278
|
+
console.log(chalk10.blue("Deploying to Netlify..."));
|
|
6289
5279
|
const netlifyCmd = options.preview ? "netlify deploy" : "netlify deploy --prod";
|
|
6290
5280
|
execSync3(netlifyCmd, { cwd, stdio: "inherit" });
|
|
6291
5281
|
break;
|
|
6292
5282
|
}
|
|
6293
5283
|
case "railway":
|
|
6294
|
-
console.log(
|
|
5284
|
+
console.log(chalk10.blue("Deploying to Railway..."));
|
|
6295
5285
|
execSync3("railway up", { cwd, stdio: "inherit" });
|
|
6296
5286
|
break;
|
|
6297
5287
|
case "render":
|
|
6298
|
-
console.log(
|
|
6299
|
-
console.log(
|
|
5288
|
+
console.log(chalk10.blue("Deploying to Render..."));
|
|
5289
|
+
console.log(chalk10.yellow("Render deploys via git push. Push to your connected branch."));
|
|
6300
5290
|
break;
|
|
6301
5291
|
default:
|
|
6302
|
-
console.log(
|
|
6303
|
-
console.log(
|
|
5292
|
+
console.log(chalk10.yellow("Platform not detected. Please specify with --platform"));
|
|
5293
|
+
console.log(chalk10.dim("Supported: fly, vercel, netlify, railway, render"));
|
|
6304
5294
|
}
|
|
6305
5295
|
}
|
|
6306
5296
|
|
|
6307
5297
|
// src/cli/index-cmd.ts
|
|
6308
|
-
import
|
|
5298
|
+
import chalk11 from "chalk";
|
|
6309
5299
|
|
|
6310
5300
|
// src/core/indexing/local.ts
|
|
6311
|
-
import { existsSync as
|
|
6312
|
-
import { readFile as
|
|
6313
|
-
import { join as
|
|
5301
|
+
import { existsSync as existsSync11, mkdirSync } from "fs";
|
|
5302
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
5303
|
+
import { join as join10, extname } from "path";
|
|
6314
5304
|
import Database from "better-sqlite3";
|
|
6315
5305
|
var CHUNK_SIZE = 1e3;
|
|
6316
5306
|
var CHUNK_OVERLAP = 200;
|
|
@@ -6357,11 +5347,11 @@ var LocalIndexer = class {
|
|
|
6357
5347
|
};
|
|
6358
5348
|
}
|
|
6359
5349
|
async init(cwd) {
|
|
6360
|
-
const archonDir =
|
|
6361
|
-
if (!
|
|
5350
|
+
const archonDir = join10(cwd, ".archon");
|
|
5351
|
+
if (!existsSync11(archonDir)) {
|
|
6362
5352
|
mkdirSync(archonDir, { recursive: true });
|
|
6363
5353
|
}
|
|
6364
|
-
this.db = new Database(
|
|
5354
|
+
this.db = new Database(join10(cwd, this.config.dbPath));
|
|
6365
5355
|
this.db.exec(`
|
|
6366
5356
|
CREATE TABLE IF NOT EXISTS embeddings (
|
|
6367
5357
|
id INTEGER PRIMARY KEY,
|
|
@@ -6411,11 +5401,11 @@ var LocalIndexer = class {
|
|
|
6411
5401
|
if (!this.isIndexableFile(filePath)) {
|
|
6412
5402
|
return 0;
|
|
6413
5403
|
}
|
|
6414
|
-
const fullPath =
|
|
6415
|
-
if (!
|
|
5404
|
+
const fullPath = join10(cwd, filePath);
|
|
5405
|
+
if (!existsSync11(fullPath)) {
|
|
6416
5406
|
return 0;
|
|
6417
5407
|
}
|
|
6418
|
-
const content = await
|
|
5408
|
+
const content = await readFile6(fullPath, "utf-8");
|
|
6419
5409
|
const chunks = this.chunkText(content);
|
|
6420
5410
|
const deleteStmt = this.db.prepare("DELETE FROM embeddings WHERE file_path = ?");
|
|
6421
5411
|
deleteStmt.run(filePath);
|
|
@@ -6493,9 +5483,9 @@ var LocalIndexer = class {
|
|
|
6493
5483
|
|
|
6494
5484
|
// src/core/indexing/cloud.ts
|
|
6495
5485
|
import { createClient } from "@supabase/supabase-js";
|
|
6496
|
-
import { readFile as
|
|
6497
|
-
import { existsSync as
|
|
6498
|
-
import { join as
|
|
5486
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
5487
|
+
import { existsSync as existsSync12 } from "fs";
|
|
5488
|
+
import { join as join11, extname as extname2 } from "path";
|
|
6499
5489
|
import { createHash as createHash2 } from "crypto";
|
|
6500
5490
|
var CHUNK_SIZE2 = 1e3;
|
|
6501
5491
|
var CHUNK_OVERLAP2 = 200;
|
|
@@ -6622,11 +5612,11 @@ var CloudIndexer = class {
|
|
|
6622
5612
|
if (!this.isIndexableFile(filePath)) {
|
|
6623
5613
|
return 0;
|
|
6624
5614
|
}
|
|
6625
|
-
const fullPath =
|
|
6626
|
-
if (!
|
|
5615
|
+
const fullPath = join11(cwd, filePath);
|
|
5616
|
+
if (!existsSync12(fullPath)) {
|
|
6627
5617
|
return 0;
|
|
6628
5618
|
}
|
|
6629
|
-
const content = await
|
|
5619
|
+
const content = await readFile7(fullPath, "utf-8");
|
|
6630
5620
|
const fileHash = await this.computeFileHash(content);
|
|
6631
5621
|
const { data: existing } = await this.client.from("code_embeddings").select("file_hash").eq("user_id", this.userId).eq("project_id", this.config.projectId).eq("file_path", filePath).eq("chunk_index", 0).single();
|
|
6632
5622
|
if (existing && existing.file_hash === fileHash) {
|
|
@@ -6713,20 +5703,20 @@ var CloudIndexer = class {
|
|
|
6713
5703
|
};
|
|
6714
5704
|
|
|
6715
5705
|
// src/cli/index-cmd.ts
|
|
6716
|
-
import { glob
|
|
6717
|
-
import { join as
|
|
5706
|
+
import { glob } from "glob";
|
|
5707
|
+
import { join as join12, basename } from "path";
|
|
6718
5708
|
async function getCloudIndexer(cwd) {
|
|
6719
5709
|
const config = await loadConfig();
|
|
6720
5710
|
const authToken = getAuthToken(config);
|
|
6721
5711
|
if (!authToken) {
|
|
6722
|
-
console.error(
|
|
5712
|
+
console.error(chalk11.red('Not authenticated. Run "archon login" first.'));
|
|
6723
5713
|
return null;
|
|
6724
5714
|
}
|
|
6725
5715
|
const openaiKey = process.env["OPENAI_API_KEY"];
|
|
6726
5716
|
if (!openaiKey) {
|
|
6727
|
-
console.error(
|
|
6728
|
-
console.log(
|
|
6729
|
-
console.log(
|
|
5717
|
+
console.error(chalk11.red("OPENAI_API_KEY environment variable not set."));
|
|
5718
|
+
console.log(chalk11.dim("Cloud indexing requires an OpenAI API key for embeddings."));
|
|
5719
|
+
console.log(chalk11.dim("Set it with: export OPENAI_API_KEY=sk-..."));
|
|
6730
5720
|
return null;
|
|
6731
5721
|
}
|
|
6732
5722
|
const projectId = basename(cwd);
|
|
@@ -6737,15 +5727,18 @@ async function getCloudIndexer(cwd) {
|
|
|
6737
5727
|
embeddingApiKey: openaiKey,
|
|
6738
5728
|
projectId
|
|
6739
5729
|
});
|
|
6740
|
-
const
|
|
5730
|
+
const { createClient: createClient2 } = await import("@supabase/supabase-js");
|
|
5731
|
+
const client = createClient2(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
5732
|
+
global: { headers: { Authorization: `Bearer ${authToken}` } }
|
|
5733
|
+
});
|
|
6741
5734
|
const { data: { user } } = await client.auth.getUser();
|
|
6742
5735
|
if (!user) {
|
|
6743
|
-
console.error(
|
|
5736
|
+
console.error(chalk11.red("Failed to get user. Try logging in again."));
|
|
6744
5737
|
return null;
|
|
6745
5738
|
}
|
|
6746
5739
|
const { data: profile } = await client.from("user_profiles").select("id").eq("auth_id", user.id).single();
|
|
6747
5740
|
if (!profile) {
|
|
6748
|
-
console.error(
|
|
5741
|
+
console.error(chalk11.red("User profile not found."));
|
|
6749
5742
|
return null;
|
|
6750
5743
|
}
|
|
6751
5744
|
indexer.setUserId(profile.id);
|
|
@@ -6754,21 +5747,21 @@ async function getCloudIndexer(cwd) {
|
|
|
6754
5747
|
async function indexInit(options) {
|
|
6755
5748
|
const cwd = process.cwd();
|
|
6756
5749
|
if (options.cloud) {
|
|
6757
|
-
console.log(
|
|
5750
|
+
console.log(chalk11.blue("Initializing cloud semantic index..."));
|
|
6758
5751
|
const indexer = await getCloudIndexer(cwd);
|
|
6759
5752
|
if (!indexer) return;
|
|
6760
5753
|
try {
|
|
6761
5754
|
const status2 = await indexer.getStatus();
|
|
6762
|
-
console.log(
|
|
6763
|
-
console.log(
|
|
6764
|
-
console.log(
|
|
5755
|
+
console.log(chalk11.green("\u2713 Cloud indexing configured"));
|
|
5756
|
+
console.log(chalk11.green(`\u2713 Project ID: ${status2.projectId}`));
|
|
5757
|
+
console.log(chalk11.dim("\nRun `archon index update --cloud` to index your codebase."));
|
|
6765
5758
|
} catch (error) {
|
|
6766
|
-
console.error(
|
|
5759
|
+
console.error(chalk11.red(`Failed to initialize cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
6767
5760
|
process.exit(1);
|
|
6768
5761
|
}
|
|
6769
5762
|
return;
|
|
6770
5763
|
}
|
|
6771
|
-
console.log(
|
|
5764
|
+
console.log(chalk11.blue("Initializing local semantic index..."));
|
|
6772
5765
|
try {
|
|
6773
5766
|
const indexer = new LocalIndexer();
|
|
6774
5767
|
await indexer.init(cwd);
|
|
@@ -6776,18 +5769,18 @@ async function indexInit(options) {
|
|
|
6776
5769
|
if (!response.ok) {
|
|
6777
5770
|
throw new Error("Ollama not responding");
|
|
6778
5771
|
}
|
|
6779
|
-
console.log(
|
|
6780
|
-
console.log(
|
|
6781
|
-
console.log(
|
|
5772
|
+
console.log(chalk11.green("\u2713 Ollama connection verified"));
|
|
5773
|
+
console.log(chalk11.green("\u2713 Index database created at .archon/index.db"));
|
|
5774
|
+
console.log(chalk11.dim("\nRun `archon index update` to index your codebase."));
|
|
6782
5775
|
indexer.close();
|
|
6783
5776
|
} catch (error) {
|
|
6784
5777
|
if (error instanceof Error && error.message.includes("Ollama")) {
|
|
6785
|
-
console.log(
|
|
6786
|
-
console.log(
|
|
6787
|
-
console.log(
|
|
6788
|
-
console.log(
|
|
5778
|
+
console.log(chalk11.red("\n\u2717 Ollama is not running"));
|
|
5779
|
+
console.log(chalk11.dim("Start Ollama with: ollama serve"));
|
|
5780
|
+
console.log(chalk11.dim("Then pull the embedding model: ollama pull nomic-embed-text"));
|
|
5781
|
+
console.log(chalk11.dim("\nOr use cloud indexing: archon index init --cloud"));
|
|
6789
5782
|
} else {
|
|
6790
|
-
console.error(
|
|
5783
|
+
console.error(chalk11.red(`Failed to initialize index: ${error instanceof Error ? error.message : String(error)}`));
|
|
6791
5784
|
}
|
|
6792
5785
|
process.exit(1);
|
|
6793
5786
|
}
|
|
@@ -6795,23 +5788,23 @@ async function indexInit(options) {
|
|
|
6795
5788
|
async function indexUpdate(options) {
|
|
6796
5789
|
const cwd = process.cwd();
|
|
6797
5790
|
if (options?.cloud) {
|
|
6798
|
-
console.log(
|
|
5791
|
+
console.log(chalk11.blue("Updating cloud semantic index..."));
|
|
6799
5792
|
const indexer = await getCloudIndexer(cwd);
|
|
6800
5793
|
if (!indexer) return;
|
|
6801
5794
|
try {
|
|
6802
|
-
const files = await
|
|
5795
|
+
const files = await glob("**/*.{ts,tsx,js,jsx,py,rb,go,rs,java,md,json,yaml,yml}", {
|
|
6803
5796
|
cwd,
|
|
6804
5797
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/build/**", "**/coverage/**"]
|
|
6805
5798
|
});
|
|
6806
|
-
console.log(
|
|
6807
|
-
console.log(
|
|
5799
|
+
console.log(chalk11.dim(`Found ${files.length} files to index...`));
|
|
5800
|
+
console.log(chalk11.dim("This may take a few minutes and will use OpenAI API credits.\n"));
|
|
6808
5801
|
let totalChunks = 0;
|
|
6809
5802
|
let indexedFiles = 0;
|
|
6810
5803
|
let skippedFiles = 0;
|
|
6811
5804
|
for (let i = 0; i < files.length; i++) {
|
|
6812
5805
|
const file = files[i];
|
|
6813
5806
|
if (!file) continue;
|
|
6814
|
-
process.stdout.write(`\r${
|
|
5807
|
+
process.stdout.write(`\r${chalk11.dim(`[${i + 1}/${files.length}] ${file.slice(0, 45).padEnd(45)}`)}`);
|
|
6815
5808
|
try {
|
|
6816
5809
|
const chunks = await indexer.indexFile(cwd, file);
|
|
6817
5810
|
if (chunks > 0) {
|
|
@@ -6822,34 +5815,34 @@ async function indexUpdate(options) {
|
|
|
6822
5815
|
}
|
|
6823
5816
|
} catch (error) {
|
|
6824
5817
|
console.log(`
|
|
6825
|
-
${
|
|
5818
|
+
${chalk11.yellow(`[!] Skipped ${file}: ${error instanceof Error ? error.message : "Unknown error"}`)}`);
|
|
6826
5819
|
}
|
|
6827
5820
|
}
|
|
6828
5821
|
console.log("\r" + " ".repeat(70));
|
|
6829
|
-
console.log(
|
|
5822
|
+
console.log(chalk11.green(`\u2713 Indexed ${indexedFiles} files (${totalChunks} chunks)`));
|
|
6830
5823
|
if (skippedFiles > 0) {
|
|
6831
|
-
console.log(
|
|
5824
|
+
console.log(chalk11.dim(` Skipped ${skippedFiles} unchanged files`));
|
|
6832
5825
|
}
|
|
6833
5826
|
} catch (error) {
|
|
6834
|
-
console.error(
|
|
5827
|
+
console.error(chalk11.red(`Failed to update cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
6835
5828
|
process.exit(1);
|
|
6836
5829
|
}
|
|
6837
5830
|
return;
|
|
6838
5831
|
}
|
|
6839
|
-
console.log(
|
|
5832
|
+
console.log(chalk11.blue("Updating local semantic index..."));
|
|
6840
5833
|
try {
|
|
6841
5834
|
const indexer = new LocalIndexer();
|
|
6842
5835
|
await indexer.init(cwd);
|
|
6843
|
-
const files = await
|
|
5836
|
+
const files = await glob("**/*.{ts,tsx,js,jsx,py,rb,go,rs,java,md,json,yaml,yml}", {
|
|
6844
5837
|
cwd,
|
|
6845
5838
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/build/**", "**/coverage/**"]
|
|
6846
5839
|
});
|
|
6847
|
-
console.log(
|
|
5840
|
+
console.log(chalk11.dim(`Found ${files.length} files to index...`));
|
|
6848
5841
|
let totalChunks = 0;
|
|
6849
5842
|
let indexedFiles = 0;
|
|
6850
5843
|
for (const file of files) {
|
|
6851
5844
|
if (!file) continue;
|
|
6852
|
-
process.stdout.write(`\r${
|
|
5845
|
+
process.stdout.write(`\r${chalk11.dim(`Indexing: ${file.slice(0, 50).padEnd(50)}`)}`);
|
|
6853
5846
|
const chunks = await indexer.indexFile(cwd, file);
|
|
6854
5847
|
if (chunks > 0) {
|
|
6855
5848
|
totalChunks += chunks;
|
|
@@ -6857,10 +5850,10 @@ ${chalk14.yellow(`[!] Skipped ${file}: ${error instanceof Error ? error.message
|
|
|
6857
5850
|
}
|
|
6858
5851
|
}
|
|
6859
5852
|
console.log("\r" + " ".repeat(60));
|
|
6860
|
-
console.log(
|
|
5853
|
+
console.log(chalk11.green(`\u2713 Indexed ${indexedFiles} files (${totalChunks} chunks)`));
|
|
6861
5854
|
indexer.close();
|
|
6862
5855
|
} catch (error) {
|
|
6863
|
-
console.error(
|
|
5856
|
+
console.error(chalk11.red(`Failed to update index: ${error instanceof Error ? error.message : String(error)}`));
|
|
6864
5857
|
process.exit(1);
|
|
6865
5858
|
}
|
|
6866
5859
|
}
|
|
@@ -6870,25 +5863,25 @@ async function indexSearch(query, options) {
|
|
|
6870
5863
|
const indexer = await getCloudIndexer(cwd);
|
|
6871
5864
|
if (!indexer) return;
|
|
6872
5865
|
try {
|
|
6873
|
-
console.log(
|
|
5866
|
+
console.log(chalk11.dim("Searching cloud index..."));
|
|
6874
5867
|
const results = await indexer.search(query, 10);
|
|
6875
5868
|
if (results.length === 0) {
|
|
6876
|
-
console.log(
|
|
6877
|
-
console.log(
|
|
5869
|
+
console.log(chalk11.yellow("\nNo results found."));
|
|
5870
|
+
console.log(chalk11.dim("Try running `archon index update --cloud` first."));
|
|
6878
5871
|
} else {
|
|
6879
|
-
console.log(
|
|
5872
|
+
console.log(chalk11.blue(`
|
|
6880
5873
|
Top ${results.length} results for: "${query}"
|
|
6881
5874
|
`));
|
|
6882
5875
|
for (const result of results) {
|
|
6883
5876
|
const score = (result.score * 100).toFixed(1);
|
|
6884
|
-
console.log(
|
|
5877
|
+
console.log(chalk11.green(`[${score}%] ${result.file}`));
|
|
6885
5878
|
const preview = result.text.slice(0, 200).replace(/\n/g, " ").trim();
|
|
6886
|
-
console.log(
|
|
5879
|
+
console.log(chalk11.dim(` ${preview}${result.text.length > 200 ? "..." : ""}`));
|
|
6887
5880
|
console.log();
|
|
6888
5881
|
}
|
|
6889
5882
|
}
|
|
6890
5883
|
} catch (error) {
|
|
6891
|
-
console.error(
|
|
5884
|
+
console.error(chalk11.red(`Cloud search failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
6892
5885
|
process.exit(1);
|
|
6893
5886
|
}
|
|
6894
5887
|
return;
|
|
@@ -6898,23 +5891,23 @@ Top ${results.length} results for: "${query}"
|
|
|
6898
5891
|
await indexer.init(cwd);
|
|
6899
5892
|
const results = await indexer.search(query, 10);
|
|
6900
5893
|
if (results.length === 0) {
|
|
6901
|
-
console.log(
|
|
6902
|
-
console.log(
|
|
5894
|
+
console.log(chalk11.yellow("No results found."));
|
|
5895
|
+
console.log(chalk11.dim("Try running `archon index update` first."));
|
|
6903
5896
|
} else {
|
|
6904
|
-
console.log(
|
|
5897
|
+
console.log(chalk11.blue(`
|
|
6905
5898
|
Top ${results.length} results for: "${query}"
|
|
6906
5899
|
`));
|
|
6907
5900
|
for (const result of results) {
|
|
6908
5901
|
const score = (result.score * 100).toFixed(1);
|
|
6909
|
-
console.log(
|
|
5902
|
+
console.log(chalk11.green(`[${score}%] ${result.file}`));
|
|
6910
5903
|
const preview = result.text.slice(0, 200).replace(/\n/g, " ").trim();
|
|
6911
|
-
console.log(
|
|
5904
|
+
console.log(chalk11.dim(` ${preview}${result.text.length > 200 ? "..." : ""}`));
|
|
6912
5905
|
console.log();
|
|
6913
5906
|
}
|
|
6914
5907
|
}
|
|
6915
5908
|
indexer.close();
|
|
6916
5909
|
} catch (error) {
|
|
6917
|
-
console.error(
|
|
5910
|
+
console.error(chalk11.red(`Search failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
6918
5911
|
process.exit(1);
|
|
6919
5912
|
}
|
|
6920
5913
|
}
|
|
@@ -6925,17 +5918,17 @@ async function indexStatus(options) {
|
|
|
6925
5918
|
if (!indexer) return;
|
|
6926
5919
|
try {
|
|
6927
5920
|
const status2 = await indexer.getStatus();
|
|
6928
|
-
console.log(
|
|
6929
|
-
console.log(` Project ID: ${
|
|
6930
|
-
console.log(` Files indexed: ${
|
|
6931
|
-
console.log(` Total chunks: ${
|
|
6932
|
-
console.log(` Last updated: ${status2.lastUpdated ?
|
|
5921
|
+
console.log(chalk11.blue("\nCloud Semantic Index Status\n"));
|
|
5922
|
+
console.log(` Project ID: ${chalk11.green(status2.projectId)}`);
|
|
5923
|
+
console.log(` Files indexed: ${chalk11.green(status2.fileCount)}`);
|
|
5924
|
+
console.log(` Total chunks: ${chalk11.green(status2.chunkCount)}`);
|
|
5925
|
+
console.log(` Last updated: ${status2.lastUpdated ? chalk11.dim(status2.lastUpdated) : chalk11.yellow("Never")}`);
|
|
6933
5926
|
if (status2.jobStatus) {
|
|
6934
|
-
console.log(` Job status: ${
|
|
5927
|
+
console.log(` Job status: ${chalk11.dim(status2.jobStatus)}`);
|
|
6935
5928
|
}
|
|
6936
|
-
console.log(` Storage: ${
|
|
5929
|
+
console.log(` Storage: ${chalk11.dim("Supabase pgvector")}`);
|
|
6937
5930
|
} catch (error) {
|
|
6938
|
-
console.error(
|
|
5931
|
+
console.error(chalk11.red(`Failed to get cloud status: ${error instanceof Error ? error.message : String(error)}`));
|
|
6939
5932
|
process.exit(1);
|
|
6940
5933
|
}
|
|
6941
5934
|
return;
|
|
@@ -6944,14 +5937,14 @@ async function indexStatus(options) {
|
|
|
6944
5937
|
const indexer = new LocalIndexer();
|
|
6945
5938
|
await indexer.init(cwd);
|
|
6946
5939
|
const status2 = await indexer.getStatus();
|
|
6947
|
-
console.log(
|
|
6948
|
-
console.log(` Files indexed: ${
|
|
6949
|
-
console.log(` Total chunks: ${
|
|
6950
|
-
console.log(` Last updated: ${status2.lastUpdated ?
|
|
6951
|
-
console.log(` Database: ${
|
|
5940
|
+
console.log(chalk11.blue("\nLocal Semantic Index Status\n"));
|
|
5941
|
+
console.log(` Files indexed: ${chalk11.green(status2.fileCount)}`);
|
|
5942
|
+
console.log(` Total chunks: ${chalk11.green(status2.chunkCount)}`);
|
|
5943
|
+
console.log(` Last updated: ${status2.lastUpdated ? chalk11.dim(status2.lastUpdated) : chalk11.yellow("Never")}`);
|
|
5944
|
+
console.log(` Database: ${chalk11.dim(join12(cwd, ".archon/index.db"))}`);
|
|
6952
5945
|
indexer.close();
|
|
6953
5946
|
} catch (error) {
|
|
6954
|
-
console.error(
|
|
5947
|
+
console.error(chalk11.red(`Failed to get status: ${error instanceof Error ? error.message : String(error)}`));
|
|
6955
5948
|
process.exit(1);
|
|
6956
5949
|
}
|
|
6957
5950
|
}
|
|
@@ -6961,30 +5954,30 @@ async function indexClear(options) {
|
|
|
6961
5954
|
const indexer = await getCloudIndexer(cwd);
|
|
6962
5955
|
if (!indexer) return;
|
|
6963
5956
|
try {
|
|
6964
|
-
console.log(
|
|
5957
|
+
console.log(chalk11.yellow("Clearing cloud index..."));
|
|
6965
5958
|
await indexer.clearProject();
|
|
6966
|
-
console.log(
|
|
5959
|
+
console.log(chalk11.green("\u2713 Cloud index cleared"));
|
|
6967
5960
|
} catch (error) {
|
|
6968
|
-
console.error(
|
|
5961
|
+
console.error(chalk11.red(`Failed to clear cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
6969
5962
|
process.exit(1);
|
|
6970
5963
|
}
|
|
6971
5964
|
return;
|
|
6972
5965
|
}
|
|
6973
|
-
console.log(
|
|
5966
|
+
console.log(chalk11.yellow("To clear local index, delete .archon/index.db"));
|
|
6974
5967
|
}
|
|
6975
5968
|
|
|
6976
5969
|
// src/cli/github.ts
|
|
6977
|
-
import
|
|
5970
|
+
import chalk12 from "chalk";
|
|
6978
5971
|
import open2 from "open";
|
|
6979
5972
|
var API_URL2 = process.env["ARCHONDEV_API_URL"] ?? "https://archondev-api.fly.dev";
|
|
6980
5973
|
async function githubConnect() {
|
|
6981
5974
|
const config = await loadConfig();
|
|
6982
5975
|
const authToken = getAuthToken(config);
|
|
6983
5976
|
if (!authToken) {
|
|
6984
|
-
console.error(
|
|
5977
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
6985
5978
|
process.exit(1);
|
|
6986
5979
|
}
|
|
6987
|
-
console.log(
|
|
5980
|
+
console.log(chalk12.dim("Starting GitHub connection..."));
|
|
6988
5981
|
try {
|
|
6989
5982
|
const response = await fetch(`${API_URL2}/api/github/connect`, {
|
|
6990
5983
|
headers: {
|
|
@@ -6993,18 +5986,18 @@ async function githubConnect() {
|
|
|
6993
5986
|
});
|
|
6994
5987
|
if (!response.ok) {
|
|
6995
5988
|
const error = await response.json();
|
|
6996
|
-
console.error(
|
|
5989
|
+
console.error(chalk12.red(error.error ?? "Failed to start GitHub connection"));
|
|
6997
5990
|
process.exit(1);
|
|
6998
5991
|
}
|
|
6999
5992
|
const data = await response.json();
|
|
7000
|
-
console.log(
|
|
7001
|
-
console.log(
|
|
7002
|
-
console.log(
|
|
5993
|
+
console.log(chalk12.dim("\nOpening browser for GitHub authorization..."));
|
|
5994
|
+
console.log(chalk12.dim("If browser does not open, visit:"));
|
|
5995
|
+
console.log(chalk12.blue(data.url));
|
|
7003
5996
|
await open2(data.url);
|
|
7004
|
-
console.log(
|
|
7005
|
-
console.log(
|
|
5997
|
+
console.log(chalk12.dim("\nComplete the authorization in your browser."));
|
|
5998
|
+
console.log(chalk12.dim('Then run "archon github status" to verify connection.'));
|
|
7006
5999
|
} catch (error) {
|
|
7007
|
-
console.error(
|
|
6000
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to connect"));
|
|
7008
6001
|
process.exit(1);
|
|
7009
6002
|
}
|
|
7010
6003
|
}
|
|
@@ -7012,7 +6005,7 @@ async function githubStatus() {
|
|
|
7012
6005
|
const config = await loadConfig();
|
|
7013
6006
|
const authToken = getAuthToken(config);
|
|
7014
6007
|
if (!authToken) {
|
|
7015
|
-
console.error(
|
|
6008
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
7016
6009
|
process.exit(1);
|
|
7017
6010
|
}
|
|
7018
6011
|
try {
|
|
@@ -7023,20 +6016,20 @@ async function githubStatus() {
|
|
|
7023
6016
|
});
|
|
7024
6017
|
if (!response.ok) {
|
|
7025
6018
|
const error = await response.json();
|
|
7026
|
-
console.error(
|
|
6019
|
+
console.error(chalk12.red(error.error ?? "Failed to get GitHub status"));
|
|
7027
6020
|
process.exit(1);
|
|
7028
6021
|
}
|
|
7029
6022
|
const data = await response.json();
|
|
7030
6023
|
if (data.connected) {
|
|
7031
|
-
console.log(
|
|
7032
|
-
console.log(
|
|
7033
|
-
console.log(
|
|
6024
|
+
console.log(chalk12.green("\u2713 GitHub connected"));
|
|
6025
|
+
console.log(chalk12.dim(` Username: ${data.username}`));
|
|
6026
|
+
console.log(chalk12.dim(` Connected: ${data.connectedAt ? new Date(data.connectedAt).toLocaleDateString() : "Unknown"}`));
|
|
7034
6027
|
} else {
|
|
7035
|
-
console.log(
|
|
7036
|
-
console.log(
|
|
6028
|
+
console.log(chalk12.yellow("GitHub not connected"));
|
|
6029
|
+
console.log(chalk12.dim('Run "archon github connect" to connect your GitHub account.'));
|
|
7037
6030
|
}
|
|
7038
6031
|
} catch (error) {
|
|
7039
|
-
console.error(
|
|
6032
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to get status"));
|
|
7040
6033
|
process.exit(1);
|
|
7041
6034
|
}
|
|
7042
6035
|
}
|
|
@@ -7044,7 +6037,7 @@ async function githubDisconnect() {
|
|
|
7044
6037
|
const config = await loadConfig();
|
|
7045
6038
|
const authToken = getAuthToken(config);
|
|
7046
6039
|
if (!authToken) {
|
|
7047
|
-
console.error(
|
|
6040
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
7048
6041
|
process.exit(1);
|
|
7049
6042
|
}
|
|
7050
6043
|
try {
|
|
@@ -7056,62 +6049,62 @@ async function githubDisconnect() {
|
|
|
7056
6049
|
});
|
|
7057
6050
|
if (!response.ok) {
|
|
7058
6051
|
const error = await response.json();
|
|
7059
|
-
console.error(
|
|
6052
|
+
console.error(chalk12.red(error.error ?? "Failed to disconnect GitHub"));
|
|
7060
6053
|
process.exit(1);
|
|
7061
6054
|
}
|
|
7062
|
-
console.log(
|
|
6055
|
+
console.log(chalk12.green("\u2713 GitHub disconnected"));
|
|
7063
6056
|
} catch (error) {
|
|
7064
|
-
console.error(
|
|
6057
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to disconnect"));
|
|
7065
6058
|
process.exit(1);
|
|
7066
6059
|
}
|
|
7067
6060
|
}
|
|
7068
6061
|
|
|
7069
6062
|
// src/cli/interview.ts
|
|
7070
|
-
import
|
|
6063
|
+
import chalk13 from "chalk";
|
|
7071
6064
|
import readline2 from "readline";
|
|
7072
6065
|
import ora3 from "ora";
|
|
7073
|
-
import { existsSync as
|
|
7074
|
-
import { join as
|
|
6066
|
+
import { existsSync as existsSync13, readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
6067
|
+
import { join as join13 } from "path";
|
|
7075
6068
|
import { randomUUID } from "crypto";
|
|
7076
6069
|
function getConstitutionPath(cwd) {
|
|
7077
|
-
return
|
|
6070
|
+
return join13(cwd, ".archon", "constitution.json");
|
|
7078
6071
|
}
|
|
7079
6072
|
function getDraftPath(cwd) {
|
|
7080
|
-
return
|
|
6073
|
+
return join13(cwd, ".archon", "constitution-draft.json");
|
|
7081
6074
|
}
|
|
7082
6075
|
async function interview(options = {}) {
|
|
7083
6076
|
const cwd = process.cwd();
|
|
7084
|
-
const archonDir =
|
|
7085
|
-
if (!
|
|
6077
|
+
const archonDir = join13(cwd, ".archon");
|
|
6078
|
+
if (!existsSync13(archonDir)) {
|
|
7086
6079
|
mkdirSync2(archonDir, { recursive: true });
|
|
7087
6080
|
}
|
|
7088
|
-
console.log(
|
|
7089
|
-
console.log(
|
|
6081
|
+
console.log(chalk13.bold("\n\u{1F3AF} ArchonDev Project Interview"));
|
|
6082
|
+
console.log(chalk13.dim("Let's define what you're building.\n"));
|
|
7090
6083
|
const constitutionPath = getConstitutionPath(cwd);
|
|
7091
6084
|
const draftPath = getDraftPath(cwd);
|
|
7092
|
-
if (
|
|
7093
|
-
const existing = deserializeConstitution(
|
|
6085
|
+
if (existsSync13(constitutionPath)) {
|
|
6086
|
+
const existing = deserializeConstitution(readFileSync5(constitutionPath, "utf-8"));
|
|
7094
6087
|
if (existing.state === "FROZEN") {
|
|
7095
|
-
console.log(
|
|
7096
|
-
console.log(
|
|
7097
|
-
console.log(
|
|
6088
|
+
console.log(chalk13.yellow("[!] A frozen Constitution already exists."));
|
|
6089
|
+
console.log(chalk13.dim(` Project: ${existing.branding.projectName}`));
|
|
6090
|
+
console.log(chalk13.dim(` Frozen: ${existing.frozenAt?.toISOString()}`));
|
|
7098
6091
|
console.log();
|
|
7099
6092
|
const action = await prompt2("Start a new interview (overwrite) or view existing? (new/view)");
|
|
7100
6093
|
if (action.toLowerCase() === "view") {
|
|
7101
6094
|
console.log("\n" + summarizeConstitution(existing));
|
|
7102
6095
|
return;
|
|
7103
6096
|
} else if (action.toLowerCase() !== "new") {
|
|
7104
|
-
console.log(
|
|
6097
|
+
console.log(chalk13.dim("Cancelled."));
|
|
7105
6098
|
return;
|
|
7106
6099
|
}
|
|
7107
6100
|
}
|
|
7108
6101
|
}
|
|
7109
6102
|
let constitution;
|
|
7110
6103
|
let startPhase = 1;
|
|
7111
|
-
if (
|
|
7112
|
-
const draft = deserializeConstitution(
|
|
7113
|
-
console.log(
|
|
7114
|
-
console.log(
|
|
6104
|
+
if (existsSync13(draftPath) && options.resume !== false) {
|
|
6105
|
+
const draft = deserializeConstitution(readFileSync5(draftPath, "utf-8"));
|
|
6106
|
+
console.log(chalk13.blue("[i] Found draft from previous session."));
|
|
6107
|
+
console.log(chalk13.dim(` Project: ${draft.branding.projectName || "(unnamed)"}`));
|
|
7115
6108
|
console.log();
|
|
7116
6109
|
const resumeChoice = await prompt2("Resume draft or start fresh? (resume/fresh)");
|
|
7117
6110
|
if (resumeChoice.toLowerCase() === "resume") {
|
|
@@ -7123,7 +6116,7 @@ async function interview(options = {}) {
|
|
|
7123
6116
|
break;
|
|
7124
6117
|
}
|
|
7125
6118
|
}
|
|
7126
|
-
console.log(
|
|
6119
|
+
console.log(chalk13.green(`
|
|
7127
6120
|
\u2713 Resuming at Phase ${startPhase}: ${INTERVIEW_PHASES[startPhase]?.name}
|
|
7128
6121
|
`));
|
|
7129
6122
|
} else {
|
|
@@ -7134,19 +6127,19 @@ async function interview(options = {}) {
|
|
|
7134
6127
|
}
|
|
7135
6128
|
if (options.phase && options.phase >= 1 && options.phase <= 5) {
|
|
7136
6129
|
startPhase = options.phase;
|
|
7137
|
-
console.log(
|
|
6130
|
+
console.log(chalk13.dim(`Starting at Phase ${startPhase}...
|
|
7138
6131
|
`));
|
|
7139
6132
|
}
|
|
7140
|
-
console.log(
|
|
7141
|
-
console.log(
|
|
7142
|
-
console.log(
|
|
6133
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
6134
|
+
console.log(chalk13.dim(formatProgressBar(startPhase)));
|
|
6135
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
7143
6136
|
console.log();
|
|
7144
6137
|
let currentPhase = startPhase;
|
|
7145
6138
|
while (currentPhase <= 5) {
|
|
7146
6139
|
const result = await runPhase(currentPhase, constitution, cwd);
|
|
7147
6140
|
if (result.action === "exit") {
|
|
7148
6141
|
saveDraft(cwd, constitution);
|
|
7149
|
-
console.log(
|
|
6142
|
+
console.log(chalk13.dim("\nDraft saved. Run `archon interview` to resume.\n"));
|
|
7150
6143
|
return;
|
|
7151
6144
|
}
|
|
7152
6145
|
if (result.action === "back" && currentPhase > 1) {
|
|
@@ -7161,9 +6154,9 @@ async function interview(options = {}) {
|
|
|
7161
6154
|
if (next) {
|
|
7162
6155
|
currentPhase = next;
|
|
7163
6156
|
console.log();
|
|
7164
|
-
console.log(
|
|
7165
|
-
console.log(
|
|
7166
|
-
console.log(
|
|
6157
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
6158
|
+
console.log(chalk13.dim(formatProgressBar(currentPhase)));
|
|
6159
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
7167
6160
|
console.log();
|
|
7168
6161
|
} else {
|
|
7169
6162
|
break;
|
|
@@ -7171,9 +6164,9 @@ async function interview(options = {}) {
|
|
|
7171
6164
|
}
|
|
7172
6165
|
const validationResult = validateConstitution(constitution);
|
|
7173
6166
|
if (!validationResult.valid) {
|
|
7174
|
-
console.log(
|
|
6167
|
+
console.log(chalk13.red("\n[!] Constitution has validation errors:\n"));
|
|
7175
6168
|
for (const error of validationResult.errors) {
|
|
7176
|
-
console.log(
|
|
6169
|
+
console.log(chalk13.red(` \u2022 ${error}`));
|
|
7177
6170
|
}
|
|
7178
6171
|
console.log();
|
|
7179
6172
|
const fix = await promptYesNo2("Would you like to go back and fix these?", true);
|
|
@@ -7183,9 +6176,9 @@ async function interview(options = {}) {
|
|
|
7183
6176
|
}
|
|
7184
6177
|
}
|
|
7185
6178
|
if (validationResult.warnings.length > 0) {
|
|
7186
|
-
console.log(
|
|
6179
|
+
console.log(chalk13.yellow("\n[!] Warnings:\n"));
|
|
7187
6180
|
for (const warning of validationResult.warnings) {
|
|
7188
|
-
console.log(
|
|
6181
|
+
console.log(chalk13.yellow(` \u2022 ${warning}`));
|
|
7189
6182
|
}
|
|
7190
6183
|
console.log();
|
|
7191
6184
|
}
|
|
@@ -7195,21 +6188,21 @@ async function interview(options = {}) {
|
|
|
7195
6188
|
constitution.costs = estimateCosts(constitution, config.tier || "FREE");
|
|
7196
6189
|
const challengeResult = analyzeForChallenges(constitution);
|
|
7197
6190
|
if (challengeResult.shouldChallenge) {
|
|
7198
|
-
console.log(
|
|
7199
|
-
console.log(
|
|
6191
|
+
console.log(chalk13.bold("\n\u{1F3AF} Challenge Mode\n"));
|
|
6192
|
+
console.log(chalk13.dim("Let me share some observations about your project scope...\n"));
|
|
7200
6193
|
for (const challenge of challengeResult.challenges) {
|
|
7201
6194
|
const prompt3 = generateChallengePrompt(challenge);
|
|
7202
|
-
const icon = challenge.severity === "critical" ?
|
|
7203
|
-
console.log(`${icon} ${
|
|
7204
|
-
console.log(
|
|
6195
|
+
const icon = challenge.severity === "critical" ? chalk13.red("\u25CF") : chalk13.yellow("\u25CF");
|
|
6196
|
+
console.log(`${icon} ${chalk13.bold(challenge.title)}`);
|
|
6197
|
+
console.log(chalk13.dim(` ${prompt3}`));
|
|
7205
6198
|
console.log();
|
|
7206
6199
|
}
|
|
7207
|
-
console.log(
|
|
6200
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
7208
6201
|
console.log(summarizeChallenges(challengeResult));
|
|
7209
|
-
console.log(
|
|
6202
|
+
console.log(chalk13.dim("\u2500".repeat(40)));
|
|
7210
6203
|
console.log();
|
|
7211
6204
|
if (challengeResult.featuresToDefer.length > 0) {
|
|
7212
|
-
console.log(
|
|
6205
|
+
console.log(chalk13.bold("Suggested Features to Defer to Post-MVP:\n"));
|
|
7213
6206
|
for (const feature of challengeResult.featuresToDefer) {
|
|
7214
6207
|
console.log(` \u2022 ${feature.name}`);
|
|
7215
6208
|
}
|
|
@@ -7224,58 +6217,58 @@ async function interview(options = {}) {
|
|
|
7224
6217
|
constitution.complexity = calculateComplexity(constitution);
|
|
7225
6218
|
constitution.estimatedBuildHours = estimateBuildHours(constitution.complexity);
|
|
7226
6219
|
constitution.costs = estimateCosts(constitution, config.tier || "FREE");
|
|
7227
|
-
console.log(
|
|
7228
|
-
console.log(
|
|
7229
|
-
console.log(
|
|
6220
|
+
console.log(chalk13.green("\n\u2713 Features deferred. Updated estimates:"));
|
|
6221
|
+
console.log(chalk13.dim(` Complexity: ${constitution.complexity.tier}`));
|
|
6222
|
+
console.log(chalk13.dim(` Build time: ~${Math.round(constitution.estimatedBuildHours)} hours`));
|
|
7230
6223
|
console.log();
|
|
7231
6224
|
saveDraft(cwd, constitution);
|
|
7232
6225
|
}
|
|
7233
6226
|
}
|
|
7234
6227
|
const criticalCount = challengeResult.challenges.filter((c) => c.severity === "critical").length;
|
|
7235
6228
|
if (criticalCount > 0) {
|
|
7236
|
-
console.log(
|
|
6229
|
+
console.log(chalk13.red(`
|
|
7237
6230
|
\u26A0\uFE0F ${criticalCount} critical issue(s) detected.`));
|
|
7238
6231
|
const proceed = await promptYesNo2("Proceed anyway?", false);
|
|
7239
6232
|
if (!proceed) {
|
|
7240
6233
|
saveDraft(cwd, constitution);
|
|
7241
|
-
console.log(
|
|
6234
|
+
console.log(chalk13.dim("\nDraft saved. Address the issues and run `archon interview` again.\n"));
|
|
7242
6235
|
return;
|
|
7243
6236
|
}
|
|
7244
6237
|
}
|
|
7245
6238
|
} else {
|
|
7246
|
-
console.log(
|
|
6239
|
+
console.log(chalk13.green("\n\u2713 Scope looks reasonable! No major concerns detected.\n"));
|
|
7247
6240
|
}
|
|
7248
|
-
console.log(
|
|
6241
|
+
console.log(chalk13.bold("\n\u{1F4CB} Constitution Summary\n"));
|
|
7249
6242
|
console.log(summarizeConstitution(constitution));
|
|
7250
6243
|
console.log();
|
|
7251
6244
|
if (options.dryRun) {
|
|
7252
|
-
console.log(
|
|
6245
|
+
console.log(chalk13.dim("(Dry run mode - not freezing Constitution)"));
|
|
7253
6246
|
saveDraft(cwd, constitution);
|
|
7254
6247
|
return;
|
|
7255
6248
|
}
|
|
7256
6249
|
const confirmFreeze = await promptYesNo2("Freeze this Constitution and start building?", true);
|
|
7257
6250
|
if (!confirmFreeze) {
|
|
7258
6251
|
saveDraft(cwd, constitution);
|
|
7259
|
-
console.log(
|
|
6252
|
+
console.log(chalk13.dim("\nDraft saved. Run `archon interview` to continue.\n"));
|
|
7260
6253
|
return;
|
|
7261
6254
|
}
|
|
7262
6255
|
const spinner = ora3("Freezing Constitution...").start();
|
|
7263
6256
|
try {
|
|
7264
6257
|
const frozen = freezeConstitution(constitution);
|
|
7265
6258
|
writeFileSync(constitutionPath, serializeConstitution(frozen));
|
|
7266
|
-
if (
|
|
6259
|
+
if (existsSync13(draftPath)) {
|
|
7267
6260
|
const { unlinkSync } = await import("fs");
|
|
7268
6261
|
unlinkSync(draftPath);
|
|
7269
6262
|
}
|
|
7270
|
-
spinner.succeed(
|
|
6263
|
+
spinner.succeed(chalk13.green("Constitution frozen!"));
|
|
7271
6264
|
console.log();
|
|
7272
|
-
console.log(
|
|
7273
|
-
console.log(
|
|
6265
|
+
console.log(chalk13.dim(`Hash: ${frozen.hash?.substring(0, 32)}...`));
|
|
6266
|
+
console.log(chalk13.dim(`Saved: ${constitutionPath}`));
|
|
7274
6267
|
console.log();
|
|
7275
|
-
console.log(
|
|
7276
|
-
console.log(` ${
|
|
7277
|
-
console.log(` ${
|
|
7278
|
-
console.log(` ${
|
|
6268
|
+
console.log(chalk13.bold("Next Steps:\n"));
|
|
6269
|
+
console.log(` ${chalk13.cyan("1.")} Run ${chalk13.bold("archon generate")} to create atoms from this Constitution`);
|
|
6270
|
+
console.log(` ${chalk13.cyan("2.")} Run ${chalk13.bold("archon list")} to see generated atoms`);
|
|
6271
|
+
console.log(` ${chalk13.cyan("3.")} Run ${chalk13.bold("archon execute <atom-id>")} to start building`);
|
|
7279
6272
|
console.log();
|
|
7280
6273
|
} catch (err) {
|
|
7281
6274
|
spinner.fail("Failed to freeze Constitution");
|
|
@@ -7284,18 +6277,18 @@ async function interview(options = {}) {
|
|
|
7284
6277
|
}
|
|
7285
6278
|
async function runPhase(phase, constitution, cwd) {
|
|
7286
6279
|
const phaseInfo = INTERVIEW_PHASES[phase];
|
|
7287
|
-
console.log(
|
|
7288
|
-
console.log(
|
|
6280
|
+
console.log(chalk13.bold(`Phase ${phase}: ${phaseInfo.name}`));
|
|
6281
|
+
console.log(chalk13.dim(phaseInfo.description));
|
|
7289
6282
|
console.log();
|
|
7290
6283
|
const skippable = getSkippableQuestions(phase, constitution);
|
|
7291
6284
|
let updatedConstitution = { ...constitution };
|
|
7292
6285
|
for (const question of phaseInfo.questions) {
|
|
7293
6286
|
if (skippable.includes(question.id)) {
|
|
7294
|
-
console.log(
|
|
6287
|
+
console.log(chalk13.dim(`[\u2713] ${question.prompt.split("?")[0]}... (already answered)`));
|
|
7295
6288
|
continue;
|
|
7296
6289
|
}
|
|
7297
6290
|
if (phase === 5 && question.id === "review_summary") {
|
|
7298
|
-
console.log(
|
|
6291
|
+
console.log(chalk13.bold("\n\u{1F4CB} Current State:\n"));
|
|
7299
6292
|
console.log(summarizeConstitution(updatedConstitution));
|
|
7300
6293
|
console.log();
|
|
7301
6294
|
}
|
|
@@ -7312,14 +6305,14 @@ async function runPhase(phase, constitution, cwd) {
|
|
|
7312
6305
|
if (question.validator) {
|
|
7313
6306
|
const validation = question.validator(answer);
|
|
7314
6307
|
if (!validation.valid) {
|
|
7315
|
-
console.log(
|
|
6308
|
+
console.log(chalk13.red(` ${validation.error}`));
|
|
7316
6309
|
continue;
|
|
7317
6310
|
}
|
|
7318
6311
|
}
|
|
7319
6312
|
const extracted = question.extractor(answer, updatedConstitution);
|
|
7320
6313
|
updatedConstitution = { ...updatedConstitution, ...extracted };
|
|
7321
6314
|
if (question.followUp && answer.length > 10) {
|
|
7322
|
-
console.log(
|
|
6315
|
+
console.log(chalk13.dim(` ${question.followUp}`));
|
|
7323
6316
|
}
|
|
7324
6317
|
}
|
|
7325
6318
|
return { action: "next", constitution: updatedConstitution };
|
|
@@ -7329,7 +6322,7 @@ function saveDraft(cwd, constitution) {
|
|
|
7329
6322
|
writeFileSync(draftPath, serializeConstitution(constitution));
|
|
7330
6323
|
}
|
|
7331
6324
|
async function promptQuestion(question) {
|
|
7332
|
-
const prefix = question.required ?
|
|
6325
|
+
const prefix = question.required ? chalk13.red("*") : " ";
|
|
7333
6326
|
return prompt2(`${prefix} ${question.prompt}`);
|
|
7334
6327
|
}
|
|
7335
6328
|
function prompt2(question) {
|
|
@@ -7338,7 +6331,7 @@ function prompt2(question) {
|
|
|
7338
6331
|
input: process.stdin,
|
|
7339
6332
|
output: process.stdout
|
|
7340
6333
|
});
|
|
7341
|
-
rl.question(`${
|
|
6334
|
+
rl.question(`${chalk13.cyan("?")} ${question}
|
|
7342
6335
|
> `, (answer) => {
|
|
7343
6336
|
rl.close();
|
|
7344
6337
|
resolve(answer.trim());
|
|
@@ -7352,7 +6345,7 @@ function promptYesNo2(question, defaultValue) {
|
|
|
7352
6345
|
output: process.stdout
|
|
7353
6346
|
});
|
|
7354
6347
|
const hint = defaultValue ? "(Y/n)" : "(y/N)";
|
|
7355
|
-
rl.question(`${
|
|
6348
|
+
rl.question(`${chalk13.cyan("?")} ${question} ${hint}: `, (answer) => {
|
|
7356
6349
|
rl.close();
|
|
7357
6350
|
if (answer.trim() === "") {
|
|
7358
6351
|
resolve(defaultValue);
|
|
@@ -7366,51 +6359,51 @@ async function showConstitution() {
|
|
|
7366
6359
|
const cwd = process.cwd();
|
|
7367
6360
|
const constitutionPath = getConstitutionPath(cwd);
|
|
7368
6361
|
const draftPath = getDraftPath(cwd);
|
|
7369
|
-
if (
|
|
7370
|
-
const constitution = deserializeConstitution(
|
|
6362
|
+
if (existsSync13(constitutionPath)) {
|
|
6363
|
+
const constitution = deserializeConstitution(readFileSync5(constitutionPath, "utf-8"));
|
|
7371
6364
|
console.log(summarizeConstitution(constitution));
|
|
7372
|
-
} else if (
|
|
7373
|
-
const draft = deserializeConstitution(
|
|
7374
|
-
console.log(
|
|
6365
|
+
} else if (existsSync13(draftPath)) {
|
|
6366
|
+
const draft = deserializeConstitution(readFileSync5(draftPath, "utf-8"));
|
|
6367
|
+
console.log(chalk13.yellow("[DRAFT]"));
|
|
7375
6368
|
console.log(summarizeConstitution(draft));
|
|
7376
6369
|
} else {
|
|
7377
|
-
console.log(
|
|
6370
|
+
console.log(chalk13.dim("No Constitution found. Run `archon interview` to create one."));
|
|
7378
6371
|
}
|
|
7379
6372
|
}
|
|
7380
6373
|
async function validateConstitutionCommand() {
|
|
7381
6374
|
const cwd = process.cwd();
|
|
7382
6375
|
const constitutionPath = getConstitutionPath(cwd);
|
|
7383
6376
|
const draftPath = getDraftPath(cwd);
|
|
7384
|
-
const path2 =
|
|
7385
|
-
if (!
|
|
7386
|
-
console.log(
|
|
6377
|
+
const path2 = existsSync13(constitutionPath) ? constitutionPath : draftPath;
|
|
6378
|
+
if (!existsSync13(path2)) {
|
|
6379
|
+
console.log(chalk13.dim("No Constitution found. Run `archon interview` to create one."));
|
|
7387
6380
|
return;
|
|
7388
6381
|
}
|
|
7389
|
-
const constitution = deserializeConstitution(
|
|
6382
|
+
const constitution = deserializeConstitution(readFileSync5(path2, "utf-8"));
|
|
7390
6383
|
const result = validateConstitution(constitution);
|
|
7391
6384
|
if (result.valid) {
|
|
7392
|
-
console.log(
|
|
6385
|
+
console.log(chalk13.green("\u2713 Constitution is valid"));
|
|
7393
6386
|
} else {
|
|
7394
|
-
console.log(
|
|
6387
|
+
console.log(chalk13.red("\u2717 Constitution has errors:"));
|
|
7395
6388
|
for (const error of result.errors) {
|
|
7396
|
-
console.log(
|
|
6389
|
+
console.log(chalk13.red(` \u2022 ${error}`));
|
|
7397
6390
|
}
|
|
7398
6391
|
}
|
|
7399
6392
|
if (result.warnings.length > 0) {
|
|
7400
|
-
console.log(
|
|
6393
|
+
console.log(chalk13.yellow("\nWarnings:"));
|
|
7401
6394
|
for (const warning of result.warnings) {
|
|
7402
|
-
console.log(
|
|
6395
|
+
console.log(chalk13.yellow(` \u2022 ${warning}`));
|
|
7403
6396
|
}
|
|
7404
6397
|
}
|
|
7405
6398
|
}
|
|
7406
6399
|
async function exportConstitution(format) {
|
|
7407
6400
|
const cwd = process.cwd();
|
|
7408
6401
|
const constitutionPath = getConstitutionPath(cwd);
|
|
7409
|
-
if (!
|
|
7410
|
-
console.log(
|
|
6402
|
+
if (!existsSync13(constitutionPath)) {
|
|
6403
|
+
console.log(chalk13.dim("No frozen Constitution found."));
|
|
7411
6404
|
return;
|
|
7412
6405
|
}
|
|
7413
|
-
const constitution = deserializeConstitution(
|
|
6406
|
+
const constitution = deserializeConstitution(readFileSync5(constitutionPath, "utf-8"));
|
|
7414
6407
|
switch (format.toLowerCase()) {
|
|
7415
6408
|
case "json":
|
|
7416
6409
|
console.log(serializeConstitution(constitution));
|
|
@@ -7420,26 +6413,26 @@ async function exportConstitution(format) {
|
|
|
7420
6413
|
console.log(summarizeConstitution(constitution));
|
|
7421
6414
|
break;
|
|
7422
6415
|
default:
|
|
7423
|
-
console.log(
|
|
6416
|
+
console.log(chalk13.red(`Unknown format: ${format}. Use 'json' or 'markdown'.`));
|
|
7424
6417
|
}
|
|
7425
6418
|
}
|
|
7426
6419
|
async function generateAtoms(options = {}) {
|
|
7427
6420
|
const cwd = process.cwd();
|
|
7428
6421
|
const constitutionPath = getConstitutionPath(cwd);
|
|
7429
|
-
if (!
|
|
7430
|
-
console.log(
|
|
7431
|
-
console.log(
|
|
6422
|
+
if (!existsSync13(constitutionPath)) {
|
|
6423
|
+
console.log(chalk13.red("No frozen Constitution found."));
|
|
6424
|
+
console.log(chalk13.dim("Run `archon interview` to create one first."));
|
|
7432
6425
|
return;
|
|
7433
6426
|
}
|
|
7434
|
-
const constitution = deserializeConstitution(
|
|
6427
|
+
const constitution = deserializeConstitution(readFileSync5(constitutionPath, "utf-8"));
|
|
7435
6428
|
if (constitution.state !== "FROZEN") {
|
|
7436
|
-
console.log(
|
|
7437
|
-
console.log(
|
|
6429
|
+
console.log(chalk13.yellow("Constitution is not frozen yet."));
|
|
6430
|
+
console.log(chalk13.dim("Complete the interview and freeze before generating atoms."));
|
|
7438
6431
|
return;
|
|
7439
6432
|
}
|
|
7440
|
-
console.log(
|
|
7441
|
-
console.log(
|
|
7442
|
-
console.log(
|
|
6433
|
+
console.log(chalk13.bold("\n\u{1F527} Generating Atoms from Constitution\n"));
|
|
6434
|
+
console.log(chalk13.dim(`Project: ${constitution.branding.projectName}`));
|
|
6435
|
+
console.log(chalk13.dim(`Hash: ${constitution.hash?.substring(0, 16)}...`));
|
|
7443
6436
|
console.log();
|
|
7444
6437
|
const spinner = ora3("Generating atoms...").start();
|
|
7445
6438
|
const result = generateAtomsFromConstitution(constitution, {
|
|
@@ -7452,34 +6445,34 @@ async function generateAtoms(options = {}) {
|
|
|
7452
6445
|
console.log(summarizeGeneration(result));
|
|
7453
6446
|
console.log();
|
|
7454
6447
|
if (options.dryRun) {
|
|
7455
|
-
console.log(
|
|
6448
|
+
console.log(chalk13.dim("(Dry run - not writing prd.json)"));
|
|
7456
6449
|
return;
|
|
7457
6450
|
}
|
|
7458
|
-
const prdPath = options.output ??
|
|
6451
|
+
const prdPath = options.output ?? join13(cwd, "prd.json");
|
|
7459
6452
|
const prdContent = formatAsPrdJson(result, constitution);
|
|
7460
|
-
if (
|
|
6453
|
+
if (existsSync13(prdPath)) {
|
|
7461
6454
|
const overwrite = await promptYesNo2("prd.json already exists. Overwrite?", false);
|
|
7462
6455
|
if (!overwrite) {
|
|
7463
|
-
console.log(
|
|
6456
|
+
console.log(chalk13.dim("Cancelled. Existing prd.json preserved."));
|
|
7464
6457
|
return;
|
|
7465
6458
|
}
|
|
7466
6459
|
}
|
|
7467
6460
|
writeFileSync(prdPath, JSON.stringify(prdContent, null, 2));
|
|
7468
|
-
console.log(
|
|
6461
|
+
console.log(chalk13.green(`
|
|
7469
6462
|
\u2713 Written to ${prdPath}`));
|
|
7470
6463
|
console.log();
|
|
7471
|
-
console.log(
|
|
7472
|
-
console.log(` ${
|
|
7473
|
-
console.log(` ${
|
|
7474
|
-
console.log(` ${
|
|
6464
|
+
console.log(chalk13.bold("Next Steps:\n"));
|
|
6465
|
+
console.log(` ${chalk13.cyan("1.")} Run ${chalk13.bold("archon list")} to see all atoms`);
|
|
6466
|
+
console.log(` ${chalk13.cyan("2.")} Run ${chalk13.bold("archon execute ATOM-001")} to start building`);
|
|
6467
|
+
console.log(` ${chalk13.cyan("3.")} Run ${chalk13.bold("archon watch")} to monitor progress`);
|
|
7475
6468
|
console.log();
|
|
7476
6469
|
}
|
|
7477
6470
|
|
|
7478
6471
|
// src/cli/eject.ts
|
|
7479
|
-
import
|
|
7480
|
-
import { existsSync as
|
|
7481
|
-
import { readFile as
|
|
7482
|
-
import { join as
|
|
6472
|
+
import chalk14 from "chalk";
|
|
6473
|
+
import { existsSync as existsSync14, rmSync } from "fs";
|
|
6474
|
+
import { readFile as readFile8, writeFile as writeFile6 } from "fs/promises";
|
|
6475
|
+
import { join as join14 } from "path";
|
|
7483
6476
|
import readline3 from "readline";
|
|
7484
6477
|
var ARCHON_FILES = [
|
|
7485
6478
|
".archon/",
|
|
@@ -7493,70 +6486,70 @@ var METADATA_FILES = [
|
|
|
7493
6486
|
];
|
|
7494
6487
|
async function eject(options = {}) {
|
|
7495
6488
|
const cwd = process.cwd();
|
|
7496
|
-
console.log(
|
|
7497
|
-
const archonDir =
|
|
7498
|
-
if (!
|
|
7499
|
-
console.log(
|
|
6489
|
+
console.log(chalk14.blue("\n\u{1F680} ArchonDev Eject\n"));
|
|
6490
|
+
const archonDir = join14(cwd, ".archon");
|
|
6491
|
+
if (!existsSync14(archonDir)) {
|
|
6492
|
+
console.log(chalk14.yellow("This does not appear to be an ArchonDev project (.archon/ not found)."));
|
|
7500
6493
|
return;
|
|
7501
6494
|
}
|
|
7502
|
-
console.log(
|
|
6495
|
+
console.log(chalk14.dim("The following will be removed/modified:\n"));
|
|
7503
6496
|
const filesToRemove = [];
|
|
7504
6497
|
for (const file of ARCHON_FILES) {
|
|
7505
|
-
const filePath =
|
|
7506
|
-
if (
|
|
6498
|
+
const filePath = join14(cwd, file);
|
|
6499
|
+
if (existsSync14(filePath)) {
|
|
7507
6500
|
filesToRemove.push(file);
|
|
7508
|
-
console.log(
|
|
6501
|
+
console.log(chalk14.red(` \u2717 ${file}`));
|
|
7509
6502
|
}
|
|
7510
6503
|
}
|
|
7511
|
-
const archMd =
|
|
7512
|
-
if (
|
|
7513
|
-
console.log(
|
|
6504
|
+
const archMd = join14(cwd, "ARCHITECTURE.md");
|
|
6505
|
+
if (existsSync14(archMd) && !options.keepArchitecture) {
|
|
6506
|
+
console.log(chalk14.yellow(` ? ARCHITECTURE.md (will be kept by default)`));
|
|
7514
6507
|
}
|
|
7515
6508
|
console.log();
|
|
7516
|
-
console.log(
|
|
6509
|
+
console.log(chalk14.dim("Files to be modified:"));
|
|
7517
6510
|
for (const file of METADATA_FILES) {
|
|
7518
|
-
const filePath =
|
|
7519
|
-
if (
|
|
7520
|
-
console.log(
|
|
6511
|
+
const filePath = join14(cwd, file);
|
|
6512
|
+
if (existsSync14(filePath)) {
|
|
6513
|
+
console.log(chalk14.yellow(` \u25CF ${file}`));
|
|
7521
6514
|
}
|
|
7522
6515
|
}
|
|
7523
6516
|
console.log();
|
|
7524
6517
|
if (!options.force) {
|
|
7525
6518
|
const confirmed = await promptYesNo3("This will permanently remove ArchonDev from your project. Continue?", false);
|
|
7526
6519
|
if (!confirmed) {
|
|
7527
|
-
console.log(
|
|
6520
|
+
console.log(chalk14.dim("Eject cancelled."));
|
|
7528
6521
|
return;
|
|
7529
6522
|
}
|
|
7530
6523
|
}
|
|
7531
6524
|
console.log();
|
|
7532
6525
|
const result = await performEject(cwd, options);
|
|
7533
6526
|
if (result.success) {
|
|
7534
|
-
console.log(
|
|
6527
|
+
console.log(chalk14.green("\n\u2705 Eject complete!\n"));
|
|
7535
6528
|
if (result.filesRemoved.length > 0) {
|
|
7536
|
-
console.log(
|
|
6529
|
+
console.log(chalk14.dim("Removed:"));
|
|
7537
6530
|
for (const file of result.filesRemoved) {
|
|
7538
|
-
console.log(
|
|
6531
|
+
console.log(chalk14.dim(` - ${file}`));
|
|
7539
6532
|
}
|
|
7540
6533
|
}
|
|
7541
6534
|
if (result.filesModified.length > 0) {
|
|
7542
|
-
console.log(
|
|
6535
|
+
console.log(chalk14.dim("\nModified:"));
|
|
7543
6536
|
for (const file of result.filesModified) {
|
|
7544
|
-
console.log(
|
|
6537
|
+
console.log(chalk14.dim(` - ${file}`));
|
|
7545
6538
|
}
|
|
7546
6539
|
}
|
|
7547
6540
|
if (result.warnings.length > 0) {
|
|
7548
|
-
console.log(
|
|
6541
|
+
console.log(chalk14.yellow("\nWarnings:"));
|
|
7549
6542
|
for (const warning of result.warnings) {
|
|
7550
|
-
console.log(
|
|
6543
|
+
console.log(chalk14.yellow(` \u26A0\uFE0F ${warning}`));
|
|
7551
6544
|
}
|
|
7552
6545
|
}
|
|
7553
6546
|
console.log();
|
|
7554
|
-
console.log(
|
|
7555
|
-
console.log(
|
|
6547
|
+
console.log(chalk14.blue("Your project is now a standard repository."));
|
|
6548
|
+
console.log(chalk14.dim("Thank you for using ArchonDev!"));
|
|
7556
6549
|
} else {
|
|
7557
|
-
console.log(
|
|
6550
|
+
console.log(chalk14.red("\n\u274C Eject failed."));
|
|
7558
6551
|
for (const warning of result.warnings) {
|
|
7559
|
-
console.log(
|
|
6552
|
+
console.log(chalk14.red(` ${warning}`));
|
|
7560
6553
|
}
|
|
7561
6554
|
}
|
|
7562
6555
|
}
|
|
@@ -7567,8 +6560,8 @@ async function performEject(cwd, options) {
|
|
|
7567
6560
|
filesModified: [],
|
|
7568
6561
|
warnings: []
|
|
7569
6562
|
};
|
|
7570
|
-
const archonDir =
|
|
7571
|
-
if (
|
|
6563
|
+
const archonDir = join14(cwd, ".archon");
|
|
6564
|
+
if (existsSync14(archonDir)) {
|
|
7572
6565
|
try {
|
|
7573
6566
|
rmSync(archonDir, { recursive: true, force: true });
|
|
7574
6567
|
result.filesRemoved.push(".archon/");
|
|
@@ -7576,8 +6569,8 @@ async function performEject(cwd, options) {
|
|
|
7576
6569
|
result.warnings.push(`Failed to remove .archon/: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7577
6570
|
}
|
|
7578
6571
|
}
|
|
7579
|
-
const prdPath =
|
|
7580
|
-
if (
|
|
6572
|
+
const prdPath = join14(cwd, "prd.json");
|
|
6573
|
+
if (existsSync14(prdPath)) {
|
|
7581
6574
|
try {
|
|
7582
6575
|
rmSync(prdPath);
|
|
7583
6576
|
result.filesRemoved.push("prd.json");
|
|
@@ -7585,8 +6578,8 @@ async function performEject(cwd, options) {
|
|
|
7585
6578
|
result.warnings.push(`Failed to remove prd.json: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7586
6579
|
}
|
|
7587
6580
|
}
|
|
7588
|
-
const progressPath =
|
|
7589
|
-
if (
|
|
6581
|
+
const progressPath = join14(cwd, "progress.txt");
|
|
6582
|
+
if (existsSync14(progressPath)) {
|
|
7590
6583
|
try {
|
|
7591
6584
|
rmSync(progressPath);
|
|
7592
6585
|
result.filesRemoved.push("progress.txt");
|
|
@@ -7594,8 +6587,8 @@ async function performEject(cwd, options) {
|
|
|
7594
6587
|
result.warnings.push(`Failed to remove progress.txt: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7595
6588
|
}
|
|
7596
6589
|
}
|
|
7597
|
-
const promptPath =
|
|
7598
|
-
if (
|
|
6590
|
+
const promptPath = join14(cwd, "prompt.md");
|
|
6591
|
+
if (existsSync14(promptPath)) {
|
|
7599
6592
|
try {
|
|
7600
6593
|
rmSync(promptPath);
|
|
7601
6594
|
result.filesRemoved.push("prompt.md");
|
|
@@ -7603,10 +6596,10 @@ async function performEject(cwd, options) {
|
|
|
7603
6596
|
result.warnings.push(`Failed to remove prompt.md: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
7604
6597
|
}
|
|
7605
6598
|
}
|
|
7606
|
-
const pkgPath =
|
|
7607
|
-
if (
|
|
6599
|
+
const pkgPath = join14(cwd, "package.json");
|
|
6600
|
+
if (existsSync14(pkgPath)) {
|
|
7608
6601
|
try {
|
|
7609
|
-
const pkgContent = await
|
|
6602
|
+
const pkgContent = await readFile8(pkgPath, "utf-8");
|
|
7610
6603
|
const pkg = JSON.parse(pkgContent);
|
|
7611
6604
|
let modified = false;
|
|
7612
6605
|
if ("archondev" in pkg) {
|
|
@@ -7623,7 +6616,7 @@ async function performEject(cwd, options) {
|
|
|
7623
6616
|
}
|
|
7624
6617
|
}
|
|
7625
6618
|
if (modified) {
|
|
7626
|
-
await
|
|
6619
|
+
await writeFile6(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
7627
6620
|
result.filesModified.push("package.json");
|
|
7628
6621
|
}
|
|
7629
6622
|
} catch (error) {
|
|
@@ -7631,10 +6624,10 @@ async function performEject(cwd, options) {
|
|
|
7631
6624
|
}
|
|
7632
6625
|
}
|
|
7633
6626
|
if (!options.keepReadme) {
|
|
7634
|
-
const readmePath =
|
|
7635
|
-
if (
|
|
6627
|
+
const readmePath = join14(cwd, "README.md");
|
|
6628
|
+
if (existsSync14(readmePath)) {
|
|
7636
6629
|
try {
|
|
7637
|
-
const content = await
|
|
6630
|
+
const content = await readFile8(readmePath, "utf-8");
|
|
7638
6631
|
const archonPatterns = [
|
|
7639
6632
|
/## ArchonDev[^#]*(?=##|$)/gi,
|
|
7640
6633
|
/\*This project was scaffolded by ArchonDev\.\*/gi,
|
|
@@ -7651,7 +6644,7 @@ async function performEject(cwd, options) {
|
|
|
7651
6644
|
}
|
|
7652
6645
|
newContent = newContent.replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
7653
6646
|
if (modified) {
|
|
7654
|
-
await
|
|
6647
|
+
await writeFile6(readmePath, newContent);
|
|
7655
6648
|
result.filesModified.push("README.md");
|
|
7656
6649
|
}
|
|
7657
6650
|
} catch (error) {
|
|
@@ -7663,29 +6656,29 @@ async function performEject(cwd, options) {
|
|
|
7663
6656
|
}
|
|
7664
6657
|
async function ejectDryRun() {
|
|
7665
6658
|
const cwd = process.cwd();
|
|
7666
|
-
console.log(
|
|
7667
|
-
const archonDir =
|
|
7668
|
-
if (!
|
|
7669
|
-
console.log(
|
|
6659
|
+
console.log(chalk14.blue("\n\u{1F50D} Eject Dry Run\n"));
|
|
6660
|
+
const archonDir = join14(cwd, ".archon");
|
|
6661
|
+
if (!existsSync14(archonDir)) {
|
|
6662
|
+
console.log(chalk14.yellow("This does not appear to be an ArchonDev project."));
|
|
7670
6663
|
return;
|
|
7671
6664
|
}
|
|
7672
6665
|
console.log("The following would be removed:\n");
|
|
7673
6666
|
for (const file of ARCHON_FILES) {
|
|
7674
|
-
const filePath =
|
|
7675
|
-
if (
|
|
7676
|
-
console.log(
|
|
6667
|
+
const filePath = join14(cwd, file);
|
|
6668
|
+
if (existsSync14(filePath)) {
|
|
6669
|
+
console.log(chalk14.red(` \u2717 ${file}`));
|
|
7677
6670
|
}
|
|
7678
6671
|
}
|
|
7679
6672
|
console.log();
|
|
7680
6673
|
console.log("The following would be modified:\n");
|
|
7681
6674
|
for (const file of METADATA_FILES) {
|
|
7682
|
-
const filePath =
|
|
7683
|
-
if (
|
|
7684
|
-
console.log(
|
|
6675
|
+
const filePath = join14(cwd, file);
|
|
6676
|
+
if (existsSync14(filePath)) {
|
|
6677
|
+
console.log(chalk14.yellow(` \u25CF ${file}`));
|
|
7685
6678
|
}
|
|
7686
6679
|
}
|
|
7687
6680
|
console.log();
|
|
7688
|
-
console.log(
|
|
6681
|
+
console.log(chalk14.dim('Run "archon eject" to proceed.'));
|
|
7689
6682
|
}
|
|
7690
6683
|
function promptYesNo3(question, defaultValue) {
|
|
7691
6684
|
return new Promise((resolve) => {
|
|
@@ -7694,7 +6687,7 @@ function promptYesNo3(question, defaultValue) {
|
|
|
7694
6687
|
output: process.stdout
|
|
7695
6688
|
});
|
|
7696
6689
|
const hint = defaultValue ? "(Y/n)" : "(y/N)";
|
|
7697
|
-
rl.question(`${
|
|
6690
|
+
rl.question(`${chalk14.cyan("?")} ${question} ${hint}: `, (answer) => {
|
|
7698
6691
|
rl.close();
|
|
7699
6692
|
if (answer.trim() === "") {
|
|
7700
6693
|
resolve(defaultValue);
|
|
@@ -7706,11 +6699,11 @@ function promptYesNo3(question, defaultValue) {
|
|
|
7706
6699
|
}
|
|
7707
6700
|
|
|
7708
6701
|
// src/cli/revert.ts
|
|
7709
|
-
import
|
|
6702
|
+
import chalk15 from "chalk";
|
|
7710
6703
|
import { execSync as execSync4 } from "child_process";
|
|
7711
|
-
import { existsSync as
|
|
7712
|
-
import { readFile as
|
|
7713
|
-
import { join as
|
|
6704
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6705
|
+
import { readFile as readFile9, writeFile as writeFile7 } from "fs/promises";
|
|
6706
|
+
import { join as join15 } from "path";
|
|
7714
6707
|
import readline4 from "readline";
|
|
7715
6708
|
async function findAtomCommits(limit = 50) {
|
|
7716
6709
|
const cwd = process.cwd();
|
|
@@ -7751,35 +6744,35 @@ async function findAtomCommits(limit = 50) {
|
|
|
7751
6744
|
}
|
|
7752
6745
|
return commits;
|
|
7753
6746
|
} catch (error) {
|
|
7754
|
-
console.error(
|
|
6747
|
+
console.error(chalk15.red("Failed to read git history:"), error instanceof Error ? error.message : "Unknown error");
|
|
7755
6748
|
return [];
|
|
7756
6749
|
}
|
|
7757
6750
|
}
|
|
7758
6751
|
async function historyCommand(options) {
|
|
7759
6752
|
const limit = options.limit ?? 20;
|
|
7760
|
-
console.log(
|
|
6753
|
+
console.log(chalk15.blue("\n\u{1F4DC} Atom Execution History\n"));
|
|
7761
6754
|
const commits = await findAtomCommits(limit);
|
|
7762
6755
|
if (commits.length === 0) {
|
|
7763
|
-
console.log(
|
|
7764
|
-
console.log(
|
|
6756
|
+
console.log(chalk15.dim("No atom commits found in git history."));
|
|
6757
|
+
console.log(chalk15.dim('Atom commits are created when you run "archon execute <atom-id>".'));
|
|
7765
6758
|
return;
|
|
7766
6759
|
}
|
|
7767
|
-
console.log(
|
|
6760
|
+
console.log(chalk15.dim(`Showing ${commits.length} atom commit(s):
|
|
7768
6761
|
`));
|
|
7769
6762
|
for (const commit of commits) {
|
|
7770
6763
|
const shortHash = commit.commitHash.substring(0, 7);
|
|
7771
6764
|
const filesLabel = commit.filesChanged === 1 ? "file" : "files";
|
|
7772
|
-
console.log(`${
|
|
7773
|
-
console.log(
|
|
7774
|
-
console.log(
|
|
6765
|
+
console.log(`${chalk15.yellow(shortHash)} ${chalk15.cyan(commit.atomId)}`);
|
|
6766
|
+
console.log(chalk15.dim(` ${commit.message}`));
|
|
6767
|
+
console.log(chalk15.dim(` ${commit.date} | ${commit.filesChanged} ${filesLabel} changed`));
|
|
7775
6768
|
console.log();
|
|
7776
6769
|
}
|
|
7777
|
-
console.log(
|
|
7778
|
-
console.log(
|
|
6770
|
+
console.log(chalk15.dim("To revert an atom: archon revert <atom-id>"));
|
|
6771
|
+
console.log(chalk15.dim("Or by commit hash: archon revert --commit <hash>"));
|
|
7779
6772
|
}
|
|
7780
6773
|
async function revertCommand(atomIdOrHash, options = {}) {
|
|
7781
6774
|
const cwd = process.cwd();
|
|
7782
|
-
console.log(
|
|
6775
|
+
console.log(chalk15.blue("\n\u23EA Atom Revert\n"));
|
|
7783
6776
|
let commit;
|
|
7784
6777
|
if (atomIdOrHash.match(/^[a-f0-9]{7,40}$/i)) {
|
|
7785
6778
|
const commits = await findAtomCommits(100);
|
|
@@ -7789,76 +6782,76 @@ async function revertCommand(atomIdOrHash, options = {}) {
|
|
|
7789
6782
|
commit = commits.find((c) => c.atomId.toLowerCase() === atomIdOrHash.toLowerCase());
|
|
7790
6783
|
}
|
|
7791
6784
|
if (!commit) {
|
|
7792
|
-
console.log(
|
|
7793
|
-
console.log(
|
|
6785
|
+
console.log(chalk15.red(`No atom commit found for: ${atomIdOrHash}`));
|
|
6786
|
+
console.log(chalk15.dim('Run "archon history" to see available atom commits.'));
|
|
7794
6787
|
return;
|
|
7795
6788
|
}
|
|
7796
|
-
console.log(`Atom: ${
|
|
7797
|
-
console.log(`Commit: ${
|
|
6789
|
+
console.log(`Atom: ${chalk15.cyan(commit.atomId)}`);
|
|
6790
|
+
console.log(`Commit: ${chalk15.yellow(commit.commitHash.substring(0, 7))}`);
|
|
7798
6791
|
console.log(`Message: ${commit.message}`);
|
|
7799
6792
|
console.log(`Date: ${commit.date}`);
|
|
7800
6793
|
console.log(`Files changed: ${commit.filesChanged}`);
|
|
7801
6794
|
console.log();
|
|
7802
6795
|
try {
|
|
7803
|
-
console.log(
|
|
6796
|
+
console.log(chalk15.dim("Changes to be reverted:"));
|
|
7804
6797
|
const diffStat = execSync4(
|
|
7805
6798
|
`git diff --stat ${commit.commitHash}^..${commit.commitHash}`,
|
|
7806
6799
|
{ cwd, encoding: "utf-8" }
|
|
7807
6800
|
);
|
|
7808
|
-
console.log(
|
|
6801
|
+
console.log(chalk15.dim(diffStat));
|
|
7809
6802
|
} catch {
|
|
7810
6803
|
}
|
|
7811
6804
|
if (!options.force) {
|
|
7812
6805
|
const confirmed = await promptYesNo4("Revert this atom commit?", false);
|
|
7813
6806
|
if (!confirmed) {
|
|
7814
|
-
console.log(
|
|
6807
|
+
console.log(chalk15.dim("Revert cancelled."));
|
|
7815
6808
|
return;
|
|
7816
6809
|
}
|
|
7817
6810
|
}
|
|
7818
6811
|
try {
|
|
7819
6812
|
const revertArgs = options.noCommit ? "--no-commit" : "";
|
|
7820
6813
|
execSync4(`git revert ${revertArgs} ${commit.commitHash}`, { cwd, stdio: "pipe" });
|
|
7821
|
-
console.log(
|
|
6814
|
+
console.log(chalk15.green(`
|
|
7822
6815
|
\u2705 Successfully reverted ${commit.atomId}`));
|
|
7823
6816
|
if (options.noCommit) {
|
|
7824
|
-
console.log(
|
|
7825
|
-
console.log(
|
|
6817
|
+
console.log(chalk15.dim("Changes are staged but not committed."));
|
|
6818
|
+
console.log(chalk15.dim('Run "git commit" to finalize the revert.'));
|
|
7826
6819
|
} else {
|
|
7827
|
-
console.log(
|
|
6820
|
+
console.log(chalk15.dim("A new commit has been created to undo the changes."));
|
|
7828
6821
|
}
|
|
7829
6822
|
await updateAtomStatus(commit.atomId, "REVERTED");
|
|
7830
6823
|
} catch (error) {
|
|
7831
6824
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
7832
6825
|
if (errorMsg.includes("conflict")) {
|
|
7833
|
-
console.log(
|
|
7834
|
-
console.log(
|
|
7835
|
-
console.log(
|
|
7836
|
-
console.log(
|
|
6826
|
+
console.log(chalk15.yellow("\n\u26A0\uFE0F Merge conflict detected during revert."));
|
|
6827
|
+
console.log(chalk15.dim("Resolve conflicts manually, then:"));
|
|
6828
|
+
console.log(chalk15.dim(" git add ."));
|
|
6829
|
+
console.log(chalk15.dim(" git revert --continue"));
|
|
7837
6830
|
} else {
|
|
7838
|
-
console.log(
|
|
7839
|
-
console.log(
|
|
6831
|
+
console.log(chalk15.red("\n\u274C Revert failed:"), errorMsg);
|
|
6832
|
+
console.log(chalk15.dim("You may need to resolve this manually."));
|
|
7840
6833
|
}
|
|
7841
6834
|
}
|
|
7842
6835
|
}
|
|
7843
6836
|
async function updateAtomStatus(atomId, status2) {
|
|
7844
|
-
const atomPath =
|
|
7845
|
-
if (!
|
|
6837
|
+
const atomPath = join15(process.cwd(), ".archon", "atoms", `${atomId}.json`);
|
|
6838
|
+
if (!existsSync15(atomPath)) {
|
|
7846
6839
|
return;
|
|
7847
6840
|
}
|
|
7848
6841
|
try {
|
|
7849
|
-
const content = await
|
|
6842
|
+
const content = await readFile9(atomPath, "utf-8");
|
|
7850
6843
|
const atom = JSON.parse(content);
|
|
7851
6844
|
atom["status"] = status2;
|
|
7852
6845
|
atom["revertedAt"] = (/* @__PURE__ */ new Date()).toISOString();
|
|
7853
|
-
await
|
|
6846
|
+
await writeFile7(atomPath, JSON.stringify(atom, null, 2));
|
|
7854
6847
|
} catch {
|
|
7855
6848
|
}
|
|
7856
6849
|
}
|
|
7857
6850
|
async function revertableAtoms() {
|
|
7858
|
-
console.log(
|
|
6851
|
+
console.log(chalk15.blue("\n\u{1F504} Revertable Atoms\n"));
|
|
7859
6852
|
const commits = await findAtomCommits(30);
|
|
7860
6853
|
if (commits.length === 0) {
|
|
7861
|
-
console.log(
|
|
6854
|
+
console.log(chalk15.dim("No atom commits found."));
|
|
7862
6855
|
return;
|
|
7863
6856
|
}
|
|
7864
6857
|
const atomMap = /* @__PURE__ */ new Map();
|
|
@@ -7867,14 +6860,14 @@ async function revertableAtoms() {
|
|
|
7867
6860
|
atomMap.set(commit.atomId, commit);
|
|
7868
6861
|
}
|
|
7869
6862
|
}
|
|
7870
|
-
console.log(
|
|
6863
|
+
console.log(chalk15.dim(`Found ${atomMap.size} unique atom(s) in history:
|
|
7871
6864
|
`));
|
|
7872
6865
|
for (const [atomId, commit] of atomMap) {
|
|
7873
6866
|
const shortHash = commit.commitHash.substring(0, 7);
|
|
7874
|
-
console.log(` ${
|
|
6867
|
+
console.log(` ${chalk15.cyan(atomId)} ${chalk15.dim(`(${shortHash})`)} - ${commit.message.substring(0, 50)}`);
|
|
7875
6868
|
}
|
|
7876
6869
|
console.log();
|
|
7877
|
-
console.log(
|
|
6870
|
+
console.log(chalk15.dim("To revert: archon revert <atom-id>"));
|
|
7878
6871
|
}
|
|
7879
6872
|
function promptYesNo4(question, defaultValue) {
|
|
7880
6873
|
return new Promise((resolve) => {
|
|
@@ -7883,7 +6876,7 @@ function promptYesNo4(question, defaultValue) {
|
|
|
7883
6876
|
output: process.stdout
|
|
7884
6877
|
});
|
|
7885
6878
|
const hint = defaultValue ? "(Y/n)" : "(y/N)";
|
|
7886
|
-
rl.question(`${
|
|
6879
|
+
rl.question(`${chalk15.cyan("?")} ${question} ${hint}: `, (answer) => {
|
|
7887
6880
|
rl.close();
|
|
7888
6881
|
if (answer.trim() === "") {
|
|
7889
6882
|
resolve(defaultValue);
|
|
@@ -7894,13 +6887,110 @@ function promptYesNo4(question, defaultValue) {
|
|
|
7894
6887
|
});
|
|
7895
6888
|
}
|
|
7896
6889
|
|
|
6890
|
+
// src/cli/models-sync.ts
|
|
6891
|
+
import chalk16 from "chalk";
|
|
6892
|
+
async function modelsSync(options) {
|
|
6893
|
+
const supabaseUrl = process.env["SUPABASE_URL"];
|
|
6894
|
+
const supabaseKey = process.env["SUPABASE_SERVICE_ROLE_KEY"] || process.env["SUPABASE_ANON_KEY"];
|
|
6895
|
+
if (!supabaseUrl) {
|
|
6896
|
+
console.log(chalk16.red("SUPABASE_URL not set. Cannot trigger remote sync."));
|
|
6897
|
+
console.log(chalk16.dim("Run the pricing check locally instead:"));
|
|
6898
|
+
console.log(chalk16.dim(" npx tsx scripts/update-model-pricing.ts --check"));
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6901
|
+
console.log(chalk16.blue("\n\u{1F504} Triggering model registry sync...\n"));
|
|
6902
|
+
try {
|
|
6903
|
+
const functionUrl = `${supabaseUrl}/functions/v1/model-registry-sync`;
|
|
6904
|
+
const response = await fetch(functionUrl, {
|
|
6905
|
+
method: "POST",
|
|
6906
|
+
headers: {
|
|
6907
|
+
"Authorization": `Bearer ${supabaseKey}`,
|
|
6908
|
+
"Content-Type": "application/json"
|
|
6909
|
+
}
|
|
6910
|
+
});
|
|
6911
|
+
if (!response.ok) {
|
|
6912
|
+
const errorText = await response.text();
|
|
6913
|
+
console.log(chalk16.red(`Sync failed: ${response.status} - ${errorText}`));
|
|
6914
|
+
return;
|
|
6915
|
+
}
|
|
6916
|
+
const result = await response.json();
|
|
6917
|
+
console.log(chalk16.bold("Sync Results:"));
|
|
6918
|
+
console.log(` Providers checked: ${result.providersChecked.join(", ")}`);
|
|
6919
|
+
console.log(` Duration: ${result.durationMs}ms`);
|
|
6920
|
+
console.log();
|
|
6921
|
+
console.log(chalk16.bold("Parse Confidence:"));
|
|
6922
|
+
for (const [provider, score] of Object.entries(result.confidenceScores)) {
|
|
6923
|
+
const pct = (score * 100).toFixed(0);
|
|
6924
|
+
const color = score >= 0.8 ? chalk16.green : score >= 0.5 ? chalk16.yellow : chalk16.red;
|
|
6925
|
+
console.log(` ${provider}: ${color(pct + "%")}`);
|
|
6926
|
+
}
|
|
6927
|
+
console.log();
|
|
6928
|
+
if (result.diffs.length === 0) {
|
|
6929
|
+
console.log(chalk16.green("\u2705 All model pricing is up to date. No changes detected."));
|
|
6930
|
+
} else {
|
|
6931
|
+
const pricingChanges = result.diffs.filter((d) => d.type === "PRICING_CHANGE");
|
|
6932
|
+
const newModels = result.diffs.filter((d) => d.type === "NEW_MODEL");
|
|
6933
|
+
if (pricingChanges.length > 0) {
|
|
6934
|
+
console.log(chalk16.yellow(`\u{1F4B0} Pricing Changes (${pricingChanges.length}):`));
|
|
6935
|
+
for (const d of pricingChanges) {
|
|
6936
|
+
console.log(` ${d.provider}/${chalk16.bold(d.modelId)}`);
|
|
6937
|
+
console.log(` Old: ${chalk16.red(d.oldValue ?? "unknown")}`);
|
|
6938
|
+
console.log(` New: ${chalk16.green(d.newValue ?? "unknown")}`);
|
|
6939
|
+
}
|
|
6940
|
+
console.log();
|
|
6941
|
+
}
|
|
6942
|
+
if (newModels.length > 0) {
|
|
6943
|
+
console.log(chalk16.cyan(`\u{1F195} New Models (${newModels.length}):`));
|
|
6944
|
+
for (const d of newModels) {
|
|
6945
|
+
console.log(` ${d.provider}/${chalk16.bold(d.modelId)}: ${d.details}`);
|
|
6946
|
+
}
|
|
6947
|
+
console.log();
|
|
6948
|
+
}
|
|
6949
|
+
console.log(chalk16.bold(`Applied: ${result.modelsUpdated} updated, ${result.modelsAdded} added`));
|
|
6950
|
+
}
|
|
6951
|
+
if (result.parseErrors.length > 0) {
|
|
6952
|
+
console.log();
|
|
6953
|
+
console.log(chalk16.red(`\u26A0\uFE0F Warnings/Errors (${result.parseErrors.length}):`));
|
|
6954
|
+
for (const err of result.parseErrors) {
|
|
6955
|
+
console.log(chalk16.red(` - ${err}`));
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
console.log();
|
|
6959
|
+
} catch (err) {
|
|
6960
|
+
console.log(chalk16.red(`Failed to run sync: ${err}`));
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6963
|
+
async function modelsList() {
|
|
6964
|
+
const { getAllActiveModels, getModelCost, isFreeModel } = await import("./models-UTFGCHAY.js");
|
|
6965
|
+
const models = getAllActiveModels();
|
|
6966
|
+
console.log(chalk16.bold("\n\u{1F4CB} Model Registry\n"));
|
|
6967
|
+
let currentProvider = "";
|
|
6968
|
+
for (const model of models) {
|
|
6969
|
+
if (model.provider !== currentProvider) {
|
|
6970
|
+
currentProvider = model.provider;
|
|
6971
|
+
console.log(chalk16.bold.blue(`
|
|
6972
|
+
${currentProvider.toUpperCase()}`));
|
|
6973
|
+
console.log(chalk16.dim(" \u2500".repeat(40)));
|
|
6974
|
+
}
|
|
6975
|
+
const cost = getModelCost(model.modelId);
|
|
6976
|
+
const free = isFreeModel(model.modelId);
|
|
6977
|
+
const tier = free ? chalk16.green(" [FREE]") : "";
|
|
6978
|
+
const category = model.category === "thinking" ? chalk16.magenta("thinking") : chalk16.cyan("fast");
|
|
6979
|
+
const inputPrice = cost ? `$${(cost.costPer1kInput * 1e3).toFixed(2)}/1M` : "?";
|
|
6980
|
+
const outputPrice = cost ? `$${(cost.costPer1kOutput * 1e3).toFixed(2)}/1M` : "?";
|
|
6981
|
+
console.log(` ${chalk16.bold(model.modelId)}${tier}`);
|
|
6982
|
+
console.log(chalk16.dim(` ${category} | Input: ${inputPrice} | Output: ${outputPrice}`));
|
|
6983
|
+
}
|
|
6984
|
+
console.log();
|
|
6985
|
+
}
|
|
6986
|
+
|
|
7897
6987
|
// src/cli/index.ts
|
|
7898
|
-
var program = new
|
|
6988
|
+
var program = new Command2();
|
|
7899
6989
|
program.name("archon").description("Local-first AI-powered development governance").version(getCurrentVersion()).action(async () => {
|
|
7900
6990
|
const cwd = process.cwd();
|
|
7901
6991
|
const wasInitialized = isInitialized(cwd);
|
|
7902
6992
|
if (!wasInitialized) {
|
|
7903
|
-
console.log(
|
|
6993
|
+
console.log(chalk17.blue("\nArchonDev is not initialized in this folder.\n"));
|
|
7904
6994
|
await init({ analyze: true, git: true });
|
|
7905
6995
|
}
|
|
7906
6996
|
await start({ skipGovernanceBanner: !wasInitialized });
|
|
@@ -7908,7 +6998,7 @@ program.name("archon").description("Local-first AI-powered development governanc
|
|
|
7908
6998
|
program.command("login").description("Authenticate with ArchonDev").option("-p, --provider <provider>", "OAuth provider (github or google)", "github").action(async (options) => {
|
|
7909
6999
|
const provider = options.provider;
|
|
7910
7000
|
if (provider !== "github" && provider !== "google") {
|
|
7911
|
-
console.error(
|
|
7001
|
+
console.error(chalk17.red('Invalid provider. Use "github" or "google"'));
|
|
7912
7002
|
process.exit(1);
|
|
7913
7003
|
}
|
|
7914
7004
|
await login(provider);
|
|
@@ -7920,11 +7010,11 @@ program.command("status").description("Show current user and project status").ac
|
|
|
7920
7010
|
await status();
|
|
7921
7011
|
});
|
|
7922
7012
|
program.command("upgrade").description("Upgrade your tier (BYOK for free unlimited, or Managed plan)").action(async () => {
|
|
7923
|
-
const { showUpgradeMenu } = await import("./tier-selection-
|
|
7013
|
+
const { showUpgradeMenu } = await import("./tier-selection-426HA765.js");
|
|
7924
7014
|
await showUpgradeMenu();
|
|
7925
7015
|
});
|
|
7926
7016
|
program.command("pricing", { hidden: true }).action(async () => {
|
|
7927
|
-
const { showUpgradeMenu } = await import("./tier-selection-
|
|
7017
|
+
const { showUpgradeMenu } = await import("./tier-selection-426HA765.js");
|
|
7928
7018
|
await showUpgradeMenu();
|
|
7929
7019
|
});
|
|
7930
7020
|
program.command("init").description("Initialize ArchonDev in current project").option("--analyze", "Run enhanced analysis of codebase").option("--no-git", "Skip git initialization").action(async (options) => {
|
|
@@ -8091,6 +7181,9 @@ parallelCmd.command("merge [atomId]").description("Merge completed worktrees bac
|
|
|
8091
7181
|
parallelCmd.command("clean").description("Clean up all parallel execution state and worktrees").action(parallelClean);
|
|
8092
7182
|
parallelCmd.command("schedule").description("Analyze atom dependencies and show execution schedule").option("--mermaid", "Output dependency graph as mermaid diagram").option("--max-parallelism <n>", "Maximum atoms per wave", parseInt).option("--only-ready", "Only include READY atoms").action(parallelSchedule);
|
|
8093
7183
|
parallelCmd.command("run-waves").description("Execute all READY atoms in dependency-ordered waves").option("--max-parallelism <n>", "Maximum atoms per wave", parseInt).option("--dry-run", "Show execution plan without running").action(parallelRunWaves);
|
|
7184
|
+
parallelCmd.command("cloud <atomIds...>").description("Queue multiple atoms for cloud execution (Credits tier only)").action(async (atomIds) => {
|
|
7185
|
+
await parallelExecuteCloud(atomIds);
|
|
7186
|
+
});
|
|
8094
7187
|
parallelCmd.action(parallelStatus);
|
|
8095
7188
|
var githubCmd = program.command("github").description("GitHub integration for cloud execution");
|
|
8096
7189
|
githubCmd.command("connect").description("Connect your GitHub account for cloud execution").action(githubConnect);
|
|
@@ -8102,7 +7195,7 @@ cleanupCmd.command("check").description("Analyze workspace for bloat and mainten
|
|
|
8102
7195
|
cleanupCmd.command("run").description("Execute cleanup (archive old entries, remove stale files)").action(cleanupRun);
|
|
8103
7196
|
cleanupCmd.command("auto").description("Enable/disable automatic cleanup checks").argument("[action]", "enable, disable, or status", "status").action(async (action) => {
|
|
8104
7197
|
if (action !== "enable" && action !== "disable" && action !== "status") {
|
|
8105
|
-
console.error(
|
|
7198
|
+
console.error(chalk17.red("Invalid action. Use: enable, disable, or status"));
|
|
8106
7199
|
process.exit(1);
|
|
8107
7200
|
}
|
|
8108
7201
|
await cleanupAuto(action);
|
|
@@ -8137,4 +7230,8 @@ program.command("eject").description("Remove all ArchonDev metadata, leaving a c
|
|
|
8137
7230
|
program.command("history").description("Show atom execution history from git commits").option("-n, --limit <n>", "Number of commits to show", parseInt).action(historyCommand);
|
|
8138
7231
|
program.command("revert <atomIdOrHash>").description("Revert a specific atom execution by atom ID or commit hash").option("--force", "Skip confirmation prompt").option("--no-commit", "Stage changes but do not create revert commit").action(revertCommand);
|
|
8139
7232
|
program.command("revertable").description("List atoms that can be reverted").action(revertableAtoms);
|
|
7233
|
+
var modelsCmd = program.command("models").description("Manage AI model registry and pricing");
|
|
7234
|
+
modelsCmd.command("sync").description("Sync model registry against provider pricing pages").option("--check", "Check only, do not apply changes").action(modelsSync);
|
|
7235
|
+
modelsCmd.command("list").description("List all active models with pricing").action(modelsList);
|
|
7236
|
+
modelsCmd.action(modelsList);
|
|
8140
7237
|
program.parse();
|