archondev 2.1.0 → 2.1.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 +26 -0
- package/dist/index.js +1091 -614
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -81,7 +81,7 @@ import "./chunk-QGM4M3NI.js";
|
|
|
81
81
|
|
|
82
82
|
// src/cli/index.ts
|
|
83
83
|
import { Command as Command4 } from "commander";
|
|
84
|
-
import
|
|
84
|
+
import chalk14 from "chalk";
|
|
85
85
|
import "dotenv/config";
|
|
86
86
|
|
|
87
87
|
// src/cli/promote.ts
|
|
@@ -300,10 +300,10 @@ function formatDateTime(date) {
|
|
|
300
300
|
}
|
|
301
301
|
|
|
302
302
|
// src/cli/start.ts
|
|
303
|
-
import
|
|
303
|
+
import chalk4 from "chalk";
|
|
304
304
|
import readline from "readline";
|
|
305
|
-
import { existsSync as
|
|
306
|
-
import { join as
|
|
305
|
+
import { existsSync as existsSync4, readFileSync, readdirSync as readdirSync2, appendFileSync } from "fs";
|
|
306
|
+
import { join as join4 } from "path";
|
|
307
307
|
|
|
308
308
|
// src/core/context/manager.ts
|
|
309
309
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -400,44 +400,510 @@ var ContextManager = class _ContextManager {
|
|
|
400
400
|
async clearPendingAtoms(cwd) {
|
|
401
401
|
const filePath = join2(cwd, _ContextManager.PENDING_ATOMS_FILE);
|
|
402
402
|
if (existsSync2(filePath)) {
|
|
403
|
-
const { unlink } = await import("fs/promises");
|
|
404
|
-
await
|
|
403
|
+
const { unlink: unlink2 } = await import("fs/promises");
|
|
404
|
+
await unlink2(filePath);
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
+
// src/cli/cleanup.ts
|
|
410
|
+
import chalk3 from "chalk";
|
|
411
|
+
import { existsSync as existsSync3, readdirSync, statSync } from "fs";
|
|
412
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2, unlink, readdir, stat } from "fs/promises";
|
|
413
|
+
import { join as join3 } from "path";
|
|
414
|
+
import yaml from "yaml";
|
|
415
|
+
import { execSync } from "child_process";
|
|
416
|
+
var CONFIG_PATH = ".archon/config.yaml";
|
|
417
|
+
var PROGRESS_FILE = "progress.txt";
|
|
418
|
+
var ARCHON_DIR = ".archon";
|
|
419
|
+
var CACHE_DIR = ".archon/cache";
|
|
420
|
+
var ARCHIVE_DIR = "docs/archive";
|
|
421
|
+
var DEFAULT_THRESHOLDS = {
|
|
422
|
+
progressMaxLines: 500,
|
|
423
|
+
progressMaxKb: 100,
|
|
424
|
+
archonDirMaxMb: 10,
|
|
425
|
+
progressArchiveDays: 30,
|
|
426
|
+
cacheRetentionDays: 7,
|
|
427
|
+
cloudLogRetentionDays: 30
|
|
428
|
+
};
|
|
429
|
+
function formatBytes(bytes) {
|
|
430
|
+
if (bytes === 0) return "0 B";
|
|
431
|
+
const k = 1024;
|
|
432
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
433
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
434
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
435
|
+
}
|
|
436
|
+
function getDirSize(dirPath) {
|
|
437
|
+
if (!existsSync3(dirPath)) return 0;
|
|
438
|
+
let totalSize = 0;
|
|
439
|
+
try {
|
|
440
|
+
const files = readdirSync(dirPath, { withFileTypes: true });
|
|
441
|
+
for (const file of files) {
|
|
442
|
+
const filePath = join3(dirPath, file.name);
|
|
443
|
+
if (file.isDirectory()) {
|
|
444
|
+
totalSize += getDirSize(filePath);
|
|
445
|
+
} else {
|
|
446
|
+
try {
|
|
447
|
+
totalSize += statSync(filePath).size;
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
return totalSize;
|
|
455
|
+
}
|
|
456
|
+
async function loadCleanupConfig(cwd) {
|
|
457
|
+
let cleanup = void 0;
|
|
458
|
+
const projectConfigPath = join3(cwd, "archon.config.yaml");
|
|
459
|
+
if (existsSync3(projectConfigPath)) {
|
|
460
|
+
try {
|
|
461
|
+
const content = await readFile3(projectConfigPath, "utf-8");
|
|
462
|
+
const config = yaml.parse(content);
|
|
463
|
+
if (config.cleanup) {
|
|
464
|
+
cleanup = config.cleanup;
|
|
465
|
+
}
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const localConfigPath = join3(cwd, CONFIG_PATH);
|
|
470
|
+
if (existsSync3(localConfigPath)) {
|
|
471
|
+
try {
|
|
472
|
+
const content = await readFile3(localConfigPath, "utf-8");
|
|
473
|
+
const config = yaml.parse(content);
|
|
474
|
+
if (config.cleanup) {
|
|
475
|
+
cleanup = { ...cleanup, ...config.cleanup };
|
|
476
|
+
}
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return cleanup;
|
|
481
|
+
}
|
|
482
|
+
async function saveCleanupConfig(cwd, cleanup) {
|
|
483
|
+
const configPath = join3(cwd, CONFIG_PATH);
|
|
484
|
+
const archonDir = join3(cwd, ARCHON_DIR);
|
|
485
|
+
if (!existsSync3(archonDir)) {
|
|
486
|
+
await mkdir2(archonDir, { recursive: true });
|
|
487
|
+
}
|
|
488
|
+
let existing = {};
|
|
489
|
+
if (existsSync3(configPath)) {
|
|
490
|
+
try {
|
|
491
|
+
const content = await readFile3(configPath, "utf-8");
|
|
492
|
+
existing = yaml.parse(content);
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
existing.cleanup = cleanup;
|
|
497
|
+
await writeFile3(configPath, yaml.stringify(existing), "utf-8");
|
|
498
|
+
}
|
|
499
|
+
function getOrphanedWorktrees(cwd) {
|
|
500
|
+
const orphaned = [];
|
|
501
|
+
try {
|
|
502
|
+
const output = execSync("git worktree list --porcelain", {
|
|
503
|
+
cwd,
|
|
504
|
+
encoding: "utf-8"
|
|
505
|
+
});
|
|
506
|
+
const lines = output.split("\n");
|
|
507
|
+
let currentWorktree = "";
|
|
508
|
+
for (const line of lines) {
|
|
509
|
+
if (line.startsWith("worktree ")) {
|
|
510
|
+
currentWorktree = line.replace("worktree ", "");
|
|
511
|
+
} else if (line.startsWith("branch ")) {
|
|
512
|
+
if (currentWorktree.includes(".archon-worktree") || currentWorktree.includes("archon-parallel")) {
|
|
513
|
+
if (!existsSync3(join3(currentWorktree, ".git"))) {
|
|
514
|
+
orphaned.push(currentWorktree);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} catch {
|
|
520
|
+
}
|
|
521
|
+
return orphaned;
|
|
522
|
+
}
|
|
523
|
+
async function getStaleFiles(dirPath, maxAgeDays) {
|
|
524
|
+
const staleFiles = [];
|
|
525
|
+
if (!existsSync3(dirPath)) return staleFiles;
|
|
526
|
+
const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
527
|
+
try {
|
|
528
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
529
|
+
for (const entry of entries) {
|
|
530
|
+
const fullPath = join3(dirPath, entry.name);
|
|
531
|
+
if (entry.isFile()) {
|
|
532
|
+
try {
|
|
533
|
+
const stats = await stat(fullPath);
|
|
534
|
+
if (stats.mtimeMs < cutoffTime) {
|
|
535
|
+
staleFiles.push({ path: fullPath, size: stats.size });
|
|
536
|
+
}
|
|
537
|
+
} catch {
|
|
538
|
+
}
|
|
539
|
+
} else if (entry.isDirectory()) {
|
|
540
|
+
const nested = await getStaleFiles(fullPath, maxAgeDays);
|
|
541
|
+
staleFiles.push(...nested);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
} catch {
|
|
545
|
+
}
|
|
546
|
+
return staleFiles;
|
|
547
|
+
}
|
|
548
|
+
async function parseProgressEntries(content) {
|
|
549
|
+
const entries = [];
|
|
550
|
+
const lines = content.split("\n");
|
|
551
|
+
let currentEntry = "";
|
|
552
|
+
let currentDate = null;
|
|
553
|
+
const datePatterns = [
|
|
554
|
+
/^\[(\d{4}-\d{2}-\d{2})/,
|
|
555
|
+
/^## (\d{4}-\d{2}-\d{2})/,
|
|
556
|
+
/^### (\d{4}-\d{2}-\d{2})/,
|
|
557
|
+
/^(\d{4}-\d{2}-\d{2})/
|
|
558
|
+
];
|
|
559
|
+
for (const line of lines) {
|
|
560
|
+
let dateMatch = null;
|
|
561
|
+
for (const pattern of datePatterns) {
|
|
562
|
+
const match = line.match(pattern);
|
|
563
|
+
if (match?.[1]) {
|
|
564
|
+
dateMatch = match[1];
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (dateMatch) {
|
|
569
|
+
if (currentDate && currentEntry.trim()) {
|
|
570
|
+
entries.push({ date: currentDate, content: currentEntry.trim() });
|
|
571
|
+
}
|
|
572
|
+
currentDate = new Date(dateMatch);
|
|
573
|
+
currentEntry = line + "\n";
|
|
574
|
+
} else {
|
|
575
|
+
currentEntry += line + "\n";
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (currentDate && currentEntry.trim()) {
|
|
579
|
+
entries.push({ date: currentDate, content: currentEntry.trim() });
|
|
580
|
+
}
|
|
581
|
+
return entries;
|
|
582
|
+
}
|
|
583
|
+
async function cleanupCheck() {
|
|
584
|
+
const cwd = process.cwd();
|
|
585
|
+
const results = [];
|
|
586
|
+
const config = await loadCleanupConfig(cwd);
|
|
587
|
+
const progressMaxKb = config?.progressMaxKb ?? DEFAULT_THRESHOLDS.progressMaxKb;
|
|
588
|
+
const archonDirMaxMb = config?.archonDirMaxMb ?? DEFAULT_THRESHOLDS.archonDirMaxMb;
|
|
589
|
+
console.log(chalk3.blue("\n\u{1F50D} Analyzing workspace for maintenance needs...\n"));
|
|
590
|
+
const progressPath = join3(cwd, PROGRESS_FILE);
|
|
591
|
+
if (existsSync3(progressPath)) {
|
|
592
|
+
const size = statSync(progressPath).size;
|
|
593
|
+
let status2 = "ok";
|
|
594
|
+
let recommendation;
|
|
595
|
+
if (size > progressMaxKb * 2 * 1024) {
|
|
596
|
+
status2 = "critical";
|
|
597
|
+
recommendation = "Archive old entries with: archon cleanup run";
|
|
598
|
+
} else if (size > progressMaxKb * 1024) {
|
|
599
|
+
status2 = "warn";
|
|
600
|
+
recommendation = "Consider archiving entries older than 30 days";
|
|
601
|
+
}
|
|
602
|
+
results.push({
|
|
603
|
+
name: "progress.txt",
|
|
604
|
+
size,
|
|
605
|
+
sizeFormatted: formatBytes(size),
|
|
606
|
+
status: status2,
|
|
607
|
+
recommendation
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
const archonPath = join3(cwd, ARCHON_DIR);
|
|
611
|
+
if (existsSync3(archonPath)) {
|
|
612
|
+
const size = getDirSize(archonPath);
|
|
613
|
+
let status2 = "ok";
|
|
614
|
+
let recommendation;
|
|
615
|
+
if (size > archonDirMaxMb * 1024 * 1024) {
|
|
616
|
+
status2 = "warn";
|
|
617
|
+
recommendation = "Clear stale cache files with: archon cleanup run";
|
|
618
|
+
}
|
|
619
|
+
results.push({
|
|
620
|
+
name: ".archon/",
|
|
621
|
+
size,
|
|
622
|
+
sizeFormatted: formatBytes(size),
|
|
623
|
+
status: status2,
|
|
624
|
+
recommendation
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
const nodeModulesPath = join3(cwd, "node_modules");
|
|
628
|
+
if (existsSync3(nodeModulesPath)) {
|
|
629
|
+
const size = getDirSize(nodeModulesPath);
|
|
630
|
+
results.push({
|
|
631
|
+
name: "node_modules/",
|
|
632
|
+
size,
|
|
633
|
+
sizeFormatted: formatBytes(size),
|
|
634
|
+
status: "ok",
|
|
635
|
+
recommendation: "Standard npm dependencies (informational)"
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
const orphanedWorktrees = getOrphanedWorktrees(cwd);
|
|
639
|
+
if (orphanedWorktrees.length > 0) {
|
|
640
|
+
results.push({
|
|
641
|
+
name: "Orphaned Worktrees",
|
|
642
|
+
size: orphanedWorktrees.length,
|
|
643
|
+
sizeFormatted: `${orphanedWorktrees.length} found`,
|
|
644
|
+
status: "warn",
|
|
645
|
+
recommendation: "Clean up with: archon cleanup run"
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const cachePath = join3(cwd, CACHE_DIR);
|
|
649
|
+
if (existsSync3(cachePath)) {
|
|
650
|
+
const staleCache = await getStaleFiles(cachePath, 7);
|
|
651
|
+
if (staleCache.length > 0) {
|
|
652
|
+
const totalSize = staleCache.reduce((sum, f) => sum + f.size, 0);
|
|
653
|
+
results.push({
|
|
654
|
+
name: "Stale Cache Files",
|
|
655
|
+
size: totalSize,
|
|
656
|
+
sizeFormatted: `${staleCache.length} files (${formatBytes(totalSize)})`,
|
|
657
|
+
status: "warn",
|
|
658
|
+
recommendation: "Clean up with: archon cleanup run"
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
console.log(chalk3.bold("Workspace Analysis Summary\n"));
|
|
663
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
664
|
+
console.log("\u2502 Item \u2502 Size \u2502 Status \u2502");
|
|
665
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
666
|
+
for (const result of results) {
|
|
667
|
+
const statusIcon = result.status === "critical" ? chalk3.red("\u2717") : result.status === "warn" ? chalk3.yellow("!") : chalk3.green("\u2713");
|
|
668
|
+
const name = result.name.padEnd(23);
|
|
669
|
+
const size = result.sizeFormatted.padEnd(14);
|
|
670
|
+
console.log(`\u2502 ${name} \u2502 ${size} \u2502 ${statusIcon} \u2502`);
|
|
671
|
+
}
|
|
672
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
673
|
+
console.log();
|
|
674
|
+
const recommendations = results.filter((r) => r.recommendation && r.status !== "ok");
|
|
675
|
+
if (recommendations.length > 0) {
|
|
676
|
+
console.log(chalk3.yellow("Recommendations:\n"));
|
|
677
|
+
for (const rec of recommendations) {
|
|
678
|
+
console.log(` ${chalk3.dim("\u2022")} ${rec.name}: ${rec.recommendation}`);
|
|
679
|
+
}
|
|
680
|
+
console.log();
|
|
681
|
+
} else {
|
|
682
|
+
console.log(chalk3.green("\u2713 Workspace is in good shape!\n"));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
async function cleanupRun() {
|
|
686
|
+
const cwd = process.cwd();
|
|
687
|
+
const results = [];
|
|
688
|
+
let totalSaved = 0;
|
|
689
|
+
const config = await loadCleanupConfig(cwd);
|
|
690
|
+
const progressArchiveDays = config?.progressArchiveDays ?? DEFAULT_THRESHOLDS.progressArchiveDays;
|
|
691
|
+
const cacheRetentionDays = config?.cacheRetentionDays ?? DEFAULT_THRESHOLDS.cacheRetentionDays;
|
|
692
|
+
const cloudLogRetentionDays = config?.cloudLogRetentionDays ?? DEFAULT_THRESHOLDS.cloudLogRetentionDays;
|
|
693
|
+
console.log(chalk3.blue("\n\u{1F9F9} Running workspace cleanup...\n"));
|
|
694
|
+
const progressPath = join3(cwd, PROGRESS_FILE);
|
|
695
|
+
if (existsSync3(progressPath)) {
|
|
696
|
+
const content = await readFile3(progressPath, "utf-8");
|
|
697
|
+
const entries = await parseProgressEntries(content);
|
|
698
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
699
|
+
cutoffDate.setDate(cutoffDate.getDate() - progressArchiveDays);
|
|
700
|
+
const oldEntries = entries.filter((e) => e.date < cutoffDate);
|
|
701
|
+
const recentEntries = entries.filter((e) => e.date >= cutoffDate);
|
|
702
|
+
if (oldEntries.length > 0) {
|
|
703
|
+
const archiveDir = join3(cwd, ARCHIVE_DIR);
|
|
704
|
+
if (!existsSync3(archiveDir)) {
|
|
705
|
+
await mkdir2(archiveDir, { recursive: true });
|
|
706
|
+
}
|
|
707
|
+
const archiveFileName = `progress-archive-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.txt`;
|
|
708
|
+
const archivePath = join3(archiveDir, archiveFileName);
|
|
709
|
+
const archiveContent = oldEntries.map((e) => e.content).join("\n\n---\n\n");
|
|
710
|
+
await writeFile3(archivePath, archiveContent, "utf-8");
|
|
711
|
+
const originalSize = statSync(progressPath).size;
|
|
712
|
+
const newContent = recentEntries.map((e) => e.content).join("\n\n");
|
|
713
|
+
await writeFile3(progressPath, newContent, "utf-8");
|
|
714
|
+
const newSize = statSync(progressPath).size;
|
|
715
|
+
const saved = originalSize - newSize;
|
|
716
|
+
results.push({
|
|
717
|
+
name: "progress.txt",
|
|
718
|
+
action: `Archived ${oldEntries.length} entries to ${archiveFileName}`,
|
|
719
|
+
spaceSaved: saved,
|
|
720
|
+
spaceSavedFormatted: formatBytes(saved)
|
|
721
|
+
});
|
|
722
|
+
totalSaved += saved;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
const orphanedWorktrees = getOrphanedWorktrees(cwd);
|
|
726
|
+
for (const worktree of orphanedWorktrees) {
|
|
727
|
+
try {
|
|
728
|
+
execSync(`git worktree remove --force "${worktree}"`, {
|
|
729
|
+
cwd,
|
|
730
|
+
encoding: "utf-8"
|
|
731
|
+
});
|
|
732
|
+
results.push({
|
|
733
|
+
name: "Worktree",
|
|
734
|
+
action: `Removed orphaned worktree: ${worktree}`,
|
|
735
|
+
spaceSaved: 0,
|
|
736
|
+
spaceSavedFormatted: "N/A"
|
|
737
|
+
});
|
|
738
|
+
} catch {
|
|
739
|
+
console.log(chalk3.yellow(` Could not remove worktree: ${worktree}`));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const cachePath = join3(cwd, CACHE_DIR);
|
|
743
|
+
if (existsSync3(cachePath)) {
|
|
744
|
+
const staleCache = await getStaleFiles(cachePath, cacheRetentionDays);
|
|
745
|
+
let cacheSaved = 0;
|
|
746
|
+
for (const file of staleCache) {
|
|
747
|
+
try {
|
|
748
|
+
await unlink(file.path);
|
|
749
|
+
cacheSaved += file.size;
|
|
750
|
+
} catch {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (staleCache.length > 0) {
|
|
754
|
+
results.push({
|
|
755
|
+
name: "Cache",
|
|
756
|
+
action: `Removed ${staleCache.length} stale cache files`,
|
|
757
|
+
spaceSaved: cacheSaved,
|
|
758
|
+
spaceSavedFormatted: formatBytes(cacheSaved)
|
|
759
|
+
});
|
|
760
|
+
totalSaved += cacheSaved;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const cloudLogsPath = join3(cwd, ".archon", "cloud-logs");
|
|
764
|
+
if (existsSync3(cloudLogsPath)) {
|
|
765
|
+
const staleLogs = await getStaleFiles(cloudLogsPath, cloudLogRetentionDays);
|
|
766
|
+
let logsSaved = 0;
|
|
767
|
+
for (const file of staleLogs) {
|
|
768
|
+
try {
|
|
769
|
+
await unlink(file.path);
|
|
770
|
+
logsSaved += file.size;
|
|
771
|
+
} catch {
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (staleLogs.length > 0) {
|
|
775
|
+
results.push({
|
|
776
|
+
name: "Cloud Logs",
|
|
777
|
+
action: `Removed ${staleLogs.length} stale log files`,
|
|
778
|
+
spaceSaved: logsSaved,
|
|
779
|
+
spaceSavedFormatted: formatBytes(logsSaved)
|
|
780
|
+
});
|
|
781
|
+
totalSaved += logsSaved;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (results.length === 0) {
|
|
785
|
+
console.log(chalk3.green("\u2713 Nothing to clean up!\n"));
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
console.log(chalk3.bold("Cleanup Results\n"));
|
|
789
|
+
for (const result of results) {
|
|
790
|
+
console.log(` ${chalk3.green("\u2713")} ${result.name}: ${result.action}`);
|
|
791
|
+
if (result.spaceSaved > 0) {
|
|
792
|
+
console.log(chalk3.dim(` Space saved: ${result.spaceSavedFormatted}`));
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
console.log();
|
|
796
|
+
console.log(chalk3.green(`\u2713 Total space saved: ${formatBytes(totalSaved)}
|
|
797
|
+
`));
|
|
798
|
+
}
|
|
799
|
+
async function cleanupAuto(action) {
|
|
800
|
+
const cwd = process.cwd();
|
|
801
|
+
const config = await loadCleanupConfig(cwd);
|
|
802
|
+
if (action === "status") {
|
|
803
|
+
const enabled = config?.autoEnabled ?? false;
|
|
804
|
+
console.log(chalk3.blue("\n\u{1F527} Auto Cleanup Settings\n"));
|
|
805
|
+
console.log(
|
|
806
|
+
` Status: ${enabled ? chalk3.green("Enabled") : chalk3.dim("Disabled")}`
|
|
807
|
+
);
|
|
808
|
+
console.log(
|
|
809
|
+
chalk3.dim(
|
|
810
|
+
` Progress archive threshold: ${config?.progressArchiveDays ?? 30} days`
|
|
811
|
+
)
|
|
812
|
+
);
|
|
813
|
+
console.log(
|
|
814
|
+
chalk3.dim(` Cache retention: ${config?.cacheRetentionDays ?? 7} days`)
|
|
815
|
+
);
|
|
816
|
+
console.log(
|
|
817
|
+
chalk3.dim(
|
|
818
|
+
` Cloud log retention: ${config?.cloudLogRetentionDays ?? 30} days`
|
|
819
|
+
)
|
|
820
|
+
);
|
|
821
|
+
console.log();
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const newConfig = {
|
|
825
|
+
autoEnabled: action === "enable",
|
|
826
|
+
progressArchiveDays: config?.progressArchiveDays ?? 30,
|
|
827
|
+
cacheRetentionDays: config?.cacheRetentionDays ?? 7,
|
|
828
|
+
cloudLogRetentionDays: config?.cloudLogRetentionDays ?? 30
|
|
829
|
+
};
|
|
830
|
+
await saveCleanupConfig(cwd, newConfig);
|
|
831
|
+
if (action === "enable") {
|
|
832
|
+
console.log(chalk3.green("\n\u2713 Auto cleanup enabled"));
|
|
833
|
+
console.log(
|
|
834
|
+
chalk3.dim(
|
|
835
|
+
' Cleanup check will run on "archon start" and warn if action needed\n'
|
|
836
|
+
)
|
|
837
|
+
);
|
|
838
|
+
} else {
|
|
839
|
+
console.log(chalk3.green("\n\u2713 Auto cleanup disabled\n"));
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
async function shouldRunAutoCleanup(cwd) {
|
|
843
|
+
const config = await loadCleanupConfig(cwd);
|
|
844
|
+
return config?.autoEnabled ?? false;
|
|
845
|
+
}
|
|
846
|
+
async function runAutoCleanupCheck(cwd) {
|
|
847
|
+
const progressPath = join3(cwd, PROGRESS_FILE);
|
|
848
|
+
const archonPath = join3(cwd, ARCHON_DIR);
|
|
849
|
+
const config = await loadCleanupConfig(cwd);
|
|
850
|
+
const progressMaxKb = config?.progressMaxKb ?? DEFAULT_THRESHOLDS.progressMaxKb;
|
|
851
|
+
const archonDirMaxMb = config?.archonDirMaxMb ?? DEFAULT_THRESHOLDS.archonDirMaxMb;
|
|
852
|
+
let needsAttention = false;
|
|
853
|
+
if (existsSync3(progressPath)) {
|
|
854
|
+
const size = statSync(progressPath).size;
|
|
855
|
+
if (size > progressMaxKb * 1024) {
|
|
856
|
+
needsAttention = true;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (existsSync3(archonPath)) {
|
|
860
|
+
const size = getDirSize(archonPath);
|
|
861
|
+
if (size > archonDirMaxMb * 1024 * 1024) {
|
|
862
|
+
needsAttention = true;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const orphaned = getOrphanedWorktrees(cwd);
|
|
866
|
+
if (orphaned.length > 0) {
|
|
867
|
+
needsAttention = true;
|
|
868
|
+
}
|
|
869
|
+
return needsAttention;
|
|
870
|
+
}
|
|
871
|
+
|
|
409
872
|
// src/cli/start.ts
|
|
410
873
|
async function start() {
|
|
411
874
|
const cwd = process.cwd();
|
|
412
|
-
console.log(
|
|
413
|
-
console.log(
|
|
414
|
-
console.log(
|
|
875
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
876
|
+
console.log(chalk4.bold.white(" ArchonDev - AI-Powered Development Governance"));
|
|
877
|
+
console.log(chalk4.blue("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
415
878
|
const contextManager = new ContextManager();
|
|
416
879
|
const pendingAtomsData = await contextManager.getPendingAtomsData(cwd);
|
|
417
880
|
if (pendingAtomsData && pendingAtomsData.atoms.length > 0) {
|
|
418
|
-
console.log(
|
|
881
|
+
console.log(chalk4.yellow("\u26A1 Previous session had pending atoms:\n"));
|
|
419
882
|
for (const atomId of pendingAtomsData.atoms) {
|
|
420
|
-
console.log(
|
|
883
|
+
console.log(chalk4.dim(` \u2022 ${atomId}`));
|
|
421
884
|
}
|
|
422
885
|
console.log();
|
|
423
|
-
console.log(
|
|
424
|
-
console.log(
|
|
886
|
+
console.log(chalk4.dim(` Saved at: ${pendingAtomsData.savedAt}`));
|
|
887
|
+
console.log(chalk4.dim(` Context usage was: ${(pendingAtomsData.contextState.usagePercent * 100).toFixed(0)}%`));
|
|
425
888
|
console.log();
|
|
426
889
|
const resume = await promptYesNo("Resume with these atoms?", true);
|
|
427
890
|
if (resume) {
|
|
428
|
-
console.log(
|
|
891
|
+
console.log(chalk4.green("\n\u2713 Resuming with pending atoms...\n"));
|
|
429
892
|
await contextManager.clearPendingAtoms(cwd);
|
|
430
893
|
} else {
|
|
431
894
|
const clear = await promptYesNo("Clear pending atoms?", false);
|
|
432
895
|
if (clear) {
|
|
433
896
|
await contextManager.clearPendingAtoms(cwd);
|
|
434
|
-
console.log(
|
|
897
|
+
console.log(chalk4.dim("Cleared pending atoms.\n"));
|
|
435
898
|
}
|
|
436
899
|
}
|
|
437
900
|
}
|
|
438
901
|
const projectState = detectProjectState(cwd);
|
|
439
902
|
const governanceStatus = await gatherGovernanceStatus(cwd);
|
|
440
903
|
displayGovernanceBanner(governanceStatus);
|
|
904
|
+
if (await shouldRunAutoCleanup(cwd)) {
|
|
905
|
+
await runAutoCleanupCheck(cwd);
|
|
906
|
+
}
|
|
441
907
|
switch (projectState.scenario) {
|
|
442
908
|
case "NEW_PROJECT":
|
|
443
909
|
await handleNewProject(cwd, projectState);
|
|
@@ -455,14 +921,14 @@ function detectProjectState(cwd) {
|
|
|
455
921
|
const sourceExtensions = [".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java", ".rb", ".php"];
|
|
456
922
|
let hasSourceFiles = false;
|
|
457
923
|
for (const dir of sourceDirs) {
|
|
458
|
-
if (
|
|
924
|
+
if (existsSync4(join4(cwd, dir))) {
|
|
459
925
|
hasSourceFiles = true;
|
|
460
926
|
break;
|
|
461
927
|
}
|
|
462
928
|
}
|
|
463
929
|
if (!hasSourceFiles) {
|
|
464
930
|
try {
|
|
465
|
-
const files =
|
|
931
|
+
const files = readdirSync2(cwd);
|
|
466
932
|
hasSourceFiles = files.some(
|
|
467
933
|
(f) => sourceExtensions.some((ext) => f.endsWith(ext))
|
|
468
934
|
);
|
|
@@ -471,16 +937,16 @@ function detectProjectState(cwd) {
|
|
|
471
937
|
}
|
|
472
938
|
const projectMarkers = ["package.json", "Cargo.toml", "pyproject.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
473
939
|
if (!hasSourceFiles) {
|
|
474
|
-
hasSourceFiles = projectMarkers.some((marker) =>
|
|
940
|
+
hasSourceFiles = projectMarkers.some((marker) => existsSync4(join4(cwd, marker)));
|
|
475
941
|
}
|
|
476
|
-
const hasArchitecture =
|
|
477
|
-
const hasProgress =
|
|
478
|
-
const hasReviewDb =
|
|
942
|
+
const hasArchitecture = existsSync4(join4(cwd, "ARCHITECTURE.md"));
|
|
943
|
+
const hasProgress = existsSync4(join4(cwd, "progress.txt"));
|
|
944
|
+
const hasReviewDb = existsSync4(join4(cwd, "docs", "code-review", "review-tasks.db"));
|
|
479
945
|
let hasProgressEntries = false;
|
|
480
946
|
let lastProgressEntry;
|
|
481
947
|
if (hasProgress) {
|
|
482
948
|
try {
|
|
483
|
-
const progressContent = readFileSync(
|
|
949
|
+
const progressContent = readFileSync(join4(cwd, "progress.txt"), "utf-8");
|
|
484
950
|
const entries = progressContent.match(/^## \d{4}-\d{2}-\d{2}/gm);
|
|
485
951
|
hasProgressEntries = entries !== null && entries.length > 0;
|
|
486
952
|
if (hasProgressEntries) {
|
|
@@ -519,8 +985,8 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
519
985
|
dependencyRulesCount: 0,
|
|
520
986
|
pendingAtomsCount: 0
|
|
521
987
|
};
|
|
522
|
-
const archPath =
|
|
523
|
-
if (
|
|
988
|
+
const archPath = join4(cwd, "ARCHITECTURE.md");
|
|
989
|
+
if (existsSync4(archPath)) {
|
|
524
990
|
const parser = new ArchitectureParser(archPath);
|
|
525
991
|
const result = await parser.parse();
|
|
526
992
|
if (result.success && result.schema) {
|
|
@@ -537,8 +1003,8 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
537
1003
|
status2.dependencyRulesCount = depResult.document.rules.length;
|
|
538
1004
|
}
|
|
539
1005
|
}
|
|
540
|
-
const progressPath =
|
|
541
|
-
if (
|
|
1006
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1007
|
+
if (existsSync4(progressPath)) {
|
|
542
1008
|
try {
|
|
543
1009
|
const content = readFileSync(progressPath, "utf-8");
|
|
544
1010
|
const dateMatches = content.match(/^## (\d{4}-\d{2}-\d{2})/gm);
|
|
@@ -552,10 +1018,10 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
552
1018
|
} catch {
|
|
553
1019
|
}
|
|
554
1020
|
}
|
|
555
|
-
const atomsDir =
|
|
556
|
-
if (
|
|
1021
|
+
const atomsDir = join4(cwd, ".archon", "atoms");
|
|
1022
|
+
if (existsSync4(atomsDir)) {
|
|
557
1023
|
try {
|
|
558
|
-
const files =
|
|
1024
|
+
const files = readdirSync2(atomsDir);
|
|
559
1025
|
status2.pendingAtomsCount = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).length;
|
|
560
1026
|
} catch {
|
|
561
1027
|
}
|
|
@@ -563,35 +1029,35 @@ async function gatherGovernanceStatus(cwd) {
|
|
|
563
1029
|
return status2;
|
|
564
1030
|
}
|
|
565
1031
|
function displayGovernanceBanner(status2) {
|
|
566
|
-
console.log(
|
|
567
|
-
console.log(
|
|
568
|
-
console.log(
|
|
1032
|
+
console.log(chalk4.blue("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
1033
|
+
console.log(chalk4.bold.white(" ArchonDev Governance Active"));
|
|
1034
|
+
console.log(chalk4.blue("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
569
1035
|
if (status2.hasArchitecture) {
|
|
570
|
-
console.log(
|
|
571
|
-
console.log(
|
|
572
|
-
console.log(
|
|
1036
|
+
console.log(chalk4.green(" \u2713") + ` ARCHITECTURE.md loaded (${status2.posture} posture)`);
|
|
1037
|
+
console.log(chalk4.green(" \u2713") + ` ${status2.invariantsCount} invariants enforced`);
|
|
1038
|
+
console.log(chalk4.green(" \u2713") + ` ${status2.protectedPathsCount} protected paths defined`);
|
|
573
1039
|
} else {
|
|
574
|
-
console.log(
|
|
1040
|
+
console.log(chalk4.yellow(" \u26A0") + " ARCHITECTURE.md not found - run " + chalk4.cyan("archon init"));
|
|
575
1041
|
}
|
|
576
|
-
console.log(
|
|
1042
|
+
console.log(chalk4.green(" \u2713") + ` ${status2.dependencyRulesCount} dependency rules active`);
|
|
577
1043
|
if (status2.lastSessionDate) {
|
|
578
|
-
console.log(
|
|
1044
|
+
console.log(chalk4.green(" \u2713") + ` Last session: ${status2.lastSessionDate}`);
|
|
579
1045
|
}
|
|
580
1046
|
if (status2.pendingAtomsCount > 0) {
|
|
581
1047
|
console.log();
|
|
582
|
-
console.log(
|
|
1048
|
+
console.log(chalk4.cyan(` Pending: ${status2.pendingAtomsCount} atoms ready for execution`));
|
|
583
1049
|
}
|
|
584
|
-
console.log(
|
|
1050
|
+
console.log(chalk4.blue("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
585
1051
|
}
|
|
586
1052
|
async function handleNewProject(cwd, state) {
|
|
587
|
-
console.log(
|
|
588
|
-
console.log(
|
|
589
|
-
console.log(
|
|
590
|
-
console.log(
|
|
591
|
-
console.log(` ${
|
|
592
|
-
console.log(` ${
|
|
593
|
-
console.log(` ${
|
|
594
|
-
console.log(` ${
|
|
1053
|
+
console.log(chalk4.yellow("\u{1F389}") + chalk4.bold(" Starting a new project? Great!\n"));
|
|
1054
|
+
console.log(chalk4.dim("I'll ask you a few quick questions to set things up right."));
|
|
1055
|
+
console.log(chalk4.dim("Answer as much or as little as you want \u2014 you can always refine later.\n"));
|
|
1056
|
+
console.log(chalk4.bold("What would you like to do?\n"));
|
|
1057
|
+
console.log(` ${chalk4.cyan("1")}) ${chalk4.bold("Start interview")} \u2014 I'll ask questions to understand your project`);
|
|
1058
|
+
console.log(` ${chalk4.cyan("2")}) ${chalk4.bold("Quick start")} \u2014 Just create basic governance files`);
|
|
1059
|
+
console.log(` ${chalk4.cyan("3")}) ${chalk4.bold("Import from template")} \u2014 Use a predefined project template`);
|
|
1060
|
+
console.log(` ${chalk4.cyan("q")}) ${chalk4.dim("Quit")}`);
|
|
595
1061
|
console.log();
|
|
596
1062
|
const choice = await prompt("Enter choice");
|
|
597
1063
|
switch (choice.toLowerCase()) {
|
|
@@ -602,19 +1068,19 @@ async function handleNewProject(cwd, state) {
|
|
|
602
1068
|
await quickStart(cwd);
|
|
603
1069
|
break;
|
|
604
1070
|
case "3":
|
|
605
|
-
console.log(
|
|
1071
|
+
console.log(chalk4.yellow("\nTemplates coming soon! Using quick start for now.\n"));
|
|
606
1072
|
await quickStart(cwd);
|
|
607
1073
|
break;
|
|
608
1074
|
case "q":
|
|
609
1075
|
process.exit(0);
|
|
610
1076
|
default:
|
|
611
|
-
console.log(
|
|
1077
|
+
console.log(chalk4.yellow("Invalid choice. Please try again."));
|
|
612
1078
|
await handleNewProject(cwd, state);
|
|
613
1079
|
}
|
|
614
1080
|
}
|
|
615
1081
|
async function runNewProjectInterview(cwd) {
|
|
616
|
-
console.log(
|
|
617
|
-
console.log(
|
|
1082
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Project Interview \u2501\u2501\u2501\n"));
|
|
1083
|
+
console.log(chalk4.bold("Phase 1: The Vision\n"));
|
|
618
1084
|
const projectName = await prompt("What's the project name?");
|
|
619
1085
|
const projectDescription = await prompt("In one sentence, what does this project do?");
|
|
620
1086
|
const audience = await promptChoice("Who is it for?", [
|
|
@@ -627,7 +1093,7 @@ async function runNewProjectInterview(cwd) {
|
|
|
627
1093
|
{ key: "2", label: "\u{1F7E1} Intermediate \u2014 I've done similar work" },
|
|
628
1094
|
{ key: "3", label: "\u{1F534} Learning \u2014 This is new to me" }
|
|
629
1095
|
]);
|
|
630
|
-
console.log(
|
|
1096
|
+
console.log(chalk4.bold("\nPhase 2: Tech Stack\n"));
|
|
631
1097
|
const language = await promptChoice("Primary language/framework?", [
|
|
632
1098
|
{ key: "1", label: "TypeScript / JavaScript" },
|
|
633
1099
|
{ key: "2", label: "Python" },
|
|
@@ -641,10 +1107,10 @@ async function runNewProjectInterview(cwd) {
|
|
|
641
1107
|
{ key: "3", label: "Full-stack (both)" },
|
|
642
1108
|
{ key: "4", label: "Library/package" }
|
|
643
1109
|
]);
|
|
644
|
-
console.log(
|
|
1110
|
+
console.log(chalk4.bold("\nPhase 3: Preferences ") + chalk4.dim("(press Enter to skip)\n"));
|
|
645
1111
|
const protectedFiles = await prompt("Any files AI should NEVER modify without asking? (comma-separated)");
|
|
646
1112
|
const noNoPatterns = await prompt('Anything AI should NEVER do? (e.g., "no console.log")');
|
|
647
|
-
console.log(
|
|
1113
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Generating Project Files \u2501\u2501\u2501\n"));
|
|
648
1114
|
const { init: init2 } = await import("./init-7EWVBX6O.js");
|
|
649
1115
|
await init2({ analyze: false, git: true });
|
|
650
1116
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -670,16 +1136,16 @@ ${noNoPatterns ? `- **Forbidden patterns:** ${noNoPatterns}` : "- No forbidden p
|
|
|
670
1136
|
- .archon/config.yaml
|
|
671
1137
|
- progress.txt
|
|
672
1138
|
`;
|
|
673
|
-
const progressPath =
|
|
674
|
-
if (!
|
|
1139
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1140
|
+
if (!existsSync4(progressPath)) {
|
|
675
1141
|
const { writeFileSync } = await import("fs");
|
|
676
1142
|
writeFileSync(progressPath, "# ArchonDev Progress Log\n\nThis file tracks learnings and decisions across sessions.\n");
|
|
677
1143
|
}
|
|
678
1144
|
appendFileSync(progressPath, progressEntry);
|
|
679
|
-
console.log(
|
|
680
|
-
console.log(
|
|
681
|
-
console.log(` 1. ${
|
|
682
|
-
console.log(` 2. ${
|
|
1145
|
+
console.log(chalk4.green("\n\u2713 Project initialized!\n"));
|
|
1146
|
+
console.log(chalk4.bold("Next steps:"));
|
|
1147
|
+
console.log(` 1. ${chalk4.cyan("Review")} ARCHITECTURE.md and customize if needed`);
|
|
1148
|
+
console.log(` 2. ${chalk4.cyan("Run")} ${chalk4.dim('archon plan "your first task"')} to create an atom`);
|
|
683
1149
|
console.log();
|
|
684
1150
|
const continueChoice = await promptYesNo("Would you like to plan your first task now?", true);
|
|
685
1151
|
if (continueChoice) {
|
|
@@ -691,12 +1157,12 @@ ${noNoPatterns ? `- **Forbidden patterns:** ${noNoPatterns}` : "- No forbidden p
|
|
|
691
1157
|
}
|
|
692
1158
|
}
|
|
693
1159
|
async function quickStart(cwd) {
|
|
694
|
-
console.log(
|
|
1160
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Quick Start \u2501\u2501\u2501\n"));
|
|
695
1161
|
const { init: init2 } = await import("./init-7EWVBX6O.js");
|
|
696
1162
|
await init2({ analyze: false, git: true });
|
|
697
1163
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
698
|
-
const progressPath =
|
|
699
|
-
if (!
|
|
1164
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1165
|
+
if (!existsSync4(progressPath)) {
|
|
700
1166
|
const { writeFileSync } = await import("fs");
|
|
701
1167
|
writeFileSync(progressPath, `# ArchonDev Progress Log
|
|
702
1168
|
|
|
@@ -718,15 +1184,15 @@ This file tracks learnings and decisions across sessions.
|
|
|
718
1184
|
await showMainMenu();
|
|
719
1185
|
}
|
|
720
1186
|
async function handleAdaptExisting(cwd, state) {
|
|
721
|
-
console.log(
|
|
722
|
-
console.log(
|
|
723
|
-
console.log(
|
|
724
|
-
console.log(
|
|
725
|
-
console.log(` ${
|
|
726
|
-
console.log(` ${
|
|
727
|
-
console.log(` ${
|
|
728
|
-
console.log(` ${
|
|
729
|
-
console.log(` ${
|
|
1187
|
+
console.log(chalk4.yellow("\u{1F4C1}") + chalk4.bold(" Existing project detected!\n"));
|
|
1188
|
+
console.log(chalk4.dim("I can analyze your codebase and adapt the governance files to match your structure."));
|
|
1189
|
+
console.log(chalk4.dim("This helps me understand your architecture without changing any code.\n"));
|
|
1190
|
+
console.log(chalk4.bold("What would you like to do?\n"));
|
|
1191
|
+
console.log(` ${chalk4.cyan("1")}) ${chalk4.bold("Analyze and adapt")} \u2014 I'll scan your project and update ARCHITECTURE.md`);
|
|
1192
|
+
console.log(` ${chalk4.cyan("2")}) ${chalk4.bold("Code review first")} \u2014 Review code for issues before setting up governance`);
|
|
1193
|
+
console.log(` ${chalk4.cyan("3")}) ${chalk4.bold("Manual setup")} \u2014 Keep template files, customize manually`);
|
|
1194
|
+
console.log(` ${chalk4.cyan("4")}) ${chalk4.bold("Just start working")} \u2014 Skip setup, use defaults`);
|
|
1195
|
+
console.log(` ${chalk4.cyan("q")}) ${chalk4.dim("Quit")}`);
|
|
730
1196
|
console.log();
|
|
731
1197
|
const choice = await prompt("Enter choice");
|
|
732
1198
|
switch (choice.toLowerCase()) {
|
|
@@ -745,17 +1211,17 @@ async function handleAdaptExisting(cwd, state) {
|
|
|
745
1211
|
case "q":
|
|
746
1212
|
process.exit(0);
|
|
747
1213
|
default:
|
|
748
|
-
console.log(
|
|
1214
|
+
console.log(chalk4.yellow("Invalid choice. Please try again."));
|
|
749
1215
|
await handleAdaptExisting(cwd, state);
|
|
750
1216
|
}
|
|
751
1217
|
}
|
|
752
1218
|
async function analyzeAndAdapt(cwd) {
|
|
753
|
-
console.log(
|
|
1219
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Analyzing Project \u2501\u2501\u2501\n"));
|
|
754
1220
|
const { init: init2 } = await import("./init-7EWVBX6O.js");
|
|
755
1221
|
await init2({ analyze: true, git: true });
|
|
756
1222
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
757
|
-
const progressPath =
|
|
758
|
-
if (!
|
|
1223
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1224
|
+
if (!existsSync4(progressPath)) {
|
|
759
1225
|
const { writeFileSync } = await import("fs");
|
|
760
1226
|
writeFileSync(progressPath, "# ArchonDev Progress Log\n\nThis file tracks learnings and decisions across sessions.\n");
|
|
761
1227
|
}
|
|
@@ -772,15 +1238,15 @@ async function analyzeAndAdapt(cwd) {
|
|
|
772
1238
|
- .archon/config.yaml - Build commands configured
|
|
773
1239
|
- progress.txt - This file
|
|
774
1240
|
`);
|
|
775
|
-
console.log(
|
|
1241
|
+
console.log(chalk4.green("\n\u2713 Governance files adapted!\n"));
|
|
776
1242
|
await showMainMenu();
|
|
777
1243
|
}
|
|
778
1244
|
async function codeReviewFirst(cwd) {
|
|
779
|
-
console.log(
|
|
780
|
-
console.log(
|
|
1245
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Code Review Mode \u2501\u2501\u2501\n"));
|
|
1246
|
+
console.log(chalk4.dim("I'll analyze your code for issues without making any changes.\n"));
|
|
781
1247
|
const { reviewInit: reviewInit2, reviewAnalyze: reviewAnalyze2, reviewRun: reviewRun2 } = await import("./review-3R6QXAXQ.js");
|
|
782
|
-
const reviewDbPath =
|
|
783
|
-
if (!
|
|
1248
|
+
const reviewDbPath = join4(cwd, "docs", "code-review", "review-tasks.db");
|
|
1249
|
+
if (!existsSync4(reviewDbPath)) {
|
|
784
1250
|
await reviewInit2();
|
|
785
1251
|
}
|
|
786
1252
|
await reviewAnalyze2();
|
|
@@ -788,27 +1254,27 @@ async function codeReviewFirst(cwd) {
|
|
|
788
1254
|
if (runReview) {
|
|
789
1255
|
await reviewRun2({ all: true });
|
|
790
1256
|
}
|
|
791
|
-
console.log(
|
|
1257
|
+
console.log(chalk4.dim("\nAfter reviewing, you can run ") + chalk4.cyan("archon") + chalk4.dim(" again to set up governance.\n"));
|
|
792
1258
|
}
|
|
793
1259
|
async function manualSetup(cwd) {
|
|
794
|
-
console.log(
|
|
795
|
-
console.log(
|
|
1260
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Manual Setup \u2501\u2501\u2501\n"));
|
|
1261
|
+
console.log(chalk4.dim("Creating template files. You can customize them manually.\n"));
|
|
796
1262
|
const { init: init2 } = await import("./init-7EWVBX6O.js");
|
|
797
1263
|
await init2({ analyze: false, git: true });
|
|
798
|
-
console.log(
|
|
799
|
-
console.log(` ${
|
|
800
|
-
console.log(` ${
|
|
801
|
-
console.log(` ${
|
|
1264
|
+
console.log(chalk4.bold("\nWhat to customize:\n"));
|
|
1265
|
+
console.log(` ${chalk4.cyan("1. ARCHITECTURE.md")} \u2014 Update components to match your folders`);
|
|
1266
|
+
console.log(` ${chalk4.cyan("2. .archon/config.yaml")} \u2014 Change build/test/lint commands`);
|
|
1267
|
+
console.log(` ${chalk4.cyan("3. progress.txt")} \u2014 Add project-specific patterns`);
|
|
802
1268
|
console.log();
|
|
803
1269
|
await showMainMenu();
|
|
804
1270
|
}
|
|
805
1271
|
async function quickAdapt(cwd) {
|
|
806
|
-
console.log(
|
|
1272
|
+
console.log(chalk4.blue("\n\u26A1 Using defaults \u2014 let's go!\n"));
|
|
807
1273
|
const { init: init2 } = await import("./init-7EWVBX6O.js");
|
|
808
1274
|
await init2({ analyze: true, git: true });
|
|
809
1275
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
810
|
-
const progressPath =
|
|
811
|
-
if (!
|
|
1276
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1277
|
+
if (!existsSync4(progressPath)) {
|
|
812
1278
|
const { writeFileSync } = await import("fs");
|
|
813
1279
|
writeFileSync(progressPath, "# ArchonDev Progress Log\n\nThis file tracks learnings and decisions across sessions.\n");
|
|
814
1280
|
}
|
|
@@ -822,20 +1288,20 @@ async function quickAdapt(cwd) {
|
|
|
822
1288
|
await showMainMenu();
|
|
823
1289
|
}
|
|
824
1290
|
async function handleContinueSession(cwd, state) {
|
|
825
|
-
console.log(
|
|
1291
|
+
console.log(chalk4.green("\u{1F44B}") + chalk4.bold(" Welcome back!\n"));
|
|
826
1292
|
if (state.lastProgressEntry) {
|
|
827
|
-
console.log(
|
|
828
|
-
console.log(
|
|
1293
|
+
console.log(chalk4.dim("Last activity:"));
|
|
1294
|
+
console.log(chalk4.dim(" " + state.lastProgressEntry.split("\n")[0]));
|
|
829
1295
|
console.log();
|
|
830
1296
|
}
|
|
831
1297
|
const handoff = checkForHandoff(cwd);
|
|
832
1298
|
if (handoff) {
|
|
833
|
-
console.log(
|
|
834
|
-
console.log(
|
|
1299
|
+
console.log(chalk4.yellow("\u{1F4CB} Found handoff from last session:\n"));
|
|
1300
|
+
console.log(chalk4.dim(handoff.nextSteps));
|
|
835
1301
|
console.log();
|
|
836
1302
|
const continueHandoff = await promptYesNo("Continue from handoff?", true);
|
|
837
1303
|
if (continueHandoff) {
|
|
838
|
-
console.log(
|
|
1304
|
+
console.log(chalk4.dim("\nPicking up where you left off...\n"));
|
|
839
1305
|
}
|
|
840
1306
|
}
|
|
841
1307
|
if (state.hasReviewDb) {
|
|
@@ -845,8 +1311,8 @@ async function handleContinueSession(cwd, state) {
|
|
|
845
1311
|
}
|
|
846
1312
|
function checkForHandoff(cwd) {
|
|
847
1313
|
try {
|
|
848
|
-
const progressPath =
|
|
849
|
-
if (!
|
|
1314
|
+
const progressPath = join4(cwd, "progress.txt");
|
|
1315
|
+
if (!existsSync4(progressPath)) return null;
|
|
850
1316
|
const content = readFileSync(progressPath, "utf-8");
|
|
851
1317
|
const handoffMatch = content.match(/## Context Handoff[^\n]*\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
852
1318
|
if (handoffMatch && handoffMatch[1]) {
|
|
@@ -863,7 +1329,7 @@ function checkForHandoff(cwd) {
|
|
|
863
1329
|
async function showMainMenu() {
|
|
864
1330
|
const cwd = process.cwd();
|
|
865
1331
|
const state = detectProjectState(cwd);
|
|
866
|
-
console.log(
|
|
1332
|
+
console.log(chalk4.bold("What would you like to do?\n"));
|
|
867
1333
|
const choices = [
|
|
868
1334
|
{ key: "1", label: "Plan a new task", action: () => planTask() },
|
|
869
1335
|
{ key: "2", label: "List atoms", action: () => listAtoms() },
|
|
@@ -875,7 +1341,7 @@ async function showMainMenu() {
|
|
|
875
1341
|
{ key: "q", label: "Quit", action: async () => process.exit(0) }
|
|
876
1342
|
];
|
|
877
1343
|
for (const choice2 of choices) {
|
|
878
|
-
console.log(` ${
|
|
1344
|
+
console.log(` ${chalk4.cyan(choice2.key)}) ${choice2.label}`);
|
|
879
1345
|
}
|
|
880
1346
|
console.log();
|
|
881
1347
|
const selected = await prompt("Enter choice");
|
|
@@ -883,7 +1349,7 @@ async function showMainMenu() {
|
|
|
883
1349
|
if (choice) {
|
|
884
1350
|
await choice.action();
|
|
885
1351
|
} else {
|
|
886
|
-
console.log(
|
|
1352
|
+
console.log(chalk4.yellow("Invalid choice. Please try again."));
|
|
887
1353
|
await showMainMenu();
|
|
888
1354
|
}
|
|
889
1355
|
}
|
|
@@ -899,7 +1365,7 @@ async function showReviewProgress(cwd) {
|
|
|
899
1365
|
const pending = stats.pending + stats.inReview;
|
|
900
1366
|
const needsFix = stats.needsFix;
|
|
901
1367
|
console.log(
|
|
902
|
-
|
|
1368
|
+
chalk4.blue("\u{1F4CA} Review Progress:") + chalk4.dim(` ${completed}/${total} completed`) + (needsFix > 0 ? chalk4.red(` (${needsFix} need fixes)`) : "") + (pending > 0 ? chalk4.yellow(` (${pending} pending)`) : "")
|
|
903
1369
|
);
|
|
904
1370
|
console.log();
|
|
905
1371
|
} catch {
|
|
@@ -924,29 +1390,29 @@ async function executeNext() {
|
|
|
924
1390
|
const atoms = await listLocalAtoms2();
|
|
925
1391
|
const pendingAtoms = atoms.filter((a) => a.status === "READY" || a.status === "IN_PROGRESS");
|
|
926
1392
|
if (pendingAtoms.length === 0) {
|
|
927
|
-
console.log(
|
|
1393
|
+
console.log(chalk4.yellow('No pending atoms. Use "archon plan" to create one.'));
|
|
928
1394
|
return;
|
|
929
1395
|
}
|
|
930
1396
|
if (pendingAtoms.length > 1) {
|
|
931
1397
|
const analysis = analyzeProject(pendingAtoms);
|
|
932
1398
|
const prefs = await loadExecutionPreferences(cwd);
|
|
933
|
-
console.log(
|
|
934
|
-
console.log(` ${
|
|
935
|
-
console.log(` ${
|
|
936
|
-
console.log(` ${
|
|
937
|
-
console.log(` ${
|
|
1399
|
+
console.log(chalk4.blue("\n\u2501\u2501\u2501 Project Analysis \u2501\u2501\u2501\n"));
|
|
1400
|
+
console.log(` ${chalk4.bold("Atoms:")} ${analysis.atomCount}`);
|
|
1401
|
+
console.log(` ${chalk4.bold("Estimated:")} ${analysis.estimatedMinutes} minutes`);
|
|
1402
|
+
console.log(` ${chalk4.bold("Complexity:")} ${analysis.complexity} - ${getComplexityDescription(analysis.complexity)}`);
|
|
1403
|
+
console.log(` ${chalk4.bold("Suggested:")} ${analysis.suggestedMode} - ${getModeDescription(analysis.suggestedMode)}`);
|
|
938
1404
|
console.log();
|
|
939
1405
|
if (analysis.suggestedMode !== "sequential" && prefs.parallelMode === "ask") {
|
|
940
1406
|
const useParallel = await promptYesNo(`Use ${analysis.suggestedMode} execution?`, true);
|
|
941
1407
|
if (useParallel) {
|
|
942
|
-
console.log(
|
|
1408
|
+
console.log(chalk4.green(`
|
|
943
1409
|
\u2713 ${analysis.suggestedMode} execution selected`));
|
|
944
|
-
console.log(
|
|
1410
|
+
console.log(chalk4.dim("(Parallel/cloud execution coming soon - running sequentially for now)\n"));
|
|
945
1411
|
}
|
|
946
1412
|
} else if (prefs.parallelMode === "always" && analysis.suggestedMode !== "sequential") {
|
|
947
|
-
console.log(
|
|
1413
|
+
console.log(chalk4.green(`
|
|
948
1414
|
\u2713 Auto-selected ${analysis.suggestedMode} execution (preference: always)`));
|
|
949
|
-
console.log(
|
|
1415
|
+
console.log(chalk4.dim("(Parallel/cloud execution coming soon - running sequentially for now)\n"));
|
|
950
1416
|
}
|
|
951
1417
|
}
|
|
952
1418
|
const atomId = await prompt("Enter atom ID to execute (or press Enter for first pending)");
|
|
@@ -955,7 +1421,7 @@ async function executeNext() {
|
|
|
955
1421
|
const { execute: execute2 } = await import("./execute-2S2UHTLN.js");
|
|
956
1422
|
await execute2(targetId, {});
|
|
957
1423
|
} else {
|
|
958
|
-
console.log(
|
|
1424
|
+
console.log(chalk4.yellow("No atom to execute."));
|
|
959
1425
|
}
|
|
960
1426
|
}
|
|
961
1427
|
async function reportBug() {
|
|
@@ -976,20 +1442,20 @@ async function settingsMenu() {
|
|
|
976
1442
|
}
|
|
977
1443
|
async function reviewCode() {
|
|
978
1444
|
const cwd = process.cwd();
|
|
979
|
-
const reviewDbPath =
|
|
980
|
-
if (!
|
|
981
|
-
console.log(
|
|
1445
|
+
const reviewDbPath = join4(cwd, "docs", "code-review", "review-tasks.db");
|
|
1446
|
+
if (!existsSync4(reviewDbPath)) {
|
|
1447
|
+
console.log(chalk4.dim("Code review not initialized. Starting setup...\n"));
|
|
982
1448
|
const { reviewInit: reviewInit2 } = await import("./review-3R6QXAXQ.js");
|
|
983
1449
|
await reviewInit2();
|
|
984
1450
|
console.log();
|
|
985
1451
|
}
|
|
986
|
-
console.log(
|
|
987
|
-
console.log(` ${
|
|
988
|
-
console.log(` ${
|
|
989
|
-
console.log(` ${
|
|
990
|
-
console.log(` ${
|
|
991
|
-
console.log(` ${
|
|
992
|
-
console.log(` ${
|
|
1452
|
+
console.log(chalk4.bold("\nCode Review Options:\n"));
|
|
1453
|
+
console.log(` ${chalk4.cyan("1")}) Analyze project`);
|
|
1454
|
+
console.log(` ${chalk4.cyan("2")}) Show review status`);
|
|
1455
|
+
console.log(` ${chalk4.cyan("3")}) Review next file`);
|
|
1456
|
+
console.log(` ${chalk4.cyan("4")}) List all tasks`);
|
|
1457
|
+
console.log(` ${chalk4.cyan("5")}) Run AI review on all pending`);
|
|
1458
|
+
console.log(` ${chalk4.cyan("b")}) Back to main menu`);
|
|
993
1459
|
console.log();
|
|
994
1460
|
const choice = await prompt("Enter choice");
|
|
995
1461
|
switch (choice.toLowerCase()) {
|
|
@@ -1022,7 +1488,7 @@ async function reviewCode() {
|
|
|
1022
1488
|
await showMainMenu();
|
|
1023
1489
|
return;
|
|
1024
1490
|
default:
|
|
1025
|
-
console.log(
|
|
1491
|
+
console.log(chalk4.yellow("Invalid choice."));
|
|
1026
1492
|
}
|
|
1027
1493
|
await reviewCode();
|
|
1028
1494
|
}
|
|
@@ -1032,7 +1498,7 @@ function prompt(question) {
|
|
|
1032
1498
|
input: process.stdin,
|
|
1033
1499
|
output: process.stdout
|
|
1034
1500
|
});
|
|
1035
|
-
rl.question(`${
|
|
1501
|
+
rl.question(`${chalk4.cyan("?")} ${question}: `, (answer) => {
|
|
1036
1502
|
rl.close();
|
|
1037
1503
|
resolve(answer);
|
|
1038
1504
|
});
|
|
@@ -1045,7 +1511,7 @@ function promptYesNo(question, defaultValue) {
|
|
|
1045
1511
|
output: process.stdout
|
|
1046
1512
|
});
|
|
1047
1513
|
const hint = defaultValue ? "(Y/n)" : "(y/N)";
|
|
1048
|
-
rl.question(`${
|
|
1514
|
+
rl.question(`${chalk4.cyan("?")} ${question} ${hint}: `, (answer) => {
|
|
1049
1515
|
rl.close();
|
|
1050
1516
|
if (answer.trim() === "") {
|
|
1051
1517
|
resolve(defaultValue);
|
|
@@ -1057,15 +1523,15 @@ function promptYesNo(question, defaultValue) {
|
|
|
1057
1523
|
}
|
|
1058
1524
|
function promptChoice(question, options) {
|
|
1059
1525
|
return new Promise((resolve) => {
|
|
1060
|
-
console.log(`${
|
|
1526
|
+
console.log(`${chalk4.cyan("?")} ${question}`);
|
|
1061
1527
|
for (const opt of options) {
|
|
1062
|
-
console.log(` ${
|
|
1528
|
+
console.log(` ${chalk4.dim(opt.key)}) ${opt.label}`);
|
|
1063
1529
|
}
|
|
1064
1530
|
const rl = readline.createInterface({
|
|
1065
1531
|
input: process.stdin,
|
|
1066
1532
|
output: process.stdout
|
|
1067
1533
|
});
|
|
1068
|
-
rl.question(` ${
|
|
1534
|
+
rl.question(` ${chalk4.dim("Enter choice")}: `, (answer) => {
|
|
1069
1535
|
rl.close();
|
|
1070
1536
|
resolve(answer.trim() || "1");
|
|
1071
1537
|
});
|
|
@@ -1073,7 +1539,7 @@ function promptChoice(question, options) {
|
|
|
1073
1539
|
}
|
|
1074
1540
|
|
|
1075
1541
|
// src/cli/credits.ts
|
|
1076
|
-
import
|
|
1542
|
+
import chalk5 from "chalk";
|
|
1077
1543
|
import ora from "ora";
|
|
1078
1544
|
import open from "open";
|
|
1079
1545
|
import { createClient } from "@supabase/supabase-js";
|
|
@@ -1099,21 +1565,21 @@ async function showCredits() {
|
|
|
1099
1565
|
const profile = data;
|
|
1100
1566
|
spinner.stop();
|
|
1101
1567
|
console.log();
|
|
1102
|
-
console.log(
|
|
1568
|
+
console.log(chalk5.bold("\u{1F4B0} Credit Balance"));
|
|
1103
1569
|
console.log();
|
|
1104
1570
|
const balance = (profile.credit_balance_cents || 0) / 100;
|
|
1105
1571
|
console.log(` Tier: ${formatTier(profile.tier)}`);
|
|
1106
|
-
console.log(` Balance: ${
|
|
1572
|
+
console.log(` Balance: ${chalk5.green(`$${balance.toFixed(2)}`)}`);
|
|
1107
1573
|
if (profile.tier === "FREE") {
|
|
1108
1574
|
console.log(` Atoms: ${profile.atoms_used_this_month}/10,000 this month`);
|
|
1109
1575
|
console.log();
|
|
1110
|
-
console.log(
|
|
1576
|
+
console.log(chalk5.dim(" Upgrade to Credits tier: archon credits add"));
|
|
1111
1577
|
} else if (profile.tier === "CREDITS") {
|
|
1112
1578
|
console.log();
|
|
1113
|
-
console.log(
|
|
1579
|
+
console.log(chalk5.dim(" Add more credits: archon credits add"));
|
|
1114
1580
|
} else if (profile.tier === "BYOK") {
|
|
1115
1581
|
console.log();
|
|
1116
|
-
console.log(
|
|
1582
|
+
console.log(chalk5.dim(" Using your own API keys - no credit charges"));
|
|
1117
1583
|
}
|
|
1118
1584
|
console.log();
|
|
1119
1585
|
} catch (err) {
|
|
@@ -1159,18 +1625,18 @@ async function addCredits(options = {}) {
|
|
|
1159
1625
|
}
|
|
1160
1626
|
spinner.succeed("Checkout ready");
|
|
1161
1627
|
console.log();
|
|
1162
|
-
console.log(
|
|
1628
|
+
console.log(chalk5.bold("\u{1F6D2} Add Credits"));
|
|
1163
1629
|
console.log();
|
|
1164
|
-
console.log(` Amount: ${
|
|
1630
|
+
console.log(` Amount: ${chalk5.green(`$${amountDollars.toFixed(2)}`)}`);
|
|
1165
1631
|
console.log();
|
|
1166
1632
|
console.log(" Opening checkout in browser...");
|
|
1167
1633
|
console.log();
|
|
1168
|
-
console.log(
|
|
1634
|
+
console.log(chalk5.dim(` Or visit: ${checkoutUrl}`));
|
|
1169
1635
|
console.log();
|
|
1170
1636
|
try {
|
|
1171
1637
|
await open(checkoutUrl);
|
|
1172
1638
|
} catch {
|
|
1173
|
-
console.log(
|
|
1639
|
+
console.log(chalk5.yellow(" Could not open browser. Please visit the URL above."));
|
|
1174
1640
|
}
|
|
1175
1641
|
} catch (err) {
|
|
1176
1642
|
spinner.fail("Error preparing checkout");
|
|
@@ -1195,15 +1661,15 @@ async function showHistory(options = {}) {
|
|
|
1195
1661
|
const usage = data;
|
|
1196
1662
|
spinner.stop();
|
|
1197
1663
|
console.log();
|
|
1198
|
-
console.log(
|
|
1664
|
+
console.log(chalk5.bold("\u{1F4CA} Usage History"));
|
|
1199
1665
|
console.log();
|
|
1200
1666
|
if (!usage || usage.length === 0) {
|
|
1201
|
-
console.log(
|
|
1667
|
+
console.log(chalk5.dim(" No usage recorded yet."));
|
|
1202
1668
|
console.log();
|
|
1203
1669
|
return;
|
|
1204
1670
|
}
|
|
1205
|
-
console.log(
|
|
1206
|
-
console.log(
|
|
1671
|
+
console.log(chalk5.dim(" Model Tokens Cost Date"));
|
|
1672
|
+
console.log(chalk5.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1207
1673
|
for (const row of usage) {
|
|
1208
1674
|
const model = row.model.padEnd(30).slice(0, 30);
|
|
1209
1675
|
const tokens = (row.input_tokens + row.output_tokens).toString().padStart(8);
|
|
@@ -1213,9 +1679,9 @@ async function showHistory(options = {}) {
|
|
|
1213
1679
|
}
|
|
1214
1680
|
const totalCost = usage.reduce((sum, r) => sum + r.base_cost, 0);
|
|
1215
1681
|
const totalTokens = usage.reduce((sum, r) => sum + r.input_tokens + r.output_tokens, 0);
|
|
1216
|
-
console.log(
|
|
1682
|
+
console.log(chalk5.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1217
1683
|
console.log(
|
|
1218
|
-
` ${"Total".padEnd(30)} ${totalTokens.toString().padStart(8)} ${
|
|
1684
|
+
` ${"Total".padEnd(30)} ${totalTokens.toString().padStart(8)} ${chalk5.green(`$${totalCost.toFixed(4)}`.padStart(10))}`
|
|
1219
1685
|
);
|
|
1220
1686
|
console.log();
|
|
1221
1687
|
} catch (err) {
|
|
@@ -1281,24 +1747,24 @@ async function manageBudget(options = {}) {
|
|
|
1281
1747
|
}
|
|
1282
1748
|
spinner.stop();
|
|
1283
1749
|
console.log();
|
|
1284
|
-
console.log(
|
|
1750
|
+
console.log(chalk5.bold("\u{1F4CA} Monthly Budget"));
|
|
1285
1751
|
console.log();
|
|
1286
1752
|
if (profile.monthly_budget_cents === null) {
|
|
1287
|
-
console.log(` Budget: ${
|
|
1753
|
+
console.log(` Budget: ${chalk5.dim("No limit set")}`);
|
|
1288
1754
|
} else {
|
|
1289
1755
|
const budget = profile.monthly_budget_cents / 100;
|
|
1290
1756
|
const spend = (profile.monthly_spend_cents || 0) / 100;
|
|
1291
1757
|
const remaining = budget - spend;
|
|
1292
1758
|
const percent = budget > 0 ? Math.round(spend / budget * 100) : 0;
|
|
1293
|
-
console.log(` Budget: ${
|
|
1759
|
+
console.log(` Budget: ${chalk5.green(`$${budget.toFixed(2)}`)} / month`);
|
|
1294
1760
|
console.log(` Spent: $${spend.toFixed(2)} (${percent}%)`);
|
|
1295
|
-
console.log(` Remaining: ${remaining >= 0 ?
|
|
1761
|
+
console.log(` Remaining: ${remaining >= 0 ? chalk5.green(`$${remaining.toFixed(2)}`) : chalk5.red(`-$${Math.abs(remaining).toFixed(2)}`)}`);
|
|
1296
1762
|
}
|
|
1297
1763
|
console.log(` Alert at: ${profile.budget_alert_threshold_percent}% of budget`);
|
|
1298
1764
|
console.log();
|
|
1299
|
-
console.log(
|
|
1300
|
-
console.log(
|
|
1301
|
-
console.log(
|
|
1765
|
+
console.log(chalk5.dim(" Set budget: archon credits budget --set 50"));
|
|
1766
|
+
console.log(chalk5.dim(" Clear budget: archon credits budget --clear"));
|
|
1767
|
+
console.log(chalk5.dim(" Set alert: archon credits budget --alert 80"));
|
|
1302
1768
|
console.log();
|
|
1303
1769
|
} catch (err) {
|
|
1304
1770
|
spinner.fail("Error managing budget");
|
|
@@ -1360,12 +1826,12 @@ async function manageAutoRecharge(options = {}) {
|
|
|
1360
1826
|
}
|
|
1361
1827
|
spinner.stop();
|
|
1362
1828
|
console.log();
|
|
1363
|
-
console.log(
|
|
1829
|
+
console.log(chalk5.bold("\u{1F504} Auto-Recharge"));
|
|
1364
1830
|
console.log();
|
|
1365
1831
|
if (!profile.auto_recharge_enabled) {
|
|
1366
|
-
console.log(` Status: ${
|
|
1832
|
+
console.log(` Status: ${chalk5.dim("Disabled")}`);
|
|
1367
1833
|
} else {
|
|
1368
|
-
console.log(` Status: ${
|
|
1834
|
+
console.log(` Status: ${chalk5.green("Enabled")}`);
|
|
1369
1835
|
if (profile.auto_recharge_threshold_cents !== null) {
|
|
1370
1836
|
console.log(` When: Balance drops below $${(profile.auto_recharge_threshold_cents / 100).toFixed(2)}`);
|
|
1371
1837
|
}
|
|
@@ -1373,10 +1839,10 @@ async function manageAutoRecharge(options = {}) {
|
|
|
1373
1839
|
console.log(` Amount: $${(profile.auto_recharge_amount_cents / 100).toFixed(2)}`);
|
|
1374
1840
|
}
|
|
1375
1841
|
}
|
|
1376
|
-
console.log(` Payment: ${profile.stripe_payment_method_id ?
|
|
1842
|
+
console.log(` Payment: ${profile.stripe_payment_method_id ? chalk5.green("Card saved") : chalk5.dim("No card saved")}`);
|
|
1377
1843
|
console.log();
|
|
1378
|
-
console.log(
|
|
1379
|
-
console.log(
|
|
1844
|
+
console.log(chalk5.dim(" Enable: archon credits auto-recharge --enable --threshold 5 --amount 20"));
|
|
1845
|
+
console.log(chalk5.dim(" Disable: archon credits auto-recharge --disable"));
|
|
1380
1846
|
console.log();
|
|
1381
1847
|
} catch (err) {
|
|
1382
1848
|
spinner.fail("Error managing auto-recharge");
|
|
@@ -1386,11 +1852,11 @@ async function manageAutoRecharge(options = {}) {
|
|
|
1386
1852
|
function formatTier(tier) {
|
|
1387
1853
|
switch (tier) {
|
|
1388
1854
|
case "FREE":
|
|
1389
|
-
return
|
|
1855
|
+
return chalk5.blue("Free (10k atoms/month)");
|
|
1390
1856
|
case "CREDITS":
|
|
1391
|
-
return
|
|
1857
|
+
return chalk5.green("Credits (Pay-as-you-go)");
|
|
1392
1858
|
case "BYOK":
|
|
1393
|
-
return
|
|
1859
|
+
return chalk5.magenta("BYOK (Bring Your Own Key)");
|
|
1394
1860
|
default:
|
|
1395
1861
|
return tier;
|
|
1396
1862
|
}
|
|
@@ -1667,9 +2133,9 @@ async function watch() {
|
|
|
1667
2133
|
|
|
1668
2134
|
// src/cli/deps.ts
|
|
1669
2135
|
import { Command } from "commander";
|
|
1670
|
-
import
|
|
1671
|
-
import { readFile as
|
|
1672
|
-
import { existsSync as
|
|
2136
|
+
import chalk6 from "chalk";
|
|
2137
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
2138
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1673
2139
|
var DEPENDENCIES_FILENAME = "DEPENDENCIES.md";
|
|
1674
2140
|
function createDepsCommand() {
|
|
1675
2141
|
const deps = new Command("deps").description("Manage file-level dependencies (DEPENDENCIES.md)").addHelpText(
|
|
@@ -1685,30 +2151,30 @@ Examples:
|
|
|
1685
2151
|
deps.command("list").description("List all dependency rules").option("-v, --verbose", "Show detailed information").action(async (options) => {
|
|
1686
2152
|
const parser = new DependencyParser();
|
|
1687
2153
|
if (!parser.exists()) {
|
|
1688
|
-
console.log(
|
|
1689
|
-
console.log(
|
|
2154
|
+
console.log(chalk6.yellow("No DEPENDENCIES.md found."));
|
|
2155
|
+
console.log(chalk6.dim("Create one with: archon deps add --source <path> --dependent <path>"));
|
|
1690
2156
|
return;
|
|
1691
2157
|
}
|
|
1692
2158
|
const result = await parser.parse();
|
|
1693
2159
|
if (!result.success) {
|
|
1694
|
-
console.log(
|
|
2160
|
+
console.log(chalk6.red(`Parse error: ${result.error}`));
|
|
1695
2161
|
return;
|
|
1696
2162
|
}
|
|
1697
2163
|
const rules = result.document?.rules ?? [];
|
|
1698
2164
|
if (rules.length === 0) {
|
|
1699
|
-
console.log(
|
|
2165
|
+
console.log(chalk6.dim("No dependency rules defined."));
|
|
1700
2166
|
return;
|
|
1701
2167
|
}
|
|
1702
|
-
console.log(
|
|
2168
|
+
console.log(chalk6.bold(`
|
|
1703
2169
|
\u{1F4E6} Dependency Rules (${rules.length})
|
|
1704
2170
|
`));
|
|
1705
2171
|
for (const rule of rules) {
|
|
1706
|
-
const severityColor = rule.severity === "BLOCKER" ?
|
|
1707
|
-
console.log(`${severityColor(`[${rule.severity}]`)} ${
|
|
1708
|
-
console.log(` Source: ${
|
|
1709
|
-
console.log(` Dependents: ${rule.dependents.map((d) =>
|
|
2172
|
+
const severityColor = rule.severity === "BLOCKER" ? chalk6.red : rule.severity === "WARNING" ? chalk6.yellow : chalk6.blue;
|
|
2173
|
+
console.log(`${severityColor(`[${rule.severity}]`)} ${chalk6.bold(rule.id)}`);
|
|
2174
|
+
console.log(` Source: ${chalk6.cyan(rule.source)}`);
|
|
2175
|
+
console.log(` Dependents: ${rule.dependents.map((d) => chalk6.dim(d)).join(", ")}`);
|
|
1710
2176
|
if (rule.reason && options.verbose) {
|
|
1711
|
-
console.log(` Reason: ${
|
|
2177
|
+
console.log(` Reason: ${chalk6.dim(rule.reason)}`);
|
|
1712
2178
|
}
|
|
1713
2179
|
if (rule.mustTest && options.verbose) {
|
|
1714
2180
|
console.log(` Must test: ${rule.mustTest.join(", ")}`);
|
|
@@ -1720,29 +2186,29 @@ Examples:
|
|
|
1720
2186
|
const files = options.files.split(",").map((f) => f.trim());
|
|
1721
2187
|
const parser = new DependencyParser();
|
|
1722
2188
|
if (!parser.exists()) {
|
|
1723
|
-
console.log(
|
|
2189
|
+
console.log(chalk6.dim("No DEPENDENCIES.md found. No dependency checks performed."));
|
|
1724
2190
|
process.exit(0);
|
|
1725
2191
|
}
|
|
1726
2192
|
const result = await parser.checkFiles(files);
|
|
1727
2193
|
if (result.impacts.length === 0) {
|
|
1728
|
-
console.log(
|
|
2194
|
+
console.log(chalk6.green("\u2705 No downstream dependency impacts found."));
|
|
1729
2195
|
process.exit(0);
|
|
1730
2196
|
}
|
|
1731
|
-
console.log(
|
|
2197
|
+
console.log(chalk6.yellow(`
|
|
1732
2198
|
\u26A0\uFE0F Found ${result.impacts.length} dependency impact(s):
|
|
1733
2199
|
`));
|
|
1734
2200
|
for (const impact of result.impacts) {
|
|
1735
|
-
const severityColor = impact.rule.severity === "BLOCKER" ?
|
|
2201
|
+
const severityColor = impact.rule.severity === "BLOCKER" ? chalk6.red : impact.rule.severity === "WARNING" ? chalk6.yellow : chalk6.blue;
|
|
1736
2202
|
console.log(severityColor(`[${impact.rule.severity}] ${impact.rule.id}`));
|
|
1737
|
-
console.log(` Changing: ${
|
|
2203
|
+
console.log(` Changing: ${chalk6.cyan(impact.matchedSource)}`);
|
|
1738
2204
|
console.log(` May impact: ${impact.affectedDependents.join(", ")}`);
|
|
1739
2205
|
if (impact.rule.reason) {
|
|
1740
|
-
console.log(` Reason: ${
|
|
2206
|
+
console.log(` Reason: ${chalk6.dim(impact.rule.reason)}`);
|
|
1741
2207
|
}
|
|
1742
2208
|
console.log("");
|
|
1743
2209
|
}
|
|
1744
2210
|
if (result.hasBlockers) {
|
|
1745
|
-
console.log(
|
|
2211
|
+
console.log(chalk6.red("\u274C BLOCKER-level impacts found. Review before proceeding."));
|
|
1746
2212
|
process.exit(1);
|
|
1747
2213
|
}
|
|
1748
2214
|
process.exit(0);
|
|
@@ -1756,7 +2222,7 @@ Examples:
|
|
|
1756
2222
|
let existingRules = [];
|
|
1757
2223
|
let markdownBody = "";
|
|
1758
2224
|
if (parser.exists()) {
|
|
1759
|
-
const content = await
|
|
2225
|
+
const content = await readFile4(DEPENDENCIES_FILENAME, "utf-8");
|
|
1760
2226
|
const result = await parser.parse();
|
|
1761
2227
|
if (result.success && result.document) {
|
|
1762
2228
|
existingRules = result.document.rules;
|
|
@@ -1784,7 +2250,7 @@ Examples:
|
|
|
1784
2250
|
(r) => r.source === source && r.dependents.includes(dependent)
|
|
1785
2251
|
);
|
|
1786
2252
|
if (existingRule) {
|
|
1787
|
-
console.log(
|
|
2253
|
+
console.log(chalk6.yellow(`Rule already exists: ${existingRule.id}`));
|
|
1788
2254
|
return;
|
|
1789
2255
|
}
|
|
1790
2256
|
const newRule = {
|
|
@@ -1795,23 +2261,23 @@ Examples:
|
|
|
1795
2261
|
reason
|
|
1796
2262
|
};
|
|
1797
2263
|
existingRules.push(newRule);
|
|
1798
|
-
const
|
|
1799
|
-
await
|
|
1800
|
-
${
|
|
1801
|
-
console.log(
|
|
1802
|
-
console.log(` Source: ${
|
|
1803
|
-
console.log(` Dependent: ${
|
|
2264
|
+
const yaml3 = generateYamlFrontmatter(existingRules);
|
|
2265
|
+
await writeFile4(DEPENDENCIES_FILENAME, `---
|
|
2266
|
+
${yaml3}---${markdownBody}`, "utf-8");
|
|
2267
|
+
console.log(chalk6.green(`\u2705 Added dependency rule: ${nextId}`));
|
|
2268
|
+
console.log(` Source: ${chalk6.cyan(source)}`);
|
|
2269
|
+
console.log(` Dependent: ${chalk6.dim(dependent)}`);
|
|
1804
2270
|
});
|
|
1805
2271
|
deps.command("graph").description("Generate Mermaid diagram of dependencies").option("--output <file>", "Write to file instead of stdout").action(async (options) => {
|
|
1806
2272
|
const parser = new DependencyParser();
|
|
1807
2273
|
if (!parser.exists()) {
|
|
1808
|
-
console.log(
|
|
2274
|
+
console.log(chalk6.yellow("No DEPENDENCIES.md found."));
|
|
1809
2275
|
return;
|
|
1810
2276
|
}
|
|
1811
2277
|
const mermaid = await parser.generateGraph();
|
|
1812
2278
|
if (options.output) {
|
|
1813
|
-
await
|
|
1814
|
-
console.log(
|
|
2279
|
+
await writeFile4(options.output, mermaid, "utf-8");
|
|
2280
|
+
console.log(chalk6.green(`\u2705 Graph written to ${options.output}`));
|
|
1815
2281
|
} else {
|
|
1816
2282
|
console.log("\n```mermaid");
|
|
1817
2283
|
console.log(mermaid);
|
|
@@ -1819,8 +2285,8 @@ ${yaml2}---${markdownBody}`, "utf-8");
|
|
|
1819
2285
|
}
|
|
1820
2286
|
});
|
|
1821
2287
|
deps.command("init").description("Create a starter DEPENDENCIES.md file").action(async () => {
|
|
1822
|
-
if (
|
|
1823
|
-
console.log(
|
|
2288
|
+
if (existsSync6(DEPENDENCIES_FILENAME)) {
|
|
2289
|
+
console.log(chalk6.yellow("DEPENDENCIES.md already exists."));
|
|
1824
2290
|
return;
|
|
1825
2291
|
}
|
|
1826
2292
|
const template = `---
|
|
@@ -1866,9 +2332,9 @@ rules:
|
|
|
1866
2332
|
|
|
1867
2333
|
*Powered by [ArchonDev](https://archondev.io)*
|
|
1868
2334
|
`;
|
|
1869
|
-
await
|
|
1870
|
-
console.log(
|
|
1871
|
-
console.log(
|
|
2335
|
+
await writeFile4(DEPENDENCIES_FILENAME, template, "utf-8");
|
|
2336
|
+
console.log(chalk6.green("\u2705 Created DEPENDENCIES.md"));
|
|
2337
|
+
console.log(chalk6.dim("Add your first rule with: archon deps add --source <path> --dependent <path>"));
|
|
1872
2338
|
});
|
|
1873
2339
|
return deps;
|
|
1874
2340
|
}
|
|
@@ -1905,10 +2371,10 @@ function generateYamlFrontmatter(rules) {
|
|
|
1905
2371
|
}
|
|
1906
2372
|
|
|
1907
2373
|
// src/cli/a11y.ts
|
|
1908
|
-
import
|
|
1909
|
-
import { existsSync as
|
|
1910
|
-
import { readFile as
|
|
1911
|
-
import { join as
|
|
2374
|
+
import chalk7 from "chalk";
|
|
2375
|
+
import { existsSync as existsSync7 } from "fs";
|
|
2376
|
+
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir3 } from "fs/promises";
|
|
2377
|
+
import { join as join6 } from "path";
|
|
1912
2378
|
import { createInterface } from "readline";
|
|
1913
2379
|
import { glob } from "glob";
|
|
1914
2380
|
var ACCESSIBILITY_CHECKS = {
|
|
@@ -1981,7 +2447,7 @@ async function scanForIssues(files) {
|
|
|
1981
2447
|
const issues = [];
|
|
1982
2448
|
for (const file of files) {
|
|
1983
2449
|
try {
|
|
1984
|
-
const content = await
|
|
2450
|
+
const content = await readFile5(file, "utf-8");
|
|
1985
2451
|
const lines = content.split("\n");
|
|
1986
2452
|
for (const [checkId, check] of Object.entries(ACCESSIBILITY_CHECKS)) {
|
|
1987
2453
|
for (const pattern of check.patterns) {
|
|
@@ -2008,8 +2474,8 @@ async function scanForIssues(files) {
|
|
|
2008
2474
|
return issues;
|
|
2009
2475
|
}
|
|
2010
2476
|
async function a11yCheck(options) {
|
|
2011
|
-
console.log(
|
|
2012
|
-
console.log(
|
|
2477
|
+
console.log(chalk7.blue("\n\u267F Pre-Deploy Accessibility Check\n"));
|
|
2478
|
+
console.log(chalk7.dim("Scanning for WCAG 2.2 AA compliance issues...\n"));
|
|
2013
2479
|
const patterns = [
|
|
2014
2480
|
"**/*.html",
|
|
2015
2481
|
"**/*.htm",
|
|
@@ -2023,13 +2489,13 @@ async function a11yCheck(options) {
|
|
|
2023
2489
|
let allFiles = [];
|
|
2024
2490
|
for (const pattern of patterns) {
|
|
2025
2491
|
const files = await glob(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
2026
|
-
allFiles = allFiles.concat(files.map((f) =>
|
|
2492
|
+
allFiles = allFiles.concat(files.map((f) => join6(process.cwd(), f)));
|
|
2027
2493
|
}
|
|
2028
2494
|
if (allFiles.length === 0) {
|
|
2029
|
-
console.log(
|
|
2495
|
+
console.log(chalk7.yellow("No web files found to check."));
|
|
2030
2496
|
return;
|
|
2031
2497
|
}
|
|
2032
|
-
console.log(
|
|
2498
|
+
console.log(chalk7.dim(`Scanning ${allFiles.length} files...
|
|
2033
2499
|
`));
|
|
2034
2500
|
const issues = await scanForIssues(allFiles);
|
|
2035
2501
|
const report = {
|
|
@@ -2039,28 +2505,28 @@ async function a11yCheck(options) {
|
|
|
2039
2505
|
issues,
|
|
2040
2506
|
passed: issues.length === 0
|
|
2041
2507
|
};
|
|
2042
|
-
const archonDir =
|
|
2043
|
-
if (!
|
|
2044
|
-
await
|
|
2508
|
+
const archonDir = join6(process.cwd(), ".archon");
|
|
2509
|
+
if (!existsSync7(archonDir)) {
|
|
2510
|
+
await mkdir3(archonDir, { recursive: true });
|
|
2045
2511
|
}
|
|
2046
|
-
await
|
|
2512
|
+
await writeFile5(join6(archonDir, "a11y-report.json"), JSON.stringify(report, null, 2));
|
|
2047
2513
|
if (issues.length === 0) {
|
|
2048
|
-
console.log(
|
|
2049
|
-
console.log(
|
|
2050
|
-
console.log(
|
|
2514
|
+
console.log(chalk7.green("\u2705 Accessibility Audit Passed\n"));
|
|
2515
|
+
console.log(chalk7.dim("Your site meets WCAG 2.2 AA requirements based on static analysis."));
|
|
2516
|
+
console.log(chalk7.dim("Note: Run manual testing with screen readers for full compliance.\n"));
|
|
2051
2517
|
return;
|
|
2052
2518
|
}
|
|
2053
|
-
console.log(
|
|
2519
|
+
console.log(chalk7.red(`\u26A0\uFE0F ${issues.length} Accessibility Issues Found
|
|
2054
2520
|
`));
|
|
2055
2521
|
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
2056
2522
|
const majorCount = issues.filter((i) => i.severity === "major").length;
|
|
2057
2523
|
const minorCount = issues.filter((i) => i.severity === "minor").length;
|
|
2058
|
-
console.log(
|
|
2059
|
-
if (criticalCount > 0) console.log(
|
|
2060
|
-
if (majorCount > 0) console.log(
|
|
2061
|
-
if (minorCount > 0) console.log(
|
|
2524
|
+
console.log(chalk7.dim("Summary:"));
|
|
2525
|
+
if (criticalCount > 0) console.log(chalk7.red(` \u2022 ${criticalCount} critical`));
|
|
2526
|
+
if (majorCount > 0) console.log(chalk7.yellow(` \u2022 ${majorCount} major`));
|
|
2527
|
+
if (minorCount > 0) console.log(chalk7.dim(` \u2022 ${minorCount} minor`));
|
|
2062
2528
|
console.log();
|
|
2063
|
-
console.log(
|
|
2529
|
+
console.log(chalk7.bold("Issues:\n"));
|
|
2064
2530
|
const issuesByFile = /* @__PURE__ */ new Map();
|
|
2065
2531
|
for (const issue of issues) {
|
|
2066
2532
|
const existing = issuesByFile.get(issue.file) || [];
|
|
@@ -2068,43 +2534,43 @@ async function a11yCheck(options) {
|
|
|
2068
2534
|
issuesByFile.set(issue.file, existing);
|
|
2069
2535
|
}
|
|
2070
2536
|
for (const [file, fileIssues] of issuesByFile) {
|
|
2071
|
-
console.log(
|
|
2537
|
+
console.log(chalk7.cyan(` ${file}`));
|
|
2072
2538
|
for (const issue of fileIssues.slice(0, 5)) {
|
|
2073
|
-
const color = issue.severity === "critical" ?
|
|
2539
|
+
const color = issue.severity === "critical" ? chalk7.red : issue.severity === "major" ? chalk7.yellow : chalk7.dim;
|
|
2074
2540
|
console.log(color(` Line ${issue.line}: ${issue.message} (WCAG ${issue.wcag})`));
|
|
2075
2541
|
}
|
|
2076
2542
|
if (fileIssues.length > 5) {
|
|
2077
|
-
console.log(
|
|
2543
|
+
console.log(chalk7.dim(` ... and ${fileIssues.length - 5} more issues`));
|
|
2078
2544
|
}
|
|
2079
2545
|
console.log();
|
|
2080
2546
|
}
|
|
2081
|
-
console.log(
|
|
2082
|
-
console.log(
|
|
2083
|
-
console.log(
|
|
2084
|
-
console.log(
|
|
2085
|
-
console.log(
|
|
2086
|
-
console.log(
|
|
2547
|
+
console.log(chalk7.bold.yellow("\n\u2696\uFE0F Legal Notice\n"));
|
|
2548
|
+
console.log(chalk7.dim("Websites that don't meet accessibility standards may violate:"));
|
|
2549
|
+
console.log(chalk7.dim(" \u2022 ADA (Americans with Disabilities Act) \u2014 US"));
|
|
2550
|
+
console.log(chalk7.dim(" \u2022 EAA (European Accessibility Act) \u2014 EU, effective June 2025"));
|
|
2551
|
+
console.log(chalk7.dim(" \u2022 Section 508 \u2014 US federal agencies and contractors"));
|
|
2552
|
+
console.log(chalk7.dim(" \u2022 AODA (Accessibility for Ontarians) \u2014 Ontario, Canada"));
|
|
2087
2553
|
console.log();
|
|
2088
|
-
console.log(
|
|
2089
|
-
console.log(
|
|
2090
|
-
console.log(
|
|
2554
|
+
console.log(chalk7.dim("Non-compliance can result in lawsuits, fines, and reputational damage."));
|
|
2555
|
+
console.log(chalk7.dim("In 2023, over 4,600 ADA web accessibility lawsuits were filed in the US.\n"));
|
|
2556
|
+
console.log(chalk7.dim(`Full report saved to: .archon/a11y-report.json`));
|
|
2091
2557
|
}
|
|
2092
2558
|
async function a11yFix(options) {
|
|
2093
2559
|
const prompt2 = createPrompt();
|
|
2094
2560
|
try {
|
|
2095
|
-
console.log(
|
|
2096
|
-
const reportPath =
|
|
2097
|
-
if (!
|
|
2098
|
-
console.log(
|
|
2561
|
+
console.log(chalk7.blue("\n\u267F Accessibility Auto-Fix\n"));
|
|
2562
|
+
const reportPath = join6(process.cwd(), ".archon/a11y-report.json");
|
|
2563
|
+
if (!existsSync7(reportPath)) {
|
|
2564
|
+
console.log(chalk7.yellow("No accessibility report found. Running check first...\n"));
|
|
2099
2565
|
await a11yCheck({});
|
|
2100
2566
|
}
|
|
2101
|
-
const reportContent = await
|
|
2567
|
+
const reportContent = await readFile5(reportPath, "utf-8");
|
|
2102
2568
|
const report = JSON.parse(reportContent);
|
|
2103
2569
|
if (report.passed || report.issues.length === 0) {
|
|
2104
|
-
console.log(
|
|
2570
|
+
console.log(chalk7.green("No issues to fix!"));
|
|
2105
2571
|
return;
|
|
2106
2572
|
}
|
|
2107
|
-
console.log(
|
|
2573
|
+
console.log(chalk7.dim(`Found ${report.issues.length} issues from last check.
|
|
2108
2574
|
`));
|
|
2109
2575
|
const fixablePatterns = [
|
|
2110
2576
|
{
|
|
@@ -2135,19 +2601,19 @@ async function a11yFix(options) {
|
|
|
2135
2601
|
for (const pattern of patterns) {
|
|
2136
2602
|
const files = await glob(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
2137
2603
|
for (const file of files) {
|
|
2138
|
-
const filePath =
|
|
2604
|
+
const filePath = join6(process.cwd(), file);
|
|
2139
2605
|
try {
|
|
2140
|
-
const content = await
|
|
2606
|
+
const content = await readFile5(filePath, "utf-8");
|
|
2141
2607
|
if (fix.pattern.test(content)) {
|
|
2142
2608
|
fix.pattern.lastIndex = 0;
|
|
2143
2609
|
const matches = content.match(fix.pattern);
|
|
2144
2610
|
const count = matches?.length || 0;
|
|
2145
2611
|
if (count > 0) {
|
|
2146
|
-
console.log(
|
|
2612
|
+
console.log(chalk7.cyan(` ${file}: ${count} fixes (${fix.description})`));
|
|
2147
2613
|
totalFixes += count;
|
|
2148
2614
|
if (!options.dryRun) {
|
|
2149
2615
|
const newContent = content.replace(fix.pattern, fix.replacement);
|
|
2150
|
-
await
|
|
2616
|
+
await writeFile5(filePath, newContent);
|
|
2151
2617
|
}
|
|
2152
2618
|
}
|
|
2153
2619
|
}
|
|
@@ -2157,21 +2623,21 @@ async function a11yFix(options) {
|
|
|
2157
2623
|
}
|
|
2158
2624
|
}
|
|
2159
2625
|
if (totalFixes === 0) {
|
|
2160
|
-
console.log(
|
|
2626
|
+
console.log(chalk7.dim("No auto-fixable issues found. Some issues require manual fixes."));
|
|
2161
2627
|
} else if (options.dryRun) {
|
|
2162
|
-
console.log(
|
|
2628
|
+
console.log(chalk7.yellow(`
|
|
2163
2629
|
${totalFixes} fixes would be applied. Run without --dry-run to apply.`));
|
|
2164
2630
|
} else {
|
|
2165
|
-
console.log(
|
|
2631
|
+
console.log(chalk7.green(`
|
|
2166
2632
|
\u2705 Applied ${totalFixes} fixes.`));
|
|
2167
|
-
console.log(
|
|
2633
|
+
console.log(chalk7.dim('Run "archon a11y check" to verify fixes.'));
|
|
2168
2634
|
}
|
|
2169
2635
|
} finally {
|
|
2170
2636
|
prompt2.close();
|
|
2171
2637
|
}
|
|
2172
2638
|
}
|
|
2173
2639
|
async function a11yBadge(options) {
|
|
2174
|
-
console.log(
|
|
2640
|
+
console.log(chalk7.blue("\n\u267F Accessibility Badge\n"));
|
|
2175
2641
|
const badgeHtml = `<!-- WCAG 2.2 AA Compliance Badge -->
|
|
2176
2642
|
<div class="flex items-center gap-2 text-xs text-text-muted dark:text-cream-300">
|
|
2177
2643
|
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
|
@@ -2186,35 +2652,35 @@ async function a11yBadge(options) {
|
|
|
2186
2652
|
footerFiles = footerFiles.concat(files);
|
|
2187
2653
|
}
|
|
2188
2654
|
if (footerFiles.length === 0) {
|
|
2189
|
-
console.log(
|
|
2190
|
-
console.log(
|
|
2655
|
+
console.log(chalk7.yellow("No footer files found."));
|
|
2656
|
+
console.log(chalk7.dim("\nManually add this badge to your footer:\n"));
|
|
2191
2657
|
console.log(badgeHtml);
|
|
2192
2658
|
return;
|
|
2193
2659
|
}
|
|
2194
|
-
console.log(
|
|
2660
|
+
console.log(chalk7.dim(`Found ${footerFiles.length} footer file(s):
|
|
2195
2661
|
`));
|
|
2196
2662
|
for (const file of footerFiles) {
|
|
2197
|
-
console.log(
|
|
2663
|
+
console.log(chalk7.cyan(` ${file}`));
|
|
2198
2664
|
}
|
|
2199
2665
|
if (options.remove) {
|
|
2200
|
-
console.log(
|
|
2201
|
-
console.log(
|
|
2666
|
+
console.log(chalk7.yellow("\nRemoving accessibility badge..."));
|
|
2667
|
+
console.log(chalk7.dim("Badge removal not yet implemented. Manually remove the WCAG badge comment block."));
|
|
2202
2668
|
} else {
|
|
2203
|
-
console.log(
|
|
2204
|
-
console.log(
|
|
2669
|
+
console.log(chalk7.dim("\nTo add the badge, insert this code before the closing </footer> tag:\n"));
|
|
2670
|
+
console.log(chalk7.green(badgeHtml));
|
|
2205
2671
|
}
|
|
2206
2672
|
}
|
|
2207
2673
|
async function a11yPreDeploy() {
|
|
2208
2674
|
const prompt2 = createPrompt();
|
|
2209
2675
|
try {
|
|
2210
|
-
console.log(
|
|
2211
|
-
console.log(
|
|
2676
|
+
console.log(chalk7.blue("\n\u26A0\uFE0F Pre-Deploy Accessibility Check\n"));
|
|
2677
|
+
console.log(chalk7.dim("Before deploying a live website, accessibility compliance is required.\n"));
|
|
2212
2678
|
await a11yCheck({});
|
|
2213
|
-
const reportPath =
|
|
2214
|
-
if (!
|
|
2679
|
+
const reportPath = join6(process.cwd(), ".archon/a11y-report.json");
|
|
2680
|
+
if (!existsSync7(reportPath)) {
|
|
2215
2681
|
return false;
|
|
2216
2682
|
}
|
|
2217
|
-
const reportContent = await
|
|
2683
|
+
const reportContent = await readFile5(reportPath, "utf-8");
|
|
2218
2684
|
const report = JSON.parse(reportContent);
|
|
2219
2685
|
if (report.passed) {
|
|
2220
2686
|
const addBadge = await prompt2.ask("\nWould you like to add a WCAG 2.2 AA badge to your footer? (y/N): ");
|
|
@@ -2223,7 +2689,7 @@ async function a11yPreDeploy() {
|
|
|
2223
2689
|
}
|
|
2224
2690
|
return true;
|
|
2225
2691
|
}
|
|
2226
|
-
console.log(
|
|
2692
|
+
console.log(chalk7.bold("\nOptions:\n"));
|
|
2227
2693
|
console.log(" 1) Fix issues now (recommended)");
|
|
2228
2694
|
console.log(" 2) Deploy anyway (not recommended)");
|
|
2229
2695
|
console.log(" 3) Cancel deployment\n");
|
|
@@ -2233,11 +2699,11 @@ async function a11yPreDeploy() {
|
|
|
2233
2699
|
await a11yCheck({});
|
|
2234
2700
|
return true;
|
|
2235
2701
|
} else if (choice === "2") {
|
|
2236
|
-
console.log(
|
|
2237
|
-
console.log(
|
|
2702
|
+
console.log(chalk7.yellow("\n\u26A0\uFE0F Acknowledged. Proceeding without full accessibility compliance."));
|
|
2703
|
+
console.log(chalk7.dim("Consider addressing these issues in a future session.\n"));
|
|
2238
2704
|
return true;
|
|
2239
2705
|
} else {
|
|
2240
|
-
console.log(
|
|
2706
|
+
console.log(chalk7.dim("\nDeployment cancelled."));
|
|
2241
2707
|
return false;
|
|
2242
2708
|
}
|
|
2243
2709
|
} finally {
|
|
@@ -2247,14 +2713,14 @@ async function a11yPreDeploy() {
|
|
|
2247
2713
|
|
|
2248
2714
|
// src/cli/geo.ts
|
|
2249
2715
|
import { Command as Command2 } from "commander";
|
|
2250
|
-
import
|
|
2251
|
-
import { readFile as
|
|
2252
|
-
import { existsSync as
|
|
2253
|
-
import { join as
|
|
2716
|
+
import chalk8 from "chalk";
|
|
2717
|
+
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
|
|
2718
|
+
import { existsSync as existsSync8 } from "fs";
|
|
2719
|
+
import { join as join7 } from "path";
|
|
2254
2720
|
import { createInterface as createInterface2 } from "readline";
|
|
2255
2721
|
import { glob as glob2 } from "glob";
|
|
2256
|
-
import * as
|
|
2257
|
-
var
|
|
2722
|
+
import * as yaml2 from "yaml";
|
|
2723
|
+
var CONFIG_PATH2 = ".archon/config.yaml";
|
|
2258
2724
|
function createPrompt2() {
|
|
2259
2725
|
const rl = createInterface2({
|
|
2260
2726
|
input: process.stdin,
|
|
@@ -2268,33 +2734,33 @@ function createPrompt2() {
|
|
|
2268
2734
|
};
|
|
2269
2735
|
}
|
|
2270
2736
|
async function loadGeoConfig(cwd) {
|
|
2271
|
-
const configPath =
|
|
2272
|
-
if (!
|
|
2737
|
+
const configPath = join7(cwd, CONFIG_PATH2);
|
|
2738
|
+
if (!existsSync8(configPath)) {
|
|
2273
2739
|
return {};
|
|
2274
2740
|
}
|
|
2275
2741
|
try {
|
|
2276
|
-
const content = await
|
|
2277
|
-
return
|
|
2742
|
+
const content = await readFile6(configPath, "utf-8");
|
|
2743
|
+
return yaml2.parse(content);
|
|
2278
2744
|
} catch {
|
|
2279
2745
|
return {};
|
|
2280
2746
|
}
|
|
2281
2747
|
}
|
|
2282
2748
|
async function saveGeoConfig(cwd, config) {
|
|
2283
|
-
const configPath =
|
|
2284
|
-
const archonDir =
|
|
2285
|
-
if (!
|
|
2286
|
-
await
|
|
2749
|
+
const configPath = join7(cwd, CONFIG_PATH2);
|
|
2750
|
+
const archonDir = join7(cwd, ".archon");
|
|
2751
|
+
if (!existsSync8(archonDir)) {
|
|
2752
|
+
await mkdir4(archonDir, { recursive: true });
|
|
2287
2753
|
}
|
|
2288
2754
|
let existing = {};
|
|
2289
|
-
if (
|
|
2755
|
+
if (existsSync8(configPath)) {
|
|
2290
2756
|
try {
|
|
2291
|
-
const content = await
|
|
2292
|
-
existing =
|
|
2757
|
+
const content = await readFile6(configPath, "utf-8");
|
|
2758
|
+
existing = yaml2.parse(content);
|
|
2293
2759
|
} catch {
|
|
2294
2760
|
}
|
|
2295
2761
|
}
|
|
2296
2762
|
const merged = { ...existing, ...config };
|
|
2297
|
-
await
|
|
2763
|
+
await writeFile6(configPath, yaml2.stringify(merged), "utf-8");
|
|
2298
2764
|
}
|
|
2299
2765
|
async function readPageContent(patterns) {
|
|
2300
2766
|
const cwd = process.cwd();
|
|
@@ -2303,7 +2769,7 @@ async function readPageContent(patterns) {
|
|
|
2303
2769
|
const files = await glob2(pattern, { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
2304
2770
|
for (const file of files.slice(0, 3)) {
|
|
2305
2771
|
try {
|
|
2306
|
-
const fileContent = await
|
|
2772
|
+
const fileContent = await readFile6(join7(cwd, file), "utf-8");
|
|
2307
2773
|
content += `
|
|
2308
2774
|
--- File: ${file} ---
|
|
2309
2775
|
${fileContent.slice(0, 5e3)}
|
|
@@ -2371,13 +2837,13 @@ async function geoIdentity() {
|
|
|
2371
2837
|
const cwd = process.cwd();
|
|
2372
2838
|
const prompt2 = createPrompt2();
|
|
2373
2839
|
try {
|
|
2374
|
-
console.log(
|
|
2840
|
+
console.log(chalk8.blue("\n\u{1F3AF} GEO Identity Generator\n"));
|
|
2375
2841
|
const { allowed, tier } = await checkStrongModelAccess();
|
|
2376
2842
|
if (!allowed) {
|
|
2377
|
-
console.log(
|
|
2378
|
-
console.log(
|
|
2843
|
+
console.log(chalk8.yellow(`\u26A0\uFE0F Your tier (${tier}) uses basic models.`));
|
|
2844
|
+
console.log(chalk8.dim("For better results, add credits or use BYOK mode.\n"));
|
|
2379
2845
|
}
|
|
2380
|
-
console.log(
|
|
2846
|
+
console.log(chalk8.dim("Reading homepage and about page content...\n"));
|
|
2381
2847
|
const pageContent = await readPageContent([
|
|
2382
2848
|
"**/index.html",
|
|
2383
2849
|
"**/index.{jsx,tsx,astro,svelte,vue}",
|
|
@@ -2388,44 +2854,44 @@ async function geoIdentity() {
|
|
|
2388
2854
|
"README.md"
|
|
2389
2855
|
]);
|
|
2390
2856
|
if (pageContent === "No content found.") {
|
|
2391
|
-
console.log(
|
|
2392
|
-
console.log(
|
|
2857
|
+
console.log(chalk8.yellow("No homepage or about page content found."));
|
|
2858
|
+
console.log(chalk8.dim("Create an index file or README.md first.\n"));
|
|
2393
2859
|
return;
|
|
2394
2860
|
}
|
|
2395
|
-
console.log(
|
|
2861
|
+
console.log(chalk8.dim("Generating identity candidates with AI...\n"));
|
|
2396
2862
|
const candidates = await generateIdentityCandidates(pageContent);
|
|
2397
|
-
console.log(
|
|
2863
|
+
console.log(chalk8.bold("\u{1F4CC} 7-Word Brand Phrases:\n"));
|
|
2398
2864
|
candidates.phrases.forEach((p, i) => {
|
|
2399
|
-
console.log(` ${
|
|
2400
|
-
console.log(` ${
|
|
2865
|
+
console.log(` ${chalk8.cyan(`${i + 1})`)} ${chalk8.bold(p.phrase)}`);
|
|
2866
|
+
console.log(` ${chalk8.dim(p.rationale)}
|
|
2401
2867
|
`);
|
|
2402
2868
|
});
|
|
2403
2869
|
const phraseChoice = await prompt2.ask('Select a phrase (1-3) or "r" to regenerate: ');
|
|
2404
2870
|
if (phraseChoice.toLowerCase() === "r") {
|
|
2405
|
-
console.log(
|
|
2871
|
+
console.log(chalk8.dim("\nRegenerating... Run the command again.\n"));
|
|
2406
2872
|
return;
|
|
2407
2873
|
}
|
|
2408
2874
|
const phraseIndex = parseInt(phraseChoice, 10) - 1;
|
|
2409
2875
|
const selectedPhrase = candidates.phrases[phraseIndex];
|
|
2410
2876
|
if (isNaN(phraseIndex) || phraseIndex < 0 || phraseIndex >= candidates.phrases.length || !selectedPhrase) {
|
|
2411
|
-
console.log(
|
|
2877
|
+
console.log(chalk8.red("Invalid selection."));
|
|
2412
2878
|
return;
|
|
2413
2879
|
}
|
|
2414
|
-
console.log(
|
|
2880
|
+
console.log(chalk8.bold("\n\u{1F4DD} 50-Word Descriptions:\n"));
|
|
2415
2881
|
candidates.descriptions.forEach((d, i) => {
|
|
2416
|
-
console.log(` ${
|
|
2417
|
-
console.log(` ${
|
|
2882
|
+
console.log(` ${chalk8.cyan(`${i + 1})`)} ${d.description}`);
|
|
2883
|
+
console.log(` ${chalk8.dim(d.rationale)}
|
|
2418
2884
|
`);
|
|
2419
2885
|
});
|
|
2420
2886
|
const descChoice = await prompt2.ask('Select a description (1-3) or "r" to regenerate: ');
|
|
2421
2887
|
if (descChoice.toLowerCase() === "r") {
|
|
2422
|
-
console.log(
|
|
2888
|
+
console.log(chalk8.dim("\nRegenerating... Run the command again.\n"));
|
|
2423
2889
|
return;
|
|
2424
2890
|
}
|
|
2425
2891
|
const descIndex = parseInt(descChoice, 10) - 1;
|
|
2426
2892
|
const selectedDescription = candidates.descriptions[descIndex];
|
|
2427
2893
|
if (isNaN(descIndex) || descIndex < 0 || descIndex >= candidates.descriptions.length || !selectedDescription) {
|
|
2428
|
-
console.log(
|
|
2894
|
+
console.log(chalk8.red("Invalid selection."));
|
|
2429
2895
|
return;
|
|
2430
2896
|
}
|
|
2431
2897
|
const geoConfig = {
|
|
@@ -2436,31 +2902,31 @@ async function geoIdentity() {
|
|
|
2436
2902
|
}
|
|
2437
2903
|
};
|
|
2438
2904
|
await saveGeoConfig(cwd, geoConfig);
|
|
2439
|
-
console.log(
|
|
2440
|
-
console.log(
|
|
2441
|
-
console.log(
|
|
2905
|
+
console.log(chalk8.green("\n\u2705 Identity saved!\n"));
|
|
2906
|
+
console.log(chalk8.dim(` Phrase: ${selectedPhrase.phrase}`));
|
|
2907
|
+
console.log(chalk8.dim(` Description: ${selectedDescription.description.slice(0, 60)}...`));
|
|
2442
2908
|
console.log();
|
|
2443
|
-
console.log(
|
|
2909
|
+
console.log(chalk8.cyan(`Use 'archon geo schema' to generate JSON-LD.`));
|
|
2444
2910
|
} finally {
|
|
2445
2911
|
prompt2.close();
|
|
2446
2912
|
}
|
|
2447
2913
|
}
|
|
2448
2914
|
async function geoSchema(options) {
|
|
2449
2915
|
const cwd = process.cwd();
|
|
2450
|
-
console.log(
|
|
2916
|
+
console.log(chalk8.blue("\n\u{1F4E6} JSON-LD Schema Generator\n"));
|
|
2451
2917
|
const config = await loadGeoConfig(cwd);
|
|
2452
2918
|
if (!config.geo?.identityPhrase || !config.geo?.shortDescription) {
|
|
2453
|
-
console.log(
|
|
2454
|
-
console.log(
|
|
2919
|
+
console.log(chalk8.yellow("No identity defined."));
|
|
2920
|
+
console.log(chalk8.dim(`Run 'archon geo identity' first.
|
|
2455
2921
|
`));
|
|
2456
2922
|
return;
|
|
2457
2923
|
}
|
|
2458
2924
|
const { identityPhrase, shortDescription } = config.geo;
|
|
2459
2925
|
let orgName = identityPhrase.split(" ").slice(0, 2).join(" ");
|
|
2460
2926
|
try {
|
|
2461
|
-
const pkgPath =
|
|
2462
|
-
if (
|
|
2463
|
-
const pkg = JSON.parse(await
|
|
2927
|
+
const pkgPath = join7(cwd, "package.json");
|
|
2928
|
+
if (existsSync8(pkgPath)) {
|
|
2929
|
+
const pkg = JSON.parse(await readFile6(pkgPath, "utf-8"));
|
|
2464
2930
|
if (pkg.name) {
|
|
2465
2931
|
orgName = pkg.name.replace(/-/g, " ").replace(/^\w/, (c) => c.toUpperCase());
|
|
2466
2932
|
}
|
|
@@ -2494,40 +2960,40 @@ async function geoSchema(options) {
|
|
|
2494
2960
|
};
|
|
2495
2961
|
const jsonLd = JSON.stringify(schema, null, 2);
|
|
2496
2962
|
if (options.output) {
|
|
2497
|
-
await
|
|
2498
|
-
console.log(
|
|
2963
|
+
await writeFile6(options.output, jsonLd, "utf-8");
|
|
2964
|
+
console.log(chalk8.green(`\u2705 Schema written to ${options.output}`));
|
|
2499
2965
|
} else {
|
|
2500
|
-
console.log(
|
|
2501
|
-
console.log(
|
|
2966
|
+
console.log(chalk8.bold("Generated JSON-LD:\n"));
|
|
2967
|
+
console.log(chalk8.cyan(jsonLd));
|
|
2502
2968
|
}
|
|
2503
2969
|
if (options.apply) {
|
|
2504
|
-
console.log(
|
|
2970
|
+
console.log(chalk8.dim("\nAttempting to insert into homepage <head>...\n"));
|
|
2505
2971
|
const indexFiles = await glob2("**/index.html", { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
2506
2972
|
if (indexFiles.length === 0) {
|
|
2507
|
-
console.log(
|
|
2508
|
-
console.log(
|
|
2973
|
+
console.log(chalk8.yellow("No index.html found. Manually add to your HTML <head>:"));
|
|
2974
|
+
console.log(chalk8.dim(`<script type="application/ld+json">
|
|
2509
2975
|
${jsonLd}
|
|
2510
2976
|
</script>`));
|
|
2511
2977
|
return;
|
|
2512
2978
|
}
|
|
2513
2979
|
const firstIndexFile = indexFiles[0];
|
|
2514
2980
|
if (!firstIndexFile) {
|
|
2515
|
-
console.log(
|
|
2981
|
+
console.log(chalk8.yellow("No index.html found."));
|
|
2516
2982
|
return;
|
|
2517
2983
|
}
|
|
2518
|
-
const indexPath =
|
|
2519
|
-
let indexContent = await
|
|
2984
|
+
const indexPath = join7(cwd, firstIndexFile);
|
|
2985
|
+
let indexContent = await readFile6(indexPath, "utf-8");
|
|
2520
2986
|
const scriptTag = `<script type="application/ld+json">
|
|
2521
2987
|
${jsonLd}
|
|
2522
2988
|
</script>`;
|
|
2523
2989
|
if (indexContent.includes("application/ld+json")) {
|
|
2524
|
-
console.log(
|
|
2990
|
+
console.log(chalk8.yellow("JSON-LD already exists in index.html. Remove it first or update manually."));
|
|
2525
2991
|
return;
|
|
2526
2992
|
}
|
|
2527
2993
|
indexContent = indexContent.replace("</head>", `${scriptTag}
|
|
2528
2994
|
</head>`);
|
|
2529
|
-
await
|
|
2530
|
-
console.log(
|
|
2995
|
+
await writeFile6(indexPath, indexContent, "utf-8");
|
|
2996
|
+
console.log(chalk8.green(`\u2705 JSON-LD inserted into ${firstIndexFile}`));
|
|
2531
2997
|
}
|
|
2532
2998
|
console.log();
|
|
2533
2999
|
}
|
|
@@ -2543,21 +3009,21 @@ Guidelines:
|
|
|
2543
3009
|
Output your response as valid JSON.`;
|
|
2544
3010
|
async function geoFaq(options) {
|
|
2545
3011
|
const cwd = process.cwd();
|
|
2546
|
-
console.log(
|
|
3012
|
+
console.log(chalk8.blue("\n\u2753 FAQ Schema Generator\n"));
|
|
2547
3013
|
const config = await loadGeoConfig(cwd);
|
|
2548
3014
|
if (!config.geo?.identityPhrase || !config.geo?.shortDescription) {
|
|
2549
|
-
console.log(
|
|
2550
|
-
console.log(
|
|
3015
|
+
console.log(chalk8.yellow("No identity defined."));
|
|
3016
|
+
console.log(chalk8.dim(`Run 'archon geo identity' first.
|
|
2551
3017
|
`));
|
|
2552
3018
|
return;
|
|
2553
3019
|
}
|
|
2554
3020
|
const { identityPhrase, shortDescription } = config.geo;
|
|
2555
3021
|
const { allowed, tier } = await checkStrongModelAccess();
|
|
2556
3022
|
if (!allowed) {
|
|
2557
|
-
console.log(
|
|
2558
|
-
console.log(
|
|
3023
|
+
console.log(chalk8.yellow(`\u26A0\uFE0F Your tier (${tier}) uses basic models.`));
|
|
3024
|
+
console.log(chalk8.dim("For better results, add credits or use BYOK mode.\n"));
|
|
2559
3025
|
}
|
|
2560
|
-
console.log(
|
|
3026
|
+
console.log(chalk8.dim("Generating FAQ content with AI...\n"));
|
|
2561
3027
|
const agent = new ArchitectAgent({ temperature: 0.7 });
|
|
2562
3028
|
const prompt2 = `Generate FAQ content for a product/service with:
|
|
2563
3029
|
- Brand phrase: "${identityPhrase}"
|
|
@@ -2577,7 +3043,7 @@ Generate 6-8 FAQs as JSON:
|
|
|
2577
3043
|
);
|
|
2578
3044
|
const jsonMatch = response.content.match(/\{[\s\S]*\}/);
|
|
2579
3045
|
if (!jsonMatch) {
|
|
2580
|
-
console.log(
|
|
3046
|
+
console.log(chalk8.red("Failed to generate FAQ content."));
|
|
2581
3047
|
return;
|
|
2582
3048
|
}
|
|
2583
3049
|
const parsed = JSON.parse(jsonMatch[0]);
|
|
@@ -2595,17 +3061,17 @@ Generate 6-8 FAQs as JSON:
|
|
|
2595
3061
|
};
|
|
2596
3062
|
const jsonLd = JSON.stringify(faqSchema, null, 2);
|
|
2597
3063
|
if (options.output) {
|
|
2598
|
-
await
|
|
2599
|
-
console.log(
|
|
3064
|
+
await writeFile6(options.output, jsonLd, "utf-8");
|
|
3065
|
+
console.log(chalk8.green(`\u2705 FAQ schema written to ${options.output}`));
|
|
2600
3066
|
} else {
|
|
2601
|
-
console.log(
|
|
2602
|
-
console.log(
|
|
3067
|
+
console.log(chalk8.bold("Generated FAQPage JSON-LD:\n"));
|
|
3068
|
+
console.log(chalk8.cyan(jsonLd));
|
|
2603
3069
|
}
|
|
2604
3070
|
console.log();
|
|
2605
3071
|
}
|
|
2606
3072
|
async function geoAudit() {
|
|
2607
3073
|
const cwd = process.cwd();
|
|
2608
|
-
console.log(
|
|
3074
|
+
console.log(chalk8.blue("\n\u{1F50D} GEO Audit\n"));
|
|
2609
3075
|
const result = {
|
|
2610
3076
|
identityDefined: false,
|
|
2611
3077
|
phraseInH1: false,
|
|
@@ -2618,75 +3084,75 @@ async function geoAudit() {
|
|
|
2618
3084
|
const config = await loadGeoConfig(cwd);
|
|
2619
3085
|
if (config.geo?.identityPhrase && config.geo?.shortDescription) {
|
|
2620
3086
|
result.identityDefined = true;
|
|
2621
|
-
console.log(
|
|
2622
|
-
console.log(
|
|
3087
|
+
console.log(chalk8.green("\u2705 Identity defined"));
|
|
3088
|
+
console.log(chalk8.dim(` Phrase: ${config.geo.identityPhrase}`));
|
|
2623
3089
|
} else {
|
|
2624
3090
|
result.issues.push('Identity not defined. Run "archon geo identity"');
|
|
2625
|
-
console.log(
|
|
3091
|
+
console.log(chalk8.red("\u274C Identity not defined"));
|
|
2626
3092
|
}
|
|
2627
3093
|
const htmlFiles = await glob2("**/index.html", { cwd, ignore: ["**/node_modules/**", "**/dist/**"] });
|
|
2628
3094
|
const firstHtmlFile = htmlFiles[0];
|
|
2629
3095
|
if (firstHtmlFile && config.geo?.identityPhrase) {
|
|
2630
|
-
const indexPath =
|
|
2631
|
-
const content = await
|
|
3096
|
+
const indexPath = join7(cwd, firstHtmlFile);
|
|
3097
|
+
const content = await readFile6(indexPath, "utf-8");
|
|
2632
3098
|
const identityPhrase = config.geo.identityPhrase;
|
|
2633
3099
|
const firstKeyword = identityPhrase.toLowerCase().split(" ")[0] ?? "";
|
|
2634
3100
|
const h1Match = content.match(/<h1[^>]*>(.*?)<\/h1>/is);
|
|
2635
3101
|
if (h1Match?.[1] && h1Match[1].toLowerCase().includes(firstKeyword)) {
|
|
2636
3102
|
result.phraseInH1 = true;
|
|
2637
|
-
console.log(
|
|
3103
|
+
console.log(chalk8.green("\u2705 Brand keyword in H1"));
|
|
2638
3104
|
} else {
|
|
2639
3105
|
result.issues.push("Brand phrase keyword not found in H1");
|
|
2640
|
-
console.log(
|
|
3106
|
+
console.log(chalk8.yellow("\u26A0\uFE0F Brand keyword not in H1"));
|
|
2641
3107
|
}
|
|
2642
3108
|
const metaMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i);
|
|
2643
3109
|
if (metaMatch?.[1] && metaMatch[1].toLowerCase().includes(firstKeyword)) {
|
|
2644
3110
|
result.phraseInMetaDescription = true;
|
|
2645
|
-
console.log(
|
|
3111
|
+
console.log(chalk8.green("\u2705 Brand keyword in meta description"));
|
|
2646
3112
|
} else {
|
|
2647
3113
|
result.issues.push("Brand phrase keyword not found in meta description");
|
|
2648
|
-
console.log(
|
|
3114
|
+
console.log(chalk8.yellow("\u26A0\uFE0F Brand keyword not in meta description"));
|
|
2649
3115
|
}
|
|
2650
3116
|
if (content.includes("application/ld+json")) {
|
|
2651
3117
|
if (content.includes('"@type":"Organization"') || content.includes('"@type": "Organization"')) {
|
|
2652
3118
|
result.hasOrganizationSchema = true;
|
|
2653
|
-
console.log(
|
|
3119
|
+
console.log(chalk8.green("\u2705 Organization schema present"));
|
|
2654
3120
|
} else {
|
|
2655
3121
|
result.issues.push("Organization schema not found");
|
|
2656
|
-
console.log(
|
|
3122
|
+
console.log(chalk8.yellow("\u26A0\uFE0F Organization schema missing"));
|
|
2657
3123
|
}
|
|
2658
3124
|
if (content.includes('"@type":"Service"') || content.includes('"@type": "Service"')) {
|
|
2659
3125
|
result.hasServiceSchema = true;
|
|
2660
|
-
console.log(
|
|
3126
|
+
console.log(chalk8.green("\u2705 Service schema present"));
|
|
2661
3127
|
} else {
|
|
2662
3128
|
result.issues.push("Service schema not found");
|
|
2663
|
-
console.log(
|
|
3129
|
+
console.log(chalk8.yellow("\u26A0\uFE0F Service schema missing"));
|
|
2664
3130
|
}
|
|
2665
3131
|
if (content.includes('"@type":"FAQPage"') || content.includes('"@type": "FAQPage"')) {
|
|
2666
3132
|
result.hasFAQSchema = true;
|
|
2667
|
-
console.log(
|
|
3133
|
+
console.log(chalk8.green("\u2705 FAQPage schema present"));
|
|
2668
3134
|
} else {
|
|
2669
3135
|
result.issues.push("FAQPage schema not found");
|
|
2670
|
-
console.log(
|
|
3136
|
+
console.log(chalk8.yellow("\u26A0\uFE0F FAQPage schema missing"));
|
|
2671
3137
|
}
|
|
2672
3138
|
} else {
|
|
2673
3139
|
result.issues.push("No JSON-LD schemas found");
|
|
2674
|
-
console.log(
|
|
3140
|
+
console.log(chalk8.red("\u274C No JSON-LD schemas found"));
|
|
2675
3141
|
}
|
|
2676
3142
|
} else if (htmlFiles.length === 0) {
|
|
2677
3143
|
result.issues.push("No index.html found");
|
|
2678
|
-
console.log(
|
|
3144
|
+
console.log(chalk8.yellow("\u26A0\uFE0F No index.html found to audit"));
|
|
2679
3145
|
}
|
|
2680
3146
|
console.log();
|
|
2681
3147
|
const passed = result.issues.length === 0;
|
|
2682
3148
|
if (passed) {
|
|
2683
|
-
console.log(
|
|
3149
|
+
console.log(chalk8.green.bold("\u2705 GEO Audit Passed"));
|
|
2684
3150
|
} else {
|
|
2685
|
-
console.log(
|
|
3151
|
+
console.log(chalk8.yellow.bold(`\u26A0\uFE0F ${result.issues.length} issue(s) found`));
|
|
2686
3152
|
console.log();
|
|
2687
|
-
console.log(
|
|
3153
|
+
console.log(chalk8.bold("Recommendations:"));
|
|
2688
3154
|
result.issues.forEach((issue, i) => {
|
|
2689
|
-
console.log(
|
|
3155
|
+
console.log(chalk8.dim(` ${i + 1}. ${issue}`));
|
|
2690
3156
|
});
|
|
2691
3157
|
}
|
|
2692
3158
|
console.log();
|
|
@@ -2723,10 +3189,10 @@ Examples:
|
|
|
2723
3189
|
|
|
2724
3190
|
// src/cli/seo.ts
|
|
2725
3191
|
import { Command as Command3 } from "commander";
|
|
2726
|
-
import
|
|
2727
|
-
import { readFile as
|
|
2728
|
-
import { existsSync as
|
|
2729
|
-
import { join as
|
|
3192
|
+
import chalk9 from "chalk";
|
|
3193
|
+
import { readFile as readFile7, writeFile as writeFile7, mkdir as mkdir5 } from "fs/promises";
|
|
3194
|
+
import { existsSync as existsSync9 } from "fs";
|
|
3195
|
+
import { join as join8 } from "path";
|
|
2730
3196
|
import { createInterface as createInterface3 } from "readline";
|
|
2731
3197
|
import { glob as glob3 } from "glob";
|
|
2732
3198
|
var SEO_CHECKS = {
|
|
@@ -2835,7 +3301,7 @@ async function getWebFiles() {
|
|
|
2835
3301
|
let allFiles = [];
|
|
2836
3302
|
for (const pattern of patterns) {
|
|
2837
3303
|
const files = await glob3(pattern, { cwd: process.cwd(), ignore: ignorePatterns });
|
|
2838
|
-
allFiles = allFiles.concat(files.map((f) =>
|
|
3304
|
+
allFiles = allFiles.concat(files.map((f) => join8(process.cwd(), f)));
|
|
2839
3305
|
}
|
|
2840
3306
|
return allFiles;
|
|
2841
3307
|
}
|
|
@@ -2843,7 +3309,7 @@ async function scanForSeoIssues(files) {
|
|
|
2843
3309
|
const issues = [];
|
|
2844
3310
|
for (const file of files) {
|
|
2845
3311
|
try {
|
|
2846
|
-
const content = await
|
|
3312
|
+
const content = await readFile7(file, "utf-8");
|
|
2847
3313
|
const relativePath = file.replace(process.cwd() + "/", "");
|
|
2848
3314
|
for (const [checkId, check] of Object.entries(SEO_CHECKS)) {
|
|
2849
3315
|
const hasTag = check.regex.test(content);
|
|
@@ -2873,19 +3339,19 @@ function findHeadInsertionPoint(content) {
|
|
|
2873
3339
|
return null;
|
|
2874
3340
|
}
|
|
2875
3341
|
async function seoCheck(options) {
|
|
2876
|
-
console.log(
|
|
2877
|
-
console.log(
|
|
3342
|
+
console.log(chalk9.blue("\n\u{1F50D} SEO Check\n"));
|
|
3343
|
+
console.log(chalk9.dim("Scanning for SEO issues...\n"));
|
|
2878
3344
|
const files = await getWebFiles();
|
|
2879
3345
|
if (files.length === 0) {
|
|
2880
|
-
console.log(
|
|
3346
|
+
console.log(chalk9.yellow("No web files found to check."));
|
|
2881
3347
|
return;
|
|
2882
3348
|
}
|
|
2883
|
-
console.log(
|
|
3349
|
+
console.log(chalk9.dim(`Scanning ${files.length} files...
|
|
2884
3350
|
`));
|
|
2885
3351
|
const issues = await scanForSeoIssues(files);
|
|
2886
|
-
const archonDir =
|
|
2887
|
-
if (!
|
|
2888
|
-
await
|
|
3352
|
+
const archonDir = join8(process.cwd(), ".archon");
|
|
3353
|
+
if (!existsSync9(archonDir)) {
|
|
3354
|
+
await mkdir5(archonDir, { recursive: true });
|
|
2889
3355
|
}
|
|
2890
3356
|
const report = {
|
|
2891
3357
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -2894,24 +3360,24 @@ async function seoCheck(options) {
|
|
|
2894
3360
|
issues,
|
|
2895
3361
|
passed: issues.length === 0
|
|
2896
3362
|
};
|
|
2897
|
-
await
|
|
3363
|
+
await writeFile7(join8(archonDir, "seo-report.json"), JSON.stringify(report, null, 2));
|
|
2898
3364
|
if (issues.length === 0) {
|
|
2899
|
-
console.log(
|
|
2900
|
-
console.log(
|
|
3365
|
+
console.log(chalk9.green("\u2705 SEO Check Passed\n"));
|
|
3366
|
+
console.log(chalk9.dim("All essential meta tags are present."));
|
|
2901
3367
|
return;
|
|
2902
3368
|
}
|
|
2903
|
-
console.log(
|
|
3369
|
+
console.log(chalk9.yellow(`\u26A0\uFE0F ${issues.length} SEO Issues Found
|
|
2904
3370
|
`));
|
|
2905
3371
|
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
2906
3372
|
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
2907
3373
|
const infoCount = issues.filter((i) => i.severity === "info").length;
|
|
2908
|
-
console.log(
|
|
2909
|
-
if (criticalCount > 0) console.log(
|
|
2910
|
-
if (warningCount > 0) console.log(
|
|
2911
|
-
if (infoCount > 0) console.log(
|
|
3374
|
+
console.log(chalk9.dim("Summary:"));
|
|
3375
|
+
if (criticalCount > 0) console.log(chalk9.red(` \u2022 ${criticalCount} critical`));
|
|
3376
|
+
if (warningCount > 0) console.log(chalk9.yellow(` \u2022 ${warningCount} warning`));
|
|
3377
|
+
if (infoCount > 0) console.log(chalk9.dim(` \u2022 ${infoCount} info`));
|
|
2912
3378
|
console.log();
|
|
2913
|
-
console.log(
|
|
2914
|
-
console.log(
|
|
3379
|
+
console.log(chalk9.bold("File | Issue | Recommendation"));
|
|
3380
|
+
console.log(chalk9.dim("\u2500".repeat(100)));
|
|
2915
3381
|
const issuesByFile = /* @__PURE__ */ new Map();
|
|
2916
3382
|
for (const issue of issues) {
|
|
2917
3383
|
const existing = issuesByFile.get(issue.file) || [];
|
|
@@ -2921,31 +3387,31 @@ async function seoCheck(options) {
|
|
|
2921
3387
|
for (const [file, fileIssues] of issuesByFile) {
|
|
2922
3388
|
const displayFile = file.length > 30 ? "..." + file.slice(-27) : file.padEnd(30);
|
|
2923
3389
|
for (const issue of fileIssues.slice(0, options.verbose ? 20 : 5)) {
|
|
2924
|
-
const color = issue.severity === "critical" ?
|
|
3390
|
+
const color = issue.severity === "critical" ? chalk9.red : issue.severity === "warning" ? chalk9.yellow : chalk9.dim;
|
|
2925
3391
|
const displayIssue = issue.issue.padEnd(24).slice(0, 24);
|
|
2926
3392
|
const displayRec = issue.recommendation.slice(0, 50);
|
|
2927
3393
|
console.log(color(`${displayFile} | ${displayIssue} | ${displayRec}`));
|
|
2928
3394
|
}
|
|
2929
3395
|
if (!options.verbose && fileIssues.length > 5) {
|
|
2930
|
-
console.log(
|
|
3396
|
+
console.log(chalk9.dim(`${"".padEnd(30)} | ... and ${fileIssues.length - 5} more issues`));
|
|
2931
3397
|
}
|
|
2932
3398
|
}
|
|
2933
|
-
console.log(
|
|
3399
|
+
console.log(chalk9.dim(`
|
|
2934
3400
|
Full report saved to: .archon/seo-report.json`));
|
|
2935
3401
|
}
|
|
2936
3402
|
async function seoFix(options) {
|
|
2937
3403
|
const prompt2 = createPrompt3();
|
|
2938
3404
|
try {
|
|
2939
|
-
console.log(
|
|
2940
|
-
const reportPath =
|
|
2941
|
-
if (!
|
|
2942
|
-
console.log(
|
|
3405
|
+
console.log(chalk9.blue("\n\u{1F527} SEO Auto-Fix\n"));
|
|
3406
|
+
const reportPath = join8(process.cwd(), ".archon/seo-report.json");
|
|
3407
|
+
if (!existsSync9(reportPath)) {
|
|
3408
|
+
console.log(chalk9.yellow("No SEO report found. Running check first...\n"));
|
|
2943
3409
|
await seoCheck({});
|
|
2944
3410
|
}
|
|
2945
|
-
const reportContent = await
|
|
3411
|
+
const reportContent = await readFile7(reportPath, "utf-8");
|
|
2946
3412
|
const report = JSON.parse(reportContent);
|
|
2947
3413
|
if (report.passed || report.issues.length === 0) {
|
|
2948
|
-
console.log(
|
|
3414
|
+
console.log(chalk9.green("No issues to fix!"));
|
|
2949
3415
|
return;
|
|
2950
3416
|
}
|
|
2951
3417
|
const issuesByFile = /* @__PURE__ */ new Map();
|
|
@@ -2956,16 +3422,16 @@ async function seoFix(options) {
|
|
|
2956
3422
|
}
|
|
2957
3423
|
let totalFixes = 0;
|
|
2958
3424
|
for (const [file, fileIssues] of issuesByFile) {
|
|
2959
|
-
const filePath =
|
|
3425
|
+
const filePath = join8(process.cwd(), file);
|
|
2960
3426
|
let content;
|
|
2961
3427
|
try {
|
|
2962
|
-
content = await
|
|
3428
|
+
content = await readFile7(filePath, "utf-8");
|
|
2963
3429
|
} catch {
|
|
2964
3430
|
continue;
|
|
2965
3431
|
}
|
|
2966
3432
|
const insertPoint = findHeadInsertionPoint(content);
|
|
2967
3433
|
if (!insertPoint) {
|
|
2968
|
-
console.log(
|
|
3434
|
+
console.log(chalk9.yellow(` ${file}: No <head> tag found, skipping`));
|
|
2969
3435
|
continue;
|
|
2970
3436
|
}
|
|
2971
3437
|
const tagsToAdd = [];
|
|
@@ -2984,33 +3450,33 @@ async function seoFix(options) {
|
|
|
2984
3450
|
}
|
|
2985
3451
|
if (tagsToAdd.length === 0) continue;
|
|
2986
3452
|
const newContent = content.slice(0, insertPoint.index) + "\n" + tagsToAdd.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
2987
|
-
console.log(
|
|
3453
|
+
console.log(chalk9.cyan(`
|
|
2988
3454
|
${file}:`));
|
|
2989
3455
|
for (const tag of tagsToAdd) {
|
|
2990
|
-
console.log(
|
|
3456
|
+
console.log(chalk9.green(` + ${tag.slice(0, 70)}${tag.length > 70 ? "..." : ""}`));
|
|
2991
3457
|
}
|
|
2992
3458
|
if (!options.dryRun) {
|
|
2993
|
-
const confirm = await prompt2.ask(
|
|
3459
|
+
const confirm = await prompt2.ask(chalk9.dim(" Apply these changes? (y/N): "));
|
|
2994
3460
|
if (confirm.toLowerCase() === "y") {
|
|
2995
|
-
await
|
|
3461
|
+
await writeFile7(filePath, newContent);
|
|
2996
3462
|
totalFixes += tagsToAdd.length;
|
|
2997
|
-
console.log(
|
|
3463
|
+
console.log(chalk9.green(" \u2713 Applied"));
|
|
2998
3464
|
} else {
|
|
2999
|
-
console.log(
|
|
3465
|
+
console.log(chalk9.dim(" Skipped"));
|
|
3000
3466
|
}
|
|
3001
3467
|
} else {
|
|
3002
3468
|
totalFixes += tagsToAdd.length;
|
|
3003
3469
|
}
|
|
3004
3470
|
}
|
|
3005
3471
|
if (totalFixes === 0) {
|
|
3006
|
-
console.log(
|
|
3472
|
+
console.log(chalk9.dim("\nNo auto-fixable issues found. Some issues require manual configuration."));
|
|
3007
3473
|
} else if (options.dryRun) {
|
|
3008
|
-
console.log(
|
|
3474
|
+
console.log(chalk9.yellow(`
|
|
3009
3475
|
${totalFixes} fixes would be applied. Run without --dry-run to apply.`));
|
|
3010
3476
|
} else {
|
|
3011
|
-
console.log(
|
|
3477
|
+
console.log(chalk9.green(`
|
|
3012
3478
|
\u2705 Applied ${totalFixes} fixes.`));
|
|
3013
|
-
console.log(
|
|
3479
|
+
console.log(chalk9.dim('Run "archon seo check" to verify fixes.'));
|
|
3014
3480
|
}
|
|
3015
3481
|
} finally {
|
|
3016
3482
|
prompt2.close();
|
|
@@ -3019,33 +3485,33 @@ ${totalFixes} fixes would be applied. Run without --dry-run to apply.`));
|
|
|
3019
3485
|
async function seoOpenGraph(options) {
|
|
3020
3486
|
const prompt2 = createPrompt3();
|
|
3021
3487
|
try {
|
|
3022
|
-
console.log(
|
|
3488
|
+
console.log(chalk9.blue("\n\u{1F4F1} Add Open Graph Tags\n"));
|
|
3023
3489
|
let targetFile;
|
|
3024
3490
|
if (options.file) {
|
|
3025
|
-
targetFile = options.file.startsWith("/") ? options.file :
|
|
3491
|
+
targetFile = options.file.startsWith("/") ? options.file : join8(process.cwd(), options.file);
|
|
3026
3492
|
} else {
|
|
3027
3493
|
const files = await getWebFiles();
|
|
3028
3494
|
if (files.length === 0) {
|
|
3029
|
-
console.log(
|
|
3495
|
+
console.log(chalk9.yellow("No web files found."));
|
|
3030
3496
|
return;
|
|
3031
3497
|
}
|
|
3032
|
-
console.log(
|
|
3498
|
+
console.log(chalk9.dim("Available files:"));
|
|
3033
3499
|
files.slice(0, 10).forEach((f, i) => {
|
|
3034
3500
|
console.log(` ${i + 1}) ${f.replace(process.cwd() + "/", "")}`);
|
|
3035
3501
|
});
|
|
3036
3502
|
if (files.length > 10) {
|
|
3037
|
-
console.log(
|
|
3503
|
+
console.log(chalk9.dim(` ... and ${files.length - 10} more`));
|
|
3038
3504
|
}
|
|
3039
3505
|
const fileChoice = await prompt2.ask("\nEnter file path or number: ");
|
|
3040
3506
|
const num = parseInt(fileChoice, 10);
|
|
3041
3507
|
if (num > 0 && num <= files.length) {
|
|
3042
3508
|
targetFile = files[num - 1] ?? "";
|
|
3043
3509
|
} else {
|
|
3044
|
-
targetFile = fileChoice.startsWith("/") ? fileChoice :
|
|
3510
|
+
targetFile = fileChoice.startsWith("/") ? fileChoice : join8(process.cwd(), fileChoice);
|
|
3045
3511
|
}
|
|
3046
3512
|
}
|
|
3047
|
-
if (!
|
|
3048
|
-
console.log(
|
|
3513
|
+
if (!existsSync9(targetFile)) {
|
|
3514
|
+
console.log(chalk9.red(`File not found: ${targetFile}`));
|
|
3049
3515
|
return;
|
|
3050
3516
|
}
|
|
3051
3517
|
const ogTitle = await prompt2.ask("og:title (page title for social): ");
|
|
@@ -3059,22 +3525,22 @@ async function seoOpenGraph(options) {
|
|
|
3059
3525
|
`<meta property="og:image" content="${ogImage}">`,
|
|
3060
3526
|
`<meta property="og:url" content="${ogUrl}">`
|
|
3061
3527
|
];
|
|
3062
|
-
const content = await
|
|
3528
|
+
const content = await readFile7(targetFile, "utf-8");
|
|
3063
3529
|
const insertPoint = findHeadInsertionPoint(content);
|
|
3064
3530
|
if (!insertPoint) {
|
|
3065
|
-
console.log(
|
|
3066
|
-
tags.forEach((tag) => console.log(
|
|
3531
|
+
console.log(chalk9.yellow("No <head> tag found. Add these tags manually:"));
|
|
3532
|
+
tags.forEach((tag) => console.log(chalk9.cyan(` ${tag}`)));
|
|
3067
3533
|
return;
|
|
3068
3534
|
}
|
|
3069
|
-
console.log(
|
|
3070
|
-
tags.forEach((tag) => console.log(
|
|
3535
|
+
console.log(chalk9.dim("\nTags to add:"));
|
|
3536
|
+
tags.forEach((tag) => console.log(chalk9.green(` + ${tag}`)));
|
|
3071
3537
|
const confirm = await prompt2.ask("\nApply changes? (y/N): ");
|
|
3072
3538
|
if (confirm.toLowerCase() === "y") {
|
|
3073
3539
|
const newContent = content.slice(0, insertPoint.index) + "\n" + tags.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
3074
|
-
await
|
|
3075
|
-
console.log(
|
|
3540
|
+
await writeFile7(targetFile, newContent);
|
|
3541
|
+
console.log(chalk9.green("\n\u2705 Open Graph tags added."));
|
|
3076
3542
|
} else {
|
|
3077
|
-
console.log(
|
|
3543
|
+
console.log(chalk9.dim("Cancelled."));
|
|
3078
3544
|
}
|
|
3079
3545
|
} finally {
|
|
3080
3546
|
prompt2.close();
|
|
@@ -3083,36 +3549,36 @@ async function seoOpenGraph(options) {
|
|
|
3083
3549
|
async function seoTwitter(options) {
|
|
3084
3550
|
const prompt2 = createPrompt3();
|
|
3085
3551
|
try {
|
|
3086
|
-
console.log(
|
|
3552
|
+
console.log(chalk9.blue("\n\u{1F426} Add Twitter Card Tags\n"));
|
|
3087
3553
|
let targetFile;
|
|
3088
3554
|
if (options.file) {
|
|
3089
|
-
targetFile = options.file.startsWith("/") ? options.file :
|
|
3555
|
+
targetFile = options.file.startsWith("/") ? options.file : join8(process.cwd(), options.file);
|
|
3090
3556
|
} else {
|
|
3091
3557
|
const files = await getWebFiles();
|
|
3092
3558
|
if (files.length === 0) {
|
|
3093
|
-
console.log(
|
|
3559
|
+
console.log(chalk9.yellow("No web files found."));
|
|
3094
3560
|
return;
|
|
3095
3561
|
}
|
|
3096
|
-
console.log(
|
|
3562
|
+
console.log(chalk9.dim("Available files:"));
|
|
3097
3563
|
files.slice(0, 10).forEach((f, i) => {
|
|
3098
3564
|
console.log(` ${i + 1}) ${f.replace(process.cwd() + "/", "")}`);
|
|
3099
3565
|
});
|
|
3100
3566
|
if (files.length > 10) {
|
|
3101
|
-
console.log(
|
|
3567
|
+
console.log(chalk9.dim(` ... and ${files.length - 10} more`));
|
|
3102
3568
|
}
|
|
3103
3569
|
const fileChoice = await prompt2.ask("\nEnter file path or number: ");
|
|
3104
3570
|
const num = parseInt(fileChoice, 10);
|
|
3105
3571
|
if (num > 0 && num <= files.length) {
|
|
3106
3572
|
targetFile = files[num - 1] ?? "";
|
|
3107
3573
|
} else {
|
|
3108
|
-
targetFile = fileChoice.startsWith("/") ? fileChoice :
|
|
3574
|
+
targetFile = fileChoice.startsWith("/") ? fileChoice : join8(process.cwd(), fileChoice);
|
|
3109
3575
|
}
|
|
3110
3576
|
}
|
|
3111
|
-
if (!
|
|
3112
|
-
console.log(
|
|
3577
|
+
if (!existsSync9(targetFile)) {
|
|
3578
|
+
console.log(chalk9.red(`File not found: ${targetFile}`));
|
|
3113
3579
|
return;
|
|
3114
3580
|
}
|
|
3115
|
-
console.log(
|
|
3581
|
+
console.log(chalk9.dim("Card types: summary, summary_large_image, app, player"));
|
|
3116
3582
|
const cardType = await prompt2.ask("twitter:card type (default: summary_large_image): ") || "summary_large_image";
|
|
3117
3583
|
const twitterTitle = await prompt2.ask("twitter:title: ");
|
|
3118
3584
|
const twitterDescription = await prompt2.ask("twitter:description: ");
|
|
@@ -3123,22 +3589,22 @@ async function seoTwitter(options) {
|
|
|
3123
3589
|
`<meta name="twitter:description" content="${twitterDescription}">`,
|
|
3124
3590
|
`<meta name="twitter:image" content="${twitterImage}">`
|
|
3125
3591
|
];
|
|
3126
|
-
const content = await
|
|
3592
|
+
const content = await readFile7(targetFile, "utf-8");
|
|
3127
3593
|
const insertPoint = findHeadInsertionPoint(content);
|
|
3128
3594
|
if (!insertPoint) {
|
|
3129
|
-
console.log(
|
|
3130
|
-
tags.forEach((tag) => console.log(
|
|
3595
|
+
console.log(chalk9.yellow("No <head> tag found. Add these tags manually:"));
|
|
3596
|
+
tags.forEach((tag) => console.log(chalk9.cyan(` ${tag}`)));
|
|
3131
3597
|
return;
|
|
3132
3598
|
}
|
|
3133
|
-
console.log(
|
|
3134
|
-
tags.forEach((tag) => console.log(
|
|
3599
|
+
console.log(chalk9.dim("\nTags to add:"));
|
|
3600
|
+
tags.forEach((tag) => console.log(chalk9.green(` + ${tag}`)));
|
|
3135
3601
|
const confirm = await prompt2.ask("\nApply changes? (y/N): ");
|
|
3136
3602
|
if (confirm.toLowerCase() === "y") {
|
|
3137
3603
|
const newContent = content.slice(0, insertPoint.index) + "\n" + tags.map((tag) => insertPoint.indent + tag).join("\n") + content.slice(insertPoint.index);
|
|
3138
|
-
await
|
|
3139
|
-
console.log(
|
|
3604
|
+
await writeFile7(targetFile, newContent);
|
|
3605
|
+
console.log(chalk9.green("\n\u2705 Twitter Card tags added."));
|
|
3140
3606
|
} else {
|
|
3141
|
-
console.log(
|
|
3607
|
+
console.log(chalk9.dim("Cancelled."));
|
|
3142
3608
|
}
|
|
3143
3609
|
} finally {
|
|
3144
3610
|
prompt2.close();
|
|
@@ -3162,7 +3628,7 @@ Examples:
|
|
|
3162
3628
|
await seoCheck(options);
|
|
3163
3629
|
process.exit(0);
|
|
3164
3630
|
} catch (error) {
|
|
3165
|
-
console.error(
|
|
3631
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
3166
3632
|
process.exit(1);
|
|
3167
3633
|
}
|
|
3168
3634
|
});
|
|
@@ -3171,7 +3637,7 @@ Examples:
|
|
|
3171
3637
|
await seoFix(options);
|
|
3172
3638
|
process.exit(0);
|
|
3173
3639
|
} catch (error) {
|
|
3174
|
-
console.error(
|
|
3640
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
3175
3641
|
process.exit(1);
|
|
3176
3642
|
}
|
|
3177
3643
|
});
|
|
@@ -3180,7 +3646,7 @@ Examples:
|
|
|
3180
3646
|
await seoOpenGraph(options);
|
|
3181
3647
|
process.exit(0);
|
|
3182
3648
|
} catch (error) {
|
|
3183
|
-
console.error(
|
|
3649
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
3184
3650
|
process.exit(1);
|
|
3185
3651
|
}
|
|
3186
3652
|
});
|
|
@@ -3189,7 +3655,7 @@ Examples:
|
|
|
3189
3655
|
await seoTwitter(options);
|
|
3190
3656
|
process.exit(0);
|
|
3191
3657
|
} catch (error) {
|
|
3192
|
-
console.error(
|
|
3658
|
+
console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
|
|
3193
3659
|
process.exit(1);
|
|
3194
3660
|
}
|
|
3195
3661
|
});
|
|
@@ -3197,12 +3663,12 @@ Examples:
|
|
|
3197
3663
|
}
|
|
3198
3664
|
|
|
3199
3665
|
// src/cli/session.ts
|
|
3200
|
-
import
|
|
3666
|
+
import chalk10 from "chalk";
|
|
3201
3667
|
import ora2 from "ora";
|
|
3202
3668
|
import os from "os";
|
|
3203
|
-
import { readFile as
|
|
3204
|
-
import { existsSync as
|
|
3205
|
-
import { join as
|
|
3669
|
+
import { readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
3670
|
+
import { existsSync as existsSync10 } from "fs";
|
|
3671
|
+
import { join as join9 } from "path";
|
|
3206
3672
|
import { createClient as createClient2 } from "@supabase/supabase-js";
|
|
3207
3673
|
function getSupabaseClient2(accessToken) {
|
|
3208
3674
|
return createClient2(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
@@ -3213,45 +3679,45 @@ function getDeviceName() {
|
|
|
3213
3679
|
return `${os.hostname()}-${os.platform()}-${os.arch()}`;
|
|
3214
3680
|
}
|
|
3215
3681
|
async function getProjectInfo(cwd) {
|
|
3216
|
-
const archonConfigPath =
|
|
3217
|
-
const packageJsonPath =
|
|
3682
|
+
const archonConfigPath = join9(cwd, ".archon", "config.yaml");
|
|
3683
|
+
const packageJsonPath = join9(cwd, "package.json");
|
|
3218
3684
|
let projectName = "Unknown Project";
|
|
3219
|
-
if (
|
|
3685
|
+
if (existsSync10(packageJsonPath)) {
|
|
3220
3686
|
try {
|
|
3221
|
-
const pkg = JSON.parse(await
|
|
3687
|
+
const pkg = JSON.parse(await readFile8(packageJsonPath, "utf-8"));
|
|
3222
3688
|
projectName = pkg.name || projectName;
|
|
3223
3689
|
} catch {
|
|
3224
3690
|
}
|
|
3225
3691
|
}
|
|
3226
|
-
if (!
|
|
3692
|
+
if (!existsSync10(archonConfigPath)) {
|
|
3227
3693
|
return null;
|
|
3228
3694
|
}
|
|
3229
3695
|
return { name: projectName, path: cwd };
|
|
3230
3696
|
}
|
|
3231
3697
|
async function getCurrentAtomId(cwd) {
|
|
3232
|
-
const stateFile =
|
|
3233
|
-
if (!
|
|
3698
|
+
const stateFile = join9(cwd, ".archon", "state.json");
|
|
3699
|
+
if (!existsSync10(stateFile)) return null;
|
|
3234
3700
|
try {
|
|
3235
|
-
const state = JSON.parse(await
|
|
3701
|
+
const state = JSON.parse(await readFile8(stateFile, "utf-8"));
|
|
3236
3702
|
return state.currentAtomId || null;
|
|
3237
3703
|
} catch {
|
|
3238
3704
|
return null;
|
|
3239
3705
|
}
|
|
3240
3706
|
}
|
|
3241
3707
|
async function getPendingAtoms(cwd) {
|
|
3242
|
-
const stateFile =
|
|
3243
|
-
if (!
|
|
3708
|
+
const stateFile = join9(cwd, ".archon", "state.json");
|
|
3709
|
+
if (!existsSync10(stateFile)) return [];
|
|
3244
3710
|
try {
|
|
3245
|
-
const state = JSON.parse(await
|
|
3711
|
+
const state = JSON.parse(await readFile8(stateFile, "utf-8"));
|
|
3246
3712
|
return state.pendingAtoms || [];
|
|
3247
3713
|
} catch {
|
|
3248
3714
|
return [];
|
|
3249
3715
|
}
|
|
3250
3716
|
}
|
|
3251
3717
|
async function getFileContent(path2) {
|
|
3252
|
-
if (!
|
|
3718
|
+
if (!existsSync10(path2)) return null;
|
|
3253
3719
|
try {
|
|
3254
|
-
return await
|
|
3720
|
+
return await readFile8(path2, "utf-8");
|
|
3255
3721
|
} catch {
|
|
3256
3722
|
return null;
|
|
3257
3723
|
}
|
|
@@ -3278,8 +3744,8 @@ async function saveSession(name) {
|
|
|
3278
3744
|
}
|
|
3279
3745
|
const currentAtomId = await getCurrentAtomId(cwd);
|
|
3280
3746
|
const pendingAtoms = await getPendingAtoms(cwd);
|
|
3281
|
-
const progressSnapshot = await getFileContent(
|
|
3282
|
-
const architectureSnapshot = await getFileContent(
|
|
3747
|
+
const progressSnapshot = await getFileContent(join9(cwd, "progress.txt"));
|
|
3748
|
+
const architectureSnapshot = await getFileContent(join9(cwd, "ARCHITECTURE.md"));
|
|
3283
3749
|
const sessionData = {
|
|
3284
3750
|
user_id: profile.id,
|
|
3285
3751
|
project_name: name || projectInfo.name,
|
|
@@ -3296,13 +3762,13 @@ async function saveSession(name) {
|
|
|
3296
3762
|
spinner.fail(`Failed to save session: ${error.message}`);
|
|
3297
3763
|
return;
|
|
3298
3764
|
}
|
|
3299
|
-
spinner.succeed(
|
|
3765
|
+
spinner.succeed(chalk10.green("Session saved!"));
|
|
3300
3766
|
console.log();
|
|
3301
|
-
console.log(` ID: ${
|
|
3767
|
+
console.log(` ID: ${chalk10.cyan(session.id)}`);
|
|
3302
3768
|
console.log(` Project: ${session.project_name}`);
|
|
3303
3769
|
console.log(` Device: ${session.last_device}`);
|
|
3304
3770
|
console.log();
|
|
3305
|
-
console.log(
|
|
3771
|
+
console.log(chalk10.dim(" Resume on another device: archon session resume " + session.id));
|
|
3306
3772
|
console.log();
|
|
3307
3773
|
} catch (err) {
|
|
3308
3774
|
spinner.fail("Error saving session");
|
|
@@ -3330,23 +3796,23 @@ async function listSessions() {
|
|
|
3330
3796
|
}
|
|
3331
3797
|
spinner.stop();
|
|
3332
3798
|
if (!sessions || sessions.length === 0) {
|
|
3333
|
-
console.log(
|
|
3334
|
-
console.log(
|
|
3799
|
+
console.log(chalk10.yellow("\nNo saved sessions found.\n"));
|
|
3800
|
+
console.log(chalk10.dim(" Save a session: archon session save [name]\n"));
|
|
3335
3801
|
return;
|
|
3336
3802
|
}
|
|
3337
3803
|
console.log();
|
|
3338
|
-
console.log(
|
|
3804
|
+
console.log(chalk10.bold("\u{1F4C2} Saved Sessions"));
|
|
3339
3805
|
console.log();
|
|
3340
3806
|
for (const session of sessions) {
|
|
3341
3807
|
const date = new Date(session.updated_at).toLocaleDateString();
|
|
3342
|
-
const atomInfo = session.current_atom_id ?
|
|
3808
|
+
const atomInfo = session.current_atom_id ? chalk10.dim(` (atom: ${session.current_atom_id})`) : "";
|
|
3343
3809
|
console.log(
|
|
3344
|
-
` ${
|
|
3810
|
+
` ${chalk10.cyan(session.id.slice(0, 8))} ${session.project_name}${atomInfo}`
|
|
3345
3811
|
);
|
|
3346
|
-
console.log(
|
|
3812
|
+
console.log(chalk10.dim(` ${date} from ${session.last_device || "unknown device"}`));
|
|
3347
3813
|
console.log();
|
|
3348
3814
|
}
|
|
3349
|
-
console.log(
|
|
3815
|
+
console.log(chalk10.dim(" Resume: archon session resume <id>\n"));
|
|
3350
3816
|
} catch (err) {
|
|
3351
3817
|
spinner.fail("Error fetching sessions");
|
|
3352
3818
|
console.error(err);
|
|
@@ -3373,30 +3839,30 @@ async function resumeSession(sessionId) {
|
|
|
3373
3839
|
return;
|
|
3374
3840
|
}
|
|
3375
3841
|
const session = sessions[0];
|
|
3376
|
-
const stateFile =
|
|
3842
|
+
const stateFile = join9(cwd, ".archon", "state.json");
|
|
3377
3843
|
const state = {
|
|
3378
3844
|
currentAtomId: session.current_atom_id,
|
|
3379
3845
|
pendingAtoms: session.pending_atoms,
|
|
3380
3846
|
resumedFrom: session.id,
|
|
3381
3847
|
resumedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3382
3848
|
};
|
|
3383
|
-
await
|
|
3849
|
+
await writeFile8(stateFile, JSON.stringify(state, null, 2));
|
|
3384
3850
|
if (session.progress_snapshot) {
|
|
3385
|
-
const progressPath =
|
|
3386
|
-
await
|
|
3851
|
+
const progressPath = join9(cwd, "progress.txt");
|
|
3852
|
+
await writeFile8(progressPath, session.progress_snapshot);
|
|
3387
3853
|
}
|
|
3388
3854
|
if (session.architecture_snapshot) {
|
|
3389
|
-
const archPath =
|
|
3390
|
-
await
|
|
3855
|
+
const archPath = join9(cwd, "ARCHITECTURE.md");
|
|
3856
|
+
await writeFile8(archPath, session.architecture_snapshot);
|
|
3391
3857
|
}
|
|
3392
3858
|
await supabase.from("sessions").update({ last_device: getDeviceName(), updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", session.id);
|
|
3393
|
-
spinner.succeed(
|
|
3859
|
+
spinner.succeed(chalk10.green("Session resumed!"));
|
|
3394
3860
|
console.log();
|
|
3395
3861
|
console.log(` Project: ${session.project_name}`);
|
|
3396
3862
|
console.log(` Current Atom: ${session.current_atom_id || "none"}`);
|
|
3397
3863
|
console.log(` Pending: ${session.pending_atoms.length} atoms`);
|
|
3398
3864
|
console.log();
|
|
3399
|
-
console.log(
|
|
3865
|
+
console.log(chalk10.dim(" Continue working: archon start"));
|
|
3400
3866
|
console.log();
|
|
3401
3867
|
} catch (err) {
|
|
3402
3868
|
spinner.fail("Error resuming session");
|
|
@@ -3412,12 +3878,12 @@ async function syncSession() {
|
|
|
3412
3878
|
spinner.fail("Not logged in. Run: archon login");
|
|
3413
3879
|
return;
|
|
3414
3880
|
}
|
|
3415
|
-
const stateFile =
|
|
3416
|
-
if (!
|
|
3881
|
+
const stateFile = join9(cwd, ".archon", "state.json");
|
|
3882
|
+
if (!existsSync10(stateFile)) {
|
|
3417
3883
|
spinner.info("No active session to sync");
|
|
3418
3884
|
return;
|
|
3419
3885
|
}
|
|
3420
|
-
const state = JSON.parse(await
|
|
3886
|
+
const state = JSON.parse(await readFile8(stateFile, "utf-8"));
|
|
3421
3887
|
if (!state.resumedFrom) {
|
|
3422
3888
|
spinner.info('Session was not resumed from cloud - use "archon session save" to create one');
|
|
3423
3889
|
return;
|
|
@@ -3425,8 +3891,8 @@ async function syncSession() {
|
|
|
3425
3891
|
const supabase = getSupabaseClient2(config.accessToken);
|
|
3426
3892
|
const currentAtomId = await getCurrentAtomId(cwd);
|
|
3427
3893
|
const pendingAtoms = await getPendingAtoms(cwd);
|
|
3428
|
-
const progressSnapshot = await getFileContent(
|
|
3429
|
-
const architectureSnapshot = await getFileContent(
|
|
3894
|
+
const progressSnapshot = await getFileContent(join9(cwd, "progress.txt"));
|
|
3895
|
+
const architectureSnapshot = await getFileContent(join9(cwd, "ARCHITECTURE.md"));
|
|
3430
3896
|
const { error } = await supabase.from("sessions").update({
|
|
3431
3897
|
current_atom_id: currentAtomId,
|
|
3432
3898
|
pending_atoms: pendingAtoms,
|
|
@@ -3439,7 +3905,7 @@ async function syncSession() {
|
|
|
3439
3905
|
spinner.fail(`Failed to sync: ${error.message}`);
|
|
3440
3906
|
return;
|
|
3441
3907
|
}
|
|
3442
|
-
spinner.succeed(
|
|
3908
|
+
spinner.succeed(chalk10.green("Session synced to cloud"));
|
|
3443
3909
|
} catch (err) {
|
|
3444
3910
|
spinner.fail("Error syncing session");
|
|
3445
3911
|
console.error(err);
|
|
@@ -3447,66 +3913,66 @@ async function syncSession() {
|
|
|
3447
3913
|
}
|
|
3448
3914
|
|
|
3449
3915
|
// src/cli/deploy.ts
|
|
3450
|
-
import
|
|
3451
|
-
import { existsSync as
|
|
3452
|
-
import { join as
|
|
3453
|
-
import { execSync } from "child_process";
|
|
3916
|
+
import chalk11 from "chalk";
|
|
3917
|
+
import { existsSync as existsSync11 } from "fs";
|
|
3918
|
+
import { join as join10 } from "path";
|
|
3919
|
+
import { execSync as execSync2 } from "child_process";
|
|
3454
3920
|
function detectPlatform(cwd) {
|
|
3455
|
-
if (
|
|
3456
|
-
if (
|
|
3457
|
-
if (
|
|
3458
|
-
if (
|
|
3459
|
-
if (
|
|
3460
|
-
if (
|
|
3921
|
+
if (existsSync11(join10(cwd, "fly.toml"))) return "fly";
|
|
3922
|
+
if (existsSync11(join10(cwd, "vercel.json"))) return "vercel";
|
|
3923
|
+
if (existsSync11(join10(cwd, "netlify.toml"))) return "netlify";
|
|
3924
|
+
if (existsSync11(join10(cwd, "railway.json"))) return "railway";
|
|
3925
|
+
if (existsSync11(join10(cwd, "render.yaml"))) return "render";
|
|
3926
|
+
if (existsSync11(join10(cwd, "Dockerfile"))) return "fly";
|
|
3461
3927
|
return "unknown";
|
|
3462
3928
|
}
|
|
3463
3929
|
async function deploy(options) {
|
|
3464
3930
|
const cwd = process.cwd();
|
|
3465
|
-
console.log(
|
|
3931
|
+
console.log(chalk11.blue("Running pre-deploy checks..."));
|
|
3466
3932
|
const platform = options.platform ?? detectPlatform(cwd);
|
|
3467
|
-
console.log(
|
|
3933
|
+
console.log(chalk11.dim(`Detected platform: ${platform}`));
|
|
3468
3934
|
if (options.dryRun) {
|
|
3469
|
-
console.log(
|
|
3935
|
+
console.log(chalk11.dim("Dry run mode - would deploy to:"), platform);
|
|
3470
3936
|
return;
|
|
3471
3937
|
}
|
|
3472
3938
|
switch (platform) {
|
|
3473
3939
|
case "fly":
|
|
3474
|
-
console.log(
|
|
3475
|
-
|
|
3940
|
+
console.log(chalk11.blue("Deploying to Fly.io..."));
|
|
3941
|
+
execSync2("fly deploy", { cwd, stdio: "inherit" });
|
|
3476
3942
|
break;
|
|
3477
3943
|
case "vercel": {
|
|
3478
|
-
console.log(
|
|
3944
|
+
console.log(chalk11.blue("Deploying to Vercel..."));
|
|
3479
3945
|
const cmd = options.preview ? "vercel" : "vercel --prod";
|
|
3480
|
-
|
|
3946
|
+
execSync2(cmd, { cwd, stdio: "inherit" });
|
|
3481
3947
|
break;
|
|
3482
3948
|
}
|
|
3483
3949
|
case "netlify": {
|
|
3484
|
-
console.log(
|
|
3950
|
+
console.log(chalk11.blue("Deploying to Netlify..."));
|
|
3485
3951
|
const netlifyCmd = options.preview ? "netlify deploy" : "netlify deploy --prod";
|
|
3486
|
-
|
|
3952
|
+
execSync2(netlifyCmd, { cwd, stdio: "inherit" });
|
|
3487
3953
|
break;
|
|
3488
3954
|
}
|
|
3489
3955
|
case "railway":
|
|
3490
|
-
console.log(
|
|
3491
|
-
|
|
3956
|
+
console.log(chalk11.blue("Deploying to Railway..."));
|
|
3957
|
+
execSync2("railway up", { cwd, stdio: "inherit" });
|
|
3492
3958
|
break;
|
|
3493
3959
|
case "render":
|
|
3494
|
-
console.log(
|
|
3495
|
-
console.log(
|
|
3960
|
+
console.log(chalk11.blue("Deploying to Render..."));
|
|
3961
|
+
console.log(chalk11.yellow("Render deploys via git push. Push to your connected branch."));
|
|
3496
3962
|
break;
|
|
3497
3963
|
default:
|
|
3498
|
-
console.log(
|
|
3499
|
-
console.log(
|
|
3964
|
+
console.log(chalk11.yellow("Platform not detected. Please specify with --platform"));
|
|
3965
|
+
console.log(chalk11.dim("Supported: fly, vercel, netlify, railway, render"));
|
|
3500
3966
|
}
|
|
3501
3967
|
}
|
|
3502
3968
|
|
|
3503
3969
|
// src/cli/index-cmd.ts
|
|
3504
|
-
import
|
|
3970
|
+
import chalk12 from "chalk";
|
|
3505
3971
|
|
|
3506
3972
|
// src/core/indexing/local.ts
|
|
3507
|
-
import { existsSync as
|
|
3508
|
-
import { readFile as
|
|
3509
|
-
import { join as
|
|
3973
|
+
import { existsSync as existsSync12, mkdirSync } from "fs";
|
|
3974
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
3975
|
+
import { join as join11, extname } from "path";
|
|
3510
3976
|
import Database from "better-sqlite3";
|
|
3511
3977
|
var CHUNK_SIZE = 1e3;
|
|
3512
3978
|
var CHUNK_OVERLAP = 200;
|
|
@@ -3553,11 +4019,11 @@ var LocalIndexer = class {
|
|
|
3553
4019
|
};
|
|
3554
4020
|
}
|
|
3555
4021
|
async init(cwd) {
|
|
3556
|
-
const archonDir =
|
|
3557
|
-
if (!
|
|
4022
|
+
const archonDir = join11(cwd, ".archon");
|
|
4023
|
+
if (!existsSync12(archonDir)) {
|
|
3558
4024
|
mkdirSync(archonDir, { recursive: true });
|
|
3559
4025
|
}
|
|
3560
|
-
this.db = new Database(
|
|
4026
|
+
this.db = new Database(join11(cwd, this.config.dbPath));
|
|
3561
4027
|
this.db.exec(`
|
|
3562
4028
|
CREATE TABLE IF NOT EXISTS embeddings (
|
|
3563
4029
|
id INTEGER PRIMARY KEY,
|
|
@@ -3607,11 +4073,11 @@ var LocalIndexer = class {
|
|
|
3607
4073
|
if (!this.isIndexableFile(filePath)) {
|
|
3608
4074
|
return 0;
|
|
3609
4075
|
}
|
|
3610
|
-
const fullPath =
|
|
3611
|
-
if (!
|
|
4076
|
+
const fullPath = join11(cwd, filePath);
|
|
4077
|
+
if (!existsSync12(fullPath)) {
|
|
3612
4078
|
return 0;
|
|
3613
4079
|
}
|
|
3614
|
-
const content = await
|
|
4080
|
+
const content = await readFile9(fullPath, "utf-8");
|
|
3615
4081
|
const chunks = this.chunkText(content);
|
|
3616
4082
|
const deleteStmt = this.db.prepare("DELETE FROM embeddings WHERE file_path = ?");
|
|
3617
4083
|
deleteStmt.run(filePath);
|
|
@@ -3689,9 +4155,9 @@ var LocalIndexer = class {
|
|
|
3689
4155
|
|
|
3690
4156
|
// src/core/indexing/cloud.ts
|
|
3691
4157
|
import { createClient as createClient3 } from "@supabase/supabase-js";
|
|
3692
|
-
import { readFile as
|
|
3693
|
-
import { existsSync as
|
|
3694
|
-
import { join as
|
|
4158
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
4159
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4160
|
+
import { join as join12, extname as extname2 } from "path";
|
|
3695
4161
|
import { createHash } from "crypto";
|
|
3696
4162
|
var CHUNK_SIZE2 = 1e3;
|
|
3697
4163
|
var CHUNK_OVERLAP2 = 200;
|
|
@@ -3818,11 +4284,11 @@ var CloudIndexer = class {
|
|
|
3818
4284
|
if (!this.isIndexableFile(filePath)) {
|
|
3819
4285
|
return 0;
|
|
3820
4286
|
}
|
|
3821
|
-
const fullPath =
|
|
3822
|
-
if (!
|
|
4287
|
+
const fullPath = join12(cwd, filePath);
|
|
4288
|
+
if (!existsSync13(fullPath)) {
|
|
3823
4289
|
return 0;
|
|
3824
4290
|
}
|
|
3825
|
-
const content = await
|
|
4291
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
3826
4292
|
const fileHash = await this.computeFileHash(content);
|
|
3827
4293
|
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();
|
|
3828
4294
|
if (existing && existing.file_hash === fileHash) {
|
|
@@ -3910,19 +4376,19 @@ var CloudIndexer = class {
|
|
|
3910
4376
|
|
|
3911
4377
|
// src/cli/index-cmd.ts
|
|
3912
4378
|
import { glob as glob4 } from "glob";
|
|
3913
|
-
import { join as
|
|
4379
|
+
import { join as join13, basename } from "path";
|
|
3914
4380
|
async function getCloudIndexer(cwd) {
|
|
3915
4381
|
const config = await loadConfig();
|
|
3916
4382
|
const authToken = getAuthToken(config);
|
|
3917
4383
|
if (!authToken) {
|
|
3918
|
-
console.error(
|
|
4384
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
3919
4385
|
return null;
|
|
3920
4386
|
}
|
|
3921
4387
|
const openaiKey = process.env["OPENAI_API_KEY"];
|
|
3922
4388
|
if (!openaiKey) {
|
|
3923
|
-
console.error(
|
|
3924
|
-
console.log(
|
|
3925
|
-
console.log(
|
|
4389
|
+
console.error(chalk12.red("OPENAI_API_KEY environment variable not set."));
|
|
4390
|
+
console.log(chalk12.dim("Cloud indexing requires an OpenAI API key for embeddings."));
|
|
4391
|
+
console.log(chalk12.dim("Set it with: export OPENAI_API_KEY=sk-..."));
|
|
3926
4392
|
return null;
|
|
3927
4393
|
}
|
|
3928
4394
|
const projectId = basename(cwd);
|
|
@@ -3939,12 +4405,12 @@ async function getCloudIndexer(cwd) {
|
|
|
3939
4405
|
});
|
|
3940
4406
|
const { data: { user } } = await client.auth.getUser();
|
|
3941
4407
|
if (!user) {
|
|
3942
|
-
console.error(
|
|
4408
|
+
console.error(chalk12.red("Failed to get user. Try logging in again."));
|
|
3943
4409
|
return null;
|
|
3944
4410
|
}
|
|
3945
4411
|
const { data: profile } = await client.from("user_profiles").select("id").eq("auth_id", user.id).single();
|
|
3946
4412
|
if (!profile) {
|
|
3947
|
-
console.error(
|
|
4413
|
+
console.error(chalk12.red("User profile not found."));
|
|
3948
4414
|
return null;
|
|
3949
4415
|
}
|
|
3950
4416
|
indexer.setUserId(profile.id);
|
|
@@ -3953,21 +4419,21 @@ async function getCloudIndexer(cwd) {
|
|
|
3953
4419
|
async function indexInit(options) {
|
|
3954
4420
|
const cwd = process.cwd();
|
|
3955
4421
|
if (options.cloud) {
|
|
3956
|
-
console.log(
|
|
4422
|
+
console.log(chalk12.blue("Initializing cloud semantic index..."));
|
|
3957
4423
|
const indexer = await getCloudIndexer(cwd);
|
|
3958
4424
|
if (!indexer) return;
|
|
3959
4425
|
try {
|
|
3960
4426
|
const status2 = await indexer.getStatus();
|
|
3961
|
-
console.log(
|
|
3962
|
-
console.log(
|
|
3963
|
-
console.log(
|
|
4427
|
+
console.log(chalk12.green("\u2713 Cloud indexing configured"));
|
|
4428
|
+
console.log(chalk12.green(`\u2713 Project ID: ${status2.projectId}`));
|
|
4429
|
+
console.log(chalk12.dim("\nRun `archon index update --cloud` to index your codebase."));
|
|
3964
4430
|
} catch (error) {
|
|
3965
|
-
console.error(
|
|
4431
|
+
console.error(chalk12.red(`Failed to initialize cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
3966
4432
|
process.exit(1);
|
|
3967
4433
|
}
|
|
3968
4434
|
return;
|
|
3969
4435
|
}
|
|
3970
|
-
console.log(
|
|
4436
|
+
console.log(chalk12.blue("Initializing local semantic index..."));
|
|
3971
4437
|
try {
|
|
3972
4438
|
const indexer = new LocalIndexer();
|
|
3973
4439
|
await indexer.init(cwd);
|
|
@@ -3975,18 +4441,18 @@ async function indexInit(options) {
|
|
|
3975
4441
|
if (!response.ok) {
|
|
3976
4442
|
throw new Error("Ollama not responding");
|
|
3977
4443
|
}
|
|
3978
|
-
console.log(
|
|
3979
|
-
console.log(
|
|
3980
|
-
console.log(
|
|
4444
|
+
console.log(chalk12.green("\u2713 Ollama connection verified"));
|
|
4445
|
+
console.log(chalk12.green("\u2713 Index database created at .archon/index.db"));
|
|
4446
|
+
console.log(chalk12.dim("\nRun `archon index update` to index your codebase."));
|
|
3981
4447
|
indexer.close();
|
|
3982
4448
|
} catch (error) {
|
|
3983
4449
|
if (error instanceof Error && error.message.includes("Ollama")) {
|
|
3984
|
-
console.log(
|
|
3985
|
-
console.log(
|
|
3986
|
-
console.log(
|
|
3987
|
-
console.log(
|
|
4450
|
+
console.log(chalk12.red("\n\u2717 Ollama is not running"));
|
|
4451
|
+
console.log(chalk12.dim("Start Ollama with: ollama serve"));
|
|
4452
|
+
console.log(chalk12.dim("Then pull the embedding model: ollama pull nomic-embed-text"));
|
|
4453
|
+
console.log(chalk12.dim("\nOr use cloud indexing: archon index init --cloud"));
|
|
3988
4454
|
} else {
|
|
3989
|
-
console.error(
|
|
4455
|
+
console.error(chalk12.red(`Failed to initialize index: ${error instanceof Error ? error.message : String(error)}`));
|
|
3990
4456
|
}
|
|
3991
4457
|
process.exit(1);
|
|
3992
4458
|
}
|
|
@@ -3994,7 +4460,7 @@ async function indexInit(options) {
|
|
|
3994
4460
|
async function indexUpdate(options) {
|
|
3995
4461
|
const cwd = process.cwd();
|
|
3996
4462
|
if (options?.cloud) {
|
|
3997
|
-
console.log(
|
|
4463
|
+
console.log(chalk12.blue("Updating cloud semantic index..."));
|
|
3998
4464
|
const indexer = await getCloudIndexer(cwd);
|
|
3999
4465
|
if (!indexer) return;
|
|
4000
4466
|
try {
|
|
@@ -4002,15 +4468,15 @@ async function indexUpdate(options) {
|
|
|
4002
4468
|
cwd,
|
|
4003
4469
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/build/**", "**/coverage/**"]
|
|
4004
4470
|
});
|
|
4005
|
-
console.log(
|
|
4006
|
-
console.log(
|
|
4471
|
+
console.log(chalk12.dim(`Found ${files.length} files to index...`));
|
|
4472
|
+
console.log(chalk12.dim("This may take a few minutes and will use OpenAI API credits.\n"));
|
|
4007
4473
|
let totalChunks = 0;
|
|
4008
4474
|
let indexedFiles = 0;
|
|
4009
4475
|
let skippedFiles = 0;
|
|
4010
4476
|
for (let i = 0; i < files.length; i++) {
|
|
4011
4477
|
const file = files[i];
|
|
4012
4478
|
if (!file) continue;
|
|
4013
|
-
process.stdout.write(`\r${
|
|
4479
|
+
process.stdout.write(`\r${chalk12.dim(`[${i + 1}/${files.length}] ${file.slice(0, 45).padEnd(45)}`)}`);
|
|
4014
4480
|
try {
|
|
4015
4481
|
const chunks = await indexer.indexFile(cwd, file);
|
|
4016
4482
|
if (chunks > 0) {
|
|
@@ -4021,21 +4487,21 @@ async function indexUpdate(options) {
|
|
|
4021
4487
|
}
|
|
4022
4488
|
} catch (error) {
|
|
4023
4489
|
console.log(`
|
|
4024
|
-
${
|
|
4490
|
+
${chalk12.yellow(`\u26A0 Skipped ${file}: ${error instanceof Error ? error.message : "Unknown error"}`)}`);
|
|
4025
4491
|
}
|
|
4026
4492
|
}
|
|
4027
4493
|
console.log("\r" + " ".repeat(70));
|
|
4028
|
-
console.log(
|
|
4494
|
+
console.log(chalk12.green(`\u2713 Indexed ${indexedFiles} files (${totalChunks} chunks)`));
|
|
4029
4495
|
if (skippedFiles > 0) {
|
|
4030
|
-
console.log(
|
|
4496
|
+
console.log(chalk12.dim(` Skipped ${skippedFiles} unchanged files`));
|
|
4031
4497
|
}
|
|
4032
4498
|
} catch (error) {
|
|
4033
|
-
console.error(
|
|
4499
|
+
console.error(chalk12.red(`Failed to update cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
4034
4500
|
process.exit(1);
|
|
4035
4501
|
}
|
|
4036
4502
|
return;
|
|
4037
4503
|
}
|
|
4038
|
-
console.log(
|
|
4504
|
+
console.log(chalk12.blue("Updating local semantic index..."));
|
|
4039
4505
|
try {
|
|
4040
4506
|
const indexer = new LocalIndexer();
|
|
4041
4507
|
await indexer.init(cwd);
|
|
@@ -4043,12 +4509,12 @@ ${chalk11.yellow(`\u26A0 Skipped ${file}: ${error instanceof Error ? error.messa
|
|
|
4043
4509
|
cwd,
|
|
4044
4510
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/build/**", "**/coverage/**"]
|
|
4045
4511
|
});
|
|
4046
|
-
console.log(
|
|
4512
|
+
console.log(chalk12.dim(`Found ${files.length} files to index...`));
|
|
4047
4513
|
let totalChunks = 0;
|
|
4048
4514
|
let indexedFiles = 0;
|
|
4049
4515
|
for (const file of files) {
|
|
4050
4516
|
if (!file) continue;
|
|
4051
|
-
process.stdout.write(`\r${
|
|
4517
|
+
process.stdout.write(`\r${chalk12.dim(`Indexing: ${file.slice(0, 50).padEnd(50)}`)}`);
|
|
4052
4518
|
const chunks = await indexer.indexFile(cwd, file);
|
|
4053
4519
|
if (chunks > 0) {
|
|
4054
4520
|
totalChunks += chunks;
|
|
@@ -4056,10 +4522,10 @@ ${chalk11.yellow(`\u26A0 Skipped ${file}: ${error instanceof Error ? error.messa
|
|
|
4056
4522
|
}
|
|
4057
4523
|
}
|
|
4058
4524
|
console.log("\r" + " ".repeat(60));
|
|
4059
|
-
console.log(
|
|
4525
|
+
console.log(chalk12.green(`\u2713 Indexed ${indexedFiles} files (${totalChunks} chunks)`));
|
|
4060
4526
|
indexer.close();
|
|
4061
4527
|
} catch (error) {
|
|
4062
|
-
console.error(
|
|
4528
|
+
console.error(chalk12.red(`Failed to update index: ${error instanceof Error ? error.message : String(error)}`));
|
|
4063
4529
|
process.exit(1);
|
|
4064
4530
|
}
|
|
4065
4531
|
}
|
|
@@ -4069,25 +4535,25 @@ async function indexSearch(query, options) {
|
|
|
4069
4535
|
const indexer = await getCloudIndexer(cwd);
|
|
4070
4536
|
if (!indexer) return;
|
|
4071
4537
|
try {
|
|
4072
|
-
console.log(
|
|
4538
|
+
console.log(chalk12.dim("Searching cloud index..."));
|
|
4073
4539
|
const results = await indexer.search(query, 10);
|
|
4074
4540
|
if (results.length === 0) {
|
|
4075
|
-
console.log(
|
|
4076
|
-
console.log(
|
|
4541
|
+
console.log(chalk12.yellow("\nNo results found."));
|
|
4542
|
+
console.log(chalk12.dim("Try running `archon index update --cloud` first."));
|
|
4077
4543
|
} else {
|
|
4078
|
-
console.log(
|
|
4544
|
+
console.log(chalk12.blue(`
|
|
4079
4545
|
Top ${results.length} results for: "${query}"
|
|
4080
4546
|
`));
|
|
4081
4547
|
for (const result of results) {
|
|
4082
4548
|
const score = (result.score * 100).toFixed(1);
|
|
4083
|
-
console.log(
|
|
4549
|
+
console.log(chalk12.green(`[${score}%] ${result.file}`));
|
|
4084
4550
|
const preview = result.text.slice(0, 200).replace(/\n/g, " ").trim();
|
|
4085
|
-
console.log(
|
|
4551
|
+
console.log(chalk12.dim(` ${preview}${result.text.length > 200 ? "..." : ""}`));
|
|
4086
4552
|
console.log();
|
|
4087
4553
|
}
|
|
4088
4554
|
}
|
|
4089
4555
|
} catch (error) {
|
|
4090
|
-
console.error(
|
|
4556
|
+
console.error(chalk12.red(`Cloud search failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
4091
4557
|
process.exit(1);
|
|
4092
4558
|
}
|
|
4093
4559
|
return;
|
|
@@ -4097,23 +4563,23 @@ Top ${results.length} results for: "${query}"
|
|
|
4097
4563
|
await indexer.init(cwd);
|
|
4098
4564
|
const results = await indexer.search(query, 10);
|
|
4099
4565
|
if (results.length === 0) {
|
|
4100
|
-
console.log(
|
|
4101
|
-
console.log(
|
|
4566
|
+
console.log(chalk12.yellow("No results found."));
|
|
4567
|
+
console.log(chalk12.dim("Try running `archon index update` first."));
|
|
4102
4568
|
} else {
|
|
4103
|
-
console.log(
|
|
4569
|
+
console.log(chalk12.blue(`
|
|
4104
4570
|
Top ${results.length} results for: "${query}"
|
|
4105
4571
|
`));
|
|
4106
4572
|
for (const result of results) {
|
|
4107
4573
|
const score = (result.score * 100).toFixed(1);
|
|
4108
|
-
console.log(
|
|
4574
|
+
console.log(chalk12.green(`[${score}%] ${result.file}`));
|
|
4109
4575
|
const preview = result.text.slice(0, 200).replace(/\n/g, " ").trim();
|
|
4110
|
-
console.log(
|
|
4576
|
+
console.log(chalk12.dim(` ${preview}${result.text.length > 200 ? "..." : ""}`));
|
|
4111
4577
|
console.log();
|
|
4112
4578
|
}
|
|
4113
4579
|
}
|
|
4114
4580
|
indexer.close();
|
|
4115
4581
|
} catch (error) {
|
|
4116
|
-
console.error(
|
|
4582
|
+
console.error(chalk12.red(`Search failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
4117
4583
|
process.exit(1);
|
|
4118
4584
|
}
|
|
4119
4585
|
}
|
|
@@ -4124,17 +4590,17 @@ async function indexStatus(options) {
|
|
|
4124
4590
|
if (!indexer) return;
|
|
4125
4591
|
try {
|
|
4126
4592
|
const status2 = await indexer.getStatus();
|
|
4127
|
-
console.log(
|
|
4128
|
-
console.log(` Project ID: ${
|
|
4129
|
-
console.log(` Files indexed: ${
|
|
4130
|
-
console.log(` Total chunks: ${
|
|
4131
|
-
console.log(` Last updated: ${status2.lastUpdated ?
|
|
4593
|
+
console.log(chalk12.blue("\nCloud Semantic Index Status\n"));
|
|
4594
|
+
console.log(` Project ID: ${chalk12.green(status2.projectId)}`);
|
|
4595
|
+
console.log(` Files indexed: ${chalk12.green(status2.fileCount)}`);
|
|
4596
|
+
console.log(` Total chunks: ${chalk12.green(status2.chunkCount)}`);
|
|
4597
|
+
console.log(` Last updated: ${status2.lastUpdated ? chalk12.dim(status2.lastUpdated) : chalk12.yellow("Never")}`);
|
|
4132
4598
|
if (status2.jobStatus) {
|
|
4133
|
-
console.log(` Job status: ${
|
|
4599
|
+
console.log(` Job status: ${chalk12.dim(status2.jobStatus)}`);
|
|
4134
4600
|
}
|
|
4135
|
-
console.log(` Storage: ${
|
|
4601
|
+
console.log(` Storage: ${chalk12.dim("Supabase pgvector")}`);
|
|
4136
4602
|
} catch (error) {
|
|
4137
|
-
console.error(
|
|
4603
|
+
console.error(chalk12.red(`Failed to get cloud status: ${error instanceof Error ? error.message : String(error)}`));
|
|
4138
4604
|
process.exit(1);
|
|
4139
4605
|
}
|
|
4140
4606
|
return;
|
|
@@ -4143,14 +4609,14 @@ async function indexStatus(options) {
|
|
|
4143
4609
|
const indexer = new LocalIndexer();
|
|
4144
4610
|
await indexer.init(cwd);
|
|
4145
4611
|
const status2 = await indexer.getStatus();
|
|
4146
|
-
console.log(
|
|
4147
|
-
console.log(` Files indexed: ${
|
|
4148
|
-
console.log(` Total chunks: ${
|
|
4149
|
-
console.log(` Last updated: ${status2.lastUpdated ?
|
|
4150
|
-
console.log(` Database: ${
|
|
4612
|
+
console.log(chalk12.blue("\nLocal Semantic Index Status\n"));
|
|
4613
|
+
console.log(` Files indexed: ${chalk12.green(status2.fileCount)}`);
|
|
4614
|
+
console.log(` Total chunks: ${chalk12.green(status2.chunkCount)}`);
|
|
4615
|
+
console.log(` Last updated: ${status2.lastUpdated ? chalk12.dim(status2.lastUpdated) : chalk12.yellow("Never")}`);
|
|
4616
|
+
console.log(` Database: ${chalk12.dim(join13(cwd, ".archon/index.db"))}`);
|
|
4151
4617
|
indexer.close();
|
|
4152
4618
|
} catch (error) {
|
|
4153
|
-
console.error(
|
|
4619
|
+
console.error(chalk12.red(`Failed to get status: ${error instanceof Error ? error.message : String(error)}`));
|
|
4154
4620
|
process.exit(1);
|
|
4155
4621
|
}
|
|
4156
4622
|
}
|
|
@@ -4160,30 +4626,30 @@ async function indexClear(options) {
|
|
|
4160
4626
|
const indexer = await getCloudIndexer(cwd);
|
|
4161
4627
|
if (!indexer) return;
|
|
4162
4628
|
try {
|
|
4163
|
-
console.log(
|
|
4629
|
+
console.log(chalk12.yellow("Clearing cloud index..."));
|
|
4164
4630
|
await indexer.clearProject();
|
|
4165
|
-
console.log(
|
|
4631
|
+
console.log(chalk12.green("\u2713 Cloud index cleared"));
|
|
4166
4632
|
} catch (error) {
|
|
4167
|
-
console.error(
|
|
4633
|
+
console.error(chalk12.red(`Failed to clear cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
4168
4634
|
process.exit(1);
|
|
4169
4635
|
}
|
|
4170
4636
|
return;
|
|
4171
4637
|
}
|
|
4172
|
-
console.log(
|
|
4638
|
+
console.log(chalk12.yellow("To clear local index, delete .archon/index.db"));
|
|
4173
4639
|
}
|
|
4174
4640
|
|
|
4175
4641
|
// src/cli/github.ts
|
|
4176
|
-
import
|
|
4642
|
+
import chalk13 from "chalk";
|
|
4177
4643
|
import open2 from "open";
|
|
4178
4644
|
var API_URL2 = process.env["ARCHONDEV_API_URL"] ?? "https://archondev-api.fly.dev";
|
|
4179
4645
|
async function githubConnect() {
|
|
4180
4646
|
const config = await loadConfig();
|
|
4181
4647
|
const authToken = getAuthToken(config);
|
|
4182
4648
|
if (!authToken) {
|
|
4183
|
-
console.error(
|
|
4649
|
+
console.error(chalk13.red('Not authenticated. Run "archon login" first.'));
|
|
4184
4650
|
process.exit(1);
|
|
4185
4651
|
}
|
|
4186
|
-
console.log(
|
|
4652
|
+
console.log(chalk13.dim("Starting GitHub connection..."));
|
|
4187
4653
|
try {
|
|
4188
4654
|
const response = await fetch(`${API_URL2}/api/github/connect`, {
|
|
4189
4655
|
headers: {
|
|
@@ -4192,18 +4658,18 @@ async function githubConnect() {
|
|
|
4192
4658
|
});
|
|
4193
4659
|
if (!response.ok) {
|
|
4194
4660
|
const error = await response.json();
|
|
4195
|
-
console.error(
|
|
4661
|
+
console.error(chalk13.red(error.error ?? "Failed to start GitHub connection"));
|
|
4196
4662
|
process.exit(1);
|
|
4197
4663
|
}
|
|
4198
4664
|
const data = await response.json();
|
|
4199
|
-
console.log(
|
|
4200
|
-
console.log(
|
|
4201
|
-
console.log(
|
|
4665
|
+
console.log(chalk13.dim("\nOpening browser for GitHub authorization..."));
|
|
4666
|
+
console.log(chalk13.dim("If browser does not open, visit:"));
|
|
4667
|
+
console.log(chalk13.blue(data.url));
|
|
4202
4668
|
await open2(data.url);
|
|
4203
|
-
console.log(
|
|
4204
|
-
console.log(
|
|
4669
|
+
console.log(chalk13.dim("\nComplete the authorization in your browser."));
|
|
4670
|
+
console.log(chalk13.dim('Then run "archon github status" to verify connection.'));
|
|
4205
4671
|
} catch (error) {
|
|
4206
|
-
console.error(
|
|
4672
|
+
console.error(chalk13.red(error instanceof Error ? error.message : "Failed to connect"));
|
|
4207
4673
|
process.exit(1);
|
|
4208
4674
|
}
|
|
4209
4675
|
}
|
|
@@ -4211,7 +4677,7 @@ async function githubStatus() {
|
|
|
4211
4677
|
const config = await loadConfig();
|
|
4212
4678
|
const authToken = getAuthToken(config);
|
|
4213
4679
|
if (!authToken) {
|
|
4214
|
-
console.error(
|
|
4680
|
+
console.error(chalk13.red('Not authenticated. Run "archon login" first.'));
|
|
4215
4681
|
process.exit(1);
|
|
4216
4682
|
}
|
|
4217
4683
|
try {
|
|
@@ -4222,20 +4688,20 @@ async function githubStatus() {
|
|
|
4222
4688
|
});
|
|
4223
4689
|
if (!response.ok) {
|
|
4224
4690
|
const error = await response.json();
|
|
4225
|
-
console.error(
|
|
4691
|
+
console.error(chalk13.red(error.error ?? "Failed to get GitHub status"));
|
|
4226
4692
|
process.exit(1);
|
|
4227
4693
|
}
|
|
4228
4694
|
const data = await response.json();
|
|
4229
4695
|
if (data.connected) {
|
|
4230
|
-
console.log(
|
|
4231
|
-
console.log(
|
|
4232
|
-
console.log(
|
|
4696
|
+
console.log(chalk13.green("\u2713 GitHub connected"));
|
|
4697
|
+
console.log(chalk13.dim(` Username: ${data.username}`));
|
|
4698
|
+
console.log(chalk13.dim(` Connected: ${data.connectedAt ? new Date(data.connectedAt).toLocaleDateString() : "Unknown"}`));
|
|
4233
4699
|
} else {
|
|
4234
|
-
console.log(
|
|
4235
|
-
console.log(
|
|
4700
|
+
console.log(chalk13.yellow("GitHub not connected"));
|
|
4701
|
+
console.log(chalk13.dim('Run "archon github connect" to connect your GitHub account.'));
|
|
4236
4702
|
}
|
|
4237
4703
|
} catch (error) {
|
|
4238
|
-
console.error(
|
|
4704
|
+
console.error(chalk13.red(error instanceof Error ? error.message : "Failed to get status"));
|
|
4239
4705
|
process.exit(1);
|
|
4240
4706
|
}
|
|
4241
4707
|
}
|
|
@@ -4243,7 +4709,7 @@ async function githubDisconnect() {
|
|
|
4243
4709
|
const config = await loadConfig();
|
|
4244
4710
|
const authToken = getAuthToken(config);
|
|
4245
4711
|
if (!authToken) {
|
|
4246
|
-
console.error(
|
|
4712
|
+
console.error(chalk13.red('Not authenticated. Run "archon login" first.'));
|
|
4247
4713
|
process.exit(1);
|
|
4248
4714
|
}
|
|
4249
4715
|
try {
|
|
@@ -4255,12 +4721,12 @@ async function githubDisconnect() {
|
|
|
4255
4721
|
});
|
|
4256
4722
|
if (!response.ok) {
|
|
4257
4723
|
const error = await response.json();
|
|
4258
|
-
console.error(
|
|
4724
|
+
console.error(chalk13.red(error.error ?? "Failed to disconnect GitHub"));
|
|
4259
4725
|
process.exit(1);
|
|
4260
4726
|
}
|
|
4261
|
-
console.log(
|
|
4727
|
+
console.log(chalk13.green("\u2713 GitHub disconnected"));
|
|
4262
4728
|
} catch (error) {
|
|
4263
|
-
console.error(
|
|
4729
|
+
console.error(chalk13.red(error instanceof Error ? error.message : "Failed to disconnect"));
|
|
4264
4730
|
process.exit(1);
|
|
4265
4731
|
}
|
|
4266
4732
|
}
|
|
@@ -4270,7 +4736,7 @@ var program = new Command4();
|
|
|
4270
4736
|
program.name("archon").description("Local-first AI-powered development governance").version("1.1.0").action(async () => {
|
|
4271
4737
|
const cwd = process.cwd();
|
|
4272
4738
|
if (!isInitialized(cwd)) {
|
|
4273
|
-
console.log(
|
|
4739
|
+
console.log(chalk14.blue("\nArchonDev is not initialized in this folder.\n"));
|
|
4274
4740
|
await init({ analyze: true, git: true });
|
|
4275
4741
|
}
|
|
4276
4742
|
await start();
|
|
@@ -4278,7 +4744,7 @@ program.name("archon").description("Local-first AI-powered development governanc
|
|
|
4278
4744
|
program.command("login").description("Authenticate with ArchonDev").option("-p, --provider <provider>", "OAuth provider (github or google)", "github").action(async (options) => {
|
|
4279
4745
|
const provider = options.provider;
|
|
4280
4746
|
if (provider !== "github" && provider !== "google") {
|
|
4281
|
-
console.error(
|
|
4747
|
+
console.error(chalk14.red('Invalid provider. Use "github" or "google"'));
|
|
4282
4748
|
process.exit(1);
|
|
4283
4749
|
}
|
|
4284
4750
|
await login(provider);
|
|
@@ -4454,4 +4920,15 @@ githubCmd.command("connect").description("Connect your GitHub account for cloud
|
|
|
4454
4920
|
githubCmd.command("status").description("Show GitHub connection status").action(githubStatus);
|
|
4455
4921
|
githubCmd.command("disconnect").description("Disconnect your GitHub account").action(githubDisconnect);
|
|
4456
4922
|
githubCmd.action(githubStatus);
|
|
4923
|
+
var cleanupCmd = program.command("cleanup").description("Workspace maintenance and file cleanup");
|
|
4924
|
+
cleanupCmd.command("check").description("Analyze workspace for bloat and maintenance needs").action(cleanupCheck);
|
|
4925
|
+
cleanupCmd.command("run").description("Execute cleanup (archive old entries, remove stale files)").action(cleanupRun);
|
|
4926
|
+
cleanupCmd.command("auto").description("Enable/disable automatic cleanup checks").argument("[action]", "enable, disable, or status", "status").action(async (action) => {
|
|
4927
|
+
if (action !== "enable" && action !== "disable" && action !== "status") {
|
|
4928
|
+
console.error(chalk14.red("Invalid action. Use: enable, disable, or status"));
|
|
4929
|
+
process.exit(1);
|
|
4930
|
+
}
|
|
4931
|
+
await cleanupAuto(action);
|
|
4932
|
+
});
|
|
4933
|
+
cleanupCmd.action(cleanupCheck);
|
|
4457
4934
|
program.parse();
|