@contextgraph/agent 0.4.24 → 0.4.26
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/.claude/settings.local.json +9 -1
- package/dist/index.js +309 -158
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
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 join5 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/callback-server.ts
|
|
10
10
|
import http from "http";
|
|
@@ -42,6 +42,7 @@ async function startCallbackServer() {
|
|
|
42
42
|
if (url.pathname === "/callback") {
|
|
43
43
|
const token = url.searchParams.get("token");
|
|
44
44
|
const userId = url.searchParams.get("userId");
|
|
45
|
+
const email = url.searchParams.get("email");
|
|
45
46
|
if (!token) {
|
|
46
47
|
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
47
48
|
res.end(getErrorPage("Missing token parameter"));
|
|
@@ -53,7 +54,7 @@ async function startCallbackServer() {
|
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
55
56
|
if (callbackResolve) {
|
|
56
|
-
callbackResolve({ token, userId });
|
|
57
|
+
callbackResolve({ token, userId, ...email ? { email } : {} });
|
|
57
58
|
}
|
|
58
59
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
59
60
|
res.end(getSuccessPage());
|
|
@@ -542,6 +543,7 @@ async function authenticateAgent(options = {}) {
|
|
|
542
543
|
await saveCredentials({
|
|
543
544
|
clerkToken: result.token,
|
|
544
545
|
userId: result.userId,
|
|
546
|
+
...result.email ? { email: result.email } : {},
|
|
545
547
|
expiresAt,
|
|
546
548
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
547
549
|
});
|
|
@@ -550,7 +552,8 @@ async function authenticateAgent(options = {}) {
|
|
|
550
552
|
success: true,
|
|
551
553
|
credentials: {
|
|
552
554
|
token: result.token,
|
|
553
|
-
userId: result.userId
|
|
555
|
+
userId: result.userId,
|
|
556
|
+
email: result.email
|
|
554
557
|
}
|
|
555
558
|
};
|
|
556
559
|
} catch (error) {
|
|
@@ -581,69 +584,6 @@ async function runAuth() {
|
|
|
581
584
|
// src/claude-sdk.ts
|
|
582
585
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
583
586
|
|
|
584
|
-
// src/plugin-setup.ts
|
|
585
|
-
import { spawn } from "child_process";
|
|
586
|
-
import { access, mkdir } from "fs/promises";
|
|
587
|
-
import { join } from "path";
|
|
588
|
-
import { homedir } from "os";
|
|
589
|
-
var PLUGIN_REPO = "https://github.com/contextgraph/claude-code-plugin.git";
|
|
590
|
-
var PLUGIN_DIR = join(homedir(), ".contextgraph", "claude-code-plugin");
|
|
591
|
-
var PLUGIN_PATH = join(PLUGIN_DIR, "plugins", "contextgraph");
|
|
592
|
-
async function ensurePlugin() {
|
|
593
|
-
try {
|
|
594
|
-
await access(PLUGIN_PATH);
|
|
595
|
-
console.log(`\u{1F4E6} Using plugin: ${PLUGIN_PATH}`);
|
|
596
|
-
return PLUGIN_PATH;
|
|
597
|
-
} catch {
|
|
598
|
-
}
|
|
599
|
-
let repoDirExists = false;
|
|
600
|
-
try {
|
|
601
|
-
await access(PLUGIN_DIR);
|
|
602
|
-
repoDirExists = true;
|
|
603
|
-
} catch {
|
|
604
|
-
}
|
|
605
|
-
if (repoDirExists) {
|
|
606
|
-
console.log("\u{1F4E6} Plugin directory exists but incomplete, pulling latest...");
|
|
607
|
-
await runCommand("git", ["pull"], PLUGIN_DIR);
|
|
608
|
-
try {
|
|
609
|
-
await access(PLUGIN_PATH);
|
|
610
|
-
console.log(`\u{1F4E6} Plugin ready: ${PLUGIN_PATH}`);
|
|
611
|
-
return PLUGIN_PATH;
|
|
612
|
-
} catch {
|
|
613
|
-
throw new Error(`Plugin not found at ${PLUGIN_PATH} even after git pull. Check repository structure.`);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
console.log(`\u{1F4E6} Cloning plugin from ${PLUGIN_REPO}...`);
|
|
617
|
-
const contextgraphDir = join(homedir(), ".contextgraph");
|
|
618
|
-
try {
|
|
619
|
-
await mkdir(contextgraphDir, { recursive: true });
|
|
620
|
-
} catch {
|
|
621
|
-
}
|
|
622
|
-
await runCommand("git", ["clone", PLUGIN_REPO, PLUGIN_DIR]);
|
|
623
|
-
try {
|
|
624
|
-
await access(PLUGIN_PATH);
|
|
625
|
-
console.log(`\u{1F4E6} Plugin installed: ${PLUGIN_PATH}`);
|
|
626
|
-
return PLUGIN_PATH;
|
|
627
|
-
} catch {
|
|
628
|
-
throw new Error(`Plugin clone succeeded but plugin path not found at ${PLUGIN_PATH}`);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
function runCommand(command, args, cwd) {
|
|
632
|
-
return new Promise((resolve, reject) => {
|
|
633
|
-
const proc = spawn(command, args, { cwd, stdio: "inherit" });
|
|
634
|
-
proc.on("close", (code) => {
|
|
635
|
-
if (code === 0) {
|
|
636
|
-
resolve();
|
|
637
|
-
} else {
|
|
638
|
-
reject(new Error(`${command} ${args[0]} failed with exit code ${code}`));
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
proc.on("error", (err) => {
|
|
642
|
-
reject(new Error(`Failed to spawn ${command}: ${err.message}`));
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
|
|
647
587
|
// src/sdk-event-transformer.ts
|
|
648
588
|
function transformSDKMessage(message) {
|
|
649
589
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -850,8 +790,6 @@ async function executeClaude(options) {
|
|
|
850
790
|
abortController.abort();
|
|
851
791
|
}, EXECUTION_TIMEOUT_MS);
|
|
852
792
|
try {
|
|
853
|
-
const pluginPath = await ensurePlugin();
|
|
854
|
-
console.log("[Agent SDK] Loading plugin from:", pluginPath);
|
|
855
793
|
console.log("[Agent SDK] Auth token available:", !!options.authToken);
|
|
856
794
|
console.log("[Agent SDK] Auth token prefix:", options.authToken?.substring(0, 20) + "...");
|
|
857
795
|
console.log("[Agent SDK] Anthropic API key available:", !!process.env.ANTHROPIC_API_KEY);
|
|
@@ -1356,25 +1294,25 @@ var HeartbeatManager = class {
|
|
|
1356
1294
|
// src/workspace-setup.ts
|
|
1357
1295
|
import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
1358
1296
|
import { tmpdir as tmpdir2 } from "os";
|
|
1359
|
-
import { join as
|
|
1297
|
+
import { join as join3 } from "path";
|
|
1360
1298
|
|
|
1361
1299
|
// src/workspace-prep.ts
|
|
1362
|
-
import { spawn
|
|
1300
|
+
import { spawn } from "child_process";
|
|
1363
1301
|
import { mkdtemp, rm, appendFile } from "fs/promises";
|
|
1364
1302
|
import { tmpdir } from "os";
|
|
1365
|
-
import { join as
|
|
1303
|
+
import { join as join2 } from "path";
|
|
1366
1304
|
|
|
1367
1305
|
// src/skill-injection.ts
|
|
1368
|
-
import { mkdir
|
|
1369
|
-
import { join
|
|
1306
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
1307
|
+
import { join } from "path";
|
|
1370
1308
|
async function injectSkills(workspacePath, skills) {
|
|
1371
1309
|
if (skills.length === 0) {
|
|
1372
1310
|
return;
|
|
1373
1311
|
}
|
|
1374
1312
|
for (const skill of skills) {
|
|
1375
1313
|
try {
|
|
1376
|
-
const skillDir =
|
|
1377
|
-
await
|
|
1314
|
+
const skillDir = join(workspacePath, ".claude", "skills", skill.name);
|
|
1315
|
+
await mkdir(skillDir, { recursive: true });
|
|
1378
1316
|
const triggerSection = skill.trigger ? `trigger: |
|
|
1379
1317
|
${skill.trigger.split("\n").map((line) => ` ${line}`).join("\n")}
|
|
1380
1318
|
` : "";
|
|
@@ -1385,7 +1323,7 @@ ${triggerSection}---
|
|
|
1385
1323
|
|
|
1386
1324
|
${skill.content}
|
|
1387
1325
|
`;
|
|
1388
|
-
const skillFilePath =
|
|
1326
|
+
const skillFilePath = join(skillDir, "SKILL.md");
|
|
1389
1327
|
await writeFile(skillFilePath, skillContent, "utf-8");
|
|
1390
1328
|
} catch (error) {
|
|
1391
1329
|
console.error(`\u274C Failed to inject skill "${skill.name}":`, error);
|
|
@@ -1442,6 +1380,7 @@ async function fetchSkillsLibrary(options) {
|
|
|
1442
1380
|
}
|
|
1443
1381
|
|
|
1444
1382
|
// src/workspace-prep.ts
|
|
1383
|
+
import chalk from "chalk";
|
|
1445
1384
|
var API_BASE_URL2 = "https://www.contextgraph.dev";
|
|
1446
1385
|
async function fetchGitHubCredentials(authToken) {
|
|
1447
1386
|
const response = await fetchWithRetry(`${API_BASE_URL2}/api/cli/credentials`, {
|
|
@@ -1467,7 +1406,7 @@ ${errorText}`);
|
|
|
1467
1406
|
}
|
|
1468
1407
|
function runGitCommand(args, cwd) {
|
|
1469
1408
|
return new Promise((resolve, reject) => {
|
|
1470
|
-
const proc =
|
|
1409
|
+
const proc = spawn("git", args, { cwd });
|
|
1471
1410
|
let stdout = "";
|
|
1472
1411
|
let stderr = "";
|
|
1473
1412
|
proc.stdout.on("data", (data) => {
|
|
@@ -1501,27 +1440,27 @@ function buildAuthenticatedUrl(repoUrl, token, username) {
|
|
|
1501
1440
|
async function prepareWorkspace(repoUrl, options) {
|
|
1502
1441
|
const { branch, authToken, runId, skipSkills } = options;
|
|
1503
1442
|
const credentials = await fetchGitHubCredentials(authToken);
|
|
1504
|
-
const workspacePath = await mkdtemp(
|
|
1443
|
+
const workspacePath = await mkdtemp(join2(tmpdir(), "cg-workspace-"));
|
|
1505
1444
|
const cleanup = async () => {
|
|
1506
1445
|
try {
|
|
1507
1446
|
await rm(workspacePath, { recursive: true, force: true });
|
|
1508
1447
|
} catch (error) {
|
|
1509
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}
|
|
1448
|
+
console.error(chalk.yellow(`Warning: Failed to cleanup workspace at ${workspacePath}:`), error);
|
|
1510
1449
|
}
|
|
1511
1450
|
};
|
|
1512
1451
|
try {
|
|
1513
1452
|
const cloneUrl = buildAuthenticatedUrl(repoUrl, credentials.githubToken, credentials.gitCredentialsUsername);
|
|
1514
|
-
console.log(
|
|
1515
|
-
console.log(`
|
|
1453
|
+
console.log(`Cloning ${chalk.cyan(repoUrl)}`);
|
|
1454
|
+
console.log(chalk.dim(` ${workspacePath}`));
|
|
1516
1455
|
await runGitCommand(["clone", cloneUrl, workspacePath]);
|
|
1517
|
-
console.log(
|
|
1456
|
+
console.log(chalk.green("Repository cloned"));
|
|
1518
1457
|
if (credentials.githubUsername) {
|
|
1519
1458
|
await runGitCommand(["config", "user.name", credentials.githubUsername], workspacePath);
|
|
1520
1459
|
}
|
|
1521
1460
|
if (credentials.githubEmail) {
|
|
1522
1461
|
await runGitCommand(["config", "user.email", credentials.githubEmail], workspacePath);
|
|
1523
1462
|
}
|
|
1524
|
-
await appendFile(
|
|
1463
|
+
await appendFile(join2(workspacePath, ".git", "info", "exclude"), "\n.claude/skills/\n");
|
|
1525
1464
|
if (branch) {
|
|
1526
1465
|
const { stdout } = await runGitCommand(
|
|
1527
1466
|
["ls-remote", "--heads", "origin", branch],
|
|
@@ -1529,10 +1468,10 @@ async function prepareWorkspace(repoUrl, options) {
|
|
|
1529
1468
|
);
|
|
1530
1469
|
const branchExists = stdout.trim().length > 0;
|
|
1531
1470
|
if (branchExists) {
|
|
1532
|
-
console.log(
|
|
1471
|
+
console.log(`Checking out branch: ${chalk.cyan(branch)}`);
|
|
1533
1472
|
await runGitCommand(["checkout", branch], workspacePath);
|
|
1534
1473
|
} else {
|
|
1535
|
-
console.log(
|
|
1474
|
+
console.log(`Creating new branch: ${chalk.cyan(branch)}`);
|
|
1536
1475
|
await runGitCommand(["checkout", "-b", branch], workspacePath);
|
|
1537
1476
|
}
|
|
1538
1477
|
}
|
|
@@ -1540,17 +1479,17 @@ async function prepareWorkspace(repoUrl, options) {
|
|
|
1540
1479
|
const startingCommit = commitHash.trim();
|
|
1541
1480
|
console.log("");
|
|
1542
1481
|
if (skipSkills) {
|
|
1543
|
-
console.log("
|
|
1482
|
+
console.log(chalk.dim("Skipping skill injection (--no-skills flag)"));
|
|
1544
1483
|
} else {
|
|
1545
1484
|
try {
|
|
1546
1485
|
const librarySkills = await fetchSkillsLibrary({ authToken, runId });
|
|
1547
1486
|
if (librarySkills.length > 0) {
|
|
1548
1487
|
await injectSkills(workspacePath, librarySkills);
|
|
1549
1488
|
} else {
|
|
1550
|
-
console.log("
|
|
1489
|
+
console.log(chalk.dim("No skills to inject (empty library)"));
|
|
1551
1490
|
}
|
|
1552
1491
|
} catch (skillError) {
|
|
1553
|
-
console.warn("
|
|
1492
|
+
console.warn(chalk.yellow("Skill injection failed (agent will continue):"), skillError);
|
|
1554
1493
|
}
|
|
1555
1494
|
}
|
|
1556
1495
|
return { path: workspacePath, startingCommit, cleanup };
|
|
@@ -1563,7 +1502,7 @@ async function prepareWorkspace(repoUrl, options) {
|
|
|
1563
1502
|
// package.json
|
|
1564
1503
|
var package_default = {
|
|
1565
1504
|
name: "@contextgraph/agent",
|
|
1566
|
-
version: "0.4.
|
|
1505
|
+
version: "0.4.25",
|
|
1567
1506
|
description: "Autonomous agent for contextgraph action execution",
|
|
1568
1507
|
type: "module",
|
|
1569
1508
|
bin: {
|
|
@@ -1592,7 +1531,9 @@ var package_default = {
|
|
|
1592
1531
|
},
|
|
1593
1532
|
dependencies: {
|
|
1594
1533
|
"@anthropic-ai/claude-agent-sdk": "^0.1.50",
|
|
1534
|
+
chalk: "^5.6.2",
|
|
1595
1535
|
commander: "^11.0.0",
|
|
1536
|
+
listr2: "^9.0.5",
|
|
1596
1537
|
open: "^10.0.0"
|
|
1597
1538
|
},
|
|
1598
1539
|
devDependencies: {
|
|
@@ -1719,21 +1660,22 @@ var ApiClient = class {
|
|
|
1719
1660
|
};
|
|
1720
1661
|
|
|
1721
1662
|
// src/workspace-setup.ts
|
|
1663
|
+
import chalk2 from "chalk";
|
|
1722
1664
|
var API_BASE_URL3 = "https://www.contextgraph.dev";
|
|
1723
1665
|
async function setupWorkspaceForAction(actionId, options) {
|
|
1724
1666
|
const { authToken, phase, startingCommit: startingCommitOverride, skipSkills } = options;
|
|
1725
1667
|
let actionDetail = options.actionDetail;
|
|
1726
1668
|
if (!actionDetail) {
|
|
1727
1669
|
const apiClient2 = new ApiClient();
|
|
1728
|
-
console.log(`Fetching action details for ${actionId}...`);
|
|
1670
|
+
console.log(chalk2.dim(`Fetching action details for ${actionId}...`));
|
|
1729
1671
|
actionDetail = await apiClient2.getActionDetail(actionId);
|
|
1730
1672
|
}
|
|
1731
1673
|
const logTransport = new LogTransportService(API_BASE_URL3, authToken);
|
|
1732
|
-
console.log(`[Log Streaming] Creating run for ${phase} phase...`);
|
|
1674
|
+
console.log(chalk2.dim(`[Log Streaming] Creating run for ${phase} phase...`));
|
|
1733
1675
|
const runId = await logTransport.createRun(actionId, phase, {
|
|
1734
1676
|
startingCommit: startingCommitOverride
|
|
1735
1677
|
});
|
|
1736
|
-
console.log(`[Log Streaming] Run created: ${runId}`);
|
|
1678
|
+
console.log(chalk2.dim(`[Log Streaming] Run created: ${runId}`));
|
|
1737
1679
|
const repoUrl = actionDetail.resolved_repository_url || actionDetail.repository_url;
|
|
1738
1680
|
const branch = actionDetail.resolved_branch || actionDetail.branch;
|
|
1739
1681
|
let workspacePath;
|
|
@@ -1750,14 +1692,14 @@ async function setupWorkspaceForAction(actionId, options) {
|
|
|
1750
1692
|
cleanup = workspace.cleanup;
|
|
1751
1693
|
startingCommit = workspace.startingCommit;
|
|
1752
1694
|
} else {
|
|
1753
|
-
console.log(
|
|
1754
|
-
workspacePath = await mkdtemp2(
|
|
1755
|
-
console.log(`
|
|
1695
|
+
console.log(chalk2.dim("No repository configured - creating blank workspace"));
|
|
1696
|
+
workspacePath = await mkdtemp2(join3(tmpdir2(), "cg-workspace-"));
|
|
1697
|
+
console.log(chalk2.dim(` ${workspacePath}`));
|
|
1756
1698
|
cleanup = async () => {
|
|
1757
1699
|
try {
|
|
1758
1700
|
await rm2(workspacePath, { recursive: true, force: true });
|
|
1759
1701
|
} catch (error) {
|
|
1760
|
-
console.error(`Warning: Failed to cleanup workspace at ${workspacePath}
|
|
1702
|
+
console.error(chalk2.yellow(`Warning: Failed to cleanup workspace at ${workspacePath}:`), error);
|
|
1761
1703
|
}
|
|
1762
1704
|
};
|
|
1763
1705
|
}
|
|
@@ -1771,15 +1713,16 @@ async function setupWorkspaceForAction(actionId, options) {
|
|
|
1771
1713
|
}
|
|
1772
1714
|
|
|
1773
1715
|
// src/workflows/prepare.ts
|
|
1716
|
+
import chalk3 from "chalk";
|
|
1774
1717
|
var API_BASE_URL4 = "https://www.contextgraph.dev";
|
|
1775
1718
|
async function runPrepare(actionId, options) {
|
|
1776
1719
|
const credentials = await loadCredentials();
|
|
1777
1720
|
if (!credentials) {
|
|
1778
|
-
console.error("
|
|
1721
|
+
console.error(chalk3.red("Not authenticated."), "Run authentication first.");
|
|
1779
1722
|
process.exit(1);
|
|
1780
1723
|
}
|
|
1781
1724
|
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
1782
|
-
console.error("
|
|
1725
|
+
console.error(chalk3.red("Token expired."), "Re-authenticate to continue.");
|
|
1783
1726
|
process.exit(1);
|
|
1784
1727
|
}
|
|
1785
1728
|
let runId = options?.runId;
|
|
@@ -1801,12 +1744,12 @@ async function runPrepare(actionId, options) {
|
|
|
1801
1744
|
runId = setup.runId;
|
|
1802
1745
|
logTransport = setup.logTransport;
|
|
1803
1746
|
} else {
|
|
1804
|
-
console.log(`[Log Streaming] Using pre-created run: ${runId}`);
|
|
1747
|
+
console.log(chalk3.dim(`[Log Streaming] Using pre-created run: ${runId}`));
|
|
1805
1748
|
workspacePath = options?.cwd || process.cwd();
|
|
1806
1749
|
logTransport = new LogTransportService(API_BASE_URL4, credentials.clerkToken, runId);
|
|
1807
1750
|
}
|
|
1808
|
-
console.log(`Fetching preparation instructions for action ${actionId}...
|
|
1809
|
-
`);
|
|
1751
|
+
console.log(chalk3.dim(`Fetching preparation instructions for action ${actionId}...
|
|
1752
|
+
`));
|
|
1810
1753
|
const response = await fetchWithRetry(
|
|
1811
1754
|
`${API_BASE_URL4}/api/prompts/prepare`,
|
|
1812
1755
|
{
|
|
@@ -1827,7 +1770,7 @@ ${errorText}`);
|
|
|
1827
1770
|
await logTransport.updateRunState("preparing");
|
|
1828
1771
|
heartbeatManager = new HeartbeatManager(API_BASE_URL4, credentials.clerkToken, runId);
|
|
1829
1772
|
heartbeatManager.start();
|
|
1830
|
-
console.log("[Log Streaming] Heartbeat started");
|
|
1773
|
+
console.log(chalk3.dim("[Log Streaming] Heartbeat started"));
|
|
1831
1774
|
logBuffer = new LogBuffer(logTransport);
|
|
1832
1775
|
logBuffer.start();
|
|
1833
1776
|
console.log("Spawning Claude for preparation...\n");
|
|
@@ -1846,14 +1789,13 @@ ${errorText}`);
|
|
|
1846
1789
|
cost: claudeResult.cost,
|
|
1847
1790
|
usage: claudeResult.usage
|
|
1848
1791
|
});
|
|
1849
|
-
console.log("\n
|
|
1792
|
+
console.log("\n" + chalk3.green("Preparation complete"));
|
|
1850
1793
|
} else {
|
|
1851
1794
|
await logTransport.finishRun("error", {
|
|
1852
1795
|
exitCode: claudeResult.exitCode,
|
|
1853
1796
|
errorMessage: `Claude preparation failed with exit code ${claudeResult.exitCode}`
|
|
1854
1797
|
});
|
|
1855
|
-
console.error(`
|
|
1856
|
-
\u274C Claude preparation failed with exit code ${claudeResult.exitCode}`);
|
|
1798
|
+
console.error("\n" + chalk3.red(`Claude preparation failed with exit code ${claudeResult.exitCode}`));
|
|
1857
1799
|
process.exit(1);
|
|
1858
1800
|
}
|
|
1859
1801
|
} catch (error) {
|
|
@@ -1863,18 +1805,18 @@ ${errorText}`);
|
|
|
1863
1805
|
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1864
1806
|
});
|
|
1865
1807
|
} catch (stateError) {
|
|
1866
|
-
console.error("[Log Streaming] Failed to update run state:", stateError);
|
|
1808
|
+
console.error(chalk3.dim("[Log Streaming] Failed to update run state:"), stateError);
|
|
1867
1809
|
}
|
|
1868
1810
|
}
|
|
1869
1811
|
throw error;
|
|
1870
1812
|
} finally {
|
|
1871
1813
|
if (heartbeatManager) {
|
|
1872
1814
|
heartbeatManager.stop();
|
|
1873
|
-
console.log("[Log Streaming] Heartbeat stopped");
|
|
1815
|
+
console.log(chalk3.dim("[Log Streaming] Heartbeat stopped"));
|
|
1874
1816
|
}
|
|
1875
1817
|
if (logBuffer) {
|
|
1876
1818
|
await logBuffer.stop();
|
|
1877
|
-
console.log("[Log Streaming] Logs flushed");
|
|
1819
|
+
console.log(chalk3.dim("[Log Streaming] Logs flushed"));
|
|
1878
1820
|
}
|
|
1879
1821
|
if (cleanup) {
|
|
1880
1822
|
await cleanup();
|
|
@@ -1883,15 +1825,16 @@ ${errorText}`);
|
|
|
1883
1825
|
}
|
|
1884
1826
|
|
|
1885
1827
|
// src/workflows/execute.ts
|
|
1828
|
+
import chalk4 from "chalk";
|
|
1886
1829
|
var API_BASE_URL5 = "https://www.contextgraph.dev";
|
|
1887
1830
|
async function runExecute(actionId, options) {
|
|
1888
1831
|
const credentials = await loadCredentials();
|
|
1889
1832
|
if (!credentials) {
|
|
1890
|
-
console.error("
|
|
1833
|
+
console.error(chalk4.red("Not authenticated."), "Run authentication first.");
|
|
1891
1834
|
process.exit(1);
|
|
1892
1835
|
}
|
|
1893
1836
|
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
1894
|
-
console.error("
|
|
1837
|
+
console.error(chalk4.red("Token expired."), "Re-authenticate to continue.");
|
|
1895
1838
|
process.exit(1);
|
|
1896
1839
|
}
|
|
1897
1840
|
let runId = options?.runId;
|
|
@@ -1913,12 +1856,12 @@ async function runExecute(actionId, options) {
|
|
|
1913
1856
|
runId = setup.runId;
|
|
1914
1857
|
logTransport = setup.logTransport;
|
|
1915
1858
|
} else {
|
|
1916
|
-
console.log(`[Log Streaming] Using pre-created run: ${runId}`);
|
|
1859
|
+
console.log(chalk4.dim(`[Log Streaming] Using pre-created run: ${runId}`));
|
|
1917
1860
|
workspacePath = options?.cwd || process.cwd();
|
|
1918
1861
|
logTransport = new LogTransportService(API_BASE_URL5, credentials.clerkToken, runId);
|
|
1919
1862
|
}
|
|
1920
|
-
console.log(`Fetching execution instructions for action ${actionId}...
|
|
1921
|
-
`);
|
|
1863
|
+
console.log(chalk4.dim(`Fetching execution instructions for action ${actionId}...
|
|
1864
|
+
`));
|
|
1922
1865
|
const response = await fetchWithRetry(
|
|
1923
1866
|
`${API_BASE_URL5}/api/prompts/execute`,
|
|
1924
1867
|
{
|
|
@@ -1939,7 +1882,7 @@ ${errorText}`);
|
|
|
1939
1882
|
await logTransport.updateRunState("executing");
|
|
1940
1883
|
heartbeatManager = new HeartbeatManager(API_BASE_URL5, credentials.clerkToken, runId);
|
|
1941
1884
|
heartbeatManager.start();
|
|
1942
|
-
console.log("[Log Streaming] Heartbeat started");
|
|
1885
|
+
console.log(chalk4.dim("[Log Streaming] Heartbeat started"));
|
|
1943
1886
|
logBuffer = new LogBuffer(logTransport);
|
|
1944
1887
|
logBuffer.start();
|
|
1945
1888
|
console.log("Spawning Claude for execution...\n");
|
|
@@ -1958,7 +1901,7 @@ ${errorText}`);
|
|
|
1958
1901
|
cost: claudeResult.cost,
|
|
1959
1902
|
usage: claudeResult.usage
|
|
1960
1903
|
});
|
|
1961
|
-
console.log("\n
|
|
1904
|
+
console.log("\n" + chalk4.green("Execution complete"));
|
|
1962
1905
|
} else {
|
|
1963
1906
|
await logTransport.finishRun("error", {
|
|
1964
1907
|
exitCode: claudeResult.exitCode,
|
|
@@ -1973,18 +1916,18 @@ ${errorText}`);
|
|
|
1973
1916
|
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1974
1917
|
});
|
|
1975
1918
|
} catch (stateError) {
|
|
1976
|
-
console.error("[Log Streaming] Failed to update run state:", stateError);
|
|
1919
|
+
console.error(chalk4.dim("[Log Streaming] Failed to update run state:"), stateError);
|
|
1977
1920
|
}
|
|
1978
1921
|
}
|
|
1979
1922
|
throw error;
|
|
1980
1923
|
} finally {
|
|
1981
1924
|
if (heartbeatManager) {
|
|
1982
1925
|
heartbeatManager.stop();
|
|
1983
|
-
console.log("[Log Streaming] Heartbeat stopped");
|
|
1926
|
+
console.log(chalk4.dim("[Log Streaming] Heartbeat stopped"));
|
|
1984
1927
|
}
|
|
1985
1928
|
if (logBuffer) {
|
|
1986
1929
|
await logBuffer.stop();
|
|
1987
|
-
console.log("[Log Streaming] Logs flushed");
|
|
1930
|
+
console.log(chalk4.dim("[Log Streaming] Logs flushed"));
|
|
1988
1931
|
}
|
|
1989
1932
|
if (cleanup) {
|
|
1990
1933
|
await cleanup();
|
|
@@ -1996,11 +1939,12 @@ ${errorText}`);
|
|
|
1996
1939
|
import { randomUUID } from "crypto";
|
|
1997
1940
|
import { readFileSync } from "fs";
|
|
1998
1941
|
import { fileURLToPath } from "url";
|
|
1999
|
-
import { dirname, join as
|
|
1942
|
+
import { dirname, join as join4 } from "path";
|
|
1943
|
+
import chalk5 from "chalk";
|
|
2000
1944
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
2001
1945
|
var __dirname2 = dirname(__filename2);
|
|
2002
1946
|
var packageJson = JSON.parse(
|
|
2003
|
-
readFileSync(
|
|
1947
|
+
readFileSync(join4(__dirname2, "../package.json"), "utf-8")
|
|
2004
1948
|
);
|
|
2005
1949
|
var INITIAL_POLL_INTERVAL = parseInt(process.env.WORKER_INITIAL_POLL_INTERVAL || "2000", 10);
|
|
2006
1950
|
var MAX_POLL_INTERVAL = parseInt(process.env.WORKER_MAX_POLL_INTERVAL || "5000", 10);
|
|
@@ -2032,34 +1976,34 @@ function formatDuration(ms) {
|
|
|
2032
1976
|
function printStatus() {
|
|
2033
1977
|
const uptime = formatDuration(Date.now() - stats.startTime);
|
|
2034
1978
|
const total = stats.prepared + stats.executed;
|
|
2035
|
-
console.log(`Status: ${total} actions (${stats.prepared} prepared, ${stats.executed} executed, ${stats.errors} errors) | Uptime: ${uptime}`);
|
|
1979
|
+
console.log(chalk5.dim(`Status: ${total} actions (${stats.prepared} prepared, ${stats.executed} executed, ${stats.errors} errors) | Uptime: ${uptime}`));
|
|
2036
1980
|
}
|
|
2037
1981
|
async function cleanupAndExit() {
|
|
2038
1982
|
if (currentClaim && apiClient) {
|
|
2039
1983
|
try {
|
|
2040
|
-
console.log(`
|
|
2041
|
-
|
|
1984
|
+
console.log(chalk5.dim(`
|
|
1985
|
+
Releasing claim on action ${currentClaim.actionId}...`));
|
|
2042
1986
|
await apiClient.releaseClaim({
|
|
2043
1987
|
action_id: currentClaim.actionId,
|
|
2044
1988
|
worker_id: currentClaim.workerId,
|
|
2045
1989
|
claim_id: currentClaim.claimId
|
|
2046
1990
|
});
|
|
2047
|
-
console.log("
|
|
1991
|
+
console.log(chalk5.dim("Claim released successfully"));
|
|
2048
1992
|
} catch (error) {
|
|
2049
|
-
console.error("
|
|
1993
|
+
console.error(chalk5.yellow("Failed to release claim:"), error.message);
|
|
2050
1994
|
}
|
|
2051
1995
|
}
|
|
2052
|
-
console.log("
|
|
1996
|
+
console.log("Shutdown complete");
|
|
2053
1997
|
process.exit(0);
|
|
2054
1998
|
}
|
|
2055
1999
|
function setupSignalHandlers() {
|
|
2056
2000
|
process.on("SIGINT", async () => {
|
|
2057
|
-
console.log("\n\
|
|
2001
|
+
console.log(chalk5.yellow("\n\nReceived SIGINT (Ctrl+C). Shutting down gracefully..."));
|
|
2058
2002
|
running = false;
|
|
2059
2003
|
await cleanupAndExit();
|
|
2060
2004
|
});
|
|
2061
2005
|
process.on("SIGTERM", async () => {
|
|
2062
|
-
console.log("\n\
|
|
2006
|
+
console.log(chalk5.yellow("\n\nReceived SIGTERM. Shutting down gracefully..."));
|
|
2063
2007
|
running = false;
|
|
2064
2008
|
await cleanupAndExit();
|
|
2065
2009
|
});
|
|
@@ -2076,25 +2020,23 @@ async function runLocalAgent(options) {
|
|
|
2076
2020
|
setupSignalHandlers();
|
|
2077
2021
|
const credentials = await loadCredentials();
|
|
2078
2022
|
if (!credentials) {
|
|
2079
|
-
console.error("
|
|
2080
|
-
console.error(
|
|
2023
|
+
console.error(chalk5.red("Not authenticated."));
|
|
2024
|
+
console.error(` Set CONTEXTGRAPH_API_TOKEN environment variable or run ${chalk5.cyan("contextgraph-agent auth")}`);
|
|
2081
2025
|
process.exit(1);
|
|
2082
2026
|
}
|
|
2083
2027
|
if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
|
|
2084
|
-
console.error("
|
|
2028
|
+
console.error(chalk5.red("Token expired."), `Run ${chalk5.cyan("contextgraph-agent auth")} to re-authenticate.`);
|
|
2085
2029
|
process.exit(1);
|
|
2086
2030
|
}
|
|
2087
2031
|
const usingApiToken = !!process.env.CONTEXTGRAPH_API_TOKEN;
|
|
2088
2032
|
if (usingApiToken) {
|
|
2089
|
-
console.log("
|
|
2033
|
+
console.log(chalk5.dim("Authenticated via CONTEXTGRAPH_API_TOKEN"));
|
|
2090
2034
|
}
|
|
2091
2035
|
const workerId = randomUUID();
|
|
2092
|
-
console.log(
|
|
2093
|
-
console.log(
|
|
2094
|
-
console.log(
|
|
2095
|
-
|
|
2096
|
-
console.log(`\u{1F4A1} Press Ctrl+C to gracefully shutdown and release any claimed work
|
|
2097
|
-
`);
|
|
2036
|
+
console.log(`${chalk5.bold("ContextGraph Agent")} ${chalk5.dim(`v${packageJson.version}`)}`);
|
|
2037
|
+
console.log(chalk5.dim(`Worker ID: ${workerId}`));
|
|
2038
|
+
console.log("Starting continuous worker loop...\n");
|
|
2039
|
+
console.log(chalk5.dim("Press Ctrl+C to gracefully shutdown and release any claimed work\n"));
|
|
2098
2040
|
let currentPollInterval = INITIAL_POLL_INTERVAL;
|
|
2099
2041
|
let lastStatusTime = Date.now();
|
|
2100
2042
|
let consecutiveApiErrors = 0;
|
|
@@ -2110,20 +2052,20 @@ async function runLocalAgent(options) {
|
|
|
2110
2052
|
if (isRetryableError(err)) {
|
|
2111
2053
|
consecutiveApiErrors++;
|
|
2112
2054
|
if (consecutiveApiErrors === OUTAGE_WARNING_THRESHOLD) {
|
|
2113
|
-
console.warn(`
|
|
2114
|
-
|
|
2115
|
-
console.warn(` Will continue retrying indefinitely (every ${MAX_RETRY_DELAY / 1e3}s max).`);
|
|
2116
|
-
console.warn(` Press Ctrl+C to stop.
|
|
2117
|
-
`);
|
|
2055
|
+
console.warn(chalk5.yellow(`
|
|
2056
|
+
API appears to be experiencing an outage.`));
|
|
2057
|
+
console.warn(chalk5.yellow(` Will continue retrying indefinitely (every ${MAX_RETRY_DELAY / 1e3}s max).`));
|
|
2058
|
+
console.warn(chalk5.yellow(` Press Ctrl+C to stop.
|
|
2059
|
+
`));
|
|
2118
2060
|
}
|
|
2119
2061
|
if (consecutiveApiErrors < OUTAGE_WARNING_THRESHOLD) {
|
|
2120
|
-
console.warn(
|
|
2062
|
+
console.warn(chalk5.yellow(`API error (attempt ${consecutiveApiErrors}):`), err.message);
|
|
2121
2063
|
} else if (consecutiveApiErrors % 10 === 0) {
|
|
2122
|
-
console.warn(
|
|
2064
|
+
console.warn(chalk5.yellow(`Still retrying... (attempt ${consecutiveApiErrors}, last error: ${err.message})`));
|
|
2123
2065
|
}
|
|
2124
2066
|
const delaySeconds = Math.round(apiRetryDelay / 1e3);
|
|
2125
2067
|
if (consecutiveApiErrors < OUTAGE_WARNING_THRESHOLD) {
|
|
2126
|
-
console.warn(` Retrying in ${delaySeconds}s...`);
|
|
2068
|
+
console.warn(chalk5.dim(` Retrying in ${delaySeconds}s...`));
|
|
2127
2069
|
}
|
|
2128
2070
|
await sleep(apiRetryDelay);
|
|
2129
2071
|
apiRetryDelay = Math.min(apiRetryDelay * 2, MAX_RETRY_DELAY);
|
|
@@ -2154,7 +2096,7 @@ async function runLocalAgent(options) {
|
|
|
2154
2096
|
} else if (!actionDetail.done) {
|
|
2155
2097
|
phase = "execute";
|
|
2156
2098
|
} else {
|
|
2157
|
-
console.log(
|
|
2099
|
+
console.log(chalk5.dim(`Skipping action "${actionDetail.title}" - already completed`));
|
|
2158
2100
|
if (currentClaim && apiClient) {
|
|
2159
2101
|
try {
|
|
2160
2102
|
await apiClient.releaseClaim({
|
|
@@ -2163,13 +2105,13 @@ async function runLocalAgent(options) {
|
|
|
2163
2105
|
claim_id: currentClaim.claimId
|
|
2164
2106
|
});
|
|
2165
2107
|
} catch (releaseError) {
|
|
2166
|
-
console.error("
|
|
2108
|
+
console.error(chalk5.yellow("Failed to release claim:"), releaseError.message);
|
|
2167
2109
|
}
|
|
2168
2110
|
}
|
|
2169
2111
|
currentClaim = null;
|
|
2170
2112
|
continue;
|
|
2171
2113
|
}
|
|
2172
|
-
console.log(
|
|
2114
|
+
console.log(`${chalk5.bold("Working:")} ${chalk5.cyan(actionDetail.title)}`);
|
|
2173
2115
|
let workspacePath;
|
|
2174
2116
|
let cleanup;
|
|
2175
2117
|
let startingCommit;
|
|
@@ -2197,7 +2139,7 @@ async function runLocalAgent(options) {
|
|
|
2197
2139
|
claim_id: currentClaim.claimId
|
|
2198
2140
|
});
|
|
2199
2141
|
} catch (releaseError) {
|
|
2200
|
-
console.error("
|
|
2142
|
+
console.error(chalk5.yellow("Failed to release claim after preparation:"), releaseError.message);
|
|
2201
2143
|
}
|
|
2202
2144
|
}
|
|
2203
2145
|
currentClaim = null;
|
|
@@ -2206,10 +2148,10 @@ async function runLocalAgent(options) {
|
|
|
2206
2148
|
try {
|
|
2207
2149
|
await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel, runId });
|
|
2208
2150
|
stats.executed++;
|
|
2209
|
-
console.log(
|
|
2151
|
+
console.log(`${chalk5.bold.green("Completed:")} ${chalk5.cyan(actionDetail.title)}`);
|
|
2210
2152
|
} catch (executeError) {
|
|
2211
2153
|
stats.errors++;
|
|
2212
|
-
console.error(
|
|
2154
|
+
console.error(chalk5.red("Error:"), `${executeError.message}. Continuing...`);
|
|
2213
2155
|
} finally {
|
|
2214
2156
|
if (currentClaim && apiClient) {
|
|
2215
2157
|
try {
|
|
@@ -2219,25 +2161,25 @@ async function runLocalAgent(options) {
|
|
|
2219
2161
|
claim_id: currentClaim.claimId
|
|
2220
2162
|
});
|
|
2221
2163
|
} catch (releaseError) {
|
|
2222
|
-
console.error("
|
|
2164
|
+
console.error(chalk5.yellow("Failed to release claim:"), releaseError.message);
|
|
2223
2165
|
}
|
|
2224
2166
|
}
|
|
2225
2167
|
currentClaim = null;
|
|
2226
2168
|
}
|
|
2227
2169
|
} catch (workspaceError) {
|
|
2228
2170
|
stats.errors++;
|
|
2229
|
-
console.error(
|
|
2171
|
+
console.error(chalk5.red("Error preparing workspace:"), `${workspaceError.message}. Continuing...`);
|
|
2230
2172
|
if (currentClaim && apiClient) {
|
|
2231
2173
|
try {
|
|
2232
|
-
console.log(
|
|
2174
|
+
console.log(chalk5.dim("Releasing claim due to workspace error..."));
|
|
2233
2175
|
await apiClient.releaseClaim({
|
|
2234
2176
|
action_id: currentClaim.actionId,
|
|
2235
2177
|
worker_id: currentClaim.workerId,
|
|
2236
2178
|
claim_id: currentClaim.claimId
|
|
2237
2179
|
});
|
|
2238
|
-
console.log("
|
|
2180
|
+
console.log(chalk5.dim("Claim released"));
|
|
2239
2181
|
} catch (releaseError) {
|
|
2240
|
-
console.error("
|
|
2182
|
+
console.error(chalk5.yellow("Failed to release claim:"), releaseError.message);
|
|
2241
2183
|
}
|
|
2242
2184
|
}
|
|
2243
2185
|
currentClaim = null;
|
|
@@ -2249,14 +2191,223 @@ async function runLocalAgent(options) {
|
|
|
2249
2191
|
}
|
|
2250
2192
|
}
|
|
2251
2193
|
|
|
2194
|
+
// src/workflows/setup.ts
|
|
2195
|
+
import chalk6 from "chalk";
|
|
2196
|
+
import { Listr } from "listr2";
|
|
2197
|
+
|
|
2198
|
+
// src/plugin-setup.ts
|
|
2199
|
+
import { spawn as spawn2 } from "child_process";
|
|
2200
|
+
var MARKETPLACE_SOURCE = "contextgraph/claude-code-plugin";
|
|
2201
|
+
var MARKETPLACE_NAME = "contextgraph";
|
|
2202
|
+
var PLUGIN_NAME = "contextgraph";
|
|
2203
|
+
function runCommand(command, args) {
|
|
2204
|
+
return new Promise((resolve, reject) => {
|
|
2205
|
+
const proc = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2206
|
+
let stdout = "";
|
|
2207
|
+
proc.stdout.on("data", (data) => {
|
|
2208
|
+
stdout += data.toString();
|
|
2209
|
+
});
|
|
2210
|
+
proc.on("close", (code) => {
|
|
2211
|
+
resolve({ stdout, exitCode: code ?? 1 });
|
|
2212
|
+
});
|
|
2213
|
+
proc.on("error", (err) => {
|
|
2214
|
+
reject(new Error(`Failed to spawn ${command}: ${err.message}`));
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
async function isClaudeCodeAvailable() {
|
|
2219
|
+
try {
|
|
2220
|
+
const { exitCode } = await runCommand("claude", ["--version"]);
|
|
2221
|
+
return exitCode === 0;
|
|
2222
|
+
} catch {
|
|
2223
|
+
return false;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
async function isMarketplaceConfigured() {
|
|
2227
|
+
try {
|
|
2228
|
+
const { stdout, exitCode } = await runCommand("claude", ["plugin", "marketplace", "list"]);
|
|
2229
|
+
return exitCode === 0 && stdout.includes(MARKETPLACE_NAME);
|
|
2230
|
+
} catch {
|
|
2231
|
+
return false;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
async function addMarketplace() {
|
|
2235
|
+
console.log("Adding ContextGraph marketplace...");
|
|
2236
|
+
const { exitCode } = await runCommand("claude", ["plugin", "marketplace", "add", MARKETPLACE_SOURCE]);
|
|
2237
|
+
if (exitCode !== 0) {
|
|
2238
|
+
throw new Error(`Failed to add marketplace (exit code ${exitCode})`);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
async function isPluginInstalled() {
|
|
2242
|
+
try {
|
|
2243
|
+
const { stdout, exitCode } = await runCommand("claude", ["plugin", "list"]);
|
|
2244
|
+
return exitCode === 0 && stdout.includes(PLUGIN_NAME);
|
|
2245
|
+
} catch {
|
|
2246
|
+
return false;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
async function ensurePluginInstalled() {
|
|
2250
|
+
if (await isPluginInstalled()) {
|
|
2251
|
+
return;
|
|
2252
|
+
}
|
|
2253
|
+
if (!await isMarketplaceConfigured()) {
|
|
2254
|
+
await addMarketplace();
|
|
2255
|
+
}
|
|
2256
|
+
console.log("Installing ContextGraph plugin for Claude Code...");
|
|
2257
|
+
const { exitCode } = await runCommand("claude", ["plugin", "install", PLUGIN_NAME]);
|
|
2258
|
+
if (exitCode !== 0) {
|
|
2259
|
+
throw new Error(`Failed to install plugin (exit code ${exitCode})`);
|
|
2260
|
+
}
|
|
2261
|
+
console.log("ContextGraph plugin installed (includes MCP server)");
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
// src/workflows/setup.ts
|
|
2265
|
+
async function runSetup() {
|
|
2266
|
+
console.log(`
|
|
2267
|
+
${chalk6.bold("Welcome to ContextGraph!")}`);
|
|
2268
|
+
console.log("This setup wizard will help you get started.\n");
|
|
2269
|
+
const tasks = new Listr(
|
|
2270
|
+
[
|
|
2271
|
+
{
|
|
2272
|
+
title: "Checking authentication",
|
|
2273
|
+
task: async (ctx2, task) => {
|
|
2274
|
+
const creds = await loadCredentials();
|
|
2275
|
+
if (!creds) {
|
|
2276
|
+
ctx2.needsAuth = true;
|
|
2277
|
+
} else if (isExpired(creds) || isTokenExpired(creds.clerkToken)) {
|
|
2278
|
+
ctx2.needsAuth = true;
|
|
2279
|
+
} else {
|
|
2280
|
+
ctx2.needsAuth = false;
|
|
2281
|
+
ctx2.userId = creds.userId;
|
|
2282
|
+
ctx2.displayName = creds.email ?? creds.userId;
|
|
2283
|
+
task.title = `Checking authentication \u2014 logged in as ${chalk6.cyan(ctx2.displayName)}`;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
},
|
|
2287
|
+
{
|
|
2288
|
+
title: "Authenticating with ContextGraph",
|
|
2289
|
+
skip: (ctx2) => !ctx2.needsAuth ? "Already authenticated" : false,
|
|
2290
|
+
task: async (ctx2, task) => {
|
|
2291
|
+
task.output = "Waiting for browser...";
|
|
2292
|
+
const result = await authenticateAgent();
|
|
2293
|
+
if (!result.success) {
|
|
2294
|
+
throw new Error("Authentication failed: " + result.error);
|
|
2295
|
+
}
|
|
2296
|
+
ctx2.userId = result.credentials.userId;
|
|
2297
|
+
ctx2.displayName = result.credentials.email ?? result.credentials.userId;
|
|
2298
|
+
task.title = `Authenticated as ${chalk6.cyan(ctx2.displayName)}`;
|
|
2299
|
+
},
|
|
2300
|
+
options: { bottomBar: Infinity }
|
|
2301
|
+
},
|
|
2302
|
+
{
|
|
2303
|
+
title: "Checking for Claude Code",
|
|
2304
|
+
task: async (ctx2, task) => {
|
|
2305
|
+
ctx2.hasClaudeCode = await isClaudeCodeAvailable();
|
|
2306
|
+
task.title = ctx2.hasClaudeCode ? "Checking for Claude Code \u2014 detected" : "Checking for Claude Code \u2014 not found";
|
|
2307
|
+
}
|
|
2308
|
+
},
|
|
2309
|
+
{
|
|
2310
|
+
title: "Installing ContextGraph plugin",
|
|
2311
|
+
skip: (ctx2) => !ctx2.hasClaudeCode ? "Claude Code not detected" : false,
|
|
2312
|
+
task: async (ctx2, task) => {
|
|
2313
|
+
try {
|
|
2314
|
+
if (await isPluginInstalled()) {
|
|
2315
|
+
ctx2.pluginInstalled = true;
|
|
2316
|
+
task.title = "ContextGraph plugin already installed";
|
|
2317
|
+
} else {
|
|
2318
|
+
await ensurePluginInstalled();
|
|
2319
|
+
ctx2.pluginInstalled = true;
|
|
2320
|
+
task.title = "ContextGraph plugin installed";
|
|
2321
|
+
}
|
|
2322
|
+
} catch {
|
|
2323
|
+
ctx2.pluginInstalled = false;
|
|
2324
|
+
task.skip("Could not install automatically \u2014 see manual instructions below");
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
],
|
|
2329
|
+
{
|
|
2330
|
+
ctx: { needsAuth: false, userId: null, displayName: null, hasClaudeCode: false, pluginInstalled: false },
|
|
2331
|
+
rendererOptions: { collapseErrors: false }
|
|
2332
|
+
}
|
|
2333
|
+
);
|
|
2334
|
+
const ctx = await tasks.run();
|
|
2335
|
+
console.log("");
|
|
2336
|
+
if (ctx.hasClaudeCode) {
|
|
2337
|
+
if (!ctx.pluginInstalled) {
|
|
2338
|
+
console.log(chalk6.yellow("Plugin could not be installed automatically."));
|
|
2339
|
+
console.log("You can install it manually with:\n");
|
|
2340
|
+
console.log(` ${chalk6.dim("$")} claude plugin marketplace add contextgraph/claude-code-plugin`);
|
|
2341
|
+
console.log(` ${chalk6.dim("$")} claude plugin install contextgraph
|
|
2342
|
+
`);
|
|
2343
|
+
}
|
|
2344
|
+
console.log(chalk6.bold.green("Setup complete!") + " You're ready to go.\n");
|
|
2345
|
+
console.log(`There are three ways to work with ContextGraph:
|
|
2346
|
+
`);
|
|
2347
|
+
console.log(chalk6.bold("1. Run the ContextGraph agent"));
|
|
2348
|
+
console.log(" Wraps Claude Code and automatically prepares and executes");
|
|
2349
|
+
console.log(" actions for you in your codebase.\n");
|
|
2350
|
+
console.log(` ${chalk6.cyan("npx @contextgraph/agent run")}
|
|
2351
|
+
`);
|
|
2352
|
+
console.log(chalk6.bold("2. Use ContextGraph directly via Claude Code"));
|
|
2353
|
+
console.log(" Open Claude Code (or restart if already running) and interact");
|
|
2354
|
+
console.log(" with your action graph through the MCP tools.\n");
|
|
2355
|
+
console.log(chalk6.dim(" Examples:"));
|
|
2356
|
+
console.log(chalk6.dim(' "Create an action plan for implementing user auth"'));
|
|
2357
|
+
console.log(chalk6.dim(' "Show me my action tree"'));
|
|
2358
|
+
console.log(chalk6.dim(' "What should I work on next?"'));
|
|
2359
|
+
console.log(chalk6.dim(' "Capture this discussion as an action with sub-tasks"'));
|
|
2360
|
+
console.log("");
|
|
2361
|
+
console.log(chalk6.bold("3. Integrate the MCP server into other platforms"));
|
|
2362
|
+
console.log(" Add the ContextGraph MCP server to any compatible tool.\n");
|
|
2363
|
+
console.log(` MCP Server URL: ${chalk6.cyan("https://mcp.contextgraph.dev")}
|
|
2364
|
+
`);
|
|
2365
|
+
console.log(chalk6.dim(" Works with: Cursor, Claude.ai, ChatGPT, Codex CLI, Windsurf,"));
|
|
2366
|
+
console.log(chalk6.dim(" and any other MCP-compatible client."));
|
|
2367
|
+
console.log("");
|
|
2368
|
+
console.log(` Web dashboard: ${chalk6.cyan("https://contextgraph.dev")}`);
|
|
2369
|
+
} else {
|
|
2370
|
+
console.log("You have a few options:\n");
|
|
2371
|
+
console.log(chalk6.bold("Option 1: Install Claude Code"));
|
|
2372
|
+
console.log(` Visit: ${chalk6.cyan("https://claude.ai/download")}`);
|
|
2373
|
+
console.log(` Then run this setup again: ${chalk6.cyan("npx @contextgraph/agent@latest setup")}
|
|
2374
|
+
`);
|
|
2375
|
+
console.log(chalk6.bold("Option 2: Use ContextGraph MCP server with other editors"));
|
|
2376
|
+
console.log(" ContextGraph works with any MCP-compatible editor!\n");
|
|
2377
|
+
console.log(" To add the ContextGraph MCP server to your editor:");
|
|
2378
|
+
console.log(` - Cursor: See ${chalk6.cyan("https://docs.cursor.com/advanced/mcp")}`);
|
|
2379
|
+
console.log(` - Windsurf: See ${chalk6.cyan("https://docs.windsurf.ai/mcp")}`);
|
|
2380
|
+
console.log(` - Other: See ${chalk6.cyan("https://modelcontextprotocol.io/clients")}
|
|
2381
|
+
`);
|
|
2382
|
+
console.log(" MCP Server URL:");
|
|
2383
|
+
console.log(` ${chalk6.cyan("https://mcp.contextgraph.dev")}
|
|
2384
|
+
`);
|
|
2385
|
+
console.log(chalk6.bold("Option 3: Use the agent CLI directly"));
|
|
2386
|
+
console.log(` Run autonomous agent: ${chalk6.cyan("npx @contextgraph/agent run")}`);
|
|
2387
|
+
console.log(` Execute specific action: ${chalk6.cyan("npx @contextgraph/agent execute <action-id>")}`);
|
|
2388
|
+
console.log(` Prepare an action: ${chalk6.cyan("npx @contextgraph/agent prepare <action-id>")}
|
|
2389
|
+
`);
|
|
2390
|
+
console.log(`Authentication complete! Visit ${chalk6.cyan("https://contextgraph.dev")} to get started!`);
|
|
2391
|
+
}
|
|
2392
|
+
console.log("");
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2252
2395
|
// src/cli/index.ts
|
|
2253
2396
|
var __filename3 = fileURLToPath2(import.meta.url);
|
|
2254
2397
|
var __dirname3 = dirname2(__filename3);
|
|
2255
2398
|
var packageJson2 = JSON.parse(
|
|
2256
|
-
readFileSync2(
|
|
2399
|
+
readFileSync2(join5(__dirname3, "../package.json"), "utf-8")
|
|
2257
2400
|
);
|
|
2258
2401
|
var program = new Command();
|
|
2259
2402
|
program.name("contextgraph-agent").description("Autonomous agent for contextgraph action execution").version(packageJson2.version);
|
|
2403
|
+
program.command("setup").description("Interactive setup wizard for new users").action(async () => {
|
|
2404
|
+
try {
|
|
2405
|
+
await runSetup();
|
|
2406
|
+
} catch (error) {
|
|
2407
|
+
console.error("Error during setup:", error instanceof Error ? error.message : error);
|
|
2408
|
+
process.exit(1);
|
|
2409
|
+
}
|
|
2410
|
+
});
|
|
2260
2411
|
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) => {
|
|
2261
2412
|
try {
|
|
2262
2413
|
await runLocalAgent({
|