archondev 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -81,6 +81,17 @@ Copy governance files into any project. Works with your existing AI tools: **Cur
|
|
|
81
81
|
| `archon seo fix` | Apply recommended SEO fixes |
|
|
82
82
|
| `archon geo identity` | Generate brand identity phrases for AI citation |
|
|
83
83
|
| `archon geo schema` | Generate JSON-LD schemas |
|
|
84
|
+
| `archon github connect` | Link GitHub account for cloud execution |
|
|
85
|
+
| `archon github status` | Check GitHub connection status |
|
|
86
|
+
| `archon session save [name]` | Save current session to cloud |
|
|
87
|
+
| `archon session resume [id]` | Resume session on another device |
|
|
88
|
+
| `archon execute ATOM --cloud` | Execute in cloud (creates PR when done) |
|
|
89
|
+
| `archon cloud status` | List cloud executions |
|
|
90
|
+
| `archon index init [--local\|--cloud]` | Initialize semantic indexing |
|
|
91
|
+
| `archon index update [--cloud]` | Index changed files |
|
|
92
|
+
| `archon index search "query" [--cloud]` | Semantic code search |
|
|
93
|
+
| `archon parallel status` | Show parallel execution status |
|
|
94
|
+
| `archon deploy` | One-click deploy (auto-detect platform) |
|
|
84
95
|
|
|
85
96
|
## Pricing
|
|
86
97
|
|
|
@@ -2952,23 +2952,51 @@ Logs for ${exec.id.slice(0, 8)} (${exec.atom_id}):
|
|
|
2952
2952
|
console.log(`${ts} ${level} ${log.message}`);
|
|
2953
2953
|
}
|
|
2954
2954
|
}
|
|
2955
|
-
async function queueCloudExecution(atomId, projectName) {
|
|
2955
|
+
async function queueCloudExecution(atomId, projectName, options) {
|
|
2956
2956
|
const config = await loadConfig();
|
|
2957
2957
|
const authToken = getAuthToken(config);
|
|
2958
2958
|
if (!authToken) {
|
|
2959
2959
|
throw new Error('Not authenticated. Run "archon login" first.');
|
|
2960
2960
|
}
|
|
2961
2961
|
const client = getClient();
|
|
2962
|
-
const
|
|
2962
|
+
const insertData = {
|
|
2963
2963
|
atom_id: atomId,
|
|
2964
2964
|
project_name: projectName,
|
|
2965
2965
|
status: "queued"
|
|
2966
|
-
}
|
|
2966
|
+
};
|
|
2967
|
+
if (options?.repoUrl) {
|
|
2968
|
+
insertData["repo_url"] = options.repoUrl;
|
|
2969
|
+
}
|
|
2970
|
+
if (options?.repoBranch) {
|
|
2971
|
+
insertData["repo_branch"] = options.repoBranch;
|
|
2972
|
+
}
|
|
2973
|
+
if (options?.atomData) {
|
|
2974
|
+
insertData["atom_data"] = options.atomData;
|
|
2975
|
+
}
|
|
2976
|
+
const { data, error } = await client.from("cloud_executions").insert(insertData).select("id").single();
|
|
2967
2977
|
if (error) {
|
|
2968
2978
|
throw new Error(`Failed to queue execution: ${error.message}`);
|
|
2969
2979
|
}
|
|
2970
2980
|
return data.id;
|
|
2971
2981
|
}
|
|
2982
|
+
async function getGitRemoteUrl(cwd = process.cwd()) {
|
|
2983
|
+
try {
|
|
2984
|
+
const { execSync: execSync4 } = await import("child_process");
|
|
2985
|
+
const output = execSync4("git remote get-url origin", { cwd, encoding: "utf-8" });
|
|
2986
|
+
return output.trim();
|
|
2987
|
+
} catch {
|
|
2988
|
+
return null;
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
async function getGitBranch(cwd = process.cwd()) {
|
|
2992
|
+
try {
|
|
2993
|
+
const { execSync: execSync4 } = await import("child_process");
|
|
2994
|
+
const output = execSync4("git branch --show-current", { cwd, encoding: "utf-8" });
|
|
2995
|
+
return output.trim() || "main";
|
|
2996
|
+
} catch {
|
|
2997
|
+
return "main";
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
2972
3000
|
function getStatusIcon(status) {
|
|
2973
3001
|
switch (status) {
|
|
2974
3002
|
case "queued":
|
|
@@ -4798,10 +4826,29 @@ async function execute(atomId, options) {
|
|
|
4798
4826
|
if (options.cloud) {
|
|
4799
4827
|
const projectName = basename(cwd);
|
|
4800
4828
|
console.log(chalk3.dim(`Queueing cloud execution for ${atomId}...`));
|
|
4829
|
+
const repoUrl = await getGitRemoteUrl(cwd);
|
|
4830
|
+
const repoBranch = await getGitBranch(cwd);
|
|
4831
|
+
if (!repoUrl) {
|
|
4832
|
+
console.error(chalk3.red("No git remote found. Cloud execution requires a GitHub repository."));
|
|
4833
|
+
console.log(chalk3.dim("Add a remote with: git remote add origin <url>"));
|
|
4834
|
+
process.exit(1);
|
|
4835
|
+
}
|
|
4836
|
+
const atom2 = await loadAtom(atomId);
|
|
4837
|
+
if (!atom2) {
|
|
4838
|
+
console.error(chalk3.red(`Atom ${atomId} not found.`));
|
|
4839
|
+
process.exit(1);
|
|
4840
|
+
}
|
|
4801
4841
|
try {
|
|
4802
|
-
const executionId = await queueCloudExecution(atomId, projectName
|
|
4842
|
+
const executionId = await queueCloudExecution(atomId, projectName, {
|
|
4843
|
+
repoUrl,
|
|
4844
|
+
repoBranch,
|
|
4845
|
+
atomData: atom2
|
|
4846
|
+
});
|
|
4803
4847
|
console.log(chalk3.green(`
|
|
4804
4848
|
\u2713 Execution queued (ID: ${executionId.slice(0, 8)})`));
|
|
4849
|
+
console.log(chalk3.dim(`
|
|
4850
|
+
Repository: ${repoUrl}`));
|
|
4851
|
+
console.log(chalk3.dim(`Branch: ${repoBranch}`));
|
|
4805
4852
|
console.log(chalk3.dim("\nYou can close your terminal. Check status with:"));
|
|
4806
4853
|
console.log(chalk3.bold(" archon cloud status"));
|
|
4807
4854
|
console.log(chalk3.dim("\nOr view logs with:"));
|
|
@@ -5079,7 +5126,9 @@ async function watchCloudExecution(executionId) {
|
|
|
5079
5126
|
const logs = data.logs ?? [];
|
|
5080
5127
|
for (let i = lastLogCount; i < logs.length; i++) {
|
|
5081
5128
|
const log = logs[i];
|
|
5082
|
-
|
|
5129
|
+
if (log) {
|
|
5130
|
+
console.log(chalk3.dim(`[${log.timestamp}]`), log.message);
|
|
5131
|
+
}
|
|
5083
5132
|
}
|
|
5084
5133
|
lastLogCount = logs.length;
|
|
5085
5134
|
if (data.status === "completed") {
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
cloudLogs,
|
|
8
8
|
cloudStatus,
|
|
9
9
|
execute
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XRWIU3RD.js";
|
|
11
11
|
import {
|
|
12
12
|
list
|
|
13
13
|
} from "./chunk-N73LAU7W.js";
|
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
import "./chunk-A7QU6JC6.js";
|
|
72
72
|
import "./chunk-SMR7JQK6.js";
|
|
73
73
|
import {
|
|
74
|
+
getAuthToken,
|
|
74
75
|
loadConfig
|
|
75
76
|
} from "./chunk-IVY5AHPS.js";
|
|
76
77
|
import {
|
|
@@ -80,7 +81,7 @@ import "./chunk-QGM4M3NI.js";
|
|
|
80
81
|
|
|
81
82
|
// src/cli/index.ts
|
|
82
83
|
import { Command as Command4 } from "commander";
|
|
83
|
-
import
|
|
84
|
+
import chalk13 from "chalk";
|
|
84
85
|
import "dotenv/config";
|
|
85
86
|
|
|
86
87
|
// src/cli/promote.ts
|
|
@@ -951,7 +952,7 @@ async function executeNext() {
|
|
|
951
952
|
const atomId = await prompt("Enter atom ID to execute (or press Enter for first pending)");
|
|
952
953
|
const targetId = atomId.trim() || pendingAtoms[0]?.id;
|
|
953
954
|
if (targetId) {
|
|
954
|
-
const { execute: execute2 } = await import("./execute-
|
|
955
|
+
const { execute: execute2 } = await import("./execute-2S2UHTLN.js");
|
|
955
956
|
await execute2(targetId, {});
|
|
956
957
|
} else {
|
|
957
958
|
console.log(chalk3.yellow("No atom to execute."));
|
|
@@ -3686,13 +3687,284 @@ var LocalIndexer = class {
|
|
|
3686
3687
|
}
|
|
3687
3688
|
};
|
|
3688
3689
|
|
|
3690
|
+
// src/core/indexing/cloud.ts
|
|
3691
|
+
import { createClient as createClient3 } from "@supabase/supabase-js";
|
|
3692
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
3693
|
+
import { existsSync as existsSync12 } from "fs";
|
|
3694
|
+
import { join as join11, extname as extname2 } from "path";
|
|
3695
|
+
import { createHash } from "crypto";
|
|
3696
|
+
var CHUNK_SIZE2 = 1e3;
|
|
3697
|
+
var CHUNK_OVERLAP2 = 200;
|
|
3698
|
+
var INDEXABLE_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
3699
|
+
".ts",
|
|
3700
|
+
".tsx",
|
|
3701
|
+
".js",
|
|
3702
|
+
".jsx",
|
|
3703
|
+
".py",
|
|
3704
|
+
".rb",
|
|
3705
|
+
".go",
|
|
3706
|
+
".rs",
|
|
3707
|
+
".java",
|
|
3708
|
+
".c",
|
|
3709
|
+
".cpp",
|
|
3710
|
+
".h",
|
|
3711
|
+
".hpp",
|
|
3712
|
+
".cs",
|
|
3713
|
+
".swift",
|
|
3714
|
+
".kt",
|
|
3715
|
+
".scala",
|
|
3716
|
+
".md",
|
|
3717
|
+
".mdx",
|
|
3718
|
+
".txt",
|
|
3719
|
+
".json",
|
|
3720
|
+
".yaml",
|
|
3721
|
+
".yml",
|
|
3722
|
+
".toml",
|
|
3723
|
+
".html",
|
|
3724
|
+
".css",
|
|
3725
|
+
".scss",
|
|
3726
|
+
".less",
|
|
3727
|
+
".vue",
|
|
3728
|
+
".svelte"
|
|
3729
|
+
]);
|
|
3730
|
+
var CloudIndexer = class {
|
|
3731
|
+
config;
|
|
3732
|
+
client;
|
|
3733
|
+
userId = null;
|
|
3734
|
+
constructor(config) {
|
|
3735
|
+
this.config = config;
|
|
3736
|
+
this.client = createClient3(config.supabaseUrl, config.supabaseKey);
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Set the authenticated user ID
|
|
3740
|
+
*/
|
|
3741
|
+
setUserId(userId) {
|
|
3742
|
+
this.userId = userId;
|
|
3743
|
+
}
|
|
3744
|
+
/**
|
|
3745
|
+
* Generate embedding using OpenAI API
|
|
3746
|
+
*/
|
|
3747
|
+
async embedText(text) {
|
|
3748
|
+
if (this.config.embeddingProvider === "openai") {
|
|
3749
|
+
return this.embedWithOpenAI(text);
|
|
3750
|
+
}
|
|
3751
|
+
throw new Error(`Unsupported embedding provider: ${this.config.embeddingProvider}`);
|
|
3752
|
+
}
|
|
3753
|
+
async embedWithOpenAI(text) {
|
|
3754
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
3755
|
+
method: "POST",
|
|
3756
|
+
headers: {
|
|
3757
|
+
"Authorization": `Bearer ${this.config.embeddingApiKey}`,
|
|
3758
|
+
"Content-Type": "application/json"
|
|
3759
|
+
},
|
|
3760
|
+
body: JSON.stringify({
|
|
3761
|
+
model: "text-embedding-ada-002",
|
|
3762
|
+
input: text.slice(0, 8e3)
|
|
3763
|
+
// Limit input size
|
|
3764
|
+
})
|
|
3765
|
+
});
|
|
3766
|
+
if (!response.ok) {
|
|
3767
|
+
const error = await response.json();
|
|
3768
|
+
throw new Error(`OpenAI embedding failed: ${error.error?.message ?? response.statusText}`);
|
|
3769
|
+
}
|
|
3770
|
+
const data = await response.json();
|
|
3771
|
+
const embedding = data.data[0]?.embedding;
|
|
3772
|
+
if (!embedding) {
|
|
3773
|
+
throw new Error("No embedding returned from OpenAI");
|
|
3774
|
+
}
|
|
3775
|
+
return embedding;
|
|
3776
|
+
}
|
|
3777
|
+
/**
|
|
3778
|
+
* Chunk text into smaller pieces for embedding
|
|
3779
|
+
*/
|
|
3780
|
+
chunkText(text, filePath) {
|
|
3781
|
+
const chunks = [];
|
|
3782
|
+
const header = `File: ${filePath}
|
|
3783
|
+
|
|
3784
|
+
`;
|
|
3785
|
+
let start2 = 0;
|
|
3786
|
+
let index = 0;
|
|
3787
|
+
while (start2 < text.length) {
|
|
3788
|
+
const end = Math.min(start2 + CHUNK_SIZE2, text.length);
|
|
3789
|
+
const chunkText = index === 0 ? header + text.slice(start2, end) : text.slice(start2, end);
|
|
3790
|
+
if (chunkText.trim().length > 0) {
|
|
3791
|
+
chunks.push({ text: chunkText, index });
|
|
3792
|
+
index++;
|
|
3793
|
+
}
|
|
3794
|
+
start2 += CHUNK_SIZE2 - CHUNK_OVERLAP2;
|
|
3795
|
+
}
|
|
3796
|
+
return chunks;
|
|
3797
|
+
}
|
|
3798
|
+
/**
|
|
3799
|
+
* Check if file should be indexed
|
|
3800
|
+
*/
|
|
3801
|
+
isIndexableFile(filePath) {
|
|
3802
|
+
const ext = extname2(filePath).toLowerCase();
|
|
3803
|
+
return INDEXABLE_EXTENSIONS2.has(ext);
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Compute file hash for change detection
|
|
3807
|
+
*/
|
|
3808
|
+
async computeFileHash(content) {
|
|
3809
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
3810
|
+
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Index a single file
|
|
3813
|
+
*/
|
|
3814
|
+
async indexFile(cwd, filePath) {
|
|
3815
|
+
if (!this.userId) {
|
|
3816
|
+
throw new Error("User ID not set. Call setUserId() first.");
|
|
3817
|
+
}
|
|
3818
|
+
if (!this.isIndexableFile(filePath)) {
|
|
3819
|
+
return 0;
|
|
3820
|
+
}
|
|
3821
|
+
const fullPath = join11(cwd, filePath);
|
|
3822
|
+
if (!existsSync12(fullPath)) {
|
|
3823
|
+
return 0;
|
|
3824
|
+
}
|
|
3825
|
+
const content = await readFile9(fullPath, "utf-8");
|
|
3826
|
+
const fileHash = await this.computeFileHash(content);
|
|
3827
|
+
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
|
+
if (existing && existing.file_hash === fileHash) {
|
|
3829
|
+
return 0;
|
|
3830
|
+
}
|
|
3831
|
+
await this.client.from("code_embeddings").delete().eq("user_id", this.userId).eq("project_id", this.config.projectId).eq("file_path", filePath);
|
|
3832
|
+
const chunks = this.chunkText(content, filePath);
|
|
3833
|
+
for (const chunk of chunks) {
|
|
3834
|
+
const embedding = await this.embedText(chunk.text);
|
|
3835
|
+
const embeddingStr = `[${embedding.join(",")}]`;
|
|
3836
|
+
await this.client.from("code_embeddings").insert({
|
|
3837
|
+
user_id: this.userId,
|
|
3838
|
+
project_id: this.config.projectId,
|
|
3839
|
+
file_path: filePath,
|
|
3840
|
+
chunk_index: chunk.index,
|
|
3841
|
+
chunk_text: chunk.text,
|
|
3842
|
+
embedding: embeddingStr,
|
|
3843
|
+
file_hash: fileHash
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
return chunks.length;
|
|
3847
|
+
}
|
|
3848
|
+
/**
|
|
3849
|
+
* Search for similar code
|
|
3850
|
+
*/
|
|
3851
|
+
async search(query, limit = 10) {
|
|
3852
|
+
if (!this.userId) {
|
|
3853
|
+
throw new Error("User ID not set. Call setUserId() first.");
|
|
3854
|
+
}
|
|
3855
|
+
const queryEmbedding = await this.embedText(query);
|
|
3856
|
+
const embeddingStr = `[${queryEmbedding.join(",")}]`;
|
|
3857
|
+
const { data, error } = await this.client.rpc("search_code_embeddings", {
|
|
3858
|
+
p_user_id: this.userId,
|
|
3859
|
+
p_project_id: this.config.projectId,
|
|
3860
|
+
p_query_embedding: embeddingStr,
|
|
3861
|
+
p_limit: limit
|
|
3862
|
+
});
|
|
3863
|
+
if (error) {
|
|
3864
|
+
throw new Error(`Search failed: ${error.message}`);
|
|
3865
|
+
}
|
|
3866
|
+
return (data ?? []).map((row) => ({
|
|
3867
|
+
file: row.file_path,
|
|
3868
|
+
text: row.chunk_text,
|
|
3869
|
+
score: row.similarity
|
|
3870
|
+
}));
|
|
3871
|
+
}
|
|
3872
|
+
/**
|
|
3873
|
+
* Get indexing status for project
|
|
3874
|
+
*/
|
|
3875
|
+
async getStatus() {
|
|
3876
|
+
if (!this.userId) {
|
|
3877
|
+
throw new Error("User ID not set. Call setUserId() first.");
|
|
3878
|
+
}
|
|
3879
|
+
const { data: fileCount } = await this.client.from("code_embeddings").select("file_path", { count: "exact", head: true }).eq("user_id", this.userId).eq("project_id", this.config.projectId);
|
|
3880
|
+
const { data: chunkCount } = await this.client.from("code_embeddings").select("id", { count: "exact", head: true }).eq("user_id", this.userId).eq("project_id", this.config.projectId);
|
|
3881
|
+
const { data: lastUpdated } = await this.client.from("code_embeddings").select("updated_at").eq("user_id", this.userId).eq("project_id", this.config.projectId).order("updated_at", { ascending: false }).limit(1).single();
|
|
3882
|
+
const { data: job } = await this.client.from("indexing_jobs").select("status").eq("user_id", this.userId).eq("project_id", this.config.projectId).order("created_at", { ascending: false }).limit(1).single();
|
|
3883
|
+
return {
|
|
3884
|
+
projectId: this.config.projectId,
|
|
3885
|
+
fileCount: fileCount?.count ?? 0,
|
|
3886
|
+
chunkCount: chunkCount?.count ?? 0,
|
|
3887
|
+
lastUpdated: lastUpdated?.updated_at ?? null,
|
|
3888
|
+
jobStatus: job?.status
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Remove all embeddings for this project
|
|
3893
|
+
*/
|
|
3894
|
+
async clearProject() {
|
|
3895
|
+
if (!this.userId) {
|
|
3896
|
+
throw new Error("User ID not set. Call setUserId() first.");
|
|
3897
|
+
}
|
|
3898
|
+
await this.client.from("code_embeddings").delete().eq("user_id", this.userId).eq("project_id", this.config.projectId);
|
|
3899
|
+
}
|
|
3900
|
+
/**
|
|
3901
|
+
* Remove a single file from the index
|
|
3902
|
+
*/
|
|
3903
|
+
async removeFile(filePath) {
|
|
3904
|
+
if (!this.userId) {
|
|
3905
|
+
throw new Error("User ID not set. Call setUserId() first.");
|
|
3906
|
+
}
|
|
3907
|
+
await this.client.from("code_embeddings").delete().eq("user_id", this.userId).eq("project_id", this.config.projectId).eq("file_path", filePath);
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
|
|
3689
3911
|
// src/cli/index-cmd.ts
|
|
3690
3912
|
import { glob as glob4 } from "glob";
|
|
3691
|
-
import { join as
|
|
3913
|
+
import { join as join12, basename } from "path";
|
|
3914
|
+
async function getCloudIndexer(cwd) {
|
|
3915
|
+
const config = await loadConfig();
|
|
3916
|
+
const authToken = getAuthToken(config);
|
|
3917
|
+
if (!authToken) {
|
|
3918
|
+
console.error(chalk11.red('Not authenticated. Run "archon login" first.'));
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3921
|
+
const openaiKey = process.env["OPENAI_API_KEY"];
|
|
3922
|
+
if (!openaiKey) {
|
|
3923
|
+
console.error(chalk11.red("OPENAI_API_KEY environment variable not set."));
|
|
3924
|
+
console.log(chalk11.dim("Cloud indexing requires an OpenAI API key for embeddings."));
|
|
3925
|
+
console.log(chalk11.dim("Set it with: export OPENAI_API_KEY=sk-..."));
|
|
3926
|
+
return null;
|
|
3927
|
+
}
|
|
3928
|
+
const projectId = basename(cwd);
|
|
3929
|
+
const indexer = new CloudIndexer({
|
|
3930
|
+
supabaseUrl: SUPABASE_URL,
|
|
3931
|
+
supabaseKey: SUPABASE_ANON_KEY,
|
|
3932
|
+
embeddingProvider: "openai",
|
|
3933
|
+
embeddingApiKey: openaiKey,
|
|
3934
|
+
projectId
|
|
3935
|
+
});
|
|
3936
|
+
const { createClient: createClient4 } = await import("@supabase/supabase-js");
|
|
3937
|
+
const client = createClient4(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
3938
|
+
global: { headers: { Authorization: `Bearer ${authToken}` } }
|
|
3939
|
+
});
|
|
3940
|
+
const { data: { user } } = await client.auth.getUser();
|
|
3941
|
+
if (!user) {
|
|
3942
|
+
console.error(chalk11.red("Failed to get user. Try logging in again."));
|
|
3943
|
+
return null;
|
|
3944
|
+
}
|
|
3945
|
+
const { data: profile } = await client.from("user_profiles").select("id").eq("auth_id", user.id).single();
|
|
3946
|
+
if (!profile) {
|
|
3947
|
+
console.error(chalk11.red("User profile not found."));
|
|
3948
|
+
return null;
|
|
3949
|
+
}
|
|
3950
|
+
indexer.setUserId(profile.id);
|
|
3951
|
+
return indexer;
|
|
3952
|
+
}
|
|
3692
3953
|
async function indexInit(options) {
|
|
3693
3954
|
const cwd = process.cwd();
|
|
3694
3955
|
if (options.cloud) {
|
|
3695
|
-
console.log(chalk11.
|
|
3956
|
+
console.log(chalk11.blue("Initializing cloud semantic index..."));
|
|
3957
|
+
const indexer = await getCloudIndexer(cwd);
|
|
3958
|
+
if (!indexer) return;
|
|
3959
|
+
try {
|
|
3960
|
+
const status2 = await indexer.getStatus();
|
|
3961
|
+
console.log(chalk11.green("\u2713 Cloud indexing configured"));
|
|
3962
|
+
console.log(chalk11.green(`\u2713 Project ID: ${status2.projectId}`));
|
|
3963
|
+
console.log(chalk11.dim("\nRun `archon index update --cloud` to index your codebase."));
|
|
3964
|
+
} catch (error) {
|
|
3965
|
+
console.error(chalk11.red(`Failed to initialize cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
3966
|
+
process.exit(1);
|
|
3967
|
+
}
|
|
3696
3968
|
return;
|
|
3697
3969
|
}
|
|
3698
3970
|
console.log(chalk11.blue("Initializing local semantic index..."));
|
|
@@ -3712,15 +3984,58 @@ async function indexInit(options) {
|
|
|
3712
3984
|
console.log(chalk11.red("\n\u2717 Ollama is not running"));
|
|
3713
3985
|
console.log(chalk11.dim("Start Ollama with: ollama serve"));
|
|
3714
3986
|
console.log(chalk11.dim("Then pull the embedding model: ollama pull nomic-embed-text"));
|
|
3987
|
+
console.log(chalk11.dim("\nOr use cloud indexing: archon index init --cloud"));
|
|
3715
3988
|
} else {
|
|
3716
3989
|
console.error(chalk11.red(`Failed to initialize index: ${error instanceof Error ? error.message : String(error)}`));
|
|
3717
3990
|
}
|
|
3718
3991
|
process.exit(1);
|
|
3719
3992
|
}
|
|
3720
3993
|
}
|
|
3721
|
-
async function indexUpdate() {
|
|
3994
|
+
async function indexUpdate(options) {
|
|
3722
3995
|
const cwd = process.cwd();
|
|
3723
|
-
|
|
3996
|
+
if (options?.cloud) {
|
|
3997
|
+
console.log(chalk11.blue("Updating cloud semantic index..."));
|
|
3998
|
+
const indexer = await getCloudIndexer(cwd);
|
|
3999
|
+
if (!indexer) return;
|
|
4000
|
+
try {
|
|
4001
|
+
const files = await glob4("**/*.{ts,tsx,js,jsx,py,rb,go,rs,java,md,json,yaml,yml}", {
|
|
4002
|
+
cwd,
|
|
4003
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**", "**/build/**", "**/coverage/**"]
|
|
4004
|
+
});
|
|
4005
|
+
console.log(chalk11.dim(`Found ${files.length} files to index...`));
|
|
4006
|
+
console.log(chalk11.dim("This may take a few minutes and will use OpenAI API credits.\n"));
|
|
4007
|
+
let totalChunks = 0;
|
|
4008
|
+
let indexedFiles = 0;
|
|
4009
|
+
let skippedFiles = 0;
|
|
4010
|
+
for (let i = 0; i < files.length; i++) {
|
|
4011
|
+
const file = files[i];
|
|
4012
|
+
if (!file) continue;
|
|
4013
|
+
process.stdout.write(`\r${chalk11.dim(`[${i + 1}/${files.length}] ${file.slice(0, 45).padEnd(45)}`)}`);
|
|
4014
|
+
try {
|
|
4015
|
+
const chunks = await indexer.indexFile(cwd, file);
|
|
4016
|
+
if (chunks > 0) {
|
|
4017
|
+
totalChunks += chunks;
|
|
4018
|
+
indexedFiles++;
|
|
4019
|
+
} else {
|
|
4020
|
+
skippedFiles++;
|
|
4021
|
+
}
|
|
4022
|
+
} catch (error) {
|
|
4023
|
+
console.log(`
|
|
4024
|
+
${chalk11.yellow(`\u26A0 Skipped ${file}: ${error instanceof Error ? error.message : "Unknown error"}`)}`);
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
console.log("\r" + " ".repeat(70));
|
|
4028
|
+
console.log(chalk11.green(`\u2713 Indexed ${indexedFiles} files (${totalChunks} chunks)`));
|
|
4029
|
+
if (skippedFiles > 0) {
|
|
4030
|
+
console.log(chalk11.dim(` Skipped ${skippedFiles} unchanged files`));
|
|
4031
|
+
}
|
|
4032
|
+
} catch (error) {
|
|
4033
|
+
console.error(chalk11.red(`Failed to update cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
4034
|
+
process.exit(1);
|
|
4035
|
+
}
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
console.log(chalk11.blue("Updating local semantic index..."));
|
|
3724
4039
|
try {
|
|
3725
4040
|
const indexer = new LocalIndexer();
|
|
3726
4041
|
await indexer.init(cwd);
|
|
@@ -3732,6 +4047,7 @@ async function indexUpdate() {
|
|
|
3732
4047
|
let totalChunks = 0;
|
|
3733
4048
|
let indexedFiles = 0;
|
|
3734
4049
|
for (const file of files) {
|
|
4050
|
+
if (!file) continue;
|
|
3735
4051
|
process.stdout.write(`\r${chalk11.dim(`Indexing: ${file.slice(0, 50).padEnd(50)}`)}`);
|
|
3736
4052
|
const chunks = await indexer.indexFile(cwd, file);
|
|
3737
4053
|
if (chunks > 0) {
|
|
@@ -3747,8 +4063,35 @@ async function indexUpdate() {
|
|
|
3747
4063
|
process.exit(1);
|
|
3748
4064
|
}
|
|
3749
4065
|
}
|
|
3750
|
-
async function indexSearch(query) {
|
|
4066
|
+
async function indexSearch(query, options) {
|
|
3751
4067
|
const cwd = process.cwd();
|
|
4068
|
+
if (options?.cloud) {
|
|
4069
|
+
const indexer = await getCloudIndexer(cwd);
|
|
4070
|
+
if (!indexer) return;
|
|
4071
|
+
try {
|
|
4072
|
+
console.log(chalk11.dim("Searching cloud index..."));
|
|
4073
|
+
const results = await indexer.search(query, 10);
|
|
4074
|
+
if (results.length === 0) {
|
|
4075
|
+
console.log(chalk11.yellow("\nNo results found."));
|
|
4076
|
+
console.log(chalk11.dim("Try running `archon index update --cloud` first."));
|
|
4077
|
+
} else {
|
|
4078
|
+
console.log(chalk11.blue(`
|
|
4079
|
+
Top ${results.length} results for: "${query}"
|
|
4080
|
+
`));
|
|
4081
|
+
for (const result of results) {
|
|
4082
|
+
const score = (result.score * 100).toFixed(1);
|
|
4083
|
+
console.log(chalk11.green(`[${score}%] ${result.file}`));
|
|
4084
|
+
const preview = result.text.slice(0, 200).replace(/\n/g, " ").trim();
|
|
4085
|
+
console.log(chalk11.dim(` ${preview}${result.text.length > 200 ? "..." : ""}`));
|
|
4086
|
+
console.log();
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
} catch (error) {
|
|
4090
|
+
console.error(chalk11.red(`Cloud search failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
4091
|
+
process.exit(1);
|
|
4092
|
+
}
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
3752
4095
|
try {
|
|
3753
4096
|
const indexer = new LocalIndexer();
|
|
3754
4097
|
await indexer.init(cwd);
|
|
@@ -3774,30 +4117,160 @@ Top ${results.length} results for: "${query}"
|
|
|
3774
4117
|
process.exit(1);
|
|
3775
4118
|
}
|
|
3776
4119
|
}
|
|
3777
|
-
async function indexStatus() {
|
|
4120
|
+
async function indexStatus(options) {
|
|
3778
4121
|
const cwd = process.cwd();
|
|
4122
|
+
if (options?.cloud) {
|
|
4123
|
+
const indexer = await getCloudIndexer(cwd);
|
|
4124
|
+
if (!indexer) return;
|
|
4125
|
+
try {
|
|
4126
|
+
const status2 = await indexer.getStatus();
|
|
4127
|
+
console.log(chalk11.blue("\nCloud Semantic Index Status\n"));
|
|
4128
|
+
console.log(` Project ID: ${chalk11.green(status2.projectId)}`);
|
|
4129
|
+
console.log(` Files indexed: ${chalk11.green(status2.fileCount)}`);
|
|
4130
|
+
console.log(` Total chunks: ${chalk11.green(status2.chunkCount)}`);
|
|
4131
|
+
console.log(` Last updated: ${status2.lastUpdated ? chalk11.dim(status2.lastUpdated) : chalk11.yellow("Never")}`);
|
|
4132
|
+
if (status2.jobStatus) {
|
|
4133
|
+
console.log(` Job status: ${chalk11.dim(status2.jobStatus)}`);
|
|
4134
|
+
}
|
|
4135
|
+
console.log(` Storage: ${chalk11.dim("Supabase pgvector")}`);
|
|
4136
|
+
} catch (error) {
|
|
4137
|
+
console.error(chalk11.red(`Failed to get cloud status: ${error instanceof Error ? error.message : String(error)}`));
|
|
4138
|
+
process.exit(1);
|
|
4139
|
+
}
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
3779
4142
|
try {
|
|
3780
4143
|
const indexer = new LocalIndexer();
|
|
3781
4144
|
await indexer.init(cwd);
|
|
3782
4145
|
const status2 = await indexer.getStatus();
|
|
3783
|
-
console.log(chalk11.blue("\
|
|
4146
|
+
console.log(chalk11.blue("\nLocal Semantic Index Status\n"));
|
|
3784
4147
|
console.log(` Files indexed: ${chalk11.green(status2.fileCount)}`);
|
|
3785
4148
|
console.log(` Total chunks: ${chalk11.green(status2.chunkCount)}`);
|
|
3786
4149
|
console.log(` Last updated: ${status2.lastUpdated ? chalk11.dim(status2.lastUpdated) : chalk11.yellow("Never")}`);
|
|
3787
|
-
console.log(` Database: ${chalk11.dim(
|
|
4150
|
+
console.log(` Database: ${chalk11.dim(join12(cwd, ".archon/index.db"))}`);
|
|
3788
4151
|
indexer.close();
|
|
3789
4152
|
} catch (error) {
|
|
3790
4153
|
console.error(chalk11.red(`Failed to get status: ${error instanceof Error ? error.message : String(error)}`));
|
|
3791
4154
|
process.exit(1);
|
|
3792
4155
|
}
|
|
3793
4156
|
}
|
|
4157
|
+
async function indexClear(options) {
|
|
4158
|
+
const cwd = process.cwd();
|
|
4159
|
+
if (options?.cloud) {
|
|
4160
|
+
const indexer = await getCloudIndexer(cwd);
|
|
4161
|
+
if (!indexer) return;
|
|
4162
|
+
try {
|
|
4163
|
+
console.log(chalk11.yellow("Clearing cloud index..."));
|
|
4164
|
+
await indexer.clearProject();
|
|
4165
|
+
console.log(chalk11.green("\u2713 Cloud index cleared"));
|
|
4166
|
+
} catch (error) {
|
|
4167
|
+
console.error(chalk11.red(`Failed to clear cloud index: ${error instanceof Error ? error.message : String(error)}`));
|
|
4168
|
+
process.exit(1);
|
|
4169
|
+
}
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
console.log(chalk11.yellow("To clear local index, delete .archon/index.db"));
|
|
4173
|
+
}
|
|
4174
|
+
|
|
4175
|
+
// src/cli/github.ts
|
|
4176
|
+
import chalk12 from "chalk";
|
|
4177
|
+
import open2 from "open";
|
|
4178
|
+
var API_URL2 = process.env["ARCHONDEV_API_URL"] ?? "https://archondev-api.fly.dev";
|
|
4179
|
+
async function githubConnect() {
|
|
4180
|
+
const config = await loadConfig();
|
|
4181
|
+
const authToken = getAuthToken(config);
|
|
4182
|
+
if (!authToken) {
|
|
4183
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
4184
|
+
process.exit(1);
|
|
4185
|
+
}
|
|
4186
|
+
console.log(chalk12.dim("Starting GitHub connection..."));
|
|
4187
|
+
try {
|
|
4188
|
+
const response = await fetch(`${API_URL2}/api/github/connect`, {
|
|
4189
|
+
headers: {
|
|
4190
|
+
"Authorization": `Bearer ${authToken}`
|
|
4191
|
+
}
|
|
4192
|
+
});
|
|
4193
|
+
if (!response.ok) {
|
|
4194
|
+
const error = await response.json();
|
|
4195
|
+
console.error(chalk12.red(error.error ?? "Failed to start GitHub connection"));
|
|
4196
|
+
process.exit(1);
|
|
4197
|
+
}
|
|
4198
|
+
const data = await response.json();
|
|
4199
|
+
console.log(chalk12.dim("\nOpening browser for GitHub authorization..."));
|
|
4200
|
+
console.log(chalk12.dim("If browser does not open, visit:"));
|
|
4201
|
+
console.log(chalk12.blue(data.url));
|
|
4202
|
+
await open2(data.url);
|
|
4203
|
+
console.log(chalk12.dim("\nComplete the authorization in your browser."));
|
|
4204
|
+
console.log(chalk12.dim('Then run "archon github status" to verify connection.'));
|
|
4205
|
+
} catch (error) {
|
|
4206
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to connect"));
|
|
4207
|
+
process.exit(1);
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
async function githubStatus() {
|
|
4211
|
+
const config = await loadConfig();
|
|
4212
|
+
const authToken = getAuthToken(config);
|
|
4213
|
+
if (!authToken) {
|
|
4214
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
4215
|
+
process.exit(1);
|
|
4216
|
+
}
|
|
4217
|
+
try {
|
|
4218
|
+
const response = await fetch(`${API_URL2}/api/github/status`, {
|
|
4219
|
+
headers: {
|
|
4220
|
+
"Authorization": `Bearer ${authToken}`
|
|
4221
|
+
}
|
|
4222
|
+
});
|
|
4223
|
+
if (!response.ok) {
|
|
4224
|
+
const error = await response.json();
|
|
4225
|
+
console.error(chalk12.red(error.error ?? "Failed to get GitHub status"));
|
|
4226
|
+
process.exit(1);
|
|
4227
|
+
}
|
|
4228
|
+
const data = await response.json();
|
|
4229
|
+
if (data.connected) {
|
|
4230
|
+
console.log(chalk12.green("\u2713 GitHub connected"));
|
|
4231
|
+
console.log(chalk12.dim(` Username: ${data.username}`));
|
|
4232
|
+
console.log(chalk12.dim(` Connected: ${data.connectedAt ? new Date(data.connectedAt).toLocaleDateString() : "Unknown"}`));
|
|
4233
|
+
} else {
|
|
4234
|
+
console.log(chalk12.yellow("GitHub not connected"));
|
|
4235
|
+
console.log(chalk12.dim('Run "archon github connect" to connect your GitHub account.'));
|
|
4236
|
+
}
|
|
4237
|
+
} catch (error) {
|
|
4238
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to get status"));
|
|
4239
|
+
process.exit(1);
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
async function githubDisconnect() {
|
|
4243
|
+
const config = await loadConfig();
|
|
4244
|
+
const authToken = getAuthToken(config);
|
|
4245
|
+
if (!authToken) {
|
|
4246
|
+
console.error(chalk12.red('Not authenticated. Run "archon login" first.'));
|
|
4247
|
+
process.exit(1);
|
|
4248
|
+
}
|
|
4249
|
+
try {
|
|
4250
|
+
const response = await fetch(`${API_URL2}/api/github/disconnect`, {
|
|
4251
|
+
method: "DELETE",
|
|
4252
|
+
headers: {
|
|
4253
|
+
"Authorization": `Bearer ${authToken}`
|
|
4254
|
+
}
|
|
4255
|
+
});
|
|
4256
|
+
if (!response.ok) {
|
|
4257
|
+
const error = await response.json();
|
|
4258
|
+
console.error(chalk12.red(error.error ?? "Failed to disconnect GitHub"));
|
|
4259
|
+
process.exit(1);
|
|
4260
|
+
}
|
|
4261
|
+
console.log(chalk12.green("\u2713 GitHub disconnected"));
|
|
4262
|
+
} catch (error) {
|
|
4263
|
+
console.error(chalk12.red(error instanceof Error ? error.message : "Failed to disconnect"));
|
|
4264
|
+
process.exit(1);
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
3794
4267
|
|
|
3795
4268
|
// src/cli/index.ts
|
|
3796
4269
|
var program = new Command4();
|
|
3797
4270
|
program.name("archon").description("Local-first AI-powered development governance").version("1.1.0").action(async () => {
|
|
3798
4271
|
const cwd = process.cwd();
|
|
3799
4272
|
if (!isInitialized(cwd)) {
|
|
3800
|
-
console.log(
|
|
4273
|
+
console.log(chalk13.blue("\nArchonDev is not initialized in this folder.\n"));
|
|
3801
4274
|
await init({ analyze: true, git: true });
|
|
3802
4275
|
}
|
|
3803
4276
|
await start();
|
|
@@ -3805,7 +4278,7 @@ program.name("archon").description("Local-first AI-powered development governanc
|
|
|
3805
4278
|
program.command("login").description("Authenticate with ArchonDev").option("-p, --provider <provider>", "OAuth provider (github or google)", "github").action(async (options) => {
|
|
3806
4279
|
const provider = options.provider;
|
|
3807
4280
|
if (provider !== "github" && provider !== "google") {
|
|
3808
|
-
console.error(
|
|
4281
|
+
console.error(chalk13.red('Invalid provider. Use "github" or "google"'));
|
|
3809
4282
|
process.exit(1);
|
|
3810
4283
|
}
|
|
3811
4284
|
await login(provider);
|
|
@@ -3953,11 +4426,12 @@ a11yCommand.action(async () => {
|
|
|
3953
4426
|
program.addCommand(createSeoCommand());
|
|
3954
4427
|
program.addCommand(createGeoCommand());
|
|
3955
4428
|
program.command("deploy").description("Deploy application (auto-detects platform)").option("-p, --platform <name>", "Specify platform (fly/vercel/netlify/railway/render)").option("--preview", "Deploy to preview/staging environment").option("--dry-run", "Show what would happen without deploying").action(deploy);
|
|
3956
|
-
var indexCmd = program.command("index").description("Semantic codebase indexing
|
|
3957
|
-
indexCmd.command("init").description("Initialize semantic index").option("--local", "Use local Ollama for embeddings (default)").option("--cloud", "Use cloud
|
|
3958
|
-
indexCmd.command("update").description("Reindex changed files").action(indexUpdate);
|
|
3959
|
-
indexCmd.command("search <query>").description("Semantic search across codebase").action(indexSearch);
|
|
3960
|
-
indexCmd.command("status").description("Show index statistics").action(indexStatus);
|
|
4429
|
+
var indexCmd = program.command("index").description("Semantic codebase indexing (local Ollama or cloud pgvector)");
|
|
4430
|
+
indexCmd.command("init").description("Initialize semantic index").option("--local", "Use local Ollama for embeddings (default)").option("--cloud", "Use cloud pgvector with OpenAI embeddings").action(indexInit);
|
|
4431
|
+
indexCmd.command("update").description("Reindex changed files").option("--cloud", "Use cloud indexing").action(indexUpdate);
|
|
4432
|
+
indexCmd.command("search <query>").description("Semantic search across codebase").option("--cloud", "Search cloud index").action((query, options) => indexSearch(query, options));
|
|
4433
|
+
indexCmd.command("status").description("Show index statistics").option("--cloud", "Show cloud index status").action(indexStatus);
|
|
4434
|
+
indexCmd.command("clear").description("Clear the semantic index").option("--cloud", "Clear cloud index").action(indexClear);
|
|
3961
4435
|
indexCmd.action(indexStatus);
|
|
3962
4436
|
var sessionCmd = program.command("session").description("Cross-device session management");
|
|
3963
4437
|
sessionCmd.command("save [name]").description("Save current session").action(saveSession);
|
|
@@ -3975,4 +4449,9 @@ parallelCmd.command("status").description("Show status of all parallel execution
|
|
|
3975
4449
|
parallelCmd.command("merge [atomId]").description("Merge completed worktrees back to main").action(parallelMerge);
|
|
3976
4450
|
parallelCmd.command("clean").description("Clean up all parallel execution state and worktrees").action(parallelClean);
|
|
3977
4451
|
parallelCmd.action(parallelStatus);
|
|
4452
|
+
var githubCmd = program.command("github").description("GitHub integration for cloud execution");
|
|
4453
|
+
githubCmd.command("connect").description("Connect your GitHub account for cloud execution").action(githubConnect);
|
|
4454
|
+
githubCmd.command("status").description("Show GitHub connection status").action(githubStatus);
|
|
4455
|
+
githubCmd.command("disconnect").description("Disconnect your GitHub account").action(githubDisconnect);
|
|
4456
|
+
githubCmd.action(githubStatus);
|
|
3978
4457
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "archondev",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Local-first AI-powered development governance system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsup src/cli/index.ts --format esm --dts --clean",
|
|
15
15
|
"build:server": "tsup src/server/start.ts --format esm --out-dir dist-server --clean",
|
|
16
|
+
"build:worker": "tsup src/worker/start.ts --format esm --out-dir dist-worker --clean",
|
|
16
17
|
"dev": "tsx src/cli/index.ts",
|
|
17
18
|
"dev:server": "tsx --watch src/server/start.ts",
|
|
18
19
|
"start:server": "node dist-server/start.js",
|
|
20
|
+
"start:worker": "node dist-worker/start.js",
|
|
21
|
+
"dev:worker": "tsx --watch src/worker/start.ts",
|
|
19
22
|
"typecheck": "tsc --noEmit",
|
|
20
23
|
"test": "vitest run",
|
|
21
24
|
"test:watch": "vitest",
|