archondev 0.1.0 → 1.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 +84 -51
- package/dist/auth-2QIFQZTL.js +12 -0
- package/dist/bug-DXLBBW3U.js +10 -0
- package/dist/{chunk-R6IMTNKV.js → chunk-2CFO5GVH.js} +0 -35
- package/dist/chunk-4NZPAEKS.js +878 -0
- package/dist/chunk-A7QU6JC6.js +119 -0
- package/dist/chunk-BL5TX2UW.js +91 -0
- package/dist/chunk-CAYCSBNX.js +202 -0
- package/dist/chunk-JBKFAD4M.js +650 -0
- package/dist/chunk-MOZHC2GX.js +351 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/chunk-QPYDPMUV.js +4648 -0
- package/dist/chunk-UDBFDXJI.js +696 -0
- package/dist/chunk-VKM3HAHW.js +832 -0
- package/dist/chunk-WCCBJSNI.js +62 -0
- package/dist/code-review-FSTYDHNG.js +16 -0
- package/dist/execute-55VINPV5.js +12 -0
- package/dist/index.js +1706 -7169
- package/dist/list-LKYYAGSN.js +12 -0
- package/dist/{parser-D6PBQUJH.js → parser-M4DI7A24.js} +2 -1
- package/dist/plan-MWUUJV3W.js +15 -0
- package/dist/review-3R6QXAXQ.js +27 -0
- package/package.json +21 -1
|
@@ -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,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
listLocalAtoms
|
|
3
|
+
} from "./chunk-4NZPAEKS.js";
|
|
4
|
+
|
|
5
|
+
// src/cli/list.ts
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
var STATUS_COLORS = {
|
|
8
|
+
DRAFT: chalk.gray,
|
|
9
|
+
READY: chalk.blue,
|
|
10
|
+
IN_PROGRESS: chalk.yellow,
|
|
11
|
+
TESTING: chalk.cyan,
|
|
12
|
+
DONE: chalk.green,
|
|
13
|
+
FAILED: chalk.red,
|
|
14
|
+
BLOCKED: chalk.magenta
|
|
15
|
+
};
|
|
16
|
+
async function list(options) {
|
|
17
|
+
const atoms = await listLocalAtoms();
|
|
18
|
+
if (atoms.length === 0) {
|
|
19
|
+
console.log(chalk.dim("No atoms found."));
|
|
20
|
+
console.log(chalk.dim("Create one with: archon plan <description>"));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
let filteredAtoms = atoms;
|
|
24
|
+
if (options.status) {
|
|
25
|
+
const statusFilter = options.status.toUpperCase();
|
|
26
|
+
filteredAtoms = atoms.filter((a) => a.status === statusFilter);
|
|
27
|
+
if (filteredAtoms.length === 0) {
|
|
28
|
+
console.log(chalk.dim(`No atoms with status "${options.status}" found.`));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const sortBy = options.sortBy ?? "priority";
|
|
33
|
+
filteredAtoms.sort((a, b) => {
|
|
34
|
+
switch (sortBy) {
|
|
35
|
+
case "priority":
|
|
36
|
+
return a.priority - b.priority;
|
|
37
|
+
case "created":
|
|
38
|
+
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
|
39
|
+
case "updated":
|
|
40
|
+
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
|
|
41
|
+
case "status":
|
|
42
|
+
return a.status.localeCompare(b.status);
|
|
43
|
+
default:
|
|
44
|
+
return a.priority - b.priority;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log(
|
|
49
|
+
chalk.bold(
|
|
50
|
+
padRight("ID", 12) + padRight("Title", 40) + padRight("Status", 14) + padRight("Priority", 10) + "Created"
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
console.log(chalk.dim("\u2500".repeat(90)));
|
|
54
|
+
for (const atom of filteredAtoms) {
|
|
55
|
+
const colorFn = STATUS_COLORS[atom.status] ?? chalk.white;
|
|
56
|
+
const createdDate = formatDate(atom.createdAt);
|
|
57
|
+
console.log(
|
|
58
|
+
padRight(atom.externalId, 12) + padRight(truncate(atom.title, 38), 40) + colorFn(padRight(atom.status, 14)) + padRight(String(atom.priority), 10) + chalk.dim(createdDate)
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.dim("\u2500".repeat(90)));
|
|
62
|
+
const statusCounts = countByStatus(filteredAtoms);
|
|
63
|
+
const summaryParts = [];
|
|
64
|
+
for (const [status, count] of Object.entries(statusCounts)) {
|
|
65
|
+
const colorFn = STATUS_COLORS[status] ?? chalk.white;
|
|
66
|
+
summaryParts.push(`${count} ${colorFn(status.toLowerCase())}`);
|
|
67
|
+
}
|
|
68
|
+
console.log(`Showing ${filteredAtoms.length} atom(s): ${summaryParts.join(", ")}`);
|
|
69
|
+
}
|
|
70
|
+
function padRight(str, width) {
|
|
71
|
+
return str.padEnd(width);
|
|
72
|
+
}
|
|
73
|
+
function truncate(str, maxLen) {
|
|
74
|
+
if (str.length <= maxLen) return str;
|
|
75
|
+
return str.substring(0, maxLen - 1) + "\u2026";
|
|
76
|
+
}
|
|
77
|
+
function formatDate(date) {
|
|
78
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
79
|
+
return d.toISOString().split("T")[0] ?? "";
|
|
80
|
+
}
|
|
81
|
+
function countByStatus(atoms) {
|
|
82
|
+
const counts = {};
|
|
83
|
+
for (const atom of atoms) {
|
|
84
|
+
counts[atom.status] = (counts[atom.status] ?? 0) + 1;
|
|
85
|
+
}
|
|
86
|
+
return counts;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
list
|
|
91
|
+
};
|
|
@@ -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
|
+
};
|