@contextgraph/agent 0.4.17 → 0.4.19
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/dist/index.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync2 } from "fs";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
-
import { dirname as dirname2, join as
|
|
7
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/callback-server.ts
|
|
10
10
|
import http from "http";
|
|
11
|
-
import { URL } from "url";
|
|
11
|
+
import { URL as URL2 } from "url";
|
|
12
12
|
var MIN_PORT = 3e3;
|
|
13
13
|
var MAX_PORT = 3100;
|
|
14
14
|
async function findFreePort() {
|
|
@@ -38,7 +38,7 @@ async function startCallbackServer() {
|
|
|
38
38
|
let callbackResolve = null;
|
|
39
39
|
const connections = /* @__PURE__ */ new Set();
|
|
40
40
|
const server = http.createServer((req, res) => {
|
|
41
|
-
const url = new
|
|
41
|
+
const url = new URL2(req.url || "/", `http://localhost:${port}`);
|
|
42
42
|
if (url.pathname === "/callback") {
|
|
43
43
|
const token = url.searchParams.get("token");
|
|
44
44
|
const userId = url.searchParams.get("userId");
|
|
@@ -859,6 +859,32 @@ async function executeClaude(options) {
|
|
|
859
859
|
// Allow MCP tools to execute automatically
|
|
860
860
|
maxTurns: 100,
|
|
861
861
|
// Reasonable limit
|
|
862
|
+
// Enable skills: load from project .claude/skills/ and allow Skill tool
|
|
863
|
+
settingSources: ["project"],
|
|
864
|
+
allowedTools: [
|
|
865
|
+
// Core file operations
|
|
866
|
+
"Read",
|
|
867
|
+
"Write",
|
|
868
|
+
"Edit",
|
|
869
|
+
"MultiEdit",
|
|
870
|
+
// Search tools
|
|
871
|
+
"Grep",
|
|
872
|
+
"Glob",
|
|
873
|
+
// Execution
|
|
874
|
+
"Bash",
|
|
875
|
+
// Skills
|
|
876
|
+
"Skill",
|
|
877
|
+
// Agent orchestration
|
|
878
|
+
"Task",
|
|
879
|
+
// User interaction
|
|
880
|
+
"TodoWrite",
|
|
881
|
+
"AskUserQuestion",
|
|
882
|
+
// Web access
|
|
883
|
+
"WebFetch",
|
|
884
|
+
"WebSearch",
|
|
885
|
+
// MCP tools (pattern match for contextgraph MCP server)
|
|
886
|
+
"mcp__*"
|
|
887
|
+
],
|
|
862
888
|
env: {
|
|
863
889
|
...process.env,
|
|
864
890
|
// Pass auth token through environment for MCP server
|
|
@@ -1310,208 +1336,219 @@ var HeartbeatManager = class {
|
|
|
1310
1336
|
}
|
|
1311
1337
|
};
|
|
1312
1338
|
|
|
1313
|
-
// src/
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1339
|
+
// src/workspace-setup.ts
|
|
1340
|
+
import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1341
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
1342
|
+
import { join as join4 } from "path";
|
|
1343
|
+
|
|
1344
|
+
// src/workspace-prep.ts
|
|
1345
|
+
import { spawn as spawn2 } from "child_process";
|
|
1346
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
1347
|
+
import { tmpdir } from "os";
|
|
1348
|
+
import { join as join3 } from "path";
|
|
1349
|
+
|
|
1350
|
+
// src/skill-injection.ts
|
|
1351
|
+
import { mkdir as mkdir2, writeFile } from "fs/promises";
|
|
1352
|
+
import { join as join2 } from "path";
|
|
1353
|
+
async function injectSkills(workspacePath, skills) {
|
|
1354
|
+
if (skills.length === 0) {
|
|
1355
|
+
console.log("\u{1F4DA} No skills to inject");
|
|
1356
|
+
return;
|
|
1320
1357
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1358
|
+
console.log(`\u{1F4DA} Injecting ${skills.length} skill(s) into workspace...`);
|
|
1359
|
+
for (const skill of skills) {
|
|
1360
|
+
try {
|
|
1361
|
+
const skillDir = join2(workspacePath, ".claude", "skills", skill.name);
|
|
1362
|
+
await mkdir2(skillDir, { recursive: true });
|
|
1363
|
+
const triggerSection = skill.trigger ? `trigger: |
|
|
1364
|
+
${skill.trigger.split("\n").map((line) => ` ${line}`).join("\n")}
|
|
1365
|
+
` : "";
|
|
1366
|
+
const skillContent = `---
|
|
1367
|
+
name: ${skill.name}
|
|
1368
|
+
description: ${skill.description}
|
|
1369
|
+
${triggerSection}---
|
|
1370
|
+
|
|
1371
|
+
${skill.content}
|
|
1372
|
+
`;
|
|
1373
|
+
const skillFilePath = join2(skillDir, "SKILL.md");
|
|
1374
|
+
await writeFile(skillFilePath, skillContent, "utf-8");
|
|
1375
|
+
console.log(` \u2705 Injected skill: ${skill.name}`);
|
|
1376
|
+
} catch (error) {
|
|
1377
|
+
console.error(` \u274C Failed to inject skill "${skill.name}":`, error);
|
|
1378
|
+
throw error;
|
|
1379
|
+
}
|
|
1324
1380
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1381
|
+
console.log(`\u2705 Skills injected successfully`);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// src/skills-library-fetch.ts
|
|
1385
|
+
var API_BASE_URL = "https://www.contextgraph.dev";
|
|
1386
|
+
async function fetchSkillsLibrary(options) {
|
|
1387
|
+
const { authToken, runId } = options;
|
|
1329
1388
|
try {
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
}
|
|
1334
|
-
console.log(`[Log Streaming] Run created: ${runId}`);
|
|
1335
|
-
console.log(`Fetching preparation instructions for action ${actionId}...
|
|
1336
|
-
`);
|
|
1389
|
+
const url = new URL(`${API_BASE_URL}/api/skills/library`);
|
|
1390
|
+
if (runId) {
|
|
1391
|
+
url.searchParams.set("runId", runId);
|
|
1392
|
+
}
|
|
1337
1393
|
const response = await fetchWithRetry(
|
|
1338
|
-
|
|
1394
|
+
url.toString(),
|
|
1339
1395
|
{
|
|
1340
|
-
method: "POST",
|
|
1341
1396
|
headers: {
|
|
1342
|
-
"
|
|
1397
|
+
"x-authorization": `Bearer ${authToken}`,
|
|
1343
1398
|
"Content-Type": "application/json"
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
maxRetries: 2,
|
|
1403
|
+
baseDelayMs: 500
|
|
1346
1404
|
}
|
|
1347
1405
|
);
|
|
1348
1406
|
if (!response.ok) {
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
model: options?.model || "claude-opus-4-5-20251101",
|
|
1366
|
-
onLogEvent: (event) => {
|
|
1367
|
-
logBuffer.push(event);
|
|
1368
|
-
}
|
|
1407
|
+
console.warn(`\u26A0\uFE0F Skills library API returned ${response.status}: ${response.statusText}`);
|
|
1408
|
+
return [];
|
|
1409
|
+
}
|
|
1410
|
+
const data = await response.json();
|
|
1411
|
+
if (!data.success || !data.data?.skills) {
|
|
1412
|
+
console.warn("\u26A0\uFE0F Skills library API returned unexpected format");
|
|
1413
|
+
return [];
|
|
1414
|
+
}
|
|
1415
|
+
const skills = data.data.skills.map((skill) => {
|
|
1416
|
+
const name = skill.filename.replace(/\.md$/, "");
|
|
1417
|
+
return {
|
|
1418
|
+
name,
|
|
1419
|
+
description: skill.description,
|
|
1420
|
+
trigger: skill.trigger,
|
|
1421
|
+
content: skill.content
|
|
1422
|
+
};
|
|
1369
1423
|
});
|
|
1370
|
-
|
|
1371
|
-
await logTransport.finishRun("success", {
|
|
1372
|
-
exitCode: claudeResult.exitCode,
|
|
1373
|
-
cost: claudeResult.cost,
|
|
1374
|
-
usage: claudeResult.usage
|
|
1375
|
-
});
|
|
1376
|
-
console.log("\n\u2705 Preparation complete");
|
|
1377
|
-
} else {
|
|
1378
|
-
await logTransport.finishRun("error", {
|
|
1379
|
-
exitCode: claudeResult.exitCode,
|
|
1380
|
-
errorMessage: `Claude preparation failed with exit code ${claudeResult.exitCode}`
|
|
1381
|
-
});
|
|
1382
|
-
console.error(`
|
|
1383
|
-
\u274C Claude preparation failed with exit code ${claudeResult.exitCode}`);
|
|
1384
|
-
process.exit(1);
|
|
1385
|
-
}
|
|
1424
|
+
return skills;
|
|
1386
1425
|
} catch (error) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
await logTransport.finishRun("error", {
|
|
1390
|
-
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1391
|
-
});
|
|
1392
|
-
} catch (stateError) {
|
|
1393
|
-
console.error("[Log Streaming] Failed to update run state:", stateError);
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
throw error;
|
|
1397
|
-
} finally {
|
|
1398
|
-
if (heartbeatManager) {
|
|
1399
|
-
heartbeatManager.stop();
|
|
1400
|
-
console.log("[Log Streaming] Heartbeat stopped");
|
|
1401
|
-
}
|
|
1402
|
-
if (logBuffer) {
|
|
1403
|
-
await logBuffer.stop();
|
|
1404
|
-
console.log("[Log Streaming] Logs flushed");
|
|
1405
|
-
}
|
|
1426
|
+
console.warn("\u26A0\uFE0F Failed to fetch skills library:", error instanceof Error ? error.message : error);
|
|
1427
|
+
return [];
|
|
1406
1428
|
}
|
|
1407
1429
|
}
|
|
1408
1430
|
|
|
1409
|
-
// src/
|
|
1431
|
+
// src/workspace-prep.ts
|
|
1410
1432
|
var API_BASE_URL2 = "https://www.contextgraph.dev";
|
|
1411
|
-
async function
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1433
|
+
async function fetchGitHubCredentials(authToken) {
|
|
1434
|
+
const response = await fetchWithRetry(`${API_BASE_URL2}/api/cli/credentials`, {
|
|
1435
|
+
headers: {
|
|
1436
|
+
"x-authorization": `Bearer ${authToken}`,
|
|
1437
|
+
"Content-Type": "application/json"
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
if (response.status === 401) {
|
|
1441
|
+
throw new Error("Authentication failed. Please re-authenticate.");
|
|
1416
1442
|
}
|
|
1417
|
-
if (
|
|
1418
|
-
|
|
1419
|
-
|
|
1443
|
+
if (response.status === 404) {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
"GitHub not connected. Please connect your GitHub account at https://contextgraph.dev/settings."
|
|
1446
|
+
);
|
|
1420
1447
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1448
|
+
if (!response.ok) {
|
|
1449
|
+
const errorText = await response.text();
|
|
1450
|
+
throw new Error(`Failed to fetch GitHub credentials: ${response.statusText}
|
|
1451
|
+
${errorText}`);
|
|
1452
|
+
}
|
|
1453
|
+
return response.json();
|
|
1454
|
+
}
|
|
1455
|
+
function runGitCommand(args, cwd) {
|
|
1456
|
+
return new Promise((resolve, reject) => {
|
|
1457
|
+
const proc = spawn2("git", args, { cwd });
|
|
1458
|
+
let stdout = "";
|
|
1459
|
+
let stderr = "";
|
|
1460
|
+
proc.stdout.on("data", (data) => {
|
|
1461
|
+
stdout += data.toString();
|
|
1429
1462
|
});
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
"Authorization": `Bearer ${credentials.clerkToken}`,
|
|
1439
|
-
"Content-Type": "application/json"
|
|
1440
|
-
},
|
|
1441
|
-
body: JSON.stringify({ actionId, runId })
|
|
1463
|
+
proc.stderr.on("data", (data) => {
|
|
1464
|
+
stderr += data.toString();
|
|
1465
|
+
});
|
|
1466
|
+
proc.on("close", (code) => {
|
|
1467
|
+
if (code === 0) {
|
|
1468
|
+
resolve({ stdout, stderr });
|
|
1469
|
+
} else {
|
|
1470
|
+
reject(new Error(`git ${args[0]} failed (exit ${code}): ${stderr || stdout}`));
|
|
1442
1471
|
}
|
|
1443
|
-
);
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1472
|
+
});
|
|
1473
|
+
proc.on("error", (err) => {
|
|
1474
|
+
reject(new Error(`Failed to spawn git: ${err.message}`));
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
function buildAuthenticatedUrl(repoUrl, token) {
|
|
1479
|
+
if (repoUrl.startsWith("https://github.com/")) {
|
|
1480
|
+
return repoUrl.replace("https://github.com/", `https://${token}@github.com/`);
|
|
1481
|
+
}
|
|
1482
|
+
if (repoUrl.startsWith("https://github.com")) {
|
|
1483
|
+
return repoUrl.replace("https://github.com", `https://${token}@github.com`);
|
|
1484
|
+
}
|
|
1485
|
+
return repoUrl;
|
|
1486
|
+
}
|
|
1487
|
+
async function prepareWorkspace(repoUrl, options) {
|
|
1488
|
+
const { branch, authToken, runId, skipSkills } = options;
|
|
1489
|
+
const credentials = await fetchGitHubCredentials(authToken);
|
|
1490
|
+
const workspacePath = await mkdtemp(join3(tmpdir(), "cg-workspace-"));
|
|
1491
|
+
const cleanup = async () => {
|
|
1492
|
+
try {
|
|
1493
|
+
await rm(workspacePath, { recursive: true, force: true });
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
1448
1496
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
console.log(
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1497
|
+
};
|
|
1498
|
+
try {
|
|
1499
|
+
const cloneUrl = buildAuthenticatedUrl(repoUrl, credentials.githubToken);
|
|
1500
|
+
console.log(`\u{1F4C2} Cloning ${repoUrl}`);
|
|
1501
|
+
console.log(` \u2192 ${workspacePath}`);
|
|
1502
|
+
await runGitCommand(["clone", cloneUrl, workspacePath]);
|
|
1503
|
+
console.log(`\u2705 Repository cloned`);
|
|
1504
|
+
if (credentials.githubUsername) {
|
|
1505
|
+
await runGitCommand(["config", "user.name", credentials.githubUsername], workspacePath);
|
|
1506
|
+
}
|
|
1507
|
+
if (credentials.githubEmail) {
|
|
1508
|
+
await runGitCommand(["config", "user.email", credentials.githubEmail], workspacePath);
|
|
1509
|
+
}
|
|
1510
|
+
if (branch) {
|
|
1511
|
+
const { stdout } = await runGitCommand(
|
|
1512
|
+
["ls-remote", "--heads", "origin", branch],
|
|
1513
|
+
workspacePath
|
|
1514
|
+
);
|
|
1515
|
+
const branchExists = stdout.trim().length > 0;
|
|
1516
|
+
if (branchExists) {
|
|
1517
|
+
console.log(`\u{1F33F} Checking out branch: ${branch}`);
|
|
1518
|
+
await runGitCommand(["checkout", branch], workspacePath);
|
|
1519
|
+
} else {
|
|
1520
|
+
console.log(`\u{1F331} Creating new branch: ${branch}`);
|
|
1521
|
+
await runGitCommand(["checkout", "-b", branch], workspacePath);
|
|
1464
1522
|
}
|
|
1465
|
-
});
|
|
1466
|
-
if (claudeResult.exitCode === 0) {
|
|
1467
|
-
await logTransport.finishRun("success", {
|
|
1468
|
-
exitCode: claudeResult.exitCode,
|
|
1469
|
-
cost: claudeResult.cost,
|
|
1470
|
-
usage: claudeResult.usage
|
|
1471
|
-
});
|
|
1472
|
-
console.log("\n\u2705 Execution complete");
|
|
1473
|
-
} else {
|
|
1474
|
-
await logTransport.finishRun("error", {
|
|
1475
|
-
exitCode: claudeResult.exitCode,
|
|
1476
|
-
errorMessage: `Claude execution failed with exit code ${claudeResult.exitCode}`
|
|
1477
|
-
});
|
|
1478
|
-
throw new Error(`Claude execution failed with exit code ${claudeResult.exitCode}`);
|
|
1479
1523
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1524
|
+
const { stdout: commitHash } = await runGitCommand(["rev-parse", "HEAD"], workspacePath);
|
|
1525
|
+
const startingCommit = commitHash.trim();
|
|
1526
|
+
console.log("");
|
|
1527
|
+
if (skipSkills) {
|
|
1528
|
+
console.log("\u{1F4DA} Skipping skill injection (--no-skills flag)");
|
|
1529
|
+
} else {
|
|
1482
1530
|
try {
|
|
1483
|
-
await
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1531
|
+
const librarySkills = await fetchSkillsLibrary({ authToken, runId });
|
|
1532
|
+
if (librarySkills.length > 0) {
|
|
1533
|
+
await injectSkills(workspacePath, librarySkills);
|
|
1534
|
+
} else {
|
|
1535
|
+
console.log("\u{1F4DA} No skills to inject (empty library)");
|
|
1536
|
+
}
|
|
1537
|
+
} catch (skillError) {
|
|
1538
|
+
console.warn("\u26A0\uFE0F Skill injection failed (agent will continue):", skillError);
|
|
1488
1539
|
}
|
|
1489
1540
|
}
|
|
1541
|
+
return { path: workspacePath, startingCommit, cleanup };
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
await cleanup();
|
|
1490
1544
|
throw error;
|
|
1491
|
-
} finally {
|
|
1492
|
-
if (heartbeatManager) {
|
|
1493
|
-
heartbeatManager.stop();
|
|
1494
|
-
console.log("[Log Streaming] Heartbeat stopped");
|
|
1495
|
-
}
|
|
1496
|
-
if (logBuffer) {
|
|
1497
|
-
await logBuffer.stop();
|
|
1498
|
-
console.log("[Log Streaming] Logs flushed");
|
|
1499
|
-
}
|
|
1500
1545
|
}
|
|
1501
1546
|
}
|
|
1502
1547
|
|
|
1503
|
-
// src/workflows/agent.ts
|
|
1504
|
-
import { randomUUID } from "crypto";
|
|
1505
|
-
import { readFileSync } from "fs";
|
|
1506
|
-
import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1507
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
1508
|
-
import { fileURLToPath } from "url";
|
|
1509
|
-
import { dirname, join as join3 } from "path";
|
|
1510
|
-
|
|
1511
1548
|
// package.json
|
|
1512
1549
|
var package_default = {
|
|
1513
1550
|
name: "@contextgraph/agent",
|
|
1514
|
-
version: "0.4.
|
|
1551
|
+
version: "0.4.19",
|
|
1515
1552
|
description: "Autonomous agent for contextgraph action execution",
|
|
1516
1553
|
type: "module",
|
|
1517
1554
|
bin: {
|
|
@@ -1619,164 +1656,336 @@ var ApiClient = class {
|
|
|
1619
1656
|
async claimNextAction(workerId) {
|
|
1620
1657
|
const token = await this.getAuthToken();
|
|
1621
1658
|
const response = await fetchWithRetry(
|
|
1622
|
-
`${this.baseUrl}/api/worker/next?token=${encodeURIComponent(token)}`,
|
|
1659
|
+
`${this.baseUrl}/api/worker/next?token=${encodeURIComponent(token)}`,
|
|
1660
|
+
{
|
|
1661
|
+
method: "POST",
|
|
1662
|
+
headers: {
|
|
1663
|
+
"x-authorization": `Bearer ${token}`,
|
|
1664
|
+
"Content-Type": "application/json"
|
|
1665
|
+
},
|
|
1666
|
+
body: JSON.stringify({
|
|
1667
|
+
worker_id: workerId,
|
|
1668
|
+
agent_version: package_default.version
|
|
1669
|
+
})
|
|
1670
|
+
}
|
|
1671
|
+
);
|
|
1672
|
+
if (!response.ok) {
|
|
1673
|
+
const errorText = await response.text();
|
|
1674
|
+
throw new Error(`API error ${response.status}: ${errorText}`);
|
|
1675
|
+
}
|
|
1676
|
+
const result = await response.json();
|
|
1677
|
+
if (!result.success) {
|
|
1678
|
+
throw new Error(result.error || "API returned unsuccessful response");
|
|
1679
|
+
}
|
|
1680
|
+
return result.data;
|
|
1681
|
+
}
|
|
1682
|
+
async releaseClaim(params) {
|
|
1683
|
+
const token = await this.getAuthToken();
|
|
1684
|
+
const response = await fetchWithRetry(
|
|
1685
|
+
`${this.baseUrl}/api/worker/release?token=${encodeURIComponent(token)}`,
|
|
1686
|
+
{
|
|
1687
|
+
method: "POST",
|
|
1688
|
+
headers: {
|
|
1689
|
+
"x-authorization": `Bearer ${token}`,
|
|
1690
|
+
"Content-Type": "application/json"
|
|
1691
|
+
},
|
|
1692
|
+
body: JSON.stringify(params)
|
|
1693
|
+
}
|
|
1694
|
+
);
|
|
1695
|
+
if (!response.ok) {
|
|
1696
|
+
const errorText = await response.text();
|
|
1697
|
+
throw new Error(`API error ${response.status}: ${errorText}`);
|
|
1698
|
+
}
|
|
1699
|
+
const result = await response.json();
|
|
1700
|
+
if (!result.success) {
|
|
1701
|
+
throw new Error(result.error || "API returned unsuccessful response");
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
|
|
1706
|
+
// src/workspace-setup.ts
|
|
1707
|
+
var API_BASE_URL3 = "https://www.contextgraph.dev";
|
|
1708
|
+
async function setupWorkspaceForAction(actionId, options) {
|
|
1709
|
+
const { authToken, phase, startingCommit: startingCommitOverride, skipSkills } = options;
|
|
1710
|
+
let actionDetail = options.actionDetail;
|
|
1711
|
+
if (!actionDetail) {
|
|
1712
|
+
const apiClient2 = new ApiClient();
|
|
1713
|
+
console.log(`Fetching action details for ${actionId}...`);
|
|
1714
|
+
actionDetail = await apiClient2.getActionDetail(actionId);
|
|
1715
|
+
}
|
|
1716
|
+
const logTransport = new LogTransportService(API_BASE_URL3, authToken);
|
|
1717
|
+
console.log(`[Log Streaming] Creating run for ${phase} phase...`);
|
|
1718
|
+
const runId = await logTransport.createRun(actionId, phase, {
|
|
1719
|
+
startingCommit: startingCommitOverride
|
|
1720
|
+
});
|
|
1721
|
+
console.log(`[Log Streaming] Run created: ${runId}`);
|
|
1722
|
+
const repoUrl = actionDetail.resolved_repository_url || actionDetail.repository_url;
|
|
1723
|
+
const branch = actionDetail.resolved_branch || actionDetail.branch;
|
|
1724
|
+
let workspacePath;
|
|
1725
|
+
let cleanup;
|
|
1726
|
+
let startingCommit = startingCommitOverride;
|
|
1727
|
+
if (repoUrl) {
|
|
1728
|
+
const workspace = await prepareWorkspace(repoUrl, {
|
|
1729
|
+
branch: branch || void 0,
|
|
1730
|
+
authToken,
|
|
1731
|
+
runId,
|
|
1732
|
+
skipSkills
|
|
1733
|
+
});
|
|
1734
|
+
workspacePath = workspace.path;
|
|
1735
|
+
cleanup = workspace.cleanup;
|
|
1736
|
+
startingCommit = workspace.startingCommit;
|
|
1737
|
+
} else {
|
|
1738
|
+
console.log(`\u{1F4C2} No repository configured - creating blank workspace`);
|
|
1739
|
+
workspacePath = await mkdtemp2(join4(tmpdir2(), "cg-workspace-"));
|
|
1740
|
+
console.log(` \u2192 ${workspacePath}`);
|
|
1741
|
+
cleanup = async () => {
|
|
1742
|
+
try {
|
|
1743
|
+
await rm2(workspacePath, { recursive: true, force: true });
|
|
1744
|
+
} catch (error) {
|
|
1745
|
+
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
return {
|
|
1750
|
+
workspacePath,
|
|
1751
|
+
cleanup,
|
|
1752
|
+
startingCommit,
|
|
1753
|
+
runId,
|
|
1754
|
+
logTransport
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// src/workflows/prepare.ts
|
|
1759
|
+
var API_BASE_URL4 = "https://www.contextgraph.dev";
|
|
1760
|
+
async function runPrepare(actionId, options) {
|
|
1761
|
+
const credentials = await loadCredentials();
|
|
1762
|
+
if (!credentials) {
|
|
1763
|
+
console.error("\u274C Not authenticated. Run authentication first.");
|
|
1764
|
+
process.exit(1);
|
|
1765
|
+
}
|
|
1766
|
+
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
1767
|
+
console.error("\u274C Token expired. Re-authenticate to continue.");
|
|
1768
|
+
process.exit(1);
|
|
1769
|
+
}
|
|
1770
|
+
let runId = options?.runId;
|
|
1771
|
+
let heartbeatManager;
|
|
1772
|
+
let logBuffer;
|
|
1773
|
+
let workspacePath;
|
|
1774
|
+
let cleanup;
|
|
1775
|
+
let logTransport;
|
|
1776
|
+
try {
|
|
1777
|
+
if (!runId) {
|
|
1778
|
+
const setup = await setupWorkspaceForAction(actionId, {
|
|
1779
|
+
authToken: credentials.clerkToken,
|
|
1780
|
+
phase: "prepare",
|
|
1781
|
+
startingCommit: options?.startingCommit,
|
|
1782
|
+
skipSkills: options?.skipSkills
|
|
1783
|
+
});
|
|
1784
|
+
workspacePath = setup.workspacePath;
|
|
1785
|
+
cleanup = setup.cleanup;
|
|
1786
|
+
runId = setup.runId;
|
|
1787
|
+
logTransport = setup.logTransport;
|
|
1788
|
+
} else {
|
|
1789
|
+
console.log(`[Log Streaming] Using pre-created run: ${runId}`);
|
|
1790
|
+
workspacePath = options?.cwd || process.cwd();
|
|
1791
|
+
logTransport = new LogTransportService(API_BASE_URL4, credentials.clerkToken, runId);
|
|
1792
|
+
}
|
|
1793
|
+
console.log(`Fetching preparation instructions for action ${actionId}...
|
|
1794
|
+
`);
|
|
1795
|
+
const response = await fetchWithRetry(
|
|
1796
|
+
`${API_BASE_URL4}/api/prompts/prepare`,
|
|
1623
1797
|
{
|
|
1624
1798
|
method: "POST",
|
|
1625
1799
|
headers: {
|
|
1626
|
-
"
|
|
1800
|
+
"Authorization": `Bearer ${credentials.clerkToken}`,
|
|
1627
1801
|
"Content-Type": "application/json"
|
|
1628
1802
|
},
|
|
1629
|
-
body: JSON.stringify({
|
|
1630
|
-
worker_id: workerId,
|
|
1631
|
-
agent_version: package_default.version
|
|
1632
|
-
})
|
|
1803
|
+
body: JSON.stringify({ actionId, runId })
|
|
1633
1804
|
}
|
|
1634
1805
|
);
|
|
1635
1806
|
if (!response.ok) {
|
|
1636
1807
|
const errorText = await response.text();
|
|
1637
|
-
throw new Error(`
|
|
1808
|
+
throw new Error(`Failed to fetch prepare prompt: ${response.statusText}
|
|
1809
|
+
${errorText}`);
|
|
1638
1810
|
}
|
|
1639
|
-
const
|
|
1640
|
-
|
|
1641
|
-
|
|
1811
|
+
const { prompt } = await response.json();
|
|
1812
|
+
await logTransport.updateRunState("preparing");
|
|
1813
|
+
heartbeatManager = new HeartbeatManager(API_BASE_URL4, credentials.clerkToken, runId);
|
|
1814
|
+
heartbeatManager.start();
|
|
1815
|
+
console.log("[Log Streaming] Heartbeat started");
|
|
1816
|
+
logBuffer = new LogBuffer(logTransport);
|
|
1817
|
+
logBuffer.start();
|
|
1818
|
+
console.log("Spawning Claude for preparation...\n");
|
|
1819
|
+
const claudeResult = await executeClaude({
|
|
1820
|
+
prompt,
|
|
1821
|
+
cwd: workspacePath,
|
|
1822
|
+
authToken: credentials.clerkToken,
|
|
1823
|
+
model: options?.model || "claude-opus-4-5-20251101",
|
|
1824
|
+
onLogEvent: (event) => {
|
|
1825
|
+
logBuffer.push(event);
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
if (claudeResult.exitCode === 0) {
|
|
1829
|
+
await logTransport.finishRun("success", {
|
|
1830
|
+
exitCode: claudeResult.exitCode,
|
|
1831
|
+
cost: claudeResult.cost,
|
|
1832
|
+
usage: claudeResult.usage
|
|
1833
|
+
});
|
|
1834
|
+
console.log("\n\u2705 Preparation complete");
|
|
1835
|
+
} else {
|
|
1836
|
+
await logTransport.finishRun("error", {
|
|
1837
|
+
exitCode: claudeResult.exitCode,
|
|
1838
|
+
errorMessage: `Claude preparation failed with exit code ${claudeResult.exitCode}`
|
|
1839
|
+
});
|
|
1840
|
+
console.error(`
|
|
1841
|
+
\u274C Claude preparation failed with exit code ${claudeResult.exitCode}`);
|
|
1842
|
+
process.exit(1);
|
|
1843
|
+
}
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
if (runId) {
|
|
1846
|
+
try {
|
|
1847
|
+
await logTransport.finishRun("error", {
|
|
1848
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1849
|
+
});
|
|
1850
|
+
} catch (stateError) {
|
|
1851
|
+
console.error("[Log Streaming] Failed to update run state:", stateError);
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
throw error;
|
|
1855
|
+
} finally {
|
|
1856
|
+
if (heartbeatManager) {
|
|
1857
|
+
heartbeatManager.stop();
|
|
1858
|
+
console.log("[Log Streaming] Heartbeat stopped");
|
|
1859
|
+
}
|
|
1860
|
+
if (logBuffer) {
|
|
1861
|
+
await logBuffer.stop();
|
|
1862
|
+
console.log("[Log Streaming] Logs flushed");
|
|
1863
|
+
}
|
|
1864
|
+
if (cleanup) {
|
|
1865
|
+
await cleanup();
|
|
1642
1866
|
}
|
|
1643
|
-
return result.data;
|
|
1644
1867
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// src/workflows/execute.ts
|
|
1871
|
+
var API_BASE_URL5 = "https://www.contextgraph.dev";
|
|
1872
|
+
async function runExecute(actionId, options) {
|
|
1873
|
+
const credentials = await loadCredentials();
|
|
1874
|
+
if (!credentials) {
|
|
1875
|
+
console.error("\u274C Not authenticated. Run authentication first.");
|
|
1876
|
+
process.exit(1);
|
|
1877
|
+
}
|
|
1878
|
+
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
1879
|
+
console.error("\u274C Token expired. Re-authenticate to continue.");
|
|
1880
|
+
process.exit(1);
|
|
1881
|
+
}
|
|
1882
|
+
let runId = options?.runId;
|
|
1883
|
+
let heartbeatManager;
|
|
1884
|
+
let logBuffer;
|
|
1885
|
+
let workspacePath;
|
|
1886
|
+
let cleanup;
|
|
1887
|
+
let logTransport;
|
|
1888
|
+
try {
|
|
1889
|
+
if (!runId) {
|
|
1890
|
+
const setup = await setupWorkspaceForAction(actionId, {
|
|
1891
|
+
authToken: credentials.clerkToken,
|
|
1892
|
+
phase: "execute",
|
|
1893
|
+
startingCommit: options?.startingCommit,
|
|
1894
|
+
skipSkills: options?.skipSkills
|
|
1895
|
+
});
|
|
1896
|
+
workspacePath = setup.workspacePath;
|
|
1897
|
+
cleanup = setup.cleanup;
|
|
1898
|
+
runId = setup.runId;
|
|
1899
|
+
logTransport = setup.logTransport;
|
|
1900
|
+
} else {
|
|
1901
|
+
console.log(`[Log Streaming] Using pre-created run: ${runId}`);
|
|
1902
|
+
workspacePath = options?.cwd || process.cwd();
|
|
1903
|
+
logTransport = new LogTransportService(API_BASE_URL5, credentials.clerkToken, runId);
|
|
1904
|
+
}
|
|
1905
|
+
console.log(`Fetching execution instructions for action ${actionId}...
|
|
1906
|
+
`);
|
|
1647
1907
|
const response = await fetchWithRetry(
|
|
1648
|
-
`${
|
|
1908
|
+
`${API_BASE_URL5}/api/prompts/execute`,
|
|
1649
1909
|
{
|
|
1650
1910
|
method: "POST",
|
|
1651
1911
|
headers: {
|
|
1652
|
-
"
|
|
1912
|
+
"Authorization": `Bearer ${credentials.clerkToken}`,
|
|
1653
1913
|
"Content-Type": "application/json"
|
|
1654
1914
|
},
|
|
1655
|
-
body: JSON.stringify(
|
|
1915
|
+
body: JSON.stringify({ actionId, runId })
|
|
1656
1916
|
}
|
|
1657
1917
|
);
|
|
1658
1918
|
if (!response.ok) {
|
|
1659
1919
|
const errorText = await response.text();
|
|
1660
|
-
throw new Error(`
|
|
1661
|
-
}
|
|
1662
|
-
const result = await response.json();
|
|
1663
|
-
if (!result.success) {
|
|
1664
|
-
throw new Error(result.error || "API returned unsuccessful response");
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
};
|
|
1668
|
-
|
|
1669
|
-
// src/workspace-prep.ts
|
|
1670
|
-
import { spawn as spawn2 } from "child_process";
|
|
1671
|
-
import { mkdtemp, rm } from "fs/promises";
|
|
1672
|
-
import { tmpdir } from "os";
|
|
1673
|
-
import { join as join2 } from "path";
|
|
1674
|
-
var API_BASE_URL3 = "https://www.contextgraph.dev";
|
|
1675
|
-
async function fetchGitHubCredentials(authToken) {
|
|
1676
|
-
const response = await fetchWithRetry(`${API_BASE_URL3}/api/cli/credentials`, {
|
|
1677
|
-
headers: {
|
|
1678
|
-
"x-authorization": `Bearer ${authToken}`,
|
|
1679
|
-
"Content-Type": "application/json"
|
|
1680
|
-
}
|
|
1681
|
-
});
|
|
1682
|
-
if (response.status === 401) {
|
|
1683
|
-
throw new Error("Authentication failed. Please re-authenticate.");
|
|
1684
|
-
}
|
|
1685
|
-
if (response.status === 404) {
|
|
1686
|
-
throw new Error(
|
|
1687
|
-
"GitHub not connected. Please connect your GitHub account at https://contextgraph.dev/settings."
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
if (!response.ok) {
|
|
1691
|
-
const errorText = await response.text();
|
|
1692
|
-
throw new Error(`Failed to fetch GitHub credentials: ${response.statusText}
|
|
1920
|
+
throw new Error(`Failed to fetch execute prompt: ${response.statusText}
|
|
1693
1921
|
${errorText}`);
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
resolve({ stdout, stderr });
|
|
1711
|
-
} else {
|
|
1712
|
-
reject(new Error(`git ${args[0]} failed (exit ${code}): ${stderr || stdout}`));
|
|
1922
|
+
}
|
|
1923
|
+
const { prompt } = await response.json();
|
|
1924
|
+
await logTransport.updateRunState("executing");
|
|
1925
|
+
heartbeatManager = new HeartbeatManager(API_BASE_URL5, credentials.clerkToken, runId);
|
|
1926
|
+
heartbeatManager.start();
|
|
1927
|
+
console.log("[Log Streaming] Heartbeat started");
|
|
1928
|
+
logBuffer = new LogBuffer(logTransport);
|
|
1929
|
+
logBuffer.start();
|
|
1930
|
+
console.log("Spawning Claude for execution...\n");
|
|
1931
|
+
const claudeResult = await executeClaude({
|
|
1932
|
+
prompt,
|
|
1933
|
+
cwd: workspacePath,
|
|
1934
|
+
authToken: credentials.clerkToken,
|
|
1935
|
+
...options?.model ? { model: options.model } : {},
|
|
1936
|
+
onLogEvent: (event) => {
|
|
1937
|
+
logBuffer.push(event);
|
|
1713
1938
|
}
|
|
1714
1939
|
});
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
}
|
|
1729
|
-
async function prepareWorkspace(repoUrl, options) {
|
|
1730
|
-
const { branch, authToken } = options;
|
|
1731
|
-
const credentials = await fetchGitHubCredentials(authToken);
|
|
1732
|
-
const workspacePath = await mkdtemp(join2(tmpdir(), "cg-workspace-"));
|
|
1733
|
-
const cleanup = async () => {
|
|
1734
|
-
try {
|
|
1735
|
-
await rm(workspacePath, { recursive: true, force: true });
|
|
1736
|
-
} catch (error) {
|
|
1737
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
1738
|
-
}
|
|
1739
|
-
};
|
|
1740
|
-
try {
|
|
1741
|
-
const cloneUrl = buildAuthenticatedUrl(repoUrl, credentials.githubToken);
|
|
1742
|
-
console.log(`\u{1F4C2} Cloning ${repoUrl}`);
|
|
1743
|
-
console.log(` \u2192 ${workspacePath}`);
|
|
1744
|
-
await runGitCommand(["clone", cloneUrl, workspacePath]);
|
|
1745
|
-
console.log(`\u2705 Repository cloned`);
|
|
1746
|
-
if (credentials.githubUsername) {
|
|
1747
|
-
await runGitCommand(["config", "user.name", credentials.githubUsername], workspacePath);
|
|
1748
|
-
}
|
|
1749
|
-
if (credentials.githubEmail) {
|
|
1750
|
-
await runGitCommand(["config", "user.email", credentials.githubEmail], workspacePath);
|
|
1940
|
+
if (claudeResult.exitCode === 0) {
|
|
1941
|
+
await logTransport.finishRun("success", {
|
|
1942
|
+
exitCode: claudeResult.exitCode,
|
|
1943
|
+
cost: claudeResult.cost,
|
|
1944
|
+
usage: claudeResult.usage
|
|
1945
|
+
});
|
|
1946
|
+
console.log("\n\u2705 Execution complete");
|
|
1947
|
+
} else {
|
|
1948
|
+
await logTransport.finishRun("error", {
|
|
1949
|
+
exitCode: claudeResult.exitCode,
|
|
1950
|
+
errorMessage: `Claude execution failed with exit code ${claudeResult.exitCode}`
|
|
1951
|
+
});
|
|
1952
|
+
throw new Error(`Claude execution failed with exit code ${claudeResult.exitCode}`);
|
|
1751
1953
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
console.
|
|
1760
|
-
await runGitCommand(["checkout", branch], workspacePath);
|
|
1761
|
-
} else {
|
|
1762
|
-
console.log(`\u{1F331} Creating new branch: ${branch}`);
|
|
1763
|
-
await runGitCommand(["checkout", "-b", branch], workspacePath);
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
if (runId) {
|
|
1956
|
+
try {
|
|
1957
|
+
await logTransport.finishRun("error", {
|
|
1958
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1959
|
+
});
|
|
1960
|
+
} catch (stateError) {
|
|
1961
|
+
console.error("[Log Streaming] Failed to update run state:", stateError);
|
|
1764
1962
|
}
|
|
1765
1963
|
}
|
|
1766
|
-
const { stdout: commitHash } = await runGitCommand(["rev-parse", "HEAD"], workspacePath);
|
|
1767
|
-
const startingCommit = commitHash.trim();
|
|
1768
|
-
return { path: workspacePath, startingCommit, cleanup };
|
|
1769
|
-
} catch (error) {
|
|
1770
|
-
await cleanup();
|
|
1771
1964
|
throw error;
|
|
1965
|
+
} finally {
|
|
1966
|
+
if (heartbeatManager) {
|
|
1967
|
+
heartbeatManager.stop();
|
|
1968
|
+
console.log("[Log Streaming] Heartbeat stopped");
|
|
1969
|
+
}
|
|
1970
|
+
if (logBuffer) {
|
|
1971
|
+
await logBuffer.stop();
|
|
1972
|
+
console.log("[Log Streaming] Logs flushed");
|
|
1973
|
+
}
|
|
1974
|
+
if (cleanup) {
|
|
1975
|
+
await cleanup();
|
|
1976
|
+
}
|
|
1772
1977
|
}
|
|
1773
1978
|
}
|
|
1774
1979
|
|
|
1775
1980
|
// src/workflows/agent.ts
|
|
1981
|
+
import { randomUUID } from "crypto";
|
|
1982
|
+
import { readFileSync } from "fs";
|
|
1983
|
+
import { fileURLToPath } from "url";
|
|
1984
|
+
import { dirname, join as join5 } from "path";
|
|
1776
1985
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
1777
1986
|
var __dirname2 = dirname(__filename2);
|
|
1778
1987
|
var packageJson = JSON.parse(
|
|
1779
|
-
readFileSync(
|
|
1988
|
+
readFileSync(join5(__dirname2, "../package.json"), "utf-8")
|
|
1780
1989
|
);
|
|
1781
1990
|
var INITIAL_POLL_INTERVAL = parseInt(process.env.WORKER_INITIAL_POLL_INTERVAL || "2000", 10);
|
|
1782
1991
|
var MAX_POLL_INTERVAL = parseInt(process.env.WORKER_MAX_POLL_INTERVAL || "5000", 10);
|
|
@@ -1946,35 +2155,24 @@ async function runLocalAgent(options) {
|
|
|
1946
2155
|
continue;
|
|
1947
2156
|
}
|
|
1948
2157
|
console.log(`Working: ${actionDetail.title}`);
|
|
1949
|
-
const repoUrl = actionDetail.resolved_repository_url || actionDetail.repository_url;
|
|
1950
|
-
const branch = actionDetail.resolved_branch || actionDetail.branch;
|
|
1951
2158
|
let workspacePath;
|
|
1952
2159
|
let cleanup;
|
|
1953
2160
|
let startingCommit;
|
|
1954
|
-
|
|
2161
|
+
let runId;
|
|
1955
2162
|
try {
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
console.log(` \u2192 ${workspacePath}`);
|
|
1968
|
-
cleanup = async () => {
|
|
1969
|
-
try {
|
|
1970
|
-
await rm2(workspacePath, { recursive: true, force: true });
|
|
1971
|
-
} catch (error) {
|
|
1972
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
1973
|
-
}
|
|
1974
|
-
};
|
|
1975
|
-
}
|
|
2163
|
+
const setup = await setupWorkspaceForAction(actionDetail.id, {
|
|
2164
|
+
authToken: credentials.clerkToken,
|
|
2165
|
+
phase,
|
|
2166
|
+
actionDetail,
|
|
2167
|
+
// Pass pre-fetched action detail to avoid redundant API call
|
|
2168
|
+
skipSkills: options?.skipSkills
|
|
2169
|
+
});
|
|
2170
|
+
workspacePath = setup.workspacePath;
|
|
2171
|
+
cleanup = setup.cleanup;
|
|
2172
|
+
startingCommit = setup.startingCommit;
|
|
2173
|
+
runId = setup.runId;
|
|
1976
2174
|
if (phase === "prepare") {
|
|
1977
|
-
await runPrepare(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
|
|
2175
|
+
await runPrepare(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel, runId });
|
|
1978
2176
|
stats.prepared++;
|
|
1979
2177
|
if (currentClaim && apiClient) {
|
|
1980
2178
|
try {
|
|
@@ -1991,7 +2189,7 @@ async function runLocalAgent(options) {
|
|
|
1991
2189
|
continue;
|
|
1992
2190
|
}
|
|
1993
2191
|
try {
|
|
1994
|
-
await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
|
|
2192
|
+
await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel, runId });
|
|
1995
2193
|
stats.executed++;
|
|
1996
2194
|
console.log(`Completed: ${actionDetail.title}`);
|
|
1997
2195
|
} catch (executeError) {
|
|
@@ -2040,14 +2238,15 @@ async function runLocalAgent(options) {
|
|
|
2040
2238
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
2041
2239
|
var __dirname3 = dirname2(__filename3);
|
|
2042
2240
|
var packageJson2 = JSON.parse(
|
|
2043
|
-
readFileSync2(
|
|
2241
|
+
readFileSync2(join6(__dirname3, "../package.json"), "utf-8")
|
|
2044
2242
|
);
|
|
2045
2243
|
var program = new Command();
|
|
2046
2244
|
program.name("contextgraph-agent").description("Autonomous agent for contextgraph action execution").version(packageJson2.version);
|
|
2047
|
-
program.command("run").description("Run continuous worker loop (claims and executes actions until Ctrl+C)").option("--force-haiku", "Force all workflows to use claude-haiku-4-5 instead of default models").action(async (options) => {
|
|
2245
|
+
program.command("run").description("Run continuous worker loop (claims and executes actions until Ctrl+C)").option("--force-haiku", "Force all workflows to use claude-haiku-4-5 instead of default models").option("--skip-skills", "Skip skill injection (for testing)").action(async (options) => {
|
|
2048
2246
|
try {
|
|
2049
2247
|
await runLocalAgent({
|
|
2050
|
-
forceModel: options.forceHaiku ? "claude-haiku-4-5-20251001" : void 0
|
|
2248
|
+
forceModel: options.forceHaiku ? "claude-haiku-4-5-20251001" : void 0,
|
|
2249
|
+
skipSkills: options.skipSkills
|
|
2051
2250
|
});
|
|
2052
2251
|
} catch (error) {
|
|
2053
2252
|
if (error instanceof Error) {
|
|
@@ -2070,17 +2269,17 @@ program.command("auth").description("Authenticate with contextgraph.dev").action
|
|
|
2070
2269
|
process.exit(1);
|
|
2071
2270
|
}
|
|
2072
2271
|
});
|
|
2073
|
-
program.command("prepare").argument("<action-id>", "Action ID to prepare").description("Prepare a single action").action(async (actionId) => {
|
|
2272
|
+
program.command("prepare").argument("<action-id>", "Action ID to prepare").description("Prepare a single action").option("--skip-skills", "Skip skill injection (for testing)").action(async (actionId, options) => {
|
|
2074
2273
|
try {
|
|
2075
|
-
await runPrepare(actionId);
|
|
2274
|
+
await runPrepare(actionId, { skipSkills: options.skipSkills });
|
|
2076
2275
|
} catch (error) {
|
|
2077
2276
|
console.error("Error preparing action:", error instanceof Error ? error.message : error);
|
|
2078
2277
|
process.exit(1);
|
|
2079
2278
|
}
|
|
2080
2279
|
});
|
|
2081
|
-
program.command("execute").argument("<action-id>", "Action ID to execute").description("Execute a single action").action(async (actionId) => {
|
|
2280
|
+
program.command("execute").argument("<action-id>", "Action ID to execute").description("Execute a single action").option("--skip-skills", "Skip skill injection (for testing)").action(async (actionId, options) => {
|
|
2082
2281
|
try {
|
|
2083
|
-
await runExecute(actionId);
|
|
2282
|
+
await runExecute(actionId, { skipSkills: options.skipSkills });
|
|
2084
2283
|
} catch (error) {
|
|
2085
2284
|
console.error("Error executing action:", error instanceof Error ? error.message : error);
|
|
2086
2285
|
process.exit(1);
|