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 { data, error } = await client.from("cloud_executions").insert({
2962
+ const insertData = {
2963
2963
  atom_id: atomId,
2964
2964
  project_name: projectName,
2965
2965
  status: "queued"
2966
- }).select("id").single();
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
- console.log(chalk3.dim(`[${log.timestamp}]`), log.message);
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") {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  execute
3
- } from "./chunk-3WEJ3DXM.js";
3
+ } from "./chunk-XRWIU3RD.js";
4
4
  import "./chunk-M4LGRTLC.js";
5
5
  import "./chunk-UZT2L27M.js";
6
6
  import "./chunk-5IQKC2TD.js";
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  cloudLogs,
8
8
  cloudStatus,
9
9
  execute
10
- } from "./chunk-3WEJ3DXM.js";
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 chalk12 from "chalk";
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-IMGKMUBS.js");
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 join11 } from "path";
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.yellow("Cloud indexing is not yet implemented. Use --local for Ollama-based indexing."));
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
- console.log(chalk11.blue("Updating semantic index..."));
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("\nSemantic Index Status\n"));
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(join11(cwd, ".archon/index.db"))}`);
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(chalk12.blue("\nArchonDev is not initialized in this folder.\n"));
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(chalk12.red('Invalid provider. Use "github" or "google"'));
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 with local Ollama");
3957
- indexCmd.command("init").description("Initialize semantic index").option("--local", "Use local Ollama for embeddings (default)").option("--cloud", "Use cloud embeddings (coming soon)").action(indexInit);
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.0.0",
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",