ygl-trees 0.68.0 → 0.70.0

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.
@@ -0,0 +1,276 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-C96Ew50Q.cjs");
2
+ let node_readline = require("node:readline");
3
+ node_readline = require_rolldown_runtime.__toESM(node_readline);
4
+ //#region packages/ops/tools/src/mcp.ts
5
+ /**
6
+ * MCP (Model Context Protocol) Server — stdio transport.
7
+ *
8
+ * Eight-tool surface designed by the unified three-pillar theory (R8 research):
9
+ * agent_brief · agent_match · kb_search · claim_evaluate
10
+ * tree_state · produce_report · doctor · task_advance
11
+ *
12
+ * Each tool routes to the CLI --json backend, inheriting its frozen contracts
13
+ * (idempotent writes, pure JSON errors, snake_case fields). Agents call tools
14
+ * directly — no command-line parsing layer.
15
+ *
16
+ * Protocol: JSON-RPC 2.0 over stdio (one message per line).
17
+ * → {"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}
18
+ * ← {"jsonrpc":"2.0","id":1,"result":{...}}
19
+ *
20
+ * @module @ygl/ops-tools/mcp
21
+ */
22
+ /** Build a CLI invocation and capture JSON stdout.
23
+ * Uses process.argv[1] (the running script — bundle or source) to re-invoke
24
+ * the CLI as a child process. This works in both dev (dist/apps/...) and
25
+ * bundle (dist/bundle/bin.cjs) contexts. */
26
+ function cliJson(parts) {
27
+ const { spawn } = require("node:child_process");
28
+ const self = process.argv[1];
29
+ return new Promise((resolve, reject) => {
30
+ const child = spawn(process.execPath, [
31
+ self,
32
+ "--json",
33
+ ...parts
34
+ ], {
35
+ env: {
36
+ ...process.env,
37
+ YGL_SERVE: "1",
38
+ YGL_MCP_CHILD: "1"
39
+ },
40
+ stdio: [
41
+ "ignore",
42
+ "pipe",
43
+ "pipe"
44
+ ]
45
+ });
46
+ let out = "";
47
+ let err = "";
48
+ child.stdout.on("data", (d) => out += d);
49
+ child.stderr.on("data", (d) => err += d);
50
+ child.on("close", (code) => {
51
+ if (out.trim()) try {
52
+ resolve(JSON.parse(out));
53
+ return;
54
+ } catch {}
55
+ reject(/* @__PURE__ */ new Error(`CLI rc=${code}: ${err.slice(0, 200) || out.slice(0, 200)}`));
56
+ });
57
+ child.on("error", (e) => reject(e));
58
+ });
59
+ }
60
+ function buildTools() {
61
+ return [
62
+ {
63
+ name: "agent_brief",
64
+ description: "Current workspace state: project, progress, counts, next actions, available skills. Always start here.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {}
68
+ },
69
+ execute: () => cliJson(["agent", "brief"])
70
+ },
71
+ {
72
+ name: "agent_match",
73
+ description: "Discover which skill handles a task. Returns matched skills with relevance scores.",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: { query: {
77
+ type: "string",
78
+ description: "Task description to match"
79
+ } },
80
+ required: ["query"]
81
+ },
82
+ execute: (a) => cliJson([
83
+ "agent",
84
+ "match",
85
+ String(a.query)
86
+ ])
87
+ },
88
+ {
89
+ name: "kb_search",
90
+ description: "Search knowledge notes (CJK full-text). Returns notes with title, body, tags, confidence.",
91
+ inputSchema: {
92
+ type: "object",
93
+ properties: {
94
+ query: {
95
+ type: "string",
96
+ description: "Search query (Chinese supported)"
97
+ },
98
+ eval: {
99
+ type: "boolean",
100
+ description: "Enable decision-model semantic re-ranking"
101
+ }
102
+ },
103
+ required: ["query"]
104
+ },
105
+ execute: (a) => {
106
+ const parts = [
107
+ "kb",
108
+ "search",
109
+ String(a.query)
110
+ ];
111
+ if (a.eval) parts.push("--eval");
112
+ return cliJson(parts);
113
+ }
114
+ },
115
+ {
116
+ name: "claim_evaluate",
117
+ description: "Decision model evaluates a claim's confidence (score 0-5 → grade A/B/C/D).",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: { claim_id: {
121
+ type: "string",
122
+ description: "Claim ID to evaluate"
123
+ } },
124
+ required: ["claim_id"]
125
+ },
126
+ execute: (a) => cliJson([
127
+ "research",
128
+ "claim",
129
+ "evaluate",
130
+ String(a.claim_id)
131
+ ])
132
+ },
133
+ {
134
+ name: "tree_state",
135
+ description: "All trees with node states (locked/unlocked). Visualize the dependency graph.",
136
+ inputSchema: {
137
+ type: "object",
138
+ properties: { tree: {
139
+ type: "string",
140
+ description: "Optional tree name (omit for all)"
141
+ } }
142
+ },
143
+ execute: (a) => cliJson([
144
+ "node",
145
+ "list",
146
+ ...a.tree ? [String(a.tree)] : []
147
+ ])
148
+ },
149
+ {
150
+ name: "produce_report",
151
+ description: "Assemble claims + evidence into a markdown report. Returns report ID and content.",
152
+ inputSchema: {
153
+ type: "object",
154
+ properties: {}
155
+ },
156
+ execute: () => cliJson(["produce", "report"])
157
+ },
158
+ {
159
+ name: "doctor",
160
+ description: "9-item workspace health check. Pass fix=true to auto-fix safe issues.",
161
+ inputSchema: {
162
+ type: "object",
163
+ properties: { fix: {
164
+ type: "boolean",
165
+ description: "Auto-fix dirty index + stale locks"
166
+ } }
167
+ },
168
+ execute: (a) => cliJson(["doctor", ...a.fix ? ["--fix"] : []])
169
+ },
170
+ {
171
+ name: "task_advance",
172
+ description: "Advance a task through the state machine: todo → doing → done (or roll back).",
173
+ inputSchema: {
174
+ type: "object",
175
+ properties: {
176
+ task_id: {
177
+ type: "string",
178
+ description: "Task artifact ID"
179
+ },
180
+ action: {
181
+ type: "string",
182
+ enum: [
183
+ "doing",
184
+ "done",
185
+ "todo"
186
+ ],
187
+ description: "Target status"
188
+ }
189
+ },
190
+ required: ["task_id", "action"]
191
+ },
192
+ execute: (a) => cliJson([
193
+ "task",
194
+ String(a.action),
195
+ String(a.task_id)
196
+ ])
197
+ }
198
+ ];
199
+ }
200
+ /** Run the MCP server on stdio. */
201
+ function serveMcp() {
202
+ const tools = buildTools();
203
+ const rl = node_readline.createInterface({ input: process.stdin });
204
+ const respond = (id, result) => {
205
+ process.stdout.write(JSON.stringify({
206
+ jsonrpc: "2.0",
207
+ id,
208
+ result
209
+ }) + "\n");
210
+ };
211
+ const respondErr = (id, code, message) => {
212
+ process.stdout.write(JSON.stringify({
213
+ jsonrpc: "2.0",
214
+ id,
215
+ error: {
216
+ code,
217
+ message
218
+ }
219
+ }) + "\n");
220
+ };
221
+ rl.on("line", (line) => {
222
+ if (!line.trim()) return;
223
+ let msg;
224
+ try {
225
+ msg = JSON.parse(line);
226
+ } catch {
227
+ return;
228
+ }
229
+ const { id, method, params } = msg;
230
+ switch (method) {
231
+ case "initialize":
232
+ respond(id, {
233
+ protocolVersion: "2024-11-05",
234
+ capabilities: { tools: {} },
235
+ serverInfo: {
236
+ name: "ygl-trees",
237
+ version: "0.69.0"
238
+ }
239
+ });
240
+ break;
241
+ case "tools/list":
242
+ respond(id, { tools: tools.map((t) => ({
243
+ name: t.name,
244
+ description: t.description,
245
+ inputSchema: t.inputSchema
246
+ })) });
247
+ break;
248
+ case "tools/call": {
249
+ const name = params?.name;
250
+ const tool = tools.find((t) => t.name === name);
251
+ if (!tool) {
252
+ respondErr(id, -32602, `Unknown tool: ${name}`);
253
+ break;
254
+ }
255
+ tool.execute(params?.arguments || {}).then((data) => respond(id, { content: [{
256
+ type: "text",
257
+ text: JSON.stringify(data, null, 2)
258
+ }] })).catch((e) => respond(id, {
259
+ content: [{
260
+ type: "text",
261
+ text: `Error: ${e?.message || e}`
262
+ }],
263
+ isError: true
264
+ }));
265
+ break;
266
+ }
267
+ case "ping":
268
+ respond(id, {});
269
+ break;
270
+ default: if (id !== void 0) respondErr(id, -32601, `Method not found: ${method}`);
271
+ }
272
+ });
273
+ process.stderr.write("[ygl-trees MCP] stdio server ready — 8 tools\n");
274
+ }
275
+ //#endregion
276
+ exports.serveMcp = serveMcp;
@@ -0,0 +1,64 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esmMin = (fn, res, err) => () => {
9
+ if (err) throw err[0];
10
+ try {
11
+ return fn && (res = fn(fn = 0)), res;
12
+ } catch (e) {
13
+ throw err = [e], e;
14
+ }
15
+ };
16
+ var __exportAll = (all, no_symbols) => {
17
+ let target = {};
18
+ for (var name in all) __defProp(target, name, {
19
+ get: all[name],
20
+ enumerable: true
21
+ });
22
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
23
+ return target;
24
+ };
25
+ var __copyProps = (to, from, except, desc) => {
26
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
27
+ key = keys[i];
28
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
29
+ get: ((k) => from[k]).bind(null, key),
30
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
31
+ });
32
+ }
33
+ return to;
34
+ };
35
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
36
+ value: mod,
37
+ enumerable: true
38
+ }) : target, mod));
39
+ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
+ //#endregion
41
+ Object.defineProperty(exports, "__esmMin", {
42
+ enumerable: true,
43
+ get: function() {
44
+ return __esmMin;
45
+ }
46
+ });
47
+ Object.defineProperty(exports, "__exportAll", {
48
+ enumerable: true,
49
+ get: function() {
50
+ return __exportAll;
51
+ }
52
+ });
53
+ Object.defineProperty(exports, "__toCommonJS", {
54
+ enumerable: true,
55
+ get: function() {
56
+ return __toCommonJS;
57
+ }
58
+ });
59
+ Object.defineProperty(exports, "__toESM", {
60
+ enumerable: true,
61
+ get: function() {
62
+ return __toESM;
63
+ }
64
+ });
@@ -74,6 +74,78 @@ function buildBrief(env) {
74
74
  function run(env, action, rest, args) {
75
75
  const { ctx, store } = env;
76
76
  switch (action) {
77
+ case "metrics": {
78
+ // R11 cold-start research → automated measurement. Health/efficiency
79
+ // metrics for the workspace — one command answers "how good is this
80
+ // workbench?" with quantified data.
81
+ const t0 = Date.now();
82
+ const brief = buildBrief(env);
83
+ const briefMs = Date.now() - t0;
84
+ // Knowledge metrics
85
+ const kbTotal = env.db.get("SELECT COUNT(*) AS n FROM kb_notes")?.n || 0;
86
+ const kbRecent = env.db.get("SELECT COUNT(*) AS n FROM kb_notes WHERE created > datetime('now', '-7 days')")?.n || 0;
87
+ // Research metrics
88
+ const claims = env.db.get("SELECT COUNT(*) AS n FROM claims")?.n || 0;
89
+ const claimsVerified = env.db.get("SELECT COUNT(*) AS n FROM claims WHERE status='supported'")?.n || 0;
90
+ const evidence = env.db.get("SELECT COUNT(*) AS n FROM evidence")?.n || 0;
91
+ const sources = env.db.get("SELECT COUNT(*) AS n FROM sources")?.n || 0;
92
+ // Skill metrics
93
+ const skills = store.skillList();
94
+ const skillHits = skills.reduce((s, x) => s + (x.hits || 0), 0);
95
+ // Tree metrics
96
+ const trees = env.db.all("SELECT id, name FROM trees");
97
+ let nodesTotal = 0, nodesUnlocked = 0;
98
+ for (const t of trees) {
99
+ const r = env.db.get("SELECT COUNT(*) AS total, SUM(CASE WHEN ns.status='unlocked' THEN 1 ELSE 0 END) AS unlocked FROM nodes n LEFT JOIN node_state ns ON ns.node_id=n.id AND ns.context='default' WHERE n.tree_id=?", [t.id]);
100
+ nodesTotal += r?.total || 0;
101
+ nodesUnlocked += r?.unlocked || 0;
102
+ }
103
+ // Event metrics
104
+ const events7d = env.db.get("SELECT COUNT(*) AS n FROM events WHERE ts > datetime('now', '-7 days')")?.n || 0;
105
+ const metrics = {
106
+ cold_start: {
107
+ brief_ms: briefMs,
108
+ next_actions: brief.next_actions?.length || 0,
109
+ skills_available: skills.length,
110
+ },
111
+ knowledge: {
112
+ total_notes: kbTotal,
113
+ recent_7d: kbRecent,
114
+ growth_rate_7d: kbTotal > 0 ? Math.round(kbRecent / kbTotal * 100) : 0,
115
+ },
116
+ research: {
117
+ sources, evidence, claims,
118
+ verified_claims: claimsVerified,
119
+ verification_rate: claims > 0 ? Math.round(claimsVerified / claims * 100) : 0,
120
+ evidence_per_claim: claims > 0 ? (evidence / claims).toFixed(1) : "0",
121
+ },
122
+ skills: {
123
+ total: skills.length,
124
+ total_hits: skillHits,
125
+ avg_hits: skills.length > 0 ? (skillHits / skills.length).toFixed(1) : "0",
126
+ },
127
+ trees: {
128
+ total_trees: trees.length,
129
+ nodes: nodesTotal,
130
+ unlocked: nodesUnlocked,
131
+ unlock_rate: nodesTotal > 0 ? Math.round(nodesUnlocked / nodesTotal * 100) : 0,
132
+ },
133
+ activity: { events_7d: events7d },
134
+ doctor: "run `doctor` for health checks",
135
+ };
136
+ if (ctx.json) {
137
+ console.log(JSON.stringify(metrics, null, 2));
138
+ return 0;
139
+ }
140
+ (0, fmt_1.head)(ctx, "ygl-trees metrics");
141
+ (0, fmt_1.out)(ctx, ` 冷启动 brief ${metrics.cold_start.brief_ms}ms · ${metrics.cold_start.next_actions} next actions · ${metrics.cold_start.skills_available} skills`);
142
+ (0, fmt_1.out)(ctx, ` 知识 ${metrics.knowledge.total_notes} notes (${metrics.knowledge.recent_7d} in 7d, ${metrics.knowledge.growth_rate_7d}% growth)`);
143
+ (0, fmt_1.out)(ctx, ` 研究 ${metrics.research.sources} sources · ${metrics.research.evidence} evidence · ${metrics.research.verified_claims}/${metrics.research.claims} verified (${metrics.research.verification_rate}%)`);
144
+ (0, fmt_1.out)(ctx, ` 技能 ${metrics.skills.total} skills · ${metrics.skills.total_hits} hits (avg ${metrics.skills.avg_hits})`);
145
+ (0, fmt_1.out)(ctx, ` 科技树 ${metrics.trees.total_trees} trees · ${metrics.trees.unlocked}/${metrics.trees.nodes} unlocked (${metrics.trees.unlock_rate}%)`);
146
+ (0, fmt_1.out)(ctx, ` 活动 ${metrics.activity.events_7d} events in 7d`);
147
+ return 0;
148
+ }
77
149
  case "brief": {
78
150
  const payload = buildBrief(env);
79
151
  const cur = payload.current_project;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.apply = exports.OpsService = exports.MIRROR_TABLES = exports.migrateToPg = exports.syncToPg = exports.configAll = exports.configReset = exports.configSet = exports.configGetBool = exports.configGetNumber = exports.configGet = exports.CONFIG_REGISTRY = void 0;
3
+ exports.buildTools = exports.serveMcp = exports.apply = exports.OpsService = exports.MIRROR_TABLES = exports.migrateToPg = exports.syncToPg = exports.configAll = exports.configReset = exports.configSet = exports.configGetBool = exports.configGetNumber = exports.configGet = exports.CONFIG_REGISTRY = void 0;
4
4
  /**
5
5
  * @module @ygl/ops-tools
6
6
  */
@@ -20,3 +20,6 @@ Object.defineProperty(exports, "MIRROR_TABLES", { enumerable: true, get: functio
20
20
  var plugin_1 = require("./plugin");
21
21
  Object.defineProperty(exports, "OpsService", { enumerable: true, get: function () { return plugin_1.OpsService; } });
22
22
  Object.defineProperty(exports, "apply", { enumerable: true, get: function () { return plugin_1.apply; } });
23
+ var mcp_1 = require("./mcp");
24
+ Object.defineProperty(exports, "serveMcp", { enumerable: true, get: function () { return mcp_1.serveMcp; } });
25
+ Object.defineProperty(exports, "buildTools", { enumerable: true, get: function () { return mcp_1.buildTools; } });
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildTools = buildTools;
37
+ exports.serveMcp = serveMcp;
38
+ /**
39
+ * MCP (Model Context Protocol) Server — stdio transport.
40
+ *
41
+ * Eight-tool surface designed by the unified three-pillar theory (R8 research):
42
+ * agent_brief · agent_match · kb_search · claim_evaluate
43
+ * tree_state · produce_report · doctor · task_advance
44
+ *
45
+ * Each tool routes to the CLI --json backend, inheriting its frozen contracts
46
+ * (idempotent writes, pure JSON errors, snake_case fields). Agents call tools
47
+ * directly — no command-line parsing layer.
48
+ *
49
+ * Protocol: JSON-RPC 2.0 over stdio (one message per line).
50
+ * → {"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}
51
+ * ← {"jsonrpc":"2.0","id":1,"result":{...}}
52
+ *
53
+ * @module @ygl/ops-tools/mcp
54
+ */
55
+ const readline = __importStar(require("node:readline"));
56
+ /** Build a CLI invocation and capture JSON stdout.
57
+ * Uses process.argv[1] (the running script — bundle or source) to re-invoke
58
+ * the CLI as a child process. This works in both dev (dist/apps/...) and
59
+ * bundle (dist/bundle/bin.cjs) contexts. */
60
+ function cliJson(parts) {
61
+ const { spawn } = require("node:child_process");
62
+ const self = process.argv[1]; // the bin.cjs or bin.js that's running this MCP server
63
+ return new Promise((resolve, reject) => {
64
+ const child = spawn(process.execPath, [self, "--json", ...parts], {
65
+ env: { ...process.env, YGL_SERVE: "1", YGL_MCP_CHILD: "1" },
66
+ stdio: ["ignore", "pipe", "pipe"],
67
+ });
68
+ let out = "";
69
+ let err = "";
70
+ child.stdout.on("data", (d) => (out += d));
71
+ child.stderr.on("data", (d) => (err += d));
72
+ child.on("close", (code) => {
73
+ if (out.trim()) {
74
+ try {
75
+ resolve(JSON.parse(out));
76
+ return;
77
+ }
78
+ catch { /* fall through */ }
79
+ }
80
+ reject(new Error(`CLI rc=${code}: ${err.slice(0, 200) || out.slice(0, 200)}`));
81
+ });
82
+ child.on("error", (e) => reject(e));
83
+ });
84
+ }
85
+ function buildTools() {
86
+ return [
87
+ {
88
+ name: "agent_brief",
89
+ description: "Current workspace state: project, progress, counts, next actions, available skills. Always start here.",
90
+ inputSchema: { type: "object", properties: {} },
91
+ execute: () => cliJson(["agent", "brief"]),
92
+ },
93
+ {
94
+ name: "agent_match",
95
+ description: "Discover which skill handles a task. Returns matched skills with relevance scores.",
96
+ inputSchema: {
97
+ type: "object",
98
+ properties: { query: { type: "string", description: "Task description to match" } },
99
+ required: ["query"],
100
+ },
101
+ execute: (a) => cliJson(["agent", "match", String(a.query)]),
102
+ },
103
+ {
104
+ name: "kb_search",
105
+ description: "Search knowledge notes (CJK full-text). Returns notes with title, body, tags, confidence.",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ query: { type: "string", description: "Search query (Chinese supported)" },
110
+ eval: { type: "boolean", description: "Enable decision-model semantic re-ranking" },
111
+ },
112
+ required: ["query"],
113
+ },
114
+ execute: (a) => {
115
+ const parts = ["kb", "search", String(a.query)];
116
+ if (a.eval)
117
+ parts.push("--eval");
118
+ return cliJson(parts);
119
+ },
120
+ },
121
+ {
122
+ name: "claim_evaluate",
123
+ description: "Decision model evaluates a claim's confidence (score 0-5 → grade A/B/C/D).",
124
+ inputSchema: {
125
+ type: "object",
126
+ properties: { claim_id: { type: "string", description: "Claim ID to evaluate" } },
127
+ required: ["claim_id"],
128
+ },
129
+ execute: (a) => cliJson(["research", "claim", "evaluate", String(a.claim_id)]),
130
+ },
131
+ {
132
+ name: "tree_state",
133
+ description: "All trees with node states (locked/unlocked). Visualize the dependency graph.",
134
+ inputSchema: {
135
+ type: "object",
136
+ properties: { tree: { type: "string", description: "Optional tree name (omit for all)" } },
137
+ },
138
+ execute: (a) => cliJson(["node", "list", ...(a.tree ? [String(a.tree)] : [])]),
139
+ },
140
+ {
141
+ name: "produce_report",
142
+ description: "Assemble claims + evidence into a markdown report. Returns report ID and content.",
143
+ inputSchema: { type: "object", properties: {} },
144
+ execute: () => cliJson(["produce", "report"]),
145
+ },
146
+ {
147
+ name: "doctor",
148
+ description: "9-item workspace health check. Pass fix=true to auto-fix safe issues.",
149
+ inputSchema: {
150
+ type: "object",
151
+ properties: { fix: { type: "boolean", description: "Auto-fix dirty index + stale locks" } },
152
+ },
153
+ execute: (a) => cliJson(["doctor", ...(a.fix ? ["--fix"] : [])]),
154
+ },
155
+ {
156
+ name: "task_advance",
157
+ description: "Advance a task through the state machine: todo → doing → done (or roll back).",
158
+ inputSchema: {
159
+ type: "object",
160
+ properties: {
161
+ task_id: { type: "string", description: "Task artifact ID" },
162
+ action: { type: "string", enum: ["doing", "done", "todo"], description: "Target status" },
163
+ },
164
+ required: ["task_id", "action"],
165
+ },
166
+ execute: (a) => cliJson(["task", String(a.action), String(a.task_id)]),
167
+ },
168
+ ];
169
+ }
170
+ /** Run the MCP server on stdio. */
171
+ function serveMcp() {
172
+ const tools = buildTools();
173
+ const rl = readline.createInterface({ input: process.stdin });
174
+ const respond = (id, result) => {
175
+ process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
176
+ };
177
+ const respondErr = (id, code, message) => {
178
+ process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
179
+ };
180
+ rl.on("line", (line) => {
181
+ if (!line.trim())
182
+ return;
183
+ let msg;
184
+ try {
185
+ msg = JSON.parse(line);
186
+ }
187
+ catch {
188
+ return;
189
+ }
190
+ const { id, method, params } = msg;
191
+ switch (method) {
192
+ case "initialize":
193
+ respond(id, {
194
+ protocolVersion: "2024-11-05",
195
+ capabilities: { tools: {} },
196
+ serverInfo: { name: "ygl-trees", version: "0.69.0" },
197
+ });
198
+ break;
199
+ case "tools/list":
200
+ respond(id, {
201
+ tools: tools.map((t) => ({
202
+ name: t.name,
203
+ description: t.description,
204
+ inputSchema: t.inputSchema,
205
+ })),
206
+ });
207
+ break;
208
+ case "tools/call": {
209
+ const name = params?.name;
210
+ const tool = tools.find((t) => t.name === name);
211
+ if (!tool) {
212
+ respondErr(id, -32602, `Unknown tool: ${name}`);
213
+ break;
214
+ }
215
+ tool
216
+ .execute(params?.arguments || {})
217
+ .then((data) => respond(id, { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] }))
218
+ .catch((e) => respond(id, {
219
+ content: [{ type: "text", text: `Error: ${e?.message || e}` }],
220
+ isError: true,
221
+ }));
222
+ break;
223
+ }
224
+ case "ping":
225
+ respond(id, {});
226
+ break;
227
+ default:
228
+ if (id !== undefined)
229
+ respondErr(id, -32601, `Method not found: ${method}`);
230
+ }
231
+ });
232
+ process.stderr.write("[ygl-trees MCP] stdio server ready — 8 tools\n");
233
+ }
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # ygl-trees v0.65.0 — AI Agent Research Workbench
1
+ # ygl-trees v0.69.0 — AI Agent Research Workbench
2
2
 
3
3
  > Single SQLite source of truth. Research → Production → Knowledge Reuse + Tech Tree.
4
4
  > CLI for AI agents: every command supports --json (machine-readable, including errors).