archondev 0.1.0 → 1.2.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
@@ -1,75 +1,108 @@
1
1
  # ArchonDev
2
2
 
3
- **Local-first AI-powered development governance**
3
+ **AI Development Governance — Stop Babysitting Your AI Agent**
4
4
 
5
- ArchonDev brings architectural governance to AI-assisted development. It ensures your AI coding assistants (Cursor, Copilot, Claude, etc.) respect codebase boundaries, follow coding standards, and never violate critical invariants.
5
+ ## Two Ways to Use ArchonDev
6
6
 
7
- ## Installation
7
+ ### Option 1: Full CLI (Recommended)
8
+ The complete AI development system. It manages your entire development process so you can focus on the big picture — not constantly correcting your AI.
8
9
 
9
10
  ```bash
10
- npm install -g archondev
11
- ```
11
+ # macOS/Linux:
12
+ npm install -g archondev && archon
12
13
 
13
- ## Quick Start
14
+ # Windows PowerShell:
15
+ npm install -g archondev; archon
16
+ ```
14
17
 
15
- ```bash
16
- # Initialize ArchonDev in your project
17
- archon init
18
+ **What you get:**
19
+ - AI that reads and respects your architecture before writing code
20
+ - Adversarial planning (Architect proposes, Sentinel critiques)
21
+ - Automatic quality gates before changes are applied
22
+ - Learning persistence — mistakes are remembered and avoided
23
+ - Bug reporting with root cause analysis
24
+ - AI-powered code review for any codebase
25
+ - Multi-provider key support with adversarial features
18
26
 
19
- # Create a work item (atom) with AI-powered planning
20
- archon plan "Add user authentication with JWT"
27
+ ### Option 2: Lite Package
28
+ Copy governance files into any project. Works with your existing AI tools (Cursor, Claude Code, Windsurf, VS Code + Copilot).
21
29
 
22
- # Execute the plan with quality gates
23
- archon execute ATOM-001
24
- ```
30
+ [Download Lite Packages →](https://archondev.io/download)
25
31
 
26
- ## How It Works
32
+ **What you get:**
33
+ - ARCHITECTURE.md template with best practices
34
+ - IDE-specific rule files (.cursorrules, CLAUDE.md, etc.)
35
+ - Progress tracking templates
36
+ - Works with any AI coding assistant
27
37
 
28
- 1. **Architecture as Input** - Define your system boundaries, invariants, and protected paths in `ARCHITECTURE.md`
29
- 2. **Adversarial Planning** - An Architect agent creates plans, a Sentinel agent critiques them
30
- 3. **Governed Execution** - Changes are validated against your architecture before being applied
31
- 4. **Learning Persistence** - Insights are captured in `progress.txt` for future sessions
38
+ ---
32
39
 
33
40
  ## Commands
34
41
 
35
42
  | Command | Description |
36
43
  |---------|-------------|
37
- | `archon init` | Initialize ArchonDev in your project |
38
- | `archon plan <description>` | Create a new atom with AI planning |
39
- | `archon execute <atom-id>` | Execute a planned atom |
40
- | `archon list` | List all atoms |
41
- | `archon show <atom-id>` | Show atom details |
42
- | `archon status` | Show current user and project status |
43
- | `archon login` | Authenticate with ArchonDev |
44
- | `archon keys add <provider>` | Add API key for BYOK tier |
45
- | `archon bug report <title>` | Report a bug with RCA |
46
- | `archon promote <atom-id>` | Promote atom to higher environment |
47
-
48
- ## Tiers
49
-
50
- | Tier | Price | Features |
51
- |------|-------|----------|
52
- | **Free** | $0 | 100 atoms/month, basic models (Haiku, Flash, GPT-4 Mini) |
53
- | **BYOK** | $0 | Unlimited atoms, use your own API keys |
54
- | **Pro** | $29/mo | Premium models (Opus, Sonnet), $50 credits included |
55
-
56
- ## Light Packages
57
-
58
- Don't need the full CLI? Download governance templates for your IDE:
59
-
60
- - [Cursor Package](https://archondev.io/downloads/cursor-package.zip)
61
- - [Windsurf Package](https://archondev.io/downloads/windsurf-package.zip)
62
- - [VS Code + Copilot Package](https://archondev.io/downloads/vscode-copilot-package.zip)
63
- - [Claude Code / Amp Package](https://archondev.io/downloads/claude-amp-package.zip)
44
+ | `archon` | Interactive mode just run and follow prompts |
45
+ | `archon init` | Initialize in your project |
46
+ | `archon plan <description>` | Create a work item with AI planning |
47
+ | `archon execute <atom-id>` | Execute with quality gates |
48
+ | `archon list` | List all work items |
49
+ | `archon show <atom-id>` | Show details |
50
+ | `archon watch` | Live TUI dashboard with status and credits |
51
+ | `archon bug report <title>` | Bug report with root cause analysis |
52
+ | `archon review init` | Initialize AI-powered code review |
53
+ | `archon review analyze` | Scan project and populate review tasks |
54
+ | `archon review run` | Run AI review on pending tasks |
55
+ | `archon credits` | View credit balance |
56
+ | `archon credits add` | Purchase credits |
57
+ | `archon credits budget` | Set monthly budget and alerts |
58
+ | `archon keys add <provider>` | Add your own API key — BYOK (Bring Your Own Key) |
59
+ | `archon keys list` | Show configured API keys by provider |
60
+ | `archon preferences` | View/set model preferences |
61
+ | `archon models` | List available AI models |
62
+
63
+ ## Pricing
64
+
65
+ | Tier | Cost | What You Get |
66
+ |------|------|--------------|
67
+ | **Free** | $0 | 10,000 atoms/month, fast models (Haiku, Flash, GPT-4 Mini) |
68
+ | **Credits** | Pay as you go | All models, 10% service fee, deposit any amount |
69
+ | **BYOK** (Bring Your Own Key) | $0 | Use your own API keys, unlimited usage |
70
+
71
+ No subscriptions. No commitments. Start free.
64
72
 
65
- ## Documentation
73
+ ## How It Works
66
74
 
67
- Full documentation at [https://archondev.io](https://archondev.io)
75
+ 1. **Define Your Rules** — Create ARCHITECTURE.md with boundaries and invariants
76
+ 2. **AI Reads Rules First** — Every session starts by understanding your architecture
77
+ 3. **Changes Are Validated** — Quality gates check code before it's applied
78
+ 4. **Learnings Persist** — Insights saved for future sessions
68
79
 
69
- ## License
80
+ ## Working with Existing Projects
81
+
82
+ Have a project created by another AI agent? ArchonDev can review it first, then govern future changes.
83
+
84
+ ```bash
85
+ # Step 1: Review existing code
86
+ cd your-existing-project
87
+ archon review init # Create local review database
88
+ archon review analyze # Scan project structure
89
+ archon review run --all # AI reviews all features
90
+ archon review export > review-report.md
91
+
92
+ # Step 2: Set up governance
93
+ archon init --analyze # Creates ARCHITECTURE.md
94
+
95
+ # Step 3: Fix issues with governed workflow
96
+ archon plan "fix critical issues from review"
97
+ archon execute <atom-id>
98
+ ```
99
+
100
+ The CLI detects existing projects and suggests this workflow automatically.
101
+
102
+ ## Documentation
70
103
 
71
- MIT
104
+ [archondev.io](https://archondev.io)
72
105
 
73
106
  ---
74
107
 
75
- *Powered by ArchonDev - AI Development Governance by Jumping Ahead Corp.*
108
+ *ArchonDev by Jumping Ahead Corp.*
@@ -0,0 +1,12 @@
1
+ import {
2
+ login,
3
+ logout,
4
+ status
5
+ } from "./chunk-CAYCSBNX.js";
6
+ import "./chunk-WCCBJSNI.js";
7
+ import "./chunk-QGM4M3NI.js";
8
+ export {
9
+ login,
10
+ logout,
11
+ status
12
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ bugReport
3
+ } from "./chunk-JBKFAD4M.js";
4
+ import "./chunk-MOZHC2GX.js";
5
+ import "./chunk-A7QU6JC6.js";
6
+ import "./chunk-WCCBJSNI.js";
7
+ import "./chunk-QGM4M3NI.js";
8
+ export {
9
+ bugReport
10
+ };
@@ -1,35 +1,3 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
- }) : x)(function(x) {
10
- if (typeof require !== "undefined") return require.apply(this, arguments);
11
- throw Error('Dynamic require of "' + x + '" is not supported');
12
- });
13
- var __commonJS = (cb, mod) => function __require2() {
14
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") {
18
- for (let key of __getOwnPropNames(from))
19
- if (!__hasOwnProp.call(to, key) && key !== except)
20
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
- }
22
- return to;
23
- };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
- // If the importer is in node compatibility mode or this is not an ESM
26
- // file that has been converted to a CommonJS file using a Babel-
27
- // compatible transform (i.e. "__esModule" has not been set), then set
28
- // "default" to the CommonJS "module.exports" for node compatibility.
29
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
- mod
31
- ));
32
-
33
1
  // src/core/parser/parser.ts
34
2
  import { readFile } from "fs/promises";
35
3
  import { existsSync } from "fs";
@@ -382,8 +350,5 @@ var ArchitectureParser = class {
382
350
  };
383
351
 
384
352
  export {
385
- __require,
386
- __commonJS,
387
- __toESM,
388
353
  ArchitectureParser
389
354
  };
@@ -0,0 +1,119 @@
1
+ // src/utils/index.ts
2
+ function sleep(ms) {
3
+ return new Promise((resolve) => setTimeout(resolve, ms));
4
+ }
5
+ function generateId(prefix) {
6
+ const timestamp = Date.now().toString(36);
7
+ const random = Math.random().toString(36).substring(2, 8);
8
+ const id = `${timestamp}-${random}`;
9
+ return prefix ? `${prefix}-${id}` : id;
10
+ }
11
+
12
+ // src/agents/clients/anthropic.ts
13
+ import Anthropic from "@anthropic-ai/sdk";
14
+ var MAX_RETRIES = 3;
15
+ var RETRY_DELAY_MS = 1e3;
16
+ var MODEL_PRICING = {
17
+ "claude-opus-4-5-20250514": { input: 15, output: 75 },
18
+ "claude-sonnet-4-20250514": { input: 3, output: 15 },
19
+ "claude-haiku-3-5-20241022": { input: 0.25, output: 1.25 },
20
+ // Fallback defaults
21
+ "claude-opus": { input: 15, output: 75 },
22
+ "claude-sonnet": { input: 3, output: 15 },
23
+ "claude-haiku": { input: 0.25, output: 1.25 }
24
+ };
25
+ var AnthropicClient = class {
26
+ client;
27
+ config;
28
+ constructor(config, apiKey) {
29
+ const key = apiKey ?? process.env["ANTHROPIC_API_KEY"];
30
+ if (!key) {
31
+ throw new Error("ANTHROPIC_API_KEY is required");
32
+ }
33
+ this.client = new Anthropic({ apiKey: key });
34
+ this.config = config;
35
+ }
36
+ /**
37
+ * Send a message and get a response
38
+ */
39
+ async chat(systemPrompt, userMessage, options) {
40
+ let lastError = null;
41
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
42
+ try {
43
+ const response = await this.client.messages.create({
44
+ model: this.config.model,
45
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 4096,
46
+ temperature: options?.temperature ?? this.config.temperature ?? 0.7,
47
+ system: systemPrompt,
48
+ messages: [{ role: "user", content: userMessage }]
49
+ });
50
+ const textContent = response.content.find((c) => c.type === "text");
51
+ const text = textContent && "text" in textContent ? textContent.text : "";
52
+ const usage = this.calculateUsage(
53
+ response.usage.input_tokens,
54
+ response.usage.output_tokens
55
+ );
56
+ return {
57
+ content: text,
58
+ usage,
59
+ model: response.model,
60
+ stopReason: response.stop_reason ?? "end_turn"
61
+ };
62
+ } catch (error) {
63
+ lastError = error instanceof Error ? error : new Error(String(error));
64
+ if (lastError.message.includes("401") || lastError.message.includes("authentication")) {
65
+ throw lastError;
66
+ }
67
+ if (attempt < MAX_RETRIES - 1) {
68
+ await sleep(RETRY_DELAY_MS * Math.pow(2, attempt));
69
+ }
70
+ }
71
+ }
72
+ throw lastError ?? new Error("Unknown error");
73
+ }
74
+ /**
75
+ * Calculate token usage and costs
76
+ */
77
+ calculateUsage(inputTokens, outputTokens) {
78
+ const pricing = this.getPricing();
79
+ const baseCost = inputTokens / 1e6 * pricing.input + outputTokens / 1e6 * pricing.output;
80
+ return {
81
+ inputTokens,
82
+ outputTokens,
83
+ totalTokens: inputTokens + outputTokens,
84
+ baseCost,
85
+ markedUpCost: baseCost * 1.1
86
+ // 10% markup for PRO tier
87
+ };
88
+ }
89
+ getPricing() {
90
+ const pricing = MODEL_PRICING[this.config.model];
91
+ if (pricing) return pricing;
92
+ for (const [key, value] of Object.entries(MODEL_PRICING)) {
93
+ if (this.config.model.includes(key.replace("claude-", ""))) {
94
+ return value;
95
+ }
96
+ }
97
+ return { input: 3, output: 15 };
98
+ }
99
+ };
100
+ function getDefaultModel(role) {
101
+ switch (role) {
102
+ case "architect":
103
+ return "claude-sonnet-4-20250514";
104
+ case "sentinel":
105
+ return "claude-sonnet-4-20250514";
106
+ case "executor":
107
+ return "claude-sonnet-4-20250514";
108
+ case "reviewer":
109
+ return "claude-sonnet-4-20250514";
110
+ default:
111
+ return "claude-sonnet-4-20250514";
112
+ }
113
+ }
114
+
115
+ export {
116
+ generateId,
117
+ AnthropicClient,
118
+ getDefaultModel
119
+ };
@@ -0,0 +1,202 @@
1
+ import {
2
+ clearConfig,
3
+ loadConfig,
4
+ saveConfig
5
+ } from "./chunk-WCCBJSNI.js";
6
+
7
+ // src/cli/auth.ts
8
+ import { createClient } from "@supabase/supabase-js";
9
+ import { createServer } from "http";
10
+ import { URL } from "url";
11
+ import open from "open";
12
+ import chalk from "chalk";
13
+ import ora from "ora";
14
+
15
+ // src/cli/constants.ts
16
+ var SUPABASE_URL = "https://yjdkcepktrbabmzhcmrt.supabase.co";
17
+ var SUPABASE_ANON_KEY = "sb_publishable_XSGLVPfLZx-HA2uL6xsGCQ_KjAx2TIa";
18
+ var API_URL = "https://archondev-api.fly.dev";
19
+
20
+ // src/cli/auth.ts
21
+ var CALLBACK_PORT = 54321;
22
+ var CALLBACK_URL = `http://localhost:${CALLBACK_PORT}/callback`;
23
+ function getAuthClient() {
24
+ return createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
25
+ auth: {
26
+ flowType: "pkce"
27
+ }
28
+ });
29
+ }
30
+ async function login(provider = "github") {
31
+ const spinner = ora("Starting authentication...").start();
32
+ try {
33
+ const supabase = getAuthClient();
34
+ const { data, error } = await supabase.auth.signInWithOAuth({
35
+ provider,
36
+ options: {
37
+ redirectTo: CALLBACK_URL,
38
+ skipBrowserRedirect: true
39
+ }
40
+ });
41
+ if (error) {
42
+ spinner.fail(chalk.red(`Authentication failed: ${error.message}`));
43
+ return;
44
+ }
45
+ if (!data.url) {
46
+ spinner.fail(chalk.red("Failed to get authentication URL"));
47
+ return;
48
+ }
49
+ const authResult = await startCallbackServer(data.url, spinner);
50
+ if (authResult) {
51
+ const { data: sessionData, error: sessionError } = await supabase.auth.exchangeCodeForSession(
52
+ authResult.code
53
+ );
54
+ if (sessionError) {
55
+ spinner.fail(chalk.red(`Failed to complete authentication: ${sessionError.message}`));
56
+ return;
57
+ }
58
+ if (sessionData.session) {
59
+ let userTier = "FREE";
60
+ try {
61
+ const { data: profileData } = await supabase.from("user_profiles").select("tier").eq("auth_id", sessionData.user.id).single();
62
+ const profile = profileData;
63
+ if (profile?.tier) {
64
+ userTier = profile.tier;
65
+ }
66
+ } catch {
67
+ }
68
+ await saveConfig({
69
+ accessToken: sessionData.session.access_token,
70
+ refreshToken: sessionData.session.refresh_token,
71
+ userId: sessionData.user.id,
72
+ email: sessionData.user.email ?? "",
73
+ tier: userTier,
74
+ expiresAt: new Date(sessionData.session.expires_at ?? Date.now() + 36e5).toISOString()
75
+ });
76
+ spinner.succeed(chalk.green(`Logged in as ${sessionData.user.email}`));
77
+ console.log(chalk.dim(`Tier: ${userTier}`));
78
+ }
79
+ }
80
+ } catch (error) {
81
+ spinner.fail(chalk.red(`Authentication error: ${error instanceof Error ? error.message : "Unknown error"}`));
82
+ }
83
+ }
84
+ function startCallbackServer(authUrl, spinner) {
85
+ return new Promise((resolve) => {
86
+ const server = createServer((req, res) => {
87
+ const url = new URL(req.url ?? "", `http://localhost:${CALLBACK_PORT}`);
88
+ if (url.pathname === "/callback") {
89
+ const code = url.searchParams.get("code");
90
+ if (code) {
91
+ res.writeHead(200, { "Content-Type": "text/html" });
92
+ res.end(`
93
+ <!DOCTYPE html>
94
+ <html>
95
+ <head><title>ArchonDev - Authentication Successful</title></head>
96
+ <body style="font-family: system-ui; text-align: center; padding: 50px;">
97
+ <h1>\u2705 Authentication Successful!</h1>
98
+ <p>You can close this window and return to the terminal.</p>
99
+ </body>
100
+ </html>
101
+ `);
102
+ server.close();
103
+ resolve({ code });
104
+ } else {
105
+ const error = url.searchParams.get("error_description") ?? "Authentication failed";
106
+ res.writeHead(400, { "Content-Type": "text/html" });
107
+ res.end(`
108
+ <!DOCTYPE html>
109
+ <html>
110
+ <head><title>ArchonDev - Authentication Failed</title></head>
111
+ <body style="font-family: system-ui; text-align: center; padding: 50px;">
112
+ <h1>\u274C Authentication Failed</h1>
113
+ <p>${error}</p>
114
+ </body>
115
+ </html>
116
+ `);
117
+ server.close();
118
+ resolve(null);
119
+ }
120
+ }
121
+ });
122
+ server.listen(CALLBACK_PORT, async () => {
123
+ spinner.text = "Opening browser for authentication...";
124
+ try {
125
+ await open(authUrl);
126
+ spinner.text = "Waiting for authentication in browser...";
127
+ } catch {
128
+ spinner.text = `Please open this URL in your browser:
129
+ ${authUrl}`;
130
+ }
131
+ });
132
+ setTimeout(() => {
133
+ server.close();
134
+ spinner.fail(chalk.red("Authentication timed out"));
135
+ resolve(null);
136
+ }, 5 * 60 * 1e3);
137
+ });
138
+ }
139
+ async function logout() {
140
+ const spinner = ora("Logging out...").start();
141
+ try {
142
+ const config = await loadConfig();
143
+ if (config.accessToken) {
144
+ try {
145
+ const supabase = getAuthClient();
146
+ await supabase.auth.signOut();
147
+ } catch {
148
+ }
149
+ }
150
+ await clearConfig();
151
+ spinner.succeed(chalk.green("Logged out successfully"));
152
+ } catch (error) {
153
+ spinner.fail(chalk.red(`Logout failed: ${error instanceof Error ? error.message : "Unknown error"}`));
154
+ }
155
+ }
156
+ async function status() {
157
+ const config = await loadConfig();
158
+ if (!config.accessToken) {
159
+ console.log(chalk.yellow("Not logged in"));
160
+ console.log(chalk.dim("Run `archon login` to authenticate"));
161
+ return;
162
+ }
163
+ if (config.expiresAt) {
164
+ const expiresAt = new Date(config.expiresAt);
165
+ if (expiresAt < /* @__PURE__ */ new Date()) {
166
+ console.log(chalk.yellow("Session expired"));
167
+ console.log(chalk.dim("Run `archon login` to re-authenticate"));
168
+ return;
169
+ }
170
+ }
171
+ console.log(chalk.green("\u2713 Authenticated"));
172
+ console.log();
173
+ console.log(` ${chalk.dim("Email:")} ${config.email}`);
174
+ console.log(` ${chalk.dim("Tier:")} ${formatTier(config.tier ?? "FREE")}`);
175
+ console.log(` ${chalk.dim("User:")} ${config.userId}`);
176
+ if (config.expiresAt) {
177
+ const expiresAt = new Date(config.expiresAt);
178
+ const hoursLeft = Math.round((expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60));
179
+ console.log(` ${chalk.dim("Session expires in:")} ${hoursLeft} hours`);
180
+ }
181
+ }
182
+ function formatTier(tier) {
183
+ switch (tier) {
184
+ case "FREE":
185
+ return chalk.gray("FREE");
186
+ case "CREDITS":
187
+ return chalk.green("CREDITS (Pay-as-you-go)");
188
+ case "BYOK":
189
+ return chalk.blue("BYOK (Bring Your Own Key)");
190
+ default:
191
+ return tier;
192
+ }
193
+ }
194
+
195
+ export {
196
+ SUPABASE_URL,
197
+ SUPABASE_ANON_KEY,
198
+ API_URL,
199
+ login,
200
+ logout,
201
+ status
202
+ };