@graph-tl/graph 0.1.6 → 0.1.9

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.
@@ -2,6 +2,9 @@
2
2
  import {
3
3
  getLicenseTier
4
4
  } from "./chunk-WKOEKYTF.js";
5
+ import {
6
+ handleAgentConfig
7
+ } from "./chunk-ILTJI4ZN.js";
5
8
  import {
6
9
  EngineError,
7
10
  ValidationError,
@@ -25,14 +28,17 @@ import {
25
28
  requireString,
26
29
  setDbPath,
27
30
  updateNode
28
- } from "./chunk-NWCUIW6D.js";
31
+ } from "./chunk-TWT5GUXW.js";
29
32
 
30
33
  // src/server.ts
31
34
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
32
35
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
33
36
  import {
34
37
  CallToolRequestSchema,
35
- ListToolsRequestSchema
38
+ ListToolsRequestSchema,
39
+ ListResourcesRequestSchema,
40
+ ListResourceTemplatesRequestSchema,
41
+ ReadResourceRequestSchema
36
42
  } from "@modelcontextprotocol/sdk/types.js";
37
43
 
38
44
  // src/tools/open.ts
@@ -43,15 +49,28 @@ function handleOpen(input, agent) {
43
49
  return { projects: listProjects() };
44
50
  }
45
51
  let root = getProjectRoot(project);
52
+ let isNew = false;
46
53
  if (!root) {
47
54
  root = createNode({
48
55
  project,
49
56
  summary: goal ?? project,
57
+ discovery: "pending",
50
58
  agent
51
59
  });
60
+ isNew = true;
52
61
  }
53
62
  const summary = getProjectSummary(project);
54
- return { project, root, summary };
63
+ const result = { project, root, summary };
64
+ if (isNew) {
65
+ result.hint = `New project created. Discovery is pending \u2014 interview the user to understand scope and goals, then set discovery to "done" via graph_update before decomposing with graph_plan.`;
66
+ } else if (root.discovery === "pending") {
67
+ result.hint = `Discovery is still pending on this project. Complete the discovery interview, then set discovery to "done" via graph_update.`;
68
+ } else if (summary.actionable > 0) {
69
+ result.hint = `${summary.actionable} actionable task(s). Use graph_next to claim one.`;
70
+ } else if (summary.unresolved > 0 && summary.actionable === 0) {
71
+ result.hint = `All remaining tasks are blocked. Check dependencies with graph_query.`;
72
+ }
73
+ return result;
55
74
  }
56
75
 
57
76
  // src/edges.ts
@@ -220,6 +239,12 @@ function handlePlan(input, agent) {
220
239
  let project;
221
240
  if (parentId) {
222
241
  const parentNode = getNode(parentId);
242
+ if (parentNode.discovery === "pending") {
243
+ throw new EngineError(
244
+ "discovery_pending",
245
+ `Cannot add children to "${parentNode.summary}" \u2014 discovery is pending. Complete the discovery interview first (set discovery to 'done' via graph_update), then decompose.`
246
+ );
247
+ }
223
248
  project = parentNode.project;
224
249
  } else {
225
250
  throw new Error(
@@ -286,16 +311,21 @@ function handleUpdate(input, agent) {
286
311
  const resolvedIds = [];
287
312
  let project = null;
288
313
  for (const entry of updates) {
314
+ let evidence = entry.add_evidence;
315
+ if (entry.resolved_reason) {
316
+ evidence = [...evidence ?? [], { type: "note", ref: entry.resolved_reason }];
317
+ }
289
318
  const node = updateNode({
290
319
  node_id: entry.node_id,
291
320
  agent,
292
321
  resolved: entry.resolved,
322
+ discovery: entry.discovery,
293
323
  state: entry.state,
294
324
  summary: entry.summary,
295
325
  properties: entry.properties,
296
326
  add_context_links: entry.add_context_links,
297
327
  remove_context_links: entry.remove_context_links,
298
- add_evidence: entry.add_evidence
328
+ add_evidence: evidence
299
329
  });
300
330
  updated.push({ node_id: node.id, rev: node.rev });
301
331
  if (entry.resolved === true) {
@@ -303,10 +333,42 @@ function handleUpdate(input, agent) {
303
333
  project = node.project;
304
334
  }
305
335
  }
336
+ const autoResolved = [];
337
+ if (resolvedIds.length > 0) {
338
+ const seen = new Set(resolvedIds);
339
+ const queue = [...resolvedIds];
340
+ while (queue.length > 0) {
341
+ const nodeId = queue.shift();
342
+ const node = getNode(nodeId);
343
+ if (!node?.parent) continue;
344
+ const parentId = node.parent;
345
+ if (seen.has(parentId)) continue;
346
+ seen.add(parentId);
347
+ const parent = getNode(parentId);
348
+ if (!parent || parent.resolved) continue;
349
+ const children = getChildren(parentId);
350
+ if (children.length === 0) continue;
351
+ if (children.every((c) => c.resolved)) {
352
+ const resolved = updateNode({
353
+ node_id: parentId,
354
+ agent,
355
+ resolved: true,
356
+ add_evidence: [{ type: "note", ref: "Auto-resolved: all children completed" }]
357
+ });
358
+ updated.push({ node_id: resolved.id, rev: resolved.rev });
359
+ resolvedIds.push(parentId);
360
+ autoResolved.push({ node_id: parentId, summary: parent.summary });
361
+ queue.push(parentId);
362
+ }
363
+ }
364
+ }
306
365
  const result = { updated };
307
366
  if (resolvedIds.length > 0 && project) {
308
367
  result.newly_actionable = findNewlyActionable(project, resolvedIds);
309
368
  }
369
+ if (autoResolved.length > 0) {
370
+ result.auto_resolved = autoResolved;
371
+ }
310
372
  return result;
311
373
  }
312
374
 
@@ -373,6 +435,7 @@ function buildNodeTree(nodeId, currentDepth, maxDepth) {
373
435
  id: node.id,
374
436
  summary: node.summary,
375
437
  resolved: node.resolved,
438
+ discovery: node.discovery,
376
439
  state: node.state
377
440
  };
378
441
  if (children.length === 0) {
@@ -866,9 +929,26 @@ function handleHistory(input) {
866
929
 
867
930
  // src/tools/onboard.ts
868
931
  function handleOnboard(input) {
869
- const project = requireString(input?.project, "project");
870
932
  const evidenceLimit = optionalNumber(input?.evidence_limit, "evidence_limit", 1, 50) ?? 20;
871
933
  const db = getDb();
934
+ let project = optionalString(input?.project, "project");
935
+ if (!project) {
936
+ const projects = listProjects();
937
+ if (projects.length === 0) {
938
+ return {
939
+ projects: [],
940
+ hint: 'No projects yet. Create one with graph_open({ project: "my-project", goal: "..." }).'
941
+ };
942
+ }
943
+ if (projects.length === 1) {
944
+ project = projects[0].project;
945
+ } else {
946
+ return {
947
+ projects,
948
+ hint: `${projects.length} projects found. Call graph_onboard with a specific project name.`
949
+ };
950
+ }
951
+ }
872
952
  const root = getProjectRoot(project);
873
953
  if (!root) {
874
954
  throw new EngineError("project_not_found", `Project not found: ${project}`);
@@ -885,6 +965,7 @@ function handleOnboard(input) {
885
965
  id: child.id,
886
966
  summary: child.summary,
887
967
  resolved: child.resolved === 1,
968
+ discovery: child.discovery,
888
969
  children: grandchildren.map((gc) => ({
889
970
  id: gc.id,
890
971
  summary: gc.summary,
@@ -919,6 +1000,7 @@ function handleOnboard(input) {
919
1000
  }
920
1001
  }
921
1002
  const context_links = [...linkSet].sort();
1003
+ const knowledgeRows = db.prepare("SELECT key, content, updated_at FROM knowledge WHERE project = ? ORDER BY updated_at DESC").all(project);
922
1004
  const actionableRows = db.prepare(
923
1005
  `SELECT n.id, n.summary, n.properties FROM nodes n
924
1006
  WHERE n.project = ? AND n.resolved = 0
@@ -941,115 +1023,88 @@ function handleOnboard(input) {
941
1023
  summary: row.summary,
942
1024
  properties: JSON.parse(row.properties)
943
1025
  }));
1026
+ const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
1027
+ const recentlyResolvedRows = db.prepare(
1028
+ `SELECT id, summary, updated_at,
1029
+ (SELECT json_extract(value, '$.agent') FROM json_each(evidence) ORDER BY json_extract(value, '$.timestamp') DESC LIMIT 1) as last_agent
1030
+ FROM nodes
1031
+ WHERE project = ? AND resolved = 1 AND updated_at > ?
1032
+ ORDER BY updated_at DESC
1033
+ LIMIT 10`
1034
+ ).all(project, oneDayAgo);
1035
+ const recently_resolved = recentlyResolvedRows.map((row) => ({
1036
+ id: row.id,
1037
+ summary: row.summary,
1038
+ resolved_at: row.updated_at,
1039
+ agent: row.last_agent ?? "unknown"
1040
+ }));
1041
+ const lastActivityRow = db.prepare("SELECT MAX(updated_at) as last FROM nodes WHERE project = ?").get(project);
1042
+ const last_activity = lastActivityRow.last;
1043
+ let hint;
1044
+ if (root.discovery === "pending") {
1045
+ hint = `Discovery is pending. Interview the user to understand scope and goals, write knowledge entries with findings, then set discovery to "done" via graph_update before decomposing with graph_plan.`;
1046
+ } else if (actionable.length > 0) {
1047
+ const recentNote = recently_resolved.length > 0 ? ` ${recently_resolved.length} task(s) resolved recently.` : "";
1048
+ hint = `${actionable.length} actionable task(s) ready.${recentNote} Use graph_next({ project: "${project}", claim: true }) to claim one.`;
1049
+ } else if (summary.unresolved > 0 && summary.actionable === 0) {
1050
+ hint = `All remaining tasks are blocked. Check dependencies with graph_query.`;
1051
+ } else if (summary.total <= 1 && root.discovery !== "pending") {
1052
+ hint = `Project is empty \u2014 use graph_plan to decompose the goal into tasks.`;
1053
+ }
944
1054
  return {
945
1055
  project,
1056
+ goal: root.summary,
1057
+ discovery: root.discovery,
1058
+ hint,
946
1059
  summary,
947
1060
  tree,
948
1061
  recent_evidence,
949
1062
  context_links,
1063
+ knowledge: knowledgeRows,
1064
+ recently_resolved,
1065
+ last_activity,
950
1066
  actionable
951
1067
  };
952
1068
  }
953
1069
 
954
- // src/tools/agent-config.ts
955
- var AGENT_PROMPT = `---
956
- name: graph
957
- description: Use this agent for tasks tracked in Graph. Enforces the claim-work-resolve workflow \u2014 always checks graph_next before working, adds new work to the graph before executing, and resolves with evidence.
958
- tools: Read, Edit, Write, Bash, Glob, Grep, Task(Explore)
959
- model: sonnet
960
- ---
961
-
962
- You are a graph-optimized agent. You execute tasks tracked in a Graph project. Follow this workflow strictly. The human directs, you execute through the graph.
963
-
964
- # Workflow
965
-
966
- ## 1. ORIENT
967
- On your first call, orient yourself:
968
- \`\`\`
969
- graph_onboard({ project: "<project-name>" })
970
- \`\`\`
971
- Read the summary, recent evidence, context links, and actionable tasks. Understand what was done and what's left.
972
-
973
- ## 2. CLAIM
974
- Get your next task:
975
- \`\`\`
976
- graph_next({ project: "<project-name>", claim: true })
977
- \`\`\`
978
- Read the task summary, ancestor chain (for scope), resolved dependencies (for context on what was done before you), and context links (for files to look at).
979
-
980
- ## 3. PLAN
981
- If you discover work that isn't in the graph, add it BEFORE executing:
982
- \`\`\`
983
- graph_plan({ nodes: [{ ref: "new-work", parent_ref: "<parent-id>", summary: "..." }] })
984
- \`\`\`
985
- Never execute ad-hoc work. The graph is the source of truth.
986
-
987
- When decomposing work:
988
- - Set dependencies on LEAF nodes, not parent nodes. If "Page A" depends on "Layout", the dependency is from "Page A" to "Layout", not from the "Pages" parent to "Layout".
989
- - Keep tasks small and specific. A task should be completable in one session.
990
- - Parent nodes are organizational \u2014 they resolve when all children resolve. Don't put work in parent nodes.
991
-
992
- ## 4. WORK
993
- Execute the claimed task. While working:
994
- - Annotate key code changes with \`// [sl:nodeId]\` where nodeId is the task you're working on
995
- - This creates a traceable link from code back to the task, its evidence, and its history
996
- - Build and run tests before considering a task done
997
-
998
- ## 5. RESOLVE
999
- When done, resolve the task with evidence:
1000
- \`\`\`
1001
- graph_update({ updates: [{
1002
- node_id: "<task-id>",
1003
- resolved: true,
1004
- add_evidence: [
1005
- { type: "note", ref: "What you did and why" },
1006
- { type: "git", ref: "<commit-hash> \u2014 <summary>" },
1007
- { type: "test", ref: "Test results" }
1008
- ],
1009
- add_context_links: ["path/to/files/you/touched"]
1010
- }] })
1011
- \`\`\`
1012
- Evidence is mandatory. At minimum, include one note explaining what you did.
1013
-
1014
- ## 6. PAUSE
1015
- After resolving a task, STOP. Tell the user:
1016
- - What you just completed
1017
- - What the next actionable task is
1018
- - Wait for the user to say "continue" before claiming the next task
1019
-
1020
- The user controls the pace. Do not auto-claim the next task.
1021
-
1022
- # Rules
1023
-
1024
- - NEVER start work without a claimed task
1025
- - NEVER resolve without evidence
1026
- - NEVER execute ad-hoc work \u2014 add it to the graph first via graph_plan
1027
- - NEVER auto-continue to the next task \u2014 pause and let the user decide
1028
- - ALWAYS build and test before resolving
1029
- - ALWAYS include context_links for files you modified when resolving
1030
- - If a parent task becomes actionable (all children resolved), resolve it with a summary of what its children accomplished
1031
- - If you're approaching context limits, ensure your current task's state is captured (update with evidence even if not fully resolved) so the next agent can pick up where you left off
1032
-
1033
- # Common mistakes to avoid
1034
-
1035
- - Setting dependencies on parent nodes instead of leaf nodes
1036
- - Running project scaffolding tools (create-next-app, etc.) before planning in the graph
1037
- - Resolving tasks without running tests
1038
- - Doing work that isn't tracked in the graph
1039
- - Continuing to the next task without pausing for user review
1040
- `;
1041
- function handleAgentConfig(dbPath) {
1042
- const tier = getLicenseTier(dbPath);
1043
- if (tier !== "pro") {
1044
- throw new EngineError(
1045
- "free_tier_limit",
1046
- "The graph-optimized agent configuration is a pro feature. Activate a license key to unlock it."
1070
+ // src/tools/tree.ts
1071
+ function buildTree(node, currentDepth, maxDepth, stats) {
1072
+ stats.total++;
1073
+ if (node.resolved) stats.resolved++;
1074
+ const children = getChildren(node.id);
1075
+ const treeNode = {
1076
+ id: node.id,
1077
+ summary: node.summary,
1078
+ resolved: node.resolved,
1079
+ properties: node.properties
1080
+ };
1081
+ if (children.length === 0) return treeNode;
1082
+ if (currentDepth < maxDepth) {
1083
+ treeNode.children = children.map(
1084
+ (child) => buildTree(child, currentDepth + 1, maxDepth, stats)
1047
1085
  );
1086
+ } else {
1087
+ treeNode.child_count = children.length;
1088
+ }
1089
+ return treeNode;
1090
+ }
1091
+ function handleTree(input) {
1092
+ const project = requireString(input?.project, "project");
1093
+ const depth = optionalNumber(input?.depth, "depth", 1, 20) ?? 10;
1094
+ const root = getProjectRoot(project);
1095
+ if (!root) {
1096
+ throw new EngineError("project_not_found", `Project not found: ${project}`);
1048
1097
  }
1098
+ const stats = { total: 0, resolved: 0 };
1099
+ const tree = buildTree(root, 0, depth, stats);
1049
1100
  return {
1050
- agent_file: AGENT_PROMPT,
1051
- install_path: ".claude/agents/graph.md",
1052
- instructions: "Save the agent_file content to .claude/agents/graph.md in your project root. Claude Code will automatically discover it and use it when tasks match the agent description."
1101
+ project,
1102
+ tree,
1103
+ stats: {
1104
+ total: stats.total,
1105
+ resolved: stats.resolved,
1106
+ unresolved: stats.total - stats.resolved
1107
+ }
1053
1108
  };
1054
1109
  }
1055
1110
 
@@ -1132,10 +1187,10 @@ function handleKnowledgeSearch(input) {
1132
1187
 
1133
1188
  // src/gates.ts
1134
1189
  var FREE_LIMITS = {
1135
- maxProjects: 1,
1136
- maxNodesPerProject: 50,
1137
- onboardEvidenceLimit: 5,
1138
- scopeEnabled: false
1190
+ maxProjects: Infinity,
1191
+ maxNodesPerProject: Infinity,
1192
+ onboardEvidenceLimit: 50,
1193
+ scopeEnabled: true
1139
1194
  };
1140
1195
  function checkNodeLimit(tier, project, adding) {
1141
1196
  if (tier === "pro") return;
@@ -1163,9 +1218,11 @@ function capEvidenceLimit(tier, requested) {
1163
1218
  const max = tier === "pro" ? requested ?? 20 : FREE_LIMITS.onboardEvidenceLimit;
1164
1219
  return Math.min(requested ?? max, tier === "pro" ? 50 : FREE_LIMITS.onboardEvidenceLimit);
1165
1220
  }
1166
- function checkScope(tier, scope) {
1167
- if (tier === "pro") return scope;
1168
- return void 0;
1221
+ function checkKnowledgeTier(_tier) {
1222
+ return;
1223
+ }
1224
+ function checkScope(_tier, scope) {
1225
+ return scope;
1169
1226
  }
1170
1227
 
1171
1228
  // src/server.ts
@@ -1310,7 +1367,7 @@ var TOOLS = [
1310
1367
  },
1311
1368
  {
1312
1369
  name: "graph_update",
1313
- description: "Update one or more nodes. Can change resolved, state, summary, properties (merged), context_links, and add evidence. When resolving nodes, returns newly_actionable \u2014 nodes that became unblocked. ENFORCED: Resolving a node requires evidence \u2014 the engine rejects resolved=true if the node has no existing evidence and no add_evidence in the call. Include at least one add_evidence entry (type: 'git' for commits, 'note' for what was done and why, 'test' for results). Also add context_links to files you modified.",
1370
+ description: "Update one or more nodes. Can change resolved, state, summary, properties (merged), context_links, and add evidence. When resolving nodes, returns newly_actionable \u2014 nodes that became unblocked. ENFORCED: Resolving a node requires evidence \u2014 use resolved_reason (shorthand, auto-creates note) or add_evidence array (type: 'git' for commits, 'note' for what was done and why, 'test' for results). Also add context_links to files you modified.",
1314
1371
  inputSchema: {
1315
1372
  type: "object",
1316
1373
  properties: {
@@ -1321,6 +1378,8 @@ var TOOLS = [
1321
1378
  properties: {
1322
1379
  node_id: { type: "string" },
1323
1380
  resolved: { type: "boolean" },
1381
+ resolved_reason: { type: "string", description: "Shorthand: auto-creates a note evidence entry. Use instead of add_evidence for simple cases." },
1382
+ discovery: { type: "string", description: "Discovery phase status: 'pending' or 'done'. Set to 'done' after completing discovery interview." },
1324
1383
  state: { description: "Agent-defined state, any type" },
1325
1384
  summary: { type: "string" },
1326
1385
  properties: {
@@ -1472,18 +1531,32 @@ var TOOLS = [
1472
1531
  inputSchema: {
1473
1532
  type: "object",
1474
1533
  properties: {
1475
- project: { type: "string", description: "Project name (e.g. 'my-project')" },
1534
+ project: { type: "string", description: "Project name (e.g. 'my-project'). Omit to auto-select (works when there's exactly one project)." },
1476
1535
  evidence_limit: {
1477
1536
  type: "number",
1478
1537
  description: "Max evidence entries to return (default 20, max 50)"
1479
1538
  }
1539
+ }
1540
+ }
1541
+ },
1542
+ {
1543
+ name: "graph_tree",
1544
+ description: "Full tree visualization for a project. Returns the complete task hierarchy with resolve status. Use when you need to see the whole project structure beyond graph_context's single-node neighborhood.",
1545
+ inputSchema: {
1546
+ type: "object",
1547
+ properties: {
1548
+ project: { type: "string", description: "Project name (e.g. 'my-project')" },
1549
+ depth: {
1550
+ type: "number",
1551
+ description: "Max tree depth to return (default 10, max 20)"
1552
+ }
1480
1553
  },
1481
1554
  required: ["project"]
1482
1555
  }
1483
1556
  },
1484
1557
  {
1485
1558
  name: "graph_agent_config",
1486
- description: "Returns the graph-optimized agent configuration file for Claude Code. Pro tier only. Save the returned content to .claude/agents/graph.md to enable the graph workflow agent.",
1559
+ description: "Returns the graph-optimized agent configuration file for Claude Code. Save the returned content to .claude/agents/graph.md to enable the graph workflow agent.",
1487
1560
  inputSchema: {
1488
1561
  type: "object",
1489
1562
  properties: {}
@@ -1544,7 +1617,7 @@ async function startServer() {
1544
1617
  const tier = getLicenseTier(DB_PATH);
1545
1618
  const server = new Server(
1546
1619
  { name: "graph", version: PKG_VERSION },
1547
- { capabilities: { tools: {} } }
1620
+ { capabilities: { tools: {}, resources: {} } }
1548
1621
  );
1549
1622
  checkForUpdate();
1550
1623
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
@@ -1558,7 +1631,7 @@ async function startServer() {
1558
1631
  case "graph_open": {
1559
1632
  const openArgs = args;
1560
1633
  if (openArgs?.project) {
1561
- const { getProjectRoot: getProjectRoot2 } = await import("./nodes-7UZATPPU.js");
1634
+ const { getProjectRoot: getProjectRoot2 } = await import("./nodes-4OJBNDHG.js");
1562
1635
  if (!getProjectRoot2(openArgs.project)) {
1563
1636
  checkProjectLimit(tier);
1564
1637
  }
@@ -1569,7 +1642,7 @@ async function startServer() {
1569
1642
  case "graph_plan": {
1570
1643
  const planArgs = args;
1571
1644
  if (planArgs?.nodes?.length > 0) {
1572
- const { getNode: getNode2 } = await import("./nodes-7UZATPPU.js");
1645
+ const { getNode: getNode2 } = await import("./nodes-4OJBNDHG.js");
1573
1646
  const firstParent = planArgs.nodes[0]?.parent_ref;
1574
1647
  if (firstParent && typeof firstParent === "string" && !planArgs.nodes.some((n) => n.ref === firstParent)) {
1575
1648
  const parentNode = getNode2(firstParent);
@@ -1613,19 +1686,26 @@ async function startServer() {
1613
1686
  result = handleOnboard(onboardArgs);
1614
1687
  break;
1615
1688
  }
1689
+ case "graph_tree":
1690
+ result = handleTree(args);
1691
+ break;
1616
1692
  case "graph_agent_config":
1617
- result = handleAgentConfig(DB_PATH);
1693
+ result = handleAgentConfig();
1618
1694
  break;
1619
1695
  case "graph_knowledge_write":
1696
+ checkKnowledgeTier(tier);
1620
1697
  result = handleKnowledgeWrite(args, AGENT_IDENTITY);
1621
1698
  break;
1622
1699
  case "graph_knowledge_read":
1700
+ checkKnowledgeTier(tier);
1623
1701
  result = handleKnowledgeRead(args);
1624
1702
  break;
1625
1703
  case "graph_knowledge_delete":
1704
+ checkKnowledgeTier(tier);
1626
1705
  result = handleKnowledgeDelete(args);
1627
1706
  break;
1628
1707
  case "graph_knowledge_search":
1708
+ checkKnowledgeTier(tier);
1629
1709
  result = handleKnowledgeSearch(args);
1630
1710
  break;
1631
1711
  default:
@@ -1659,6 +1739,79 @@ async function startServer() {
1659
1739
  };
1660
1740
  }
1661
1741
  });
1742
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
1743
+ resourceTemplates: [
1744
+ {
1745
+ uriTemplate: "graph://{project}/tree",
1746
+ name: "Project Tree",
1747
+ description: "Full task tree for a project with resolve status",
1748
+ mimeType: "application/json"
1749
+ },
1750
+ {
1751
+ uriTemplate: "graph://{project}/knowledge",
1752
+ name: "Project Knowledge",
1753
+ description: "All knowledge entries for a project",
1754
+ mimeType: "application/json"
1755
+ },
1756
+ {
1757
+ uriTemplate: "graph://{project}/knowledge/{key}",
1758
+ name: "Knowledge Entry",
1759
+ description: "A specific knowledge entry",
1760
+ mimeType: "application/json"
1761
+ }
1762
+ ]
1763
+ }));
1764
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
1765
+ try {
1766
+ const { listProjects: listProjects2 } = await import("./nodes-4OJBNDHG.js");
1767
+ const projects = listProjects2();
1768
+ const resources = projects.flatMap((p) => [
1769
+ {
1770
+ uri: `graph://${p.project}/tree`,
1771
+ name: `${p.project} \u2014 Tree`,
1772
+ description: `Task tree: ${p.total} nodes (${p.resolved} resolved)`,
1773
+ mimeType: "application/json"
1774
+ },
1775
+ {
1776
+ uri: `graph://${p.project}/knowledge`,
1777
+ name: `${p.project} \u2014 Knowledge`,
1778
+ description: `Knowledge entries for ${p.project}`,
1779
+ mimeType: "application/json"
1780
+ }
1781
+ ]);
1782
+ return { resources };
1783
+ } catch {
1784
+ return { resources: [] };
1785
+ }
1786
+ });
1787
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1788
+ const uri = request.params.uri;
1789
+ const match = uri.match(/^graph:\/\/([^/]+)\/(.+)$/);
1790
+ if (!match) {
1791
+ throw new Error(`Invalid resource URI: ${uri}`);
1792
+ }
1793
+ const [, project, path] = match;
1794
+ if (path === "tree") {
1795
+ const result = handleTree({ project });
1796
+ return {
1797
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(result, null, 2) }]
1798
+ };
1799
+ }
1800
+ if (path === "knowledge") {
1801
+ const result = handleKnowledgeRead({ project });
1802
+ return {
1803
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(result, null, 2) }]
1804
+ };
1805
+ }
1806
+ const knowledgeMatch = path.match(/^knowledge\/(.+)$/);
1807
+ if (knowledgeMatch) {
1808
+ const result = handleKnowledgeRead({ project, key: knowledgeMatch[1] });
1809
+ return {
1810
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(result, null, 2) }]
1811
+ };
1812
+ }
1813
+ throw new Error(`Unknown resource path: ${path}`);
1814
+ });
1662
1815
  const transport = new StdioServerTransport();
1663
1816
  await server.connect(transport);
1664
1817
  const checkpointInterval = setInterval(() => {
@@ -1681,4 +1834,4 @@ async function startServer() {
1681
1834
  export {
1682
1835
  startServer
1683
1836
  };
1684
- //# sourceMappingURL=server-6MALRQNH.js.map
1837
+ //# sourceMappingURL=server-L7KF2UWS.js.map