ygl-trees 0.66.0 → 0.69.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.69.0(2026-10-17)MCP Server——R8 研究设计的工程落地
4
+ - **`ygl-trees mcp`**:启动 stdio MCP 服务器(JSON-RPC 2.0),Claude Desktop / Cursor / VS Code 等 IDE Agent 可直接连接
5
+ - **8 工具面**(三支柱理论驱动):agent_brief · agent_match · kb_search · claim_evaluate · tree_state · produce_report · doctor · task_advance
6
+ - **每个工具继承 CLI 冻结契约**(幂等写入、JSON 纯净、snake_case)——Agent 调用工具不需要解析命令行
7
+ - **实现**:`packages/ops/tools/src/mcp.ts`——readline stdio + JSON-RPC 路由 + `cliJson()` 子进程调用(`process.argv[1]` 自引用,bundle/源码双兼容)
8
+ - 实测:initialize ✓ · tools/list(8) ✓ · agent_brief(project) ✓ · kb_search(CJK 命中) ✓ · doctor(9 项) ✓
9
+
10
+ ## 0.68.0(2026-10-17)锁设计修复——serve/CLI 共存 + PID 活锁检测 + JSON 纯净 + v9 时间纪律
11
+ - **F1(P0):webui serve 不再持排他锁**——原实现进程级锁让 serve 生命周期内挡掉一切 CLI 命令;现 serve 跳过锁(`YGL_SERVE=1` 旁路),SQLite WAL 天然支持并发读 + 序列化写;CLI 短命令仍锁(单写者保护写窗口)
12
+ - **P1(用户反馈):PID 活锁检测替代 15 分钟 mtime**——Windows 上 `process.on("exit")` 的 rmSync 句柄未释放时静默失败→锁残留→下进程撞白锁;现用 `process.kill(pid, 0)` 探测持有者是否存活(EPERM=活/ESRCH=死),死 PID **立即清锁**(不看 mtime),活 PID **如实报告**(不误报 stale)
13
+ - **F2:--json 模式锁错误纯净**——原锁提示文本泄露到 stdout 破坏 JSON 通道契约;现 --json 模式抑制人类提示,且提示本身也改为 PID 活锁感知(活锁不叫 stale)
14
+ - **F3:taskSetStatus 更新 artifacts.updated**——任务状态变更现在同步写 updated 时间戳(v9 纪律补漏)
15
+ - **exit 清理加固**:`beforeExit` + `exit` 双钩子 + 3 次重试(Windows EBUSY 容忍)
16
+ - **vendor/cordis bin 字段清理**:bin.js 已删但声明残留——移除
17
+
18
+ ## 0.67.0(2026-10-17)WebUI antd 迭代——Ant Design 组件库升级 React 客户端
19
+ - **antd v6 + @ant-design/icons**:专业组件库驱动的富客户端
20
+ - **组件替换**:Layout(Header/Content)、Card 统计卡片、Statistic 数字展示、Table 任务清单(可筛选)、Tag 状态标签、Alert 错误提示、Space/Row/Col 布局
21
+ - **DESIGN.md token 映射**:ConfigProvider theme(colorPrimary=#7c9885 sage、darkAlgorithm 暗色主题、zh_CN 中文语言包)
22
+ - **代码分割**:manualChunks 拆分 vendor/antd/xyflow/markdown 四块,antd 不阻塞首屏
23
+ - **保持不变**:xyflow 树视图、react-markdown 产出渲染、SSE 实时推送、内嵌 dashboard 回退
24
+
3
25
  ## 0.66.0(2026-10-17)新增 `llms` 命令——结构化能力清单(Ant Design for-agents 模式)
4
26
  - **`ygl-trees llms`**:一条命令输出完整 markdown 能力清单——冻结契约表(6 项)+ 16 个命令组全部用法 + 决策模型说明 + Quick Start + 设计系统入口——**任何 LLM 一次性获取全部能力面**
5
27
  - **静态 `llms.txt`**:仓库根目录,可被任何 LLM 爬取/注入上下文(同 Ant Design 的 llms.txt 模式)
@@ -256,6 +256,14 @@ async function main(argv) {
256
256
  };
257
257
  const group = args._[0];
258
258
  const action = args._[1];
259
+ // F1 fix (a2a review): webui serve holds its DB connection for the process
260
+ // lifetime — a process-level exclusive lock there would block ALL CLI
261
+ // commands until serve exits. SQLite WAL already permits concurrent readers
262
+ // + serialized writers, so serve skips the app-level lock. Short CLI
263
+ // commands still lock (single-writer for the brief write window).
264
+ if (group === "webui" && (action === "serve" || action === "export")) {
265
+ process.env.YGL_SERVE = "1";
266
+ }
259
267
  // P4 fix: --version prints the actual version number (cold-user report);
260
268
  // the parser eats --version as a flag, so check flags not positional args.
261
269
  if (args.flags.version === true || group === "version") {
@@ -268,6 +276,14 @@ async function main(argv) {
268
276
  (0, fmt_1.out)(ctx, generateLlmsManifest());
269
277
  return 0;
270
278
  }
279
+ // MCP: Model Context Protocol server (stdio) — 8-tool surface.
280
+ // Designed by the unified three-pillar theory (R8 research); each tool
281
+ // routes to CLI --json, inheriting frozen contracts.
282
+ if (group === "mcp") {
283
+ const { serveMcp } = await Promise.resolve().then(() => __importStar(require("../../../packages/ops/tools/src/mcp")));
284
+ serveMcp();
285
+ return new Promise(() => { });
286
+ }
271
287
  if (!group || group === "help" || group === "-h" || group === "--help") {
272
288
  if (!ctx.json)
273
289
  (0, fmt_1.out)(ctx, usage());
@@ -307,10 +323,37 @@ async function main(argv) {
307
323
  return 1;
308
324
  }
309
325
  (0, fmt_1.bad)(ctx, `cannot open database ${file}: ${e?.message || e}`);
310
- const lock = `${file}.lock`;
311
- if (require("node:fs").existsSync(lock)) {
312
- (0, fmt_1.out)(ctx, ` stale lock dir detected: ${lock}`);
313
- (0, fmt_1.out)(ctx, ` if no other ygl-trees process is running, remove it: rmdir "${lock}"`);
326
+ // F2 fix (a2a review): --json mode must stay a single parseable object —
327
+ // suppress the human-only lock hints. Also check PID liveness, not just
328
+ // directory existence (a live serve holder is NOT stale).
329
+ const lock = `${file}.ygllock`;
330
+ if (!ctx.json && require("node:fs").existsSync(lock)) {
331
+ const pidFile = require("node:path").join(lock, "pid");
332
+ let holder = "?";
333
+ try {
334
+ holder = require("node:fs").readFileSync(pidFile, "utf8").trim();
335
+ }
336
+ catch { /* */ }
337
+ let alive = false;
338
+ try {
339
+ process.kill(Number(holder), 0);
340
+ alive = true;
341
+ }
342
+ catch (err) {
343
+ alive = err.code === "EPERM";
344
+ }
345
+ if (alive) {
346
+ (0, fmt_1.out)(ctx, ` lock held by live process pid ${holder} (serve? webui dashboard)`);
347
+ (0, fmt_1.out)(ctx, ` to run CLI write commands, stop that process first — or use YGL_TEST=1 to bypass`);
348
+ }
349
+ else {
350
+ (0, fmt_1.out)(ctx, ` orphaned lock (pid ${holder} is dead) — removing: ${lock}`);
351
+ try {
352
+ require("node:fs").rmSync(lock, { recursive: true, force: true });
353
+ }
354
+ catch { /* */ }
355
+ (0, fmt_1.out)(ctx, ` retry the command — the lock has been cleaned`);
356
+ }
314
357
  }
315
358
  return 1;
316
359
  }
@@ -52,9 +52,17 @@ async function bootApp(opts) {
52
52
  pluginCommands[g] = c;
53
53
  }
54
54
  }
55
- // settle the service layer (one yield; direct construction stays the CLI fallback)
56
- await new Promise((r) => setImmediate(r));
57
- const db = app.database?.db || new db_1.DB(opts.dbFile, { lock: process.env.YGL_TEST !== '1' });
55
+ // settle the service layer. Cordis runs plugin apply() inside fibers — one
56
+ // setImmediate tick does NOT guarantee the database service has constructed;
57
+ // the fallback below would then double-open the DB in one process (0.65.0
58
+ // P0: dir-lock self-deadlock / sqlite BUSY). Wait for it, bounded; the
59
+ // fallback stays as crash safety if the service never comes up.
60
+ let db = app.database?.db;
61
+ for (let i = 0; i < 50 && !db; i++) {
62
+ await new Promise((r) => setImmediate(r));
63
+ db = app.database?.db;
64
+ }
65
+ db = db || new db_1.DB(opts.dbFile, { lock: process.env.YGL_TEST !== '1' });
58
66
  const store = app.research?.store || new reads_1.Store(db);
59
67
  if (startupIssues.length) {
60
68
  throw new startup_diagnostics_1.StartupError(`${startupIssues.length} plugin issue(s) during startup`, startupIssues);