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.
- package/CHANGELOG.md +12 -0
- package/dist/apps/cli/src/bin.js +134 -126
- package/dist/bundle/bin.cjs +120 -88
- package/dist/bundle/mcp-C27lIHd1.cjs +276 -0
- package/dist/bundle/rolldown-runtime-C96Ew50Q.cjs +64 -0
- package/dist/packages/ops/tools/src/agent-cmds.js +72 -0
- package/dist/packages/ops/tools/src/index.js +4 -1
- package/dist/packages/ops/tools/src/mcp.js +233 -0
- package/llms.txt +1 -1
- package/package.json +3 -3
|
@@ -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.
|
|
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).
|