@contextgraph/agent 0.4.18 → 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 +478 -410
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
${errorText}`);
|
|
1407
|
+
console.warn(`\u26A0\uFE0F Skills library API returned ${response.status}: ${response.statusText}`);
|
|
1408
|
+
return [];
|
|
1352
1409
|
}
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
console.log("[Log Streaming] Heartbeat started");
|
|
1358
|
-
logBuffer = new LogBuffer(logTransport);
|
|
1359
|
-
logBuffer.start();
|
|
1360
|
-
console.log("Spawning Claude for preparation...\n");
|
|
1361
|
-
const claudeResult = await executeClaude({
|
|
1362
|
-
prompt,
|
|
1363
|
-
cwd: options?.cwd || process.cwd(),
|
|
1364
|
-
authToken: credentials.clerkToken,
|
|
1365
|
-
model: options?.model || "claude-opus-4-5-20251101",
|
|
1366
|
-
onLogEvent: (event) => {
|
|
1367
|
-
logBuffer.push(event);
|
|
1368
|
-
}
|
|
1369
|
-
});
|
|
1370
|
-
if (claudeResult.exitCode === 0) {
|
|
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);
|
|
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 [];
|
|
1385
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
|
+
};
|
|
1423
|
+
});
|
|
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 join4 } 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: {
|
|
@@ -1666,248 +1703,289 @@ var ApiClient = class {
|
|
|
1666
1703
|
}
|
|
1667
1704
|
};
|
|
1668
1705
|
|
|
1669
|
-
// src/workspace-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
}
|
|
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
|
+
};
|
|
1702
1748
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
content: `# ContextGraph Skill Validation Marker
|
|
1711
|
-
|
|
1712
|
-
## Purpose
|
|
1713
|
-
This skill validates that the ContextGraph skill injection mechanism is working correctly.
|
|
1714
|
-
|
|
1715
|
-
## Instructions
|
|
1716
|
-
|
|
1717
|
-
**CRITICAL**: When you start execution, IMMEDIATELY include this exact marker in your first response:
|
|
1718
|
-
|
|
1719
|
-
\`\`\`
|
|
1720
|
-
\u{1F52C} SKILL_INJECTION_VALIDATED: contextgraph-validation-marker loaded
|
|
1721
|
-
\`\`\`
|
|
1722
|
-
|
|
1723
|
-
This marker proves that:
|
|
1724
|
-
1. The skill file was successfully written to the workspace
|
|
1725
|
-
2. Claude Code loaded the skill at startup
|
|
1726
|
-
3. The skill content is available during agent execution
|
|
1727
|
-
|
|
1728
|
-
## Usage
|
|
1729
|
-
|
|
1730
|
-
This skill should be invoked automatically at the start of EVERY agent run to validate the injection mechanism. The marker should appear in the agent's output logs.
|
|
1731
|
-
|
|
1732
|
-
## Context
|
|
1733
|
-
|
|
1734
|
-
This is a prototype validation skill for the ContextGraph learning and skills system. It's designed to have a high trigger frequency to reliably demonstrate skill loading mechanics.
|
|
1735
|
-
`
|
|
1736
|
-
};
|
|
1737
|
-
function getValidationSkills() {
|
|
1738
|
-
return [VALIDATION_SKILL];
|
|
1749
|
+
return {
|
|
1750
|
+
workspacePath,
|
|
1751
|
+
cleanup,
|
|
1752
|
+
startingCommit,
|
|
1753
|
+
runId,
|
|
1754
|
+
logTransport
|
|
1755
|
+
};
|
|
1739
1756
|
}
|
|
1740
1757
|
|
|
1741
|
-
// src/
|
|
1742
|
-
var
|
|
1743
|
-
async function
|
|
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;
|
|
1744
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
|
+
`);
|
|
1745
1795
|
const response = await fetchWithRetry(
|
|
1746
|
-
`${
|
|
1796
|
+
`${API_BASE_URL4}/api/prompts/prepare`,
|
|
1747
1797
|
{
|
|
1798
|
+
method: "POST",
|
|
1748
1799
|
headers: {
|
|
1749
|
-
"
|
|
1800
|
+
"Authorization": `Bearer ${credentials.clerkToken}`,
|
|
1750
1801
|
"Content-Type": "application/json"
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
{
|
|
1754
|
-
maxRetries: 2,
|
|
1755
|
-
baseDelayMs: 500
|
|
1802
|
+
},
|
|
1803
|
+
body: JSON.stringify({ actionId, runId })
|
|
1756
1804
|
}
|
|
1757
1805
|
);
|
|
1758
1806
|
if (!response.ok) {
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
const data = await response.json();
|
|
1763
|
-
if (!data.success || !data.data?.skills) {
|
|
1764
|
-
console.warn("\u26A0\uFE0F Skills library API returned unexpected format");
|
|
1765
|
-
return [];
|
|
1807
|
+
const errorText = await response.text();
|
|
1808
|
+
throw new Error(`Failed to fetch prepare prompt: ${response.statusText}
|
|
1809
|
+
${errorText}`);
|
|
1766
1810
|
}
|
|
1767
|
-
const
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
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);
|
|
1776
1826
|
}
|
|
1777
|
-
const contentWithoutFrontmatter = skill.content.replace(/^---\n[\s\S]*?\n---\n/, "");
|
|
1778
|
-
return {
|
|
1779
|
-
name,
|
|
1780
|
-
description,
|
|
1781
|
-
content: contentWithoutFrontmatter
|
|
1782
|
-
};
|
|
1783
1827
|
});
|
|
1784
|
-
|
|
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
|
+
}
|
|
1785
1844
|
} catch (error) {
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
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();
|
|
1798
1866
|
}
|
|
1799
|
-
});
|
|
1800
|
-
if (response.status === 401) {
|
|
1801
|
-
throw new Error("Authentication failed. Please re-authenticate.");
|
|
1802
|
-
}
|
|
1803
|
-
if (response.status === 404) {
|
|
1804
|
-
throw new Error(
|
|
1805
|
-
"GitHub not connected. Please connect your GitHub account at https://contextgraph.dev/settings."
|
|
1806
|
-
);
|
|
1807
|
-
}
|
|
1808
|
-
if (!response.ok) {
|
|
1809
|
-
const errorText = await response.text();
|
|
1810
|
-
throw new Error(`Failed to fetch GitHub credentials: ${response.statusText}
|
|
1811
|
-
${errorText}`);
|
|
1812
1867
|
}
|
|
1813
|
-
return response.json();
|
|
1814
|
-
}
|
|
1815
|
-
function runGitCommand(args, cwd) {
|
|
1816
|
-
return new Promise((resolve, reject) => {
|
|
1817
|
-
const proc = spawn2("git", args, { cwd });
|
|
1818
|
-
let stdout = "";
|
|
1819
|
-
let stderr = "";
|
|
1820
|
-
proc.stdout.on("data", (data) => {
|
|
1821
|
-
stdout += data.toString();
|
|
1822
|
-
});
|
|
1823
|
-
proc.stderr.on("data", (data) => {
|
|
1824
|
-
stderr += data.toString();
|
|
1825
|
-
});
|
|
1826
|
-
proc.on("close", (code) => {
|
|
1827
|
-
if (code === 0) {
|
|
1828
|
-
resolve({ stdout, stderr });
|
|
1829
|
-
} else {
|
|
1830
|
-
reject(new Error(`git ${args[0]} failed (exit ${code}): ${stderr || stdout}`));
|
|
1831
|
-
}
|
|
1832
|
-
});
|
|
1833
|
-
proc.on("error", (err) => {
|
|
1834
|
-
reject(new Error(`Failed to spawn git: ${err.message}`));
|
|
1835
|
-
});
|
|
1836
|
-
});
|
|
1837
1868
|
}
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
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);
|
|
1841
1877
|
}
|
|
1842
|
-
if (
|
|
1843
|
-
|
|
1878
|
+
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
1879
|
+
console.error("\u274C Token expired. Re-authenticate to continue.");
|
|
1880
|
+
process.exit(1);
|
|
1844
1881
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
const cleanup = async () => {
|
|
1852
|
-
try {
|
|
1853
|
-
await rm(workspacePath, { recursive: true, force: true });
|
|
1854
|
-
} catch (error) {
|
|
1855
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
1856
|
-
}
|
|
1857
|
-
};
|
|
1882
|
+
let runId = options?.runId;
|
|
1883
|
+
let heartbeatManager;
|
|
1884
|
+
let logBuffer;
|
|
1885
|
+
let workspacePath;
|
|
1886
|
+
let cleanup;
|
|
1887
|
+
let logTransport;
|
|
1858
1888
|
try {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
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);
|
|
1869
1904
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
await runGitCommand(["checkout", "-b", branch], workspacePath);
|
|
1905
|
+
console.log(`Fetching execution instructions for action ${actionId}...
|
|
1906
|
+
`);
|
|
1907
|
+
const response = await fetchWithRetry(
|
|
1908
|
+
`${API_BASE_URL5}/api/prompts/execute`,
|
|
1909
|
+
{
|
|
1910
|
+
method: "POST",
|
|
1911
|
+
headers: {
|
|
1912
|
+
"Authorization": `Bearer ${credentials.clerkToken}`,
|
|
1913
|
+
"Content-Type": "application/json"
|
|
1914
|
+
},
|
|
1915
|
+
body: JSON.stringify({ actionId, runId })
|
|
1882
1916
|
}
|
|
1917
|
+
);
|
|
1918
|
+
if (!response.ok) {
|
|
1919
|
+
const errorText = await response.text();
|
|
1920
|
+
throw new Error(`Failed to fetch execute prompt: ${response.statusText}
|
|
1921
|
+
${errorText}`);
|
|
1883
1922
|
}
|
|
1884
|
-
const {
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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);
|
|
1895
1938
|
}
|
|
1896
|
-
}
|
|
1897
|
-
|
|
1939
|
+
});
|
|
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}`);
|
|
1898
1953
|
}
|
|
1899
|
-
return { path: workspacePath, startingCommit, cleanup };
|
|
1900
1954
|
} catch (error) {
|
|
1901
|
-
|
|
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);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1902
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
|
+
}
|
|
1903
1977
|
}
|
|
1904
1978
|
}
|
|
1905
1979
|
|
|
1906
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";
|
|
1907
1985
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
1908
1986
|
var __dirname2 = dirname(__filename2);
|
|
1909
1987
|
var packageJson = JSON.parse(
|
|
1910
|
-
readFileSync(
|
|
1988
|
+
readFileSync(join5(__dirname2, "../package.json"), "utf-8")
|
|
1911
1989
|
);
|
|
1912
1990
|
var INITIAL_POLL_INTERVAL = parseInt(process.env.WORKER_INITIAL_POLL_INTERVAL || "2000", 10);
|
|
1913
1991
|
var MAX_POLL_INTERVAL = parseInt(process.env.WORKER_MAX_POLL_INTERVAL || "5000", 10);
|
|
@@ -2077,35 +2155,24 @@ async function runLocalAgent(options) {
|
|
|
2077
2155
|
continue;
|
|
2078
2156
|
}
|
|
2079
2157
|
console.log(`Working: ${actionDetail.title}`);
|
|
2080
|
-
const repoUrl = actionDetail.resolved_repository_url || actionDetail.repository_url;
|
|
2081
|
-
const branch = actionDetail.resolved_branch || actionDetail.branch;
|
|
2082
2158
|
let workspacePath;
|
|
2083
2159
|
let cleanup;
|
|
2084
2160
|
let startingCommit;
|
|
2085
|
-
|
|
2161
|
+
let runId;
|
|
2086
2162
|
try {
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
console.log(` \u2192 ${workspacePath}`);
|
|
2099
|
-
cleanup = async () => {
|
|
2100
|
-
try {
|
|
2101
|
-
await rm2(workspacePath, { recursive: true, force: true });
|
|
2102
|
-
} catch (error) {
|
|
2103
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}:`, error);
|
|
2104
|
-
}
|
|
2105
|
-
};
|
|
2106
|
-
}
|
|
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;
|
|
2107
2174
|
if (phase === "prepare") {
|
|
2108
|
-
await runPrepare(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
|
|
2175
|
+
await runPrepare(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel, runId });
|
|
2109
2176
|
stats.prepared++;
|
|
2110
2177
|
if (currentClaim && apiClient) {
|
|
2111
2178
|
try {
|
|
@@ -2122,7 +2189,7 @@ async function runLocalAgent(options) {
|
|
|
2122
2189
|
continue;
|
|
2123
2190
|
}
|
|
2124
2191
|
try {
|
|
2125
|
-
await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
|
|
2192
|
+
await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel, runId });
|
|
2126
2193
|
stats.executed++;
|
|
2127
2194
|
console.log(`Completed: ${actionDetail.title}`);
|
|
2128
2195
|
} catch (executeError) {
|
|
@@ -2171,14 +2238,15 @@ async function runLocalAgent(options) {
|
|
|
2171
2238
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
2172
2239
|
var __dirname3 = dirname2(__filename3);
|
|
2173
2240
|
var packageJson2 = JSON.parse(
|
|
2174
|
-
readFileSync2(
|
|
2241
|
+
readFileSync2(join6(__dirname3, "../package.json"), "utf-8")
|
|
2175
2242
|
);
|
|
2176
2243
|
var program = new Command();
|
|
2177
2244
|
program.name("contextgraph-agent").description("Autonomous agent for contextgraph action execution").version(packageJson2.version);
|
|
2178
|
-
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) => {
|
|
2179
2246
|
try {
|
|
2180
2247
|
await runLocalAgent({
|
|
2181
|
-
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
|
|
2182
2250
|
});
|
|
2183
2251
|
} catch (error) {
|
|
2184
2252
|
if (error instanceof Error) {
|
|
@@ -2201,17 +2269,17 @@ program.command("auth").description("Authenticate with contextgraph.dev").action
|
|
|
2201
2269
|
process.exit(1);
|
|
2202
2270
|
}
|
|
2203
2271
|
});
|
|
2204
|
-
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) => {
|
|
2205
2273
|
try {
|
|
2206
|
-
await runPrepare(actionId);
|
|
2274
|
+
await runPrepare(actionId, { skipSkills: options.skipSkills });
|
|
2207
2275
|
} catch (error) {
|
|
2208
2276
|
console.error("Error preparing action:", error instanceof Error ? error.message : error);
|
|
2209
2277
|
process.exit(1);
|
|
2210
2278
|
}
|
|
2211
2279
|
});
|
|
2212
|
-
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) => {
|
|
2213
2281
|
try {
|
|
2214
|
-
await runExecute(actionId);
|
|
2282
|
+
await runExecute(actionId, { skipSkills: options.skipSkills });
|
|
2215
2283
|
} catch (error) {
|
|
2216
2284
|
console.error("Error executing action:", error instanceof Error ? error.message : error);
|
|
2217
2285
|
process.exit(1);
|