@contextgraph/agent 0.4.16 → 0.4.18

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,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 join4 } from "path";
7
+ import { dirname as dirname2, join as join5 } from "path";
8
8
 
9
9
  // src/callback-server.ts
10
10
  import http from "http";
@@ -975,7 +975,7 @@ var LogTransportService = class {
975
975
  /**
976
976
  * Create a new run for an action
977
977
  * @param actionId - The action ID this run is executing
978
- * @param purpose - The purpose of this run: 'execute' | 'prepare' | 'learn' | 'review'
978
+ * @param purpose - The purpose of this run: 'execute' | 'prepare' | 'review'
979
979
  * @param metadata - Optional metadata for the run (e.g., startingCommit)
980
980
  * @returns The created run ID
981
981
  */
@@ -1050,7 +1050,7 @@ var LogTransportService = class {
1050
1050
  if (!this.runId) {
1051
1051
  throw new Error("No run ID set. Call createRun() first.");
1052
1052
  }
1053
- if (state === "executing" || state === "preparing" || state === "running" || state === "learning") {
1053
+ if (state === "executing" || state === "preparing" || state === "running") {
1054
1054
  await this.startRun();
1055
1055
  } else if (state === "completed" || state === "failed") {
1056
1056
  const outcome = state === "completed" ? "success" : "error";
@@ -1500,114 +1500,18 @@ ${errorText}`);
1500
1500
  }
1501
1501
  }
1502
1502
 
1503
- // src/workflows/learn.ts
1504
- var API_BASE_URL3 = "https://www.contextgraph.dev";
1505
- async function runLearn(actionId, options) {
1506
- const credentials = await loadCredentials();
1507
- if (!credentials) {
1508
- console.error("\u274C Not authenticated. Run authentication first.");
1509
- process.exit(1);
1510
- }
1511
- if (isExpired(credentials) || isTokenExpired(credentials.clerkToken)) {
1512
- console.error("\u274C Token expired. Re-authenticate to continue.");
1513
- process.exit(1);
1514
- }
1515
- const logTransport = new LogTransportService(API_BASE_URL3, credentials.clerkToken);
1516
- let runId;
1517
- let heartbeatManager;
1518
- let logBuffer;
1519
- try {
1520
- console.log("[Log Streaming] Creating run for learn phase...");
1521
- runId = await logTransport.createRun(actionId, "learn", {
1522
- startingCommit: options?.startingCommit
1523
- });
1524
- console.log(`[Log Streaming] Run created: ${runId}`);
1525
- console.log(`Fetching learning instructions for action ${actionId}...
1526
- `);
1527
- const response = await fetchWithRetry(
1528
- `${API_BASE_URL3}/api/prompts/learn`,
1529
- {
1530
- method: "POST",
1531
- headers: {
1532
- "Authorization": `Bearer ${credentials.clerkToken}`,
1533
- "Content-Type": "application/json"
1534
- },
1535
- body: JSON.stringify({ actionId, runId })
1536
- }
1537
- );
1538
- if (!response.ok) {
1539
- const errorText = await response.text();
1540
- throw new Error(`Failed to fetch learn prompt: ${response.statusText}
1541
- ${errorText}`);
1542
- }
1543
- const { prompt } = await response.json();
1544
- await logTransport.updateRunState("learning");
1545
- heartbeatManager = new HeartbeatManager(API_BASE_URL3, credentials.clerkToken, runId);
1546
- heartbeatManager.start();
1547
- console.log("[Log Streaming] Heartbeat started");
1548
- logBuffer = new LogBuffer(logTransport);
1549
- logBuffer.start();
1550
- console.log("Spawning Claude for learning extraction...\n");
1551
- const claudeResult = await executeClaude({
1552
- prompt,
1553
- cwd: options?.cwd || process.cwd(),
1554
- authToken: credentials.clerkToken,
1555
- ...options?.model ? { model: options.model } : {},
1556
- onLogEvent: (event) => {
1557
- logBuffer.push(event);
1558
- }
1559
- });
1560
- if (claudeResult.exitCode === 0) {
1561
- await logTransport.finishRun("success", {
1562
- exitCode: claudeResult.exitCode,
1563
- cost: claudeResult.cost,
1564
- usage: claudeResult.usage
1565
- });
1566
- console.log("\n\u2705 Learning extraction complete");
1567
- } else {
1568
- await logTransport.finishRun("error", {
1569
- exitCode: claudeResult.exitCode,
1570
- errorMessage: `Claude learning extraction failed with exit code ${claudeResult.exitCode}`
1571
- });
1572
- console.error(`
1573
- \u274C Claude learning extraction failed with exit code ${claudeResult.exitCode}`);
1574
- process.exit(1);
1575
- }
1576
- } catch (error) {
1577
- if (runId) {
1578
- try {
1579
- await logTransport.finishRun("error", {
1580
- errorMessage: error instanceof Error ? error.message : String(error)
1581
- });
1582
- } catch (stateError) {
1583
- console.error("[Log Streaming] Failed to update run state:", stateError);
1584
- }
1585
- }
1586
- throw error;
1587
- } finally {
1588
- if (heartbeatManager) {
1589
- heartbeatManager.stop();
1590
- console.log("[Log Streaming] Heartbeat stopped");
1591
- }
1592
- if (logBuffer) {
1593
- await logBuffer.stop();
1594
- console.log("[Log Streaming] Logs flushed");
1595
- }
1596
- }
1597
- }
1598
-
1599
1503
  // src/workflows/agent.ts
1600
1504
  import { randomUUID } from "crypto";
1601
1505
  import { readFileSync } from "fs";
1602
1506
  import { mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
1603
1507
  import { tmpdir as tmpdir2 } from "os";
1604
1508
  import { fileURLToPath } from "url";
1605
- import { dirname, join as join3 } from "path";
1509
+ import { dirname, join as join4 } from "path";
1606
1510
 
1607
1511
  // package.json
1608
1512
  var package_default = {
1609
1513
  name: "@contextgraph/agent",
1610
- version: "0.4.15",
1514
+ version: "0.4.18",
1611
1515
  description: "Autonomous agent for contextgraph action execution",
1612
1516
  type: "module",
1613
1517
  bin: {
@@ -1766,7 +1670,125 @@ var ApiClient = class {
1766
1670
  import { spawn as spawn2 } from "child_process";
1767
1671
  import { mkdtemp, rm } from "fs/promises";
1768
1672
  import { tmpdir } from "os";
1673
+ import { join as join3 } from "path";
1674
+
1675
+ // src/skill-injection.ts
1676
+ import { mkdir as mkdir2, writeFile } from "fs/promises";
1769
1677
  import { join as join2 } from "path";
1678
+ async function injectSkills(workspacePath, skills) {
1679
+ if (skills.length === 0) {
1680
+ console.log("\u{1F4DA} No skills to inject");
1681
+ return;
1682
+ }
1683
+ console.log(`\u{1F4DA} Injecting ${skills.length} skill(s) into workspace...`);
1684
+ for (const skill of skills) {
1685
+ try {
1686
+ const skillDir = join2(workspacePath, ".claude", "skills", skill.name);
1687
+ await mkdir2(skillDir, { recursive: true });
1688
+ const skillContent = `---
1689
+ name: ${skill.name}
1690
+ description: ${skill.description}
1691
+ ---
1692
+
1693
+ ${skill.content}
1694
+ `;
1695
+ const skillFilePath = join2(skillDir, "SKILL.md");
1696
+ await writeFile(skillFilePath, skillContent, "utf-8");
1697
+ console.log(` \u2705 Injected skill: ${skill.name}`);
1698
+ } catch (error) {
1699
+ console.error(` \u274C Failed to inject skill "${skill.name}":`, error);
1700
+ throw error;
1701
+ }
1702
+ }
1703
+ console.log(`\u2705 Skills injected successfully`);
1704
+ }
1705
+
1706
+ // src/test-skills.ts
1707
+ var VALIDATION_SKILL = {
1708
+ name: "contextgraph-validation-marker",
1709
+ description: "Marks agent runs to validate skill injection is working",
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];
1739
+ }
1740
+
1741
+ // src/skills-library-fetch.ts
1742
+ var API_BASE_URL3 = "https://www.contextgraph.dev";
1743
+ async function fetchSkillsLibrary(authToken) {
1744
+ try {
1745
+ const response = await fetchWithRetry(
1746
+ `${API_BASE_URL3}/api/skills/library`,
1747
+ {
1748
+ headers: {
1749
+ "x-authorization": `Bearer ${authToken}`,
1750
+ "Content-Type": "application/json"
1751
+ }
1752
+ },
1753
+ {
1754
+ maxRetries: 2,
1755
+ baseDelayMs: 500
1756
+ }
1757
+ );
1758
+ if (!response.ok) {
1759
+ console.warn(`\u26A0\uFE0F Skills library API returned ${response.status}: ${response.statusText}`);
1760
+ return [];
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 [];
1766
+ }
1767
+ const skills = data.data.skills.map((skill) => {
1768
+ const name = skill.filename.replace(/\.md$/, "");
1769
+ let description = "Skill from ContextGraph library";
1770
+ const frontmatterMatch = skill.content.match(/^---\n([\s\S]*?)\n---/);
1771
+ if (frontmatterMatch) {
1772
+ const descMatch = frontmatterMatch[1].match(/description:\s*(.+)/);
1773
+ if (descMatch) {
1774
+ description = descMatch[1].trim();
1775
+ }
1776
+ }
1777
+ const contentWithoutFrontmatter = skill.content.replace(/^---\n[\s\S]*?\n---\n/, "");
1778
+ return {
1779
+ name,
1780
+ description,
1781
+ content: contentWithoutFrontmatter
1782
+ };
1783
+ });
1784
+ return skills;
1785
+ } catch (error) {
1786
+ console.warn("\u26A0\uFE0F Failed to fetch skills library:", error instanceof Error ? error.message : error);
1787
+ return [];
1788
+ }
1789
+ }
1790
+
1791
+ // src/workspace-prep.ts
1770
1792
  var API_BASE_URL4 = "https://www.contextgraph.dev";
1771
1793
  async function fetchGitHubCredentials(authToken) {
1772
1794
  const response = await fetchWithRetry(`${API_BASE_URL4}/api/cli/credentials`, {
@@ -1825,7 +1847,7 @@ function buildAuthenticatedUrl(repoUrl, token) {
1825
1847
  async function prepareWorkspace(repoUrl, options) {
1826
1848
  const { branch, authToken } = options;
1827
1849
  const credentials = await fetchGitHubCredentials(authToken);
1828
- const workspacePath = await mkdtemp(join2(tmpdir(), "cg-workspace-"));
1850
+ const workspacePath = await mkdtemp(join3(tmpdir(), "cg-workspace-"));
1829
1851
  const cleanup = async () => {
1830
1852
  try {
1831
1853
  await rm(workspacePath, { recursive: true, force: true });
@@ -1861,6 +1883,19 @@ async function prepareWorkspace(repoUrl, options) {
1861
1883
  }
1862
1884
  const { stdout: commitHash } = await runGitCommand(["rev-parse", "HEAD"], workspacePath);
1863
1885
  const startingCommit = commitHash.trim();
1886
+ console.log("");
1887
+ try {
1888
+ const librarySkills = await fetchSkillsLibrary(authToken);
1889
+ const validationSkills = getValidationSkills();
1890
+ const allSkills = [...librarySkills, ...validationSkills];
1891
+ if (allSkills.length > 0) {
1892
+ await injectSkills(workspacePath, allSkills);
1893
+ } else {
1894
+ console.log("\u{1F4DA} No skills to inject (empty library)");
1895
+ }
1896
+ } catch (skillError) {
1897
+ console.warn("\u26A0\uFE0F Skill injection failed (agent will continue):", skillError);
1898
+ }
1864
1899
  return { path: workspacePath, startingCommit, cleanup };
1865
1900
  } catch (error) {
1866
1901
  await cleanup();
@@ -1872,7 +1907,7 @@ async function prepareWorkspace(repoUrl, options) {
1872
1907
  var __filename2 = fileURLToPath(import.meta.url);
1873
1908
  var __dirname2 = dirname(__filename2);
1874
1909
  var packageJson = JSON.parse(
1875
- readFileSync(join3(__dirname2, "../package.json"), "utf-8")
1910
+ readFileSync(join4(__dirname2, "../package.json"), "utf-8")
1876
1911
  );
1877
1912
  var INITIAL_POLL_INTERVAL = parseInt(process.env.WORKER_INITIAL_POLL_INTERVAL || "2000", 10);
1878
1913
  var MAX_POLL_INTERVAL = parseInt(process.env.WORKER_MAX_POLL_INTERVAL || "5000", 10);
@@ -1887,7 +1922,6 @@ var apiClient = null;
1887
1922
  var stats = {
1888
1923
  startTime: Date.now(),
1889
1924
  prepared: 0,
1890
- learned: 0,
1891
1925
  executed: 0,
1892
1926
  errors: 0
1893
1927
  };
@@ -1904,8 +1938,8 @@ function formatDuration(ms) {
1904
1938
  }
1905
1939
  function printStatus() {
1906
1940
  const uptime = formatDuration(Date.now() - stats.startTime);
1907
- const total = stats.prepared + stats.learned + stats.executed;
1908
- console.log(`Status: ${total} actions (${stats.prepared} prepared, ${stats.learned} learned, ${stats.executed} executed, ${stats.errors} errors) | Uptime: ${uptime}`);
1941
+ const total = stats.prepared + stats.executed;
1942
+ console.log(`Status: ${total} actions (${stats.prepared} prepared, ${stats.executed} executed, ${stats.errors} errors) | Uptime: ${uptime}`);
1909
1943
  }
1910
1944
  async function cleanupAndExit() {
1911
1945
  if (currentClaim && apiClient) {
@@ -1966,10 +2000,6 @@ async function runLocalAgent(options) {
1966
2000
  console.log(`\u{1F477} Worker ID: ${workerId}`);
1967
2001
  console.log(`\u{1F504} Starting continuous worker loop...
1968
2002
  `);
1969
- if (options?.skipLearning) {
1970
- console.log(`\u23ED\uFE0F Skipping learning runs (--skip-learning)
1971
- `);
1972
- }
1973
2003
  console.log(`\u{1F4A1} Press Ctrl+C to gracefully shutdown and release any claimed work
1974
2004
  `);
1975
2005
  let currentPollInterval = INITIAL_POLL_INTERVAL;
@@ -2026,17 +2056,12 @@ async function runLocalAgent(options) {
2026
2056
  };
2027
2057
  }
2028
2058
  let phase;
2029
- if (actionDetail.done && actionDetail.reviewed && !actionDetail.learned && !options?.skipLearning) {
2030
- phase = "learn";
2031
- } else if (!actionDetail.prepared) {
2059
+ if (!actionDetail.prepared) {
2032
2060
  phase = "prepare";
2033
2061
  } else if (!actionDetail.done) {
2034
2062
  phase = "execute";
2035
2063
  } else {
2036
- const isLearningOnlyAndSkipped = actionDetail.done && actionDetail.reviewed && !actionDetail.learned && options?.skipLearning;
2037
- if (!isLearningOnlyAndSkipped) {
2038
- console.log(`\u23ED\uFE0F Skipping action "${actionDetail.title}" - done but not yet reviewed`);
2039
- }
2064
+ console.log(`\u23ED\uFE0F Skipping action "${actionDetail.title}" - already completed`);
2040
2065
  if (currentClaim && apiClient) {
2041
2066
  try {
2042
2067
  await apiClient.releaseClaim({
@@ -2057,7 +2082,7 @@ async function runLocalAgent(options) {
2057
2082
  let workspacePath;
2058
2083
  let cleanup;
2059
2084
  let startingCommit;
2060
- const needsRepo = phase !== "learn" && repoUrl;
2085
+ const needsRepo = repoUrl;
2061
2086
  try {
2062
2087
  if (needsRepo) {
2063
2088
  const workspace = await prepareWorkspace(repoUrl, {
@@ -2068,12 +2093,8 @@ async function runLocalAgent(options) {
2068
2093
  cleanup = workspace.cleanup;
2069
2094
  startingCommit = workspace.startingCommit;
2070
2095
  } else {
2071
- if (phase === "learn") {
2072
- console.log(`\u{1F4C2} Learning phase - creating blank workspace`);
2073
- } else {
2074
- console.log(`\u{1F4C2} No repository configured - creating blank workspace`);
2075
- }
2076
- workspacePath = await mkdtemp2(join3(tmpdir2(), "cg-workspace-"));
2096
+ console.log(`\u{1F4C2} No repository configured - creating blank workspace`);
2097
+ workspacePath = await mkdtemp2(join4(tmpdir2(), "cg-workspace-"));
2077
2098
  console.log(` \u2192 ${workspacePath}`);
2078
2099
  cleanup = async () => {
2079
2100
  try {
@@ -2100,30 +2121,6 @@ async function runLocalAgent(options) {
2100
2121
  currentClaim = null;
2101
2122
  continue;
2102
2123
  }
2103
- if (phase === "learn") {
2104
- try {
2105
- await runLearn(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
2106
- stats.learned++;
2107
- console.log(`Learning extracted: ${actionDetail.title}`);
2108
- } catch (learnError) {
2109
- stats.errors++;
2110
- console.error(`Error during learning: ${learnError.message}. Continuing...`);
2111
- } finally {
2112
- if (currentClaim && apiClient) {
2113
- try {
2114
- await apiClient.releaseClaim({
2115
- action_id: currentClaim.actionId,
2116
- worker_id: currentClaim.workerId,
2117
- claim_id: currentClaim.claimId
2118
- });
2119
- } catch (releaseError) {
2120
- console.error("\u26A0\uFE0F Failed to release claim:", releaseError.message);
2121
- }
2122
- }
2123
- currentClaim = null;
2124
- }
2125
- continue;
2126
- }
2127
2124
  try {
2128
2125
  await runExecute(actionDetail.id, { cwd: workspacePath, startingCommit, model: options?.forceModel });
2129
2126
  stats.executed++;
@@ -2174,15 +2171,14 @@ async function runLocalAgent(options) {
2174
2171
  var __filename3 = fileURLToPath2(import.meta.url);
2175
2172
  var __dirname3 = dirname2(__filename3);
2176
2173
  var packageJson2 = JSON.parse(
2177
- readFileSync2(join4(__dirname3, "../package.json"), "utf-8")
2174
+ readFileSync2(join5(__dirname3, "../package.json"), "utf-8")
2178
2175
  );
2179
2176
  var program = new Command();
2180
2177
  program.name("contextgraph-agent").description("Autonomous agent for contextgraph action execution").version(packageJson2.version);
2181
- 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-learning", "Skip learning runs and only do preparation and execution").action(async (options) => {
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) => {
2182
2179
  try {
2183
2180
  await runLocalAgent({
2184
- forceModel: options.forceHaiku ? "claude-haiku-4-5-20251001" : void 0,
2185
- skipLearning: options.skipLearning || false
2181
+ forceModel: options.forceHaiku ? "claude-haiku-4-5-20251001" : void 0
2186
2182
  });
2187
2183
  } catch (error) {
2188
2184
  if (error instanceof Error) {
@@ -2221,14 +2217,6 @@ program.command("execute").argument("<action-id>", "Action ID to execute").descr
2221
2217
  process.exit(1);
2222
2218
  }
2223
2219
  });
2224
- program.command("learn").argument("<action-id>", "Action ID to learn from").description("Extract learnings from a completed action").action(async (actionId) => {
2225
- try {
2226
- await runLearn(actionId);
2227
- } catch (error) {
2228
- console.error("Error learning from action:", error instanceof Error ? error.message : error);
2229
- process.exit(1);
2230
- }
2231
- });
2232
2220
  program.command("whoami").description("Show current authentication status").action(async () => {
2233
2221
  try {
2234
2222
  const credentials = await loadCredentials();