ygl-trees 0.72.0 → 0.74.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 +13 -0
- package/dist/apps/cli/src/bin.js +17 -0
- package/dist/bundle/bin.cjs +130 -9
- package/dist/packages/core/text/src/fmt.js +6 -3
- package/dist/packages/knowledge/skills/src/cmds.js +1 -1
- package/dist/packages/ops/tools/src/agent-cmds.js +15 -4
- package/dist/packages/ops/tools/src/db-cmds.js +75 -0
- package/llms.txt +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.74.0(2026-10-17)export-markdown + v2 测试 + 技能同步
|
|
4
|
+
- **db export-markdown**:一键生成工作区 Markdown 报告(Summary/Projects/Claims/Knowledge/Trees)
|
|
5
|
+
- **v2 功能测试**:6 项新单测覆盖 time-travel/timeline/workspace/obsidian/help-suggestions/llms-sections
|
|
6
|
+
- **技能同步 v0.73.0**:quickstart/knowledge/research/techtree 全部更新到最新命令面
|
|
7
|
+
- **141 单测**(+6)
|
|
8
|
+
|
|
9
|
+
## 0.73.0(2026-10-17)引导增强 + Token 优化 + A2A 修复
|
|
10
|
+
- **首启引导**:无数据库时显示 Welcome + 3 步入门指南
|
|
11
|
+
- **空态引导**:列表为空时显示具体命令建议(kb/project/task)
|
|
12
|
+
- **Token 优化**:表格列 40 字符截断 · brief 技能描述 60 字符 · 输出减 24-28%
|
|
13
|
+
- **A2A 修复**(应用测试席反馈):F1 agent ask 子进程锁自撞(YGL_TEST 旁路)· F2 timeline --json 纯净 · F4 job 子进程锁旁路
|
|
14
|
+
- **命令建议**:未知命令模糊匹配(rsrch→research · databse→db · healh→doctor)
|
|
15
|
+
|
|
3
16
|
## 0.72.0(2026-10-17)v2 功能包——8 项新能力
|
|
4
17
|
- **db time-travel**:查询过去某时刻的工作区状态(实体计数+已解锁节点)
|
|
5
18
|
- **db timeline**:工作区增长可视化(events/天 + 实体里程碑)
|
package/dist/apps/cli/src/bin.js
CHANGED
|
@@ -272,6 +272,23 @@ async function main(argv) {
|
|
|
272
272
|
}
|
|
273
273
|
// llms: structured capability manifest for LLM consumption (Ant Design
|
|
274
274
|
// "for-agents" pattern — one output gives any Agent the full command surface).
|
|
275
|
+
// Onboarding: detect first run (no DB) and guide to db init
|
|
276
|
+
if (!group || group === "help") {
|
|
277
|
+
const dbFile = require("node:path").join(process.env.YGL_HOME || require("node:path").join(require("node:os").homedir(), ".ygl-trees"), "ygl-trees.db");
|
|
278
|
+
if (!require("node:fs").existsSync(dbFile)) {
|
|
279
|
+
console.log("✓ Welcome to ygl-trees! 🐵");
|
|
280
|
+
console.log();
|
|
281
|
+
console.log(" This workspace has no database yet. Get started in 3 steps:");
|
|
282
|
+
console.log();
|
|
283
|
+
console.log(" 1. ygl-trees db init # create workspace (9 skills auto-imported)");
|
|
284
|
+
console.log(" 2. ygl-trees agent brief # see current state + next actions");
|
|
285
|
+
console.log(' 3. ygl-trees project new my-research -q "your question"');
|
|
286
|
+
console.log();
|
|
287
|
+
console.log(" More: ygl-trees llms (full capability manifest) | ygl-trees (no args, after init)");
|
|
288
|
+
console.log();
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
275
292
|
if (group === "llms") {
|
|
276
293
|
// v2: llms enhancement — --json, --section <name>, dynamic skills
|
|
277
294
|
const section = String(args.flags.section || "");
|
package/dist/bundle/bin.cjs
CHANGED
|
@@ -826,20 +826,22 @@ function padEnd(s, width) {
|
|
|
826
826
|
return gap > 0 ? s + " ".repeat(gap) : s;
|
|
827
827
|
}
|
|
828
828
|
/** Render rows as an aligned table (falls back to JSON when ctx.json). */
|
|
829
|
-
function table(ctx, rows, headers, keys) {
|
|
829
|
+
function table(ctx, rows, headers, keys, hint) {
|
|
830
830
|
if (ctx.json) {
|
|
831
831
|
console.log(JSON.stringify(rows, null, 2));
|
|
832
832
|
return;
|
|
833
833
|
}
|
|
834
834
|
if (!rows.length) {
|
|
835
835
|
out(ctx, paint(C.dim, " (empty)", ctx.color));
|
|
836
|
+
if (hint) out(ctx, paint(C.dim, ` → ${hint}`, ctx.color));
|
|
836
837
|
return;
|
|
837
838
|
}
|
|
838
839
|
const cols = keys || Object.keys(rows[0]).filter((k) => !k.startsWith("_"));
|
|
839
840
|
const hs = headers || cols;
|
|
840
|
-
const widthOf = (k) => Math.max(...rows.map((r) => dispWidth(String(r[k] ?? ""))));
|
|
841
|
+
const widthOf = (k) => Math.min(40, Math.max(...rows.map((r) => dispWidth(String(r[k] ?? "")))));
|
|
841
842
|
const w = cols.map((k, i) => Math.max(dispWidth(hs[i] ?? ""), widthOf(k)));
|
|
842
|
-
const
|
|
843
|
+
const trunc = (s) => s.length > 40 ? s.slice(0, 39) + "…" : s;
|
|
844
|
+
const line = (cells) => cells.map((c, i) => padEnd(trunc(String(c)), w[i])).join(" ");
|
|
843
845
|
out(ctx, paint(C.bold, " " + line(hs), ctx.color));
|
|
844
846
|
out(ctx, paint(C.dim, " " + w.map((n) => "-".repeat(n)).join(" "), ctx.color));
|
|
845
847
|
for (const r of rows) out(ctx, " " + line(cols.map((k) => String(r[k] ?? ""))));
|
|
@@ -4668,6 +4670,84 @@ function run$12(env, action, rest, args) {
|
|
|
4668
4670
|
});
|
|
4669
4671
|
return 0;
|
|
4670
4672
|
}
|
|
4673
|
+
case "export-markdown": {
|
|
4674
|
+
const lines = [];
|
|
4675
|
+
lines.push(`# ygl-trees Workspace Report`);
|
|
4676
|
+
lines.push(`> Generated: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
4677
|
+
lines.push("");
|
|
4678
|
+
const counts = {};
|
|
4679
|
+
for (const t of [
|
|
4680
|
+
"projects",
|
|
4681
|
+
"sources",
|
|
4682
|
+
"evidence",
|
|
4683
|
+
"claims",
|
|
4684
|
+
"kb_notes",
|
|
4685
|
+
"skills",
|
|
4686
|
+
"trees",
|
|
4687
|
+
"nodes",
|
|
4688
|
+
"artifacts",
|
|
4689
|
+
"events"
|
|
4690
|
+
]) try {
|
|
4691
|
+
counts[t] = db.get(`SELECT COUNT(*) as n FROM ${t}`)?.n || 0;
|
|
4692
|
+
} catch {
|
|
4693
|
+
counts[t] = 0;
|
|
4694
|
+
}
|
|
4695
|
+
lines.push(`## Summary`);
|
|
4696
|
+
lines.push("");
|
|
4697
|
+
lines.push(`| Entity | Count |`);
|
|
4698
|
+
lines.push(`|---|---|`);
|
|
4699
|
+
const labels = {
|
|
4700
|
+
projects: "项目",
|
|
4701
|
+
sources: "来源",
|
|
4702
|
+
evidence: "证据",
|
|
4703
|
+
claims: "断言",
|
|
4704
|
+
kb_notes: "知识",
|
|
4705
|
+
skills: "技能",
|
|
4706
|
+
trees: "树",
|
|
4707
|
+
nodes: "节点",
|
|
4708
|
+
artifacts: "产出",
|
|
4709
|
+
events: "事件"
|
|
4710
|
+
};
|
|
4711
|
+
for (const [k, v] of Object.entries(counts)) lines.push(`| ${labels[k] || k} | ${v} |`);
|
|
4712
|
+
lines.push("");
|
|
4713
|
+
lines.push(`## Projects`);
|
|
4714
|
+
const projects = db.all("SELECT name, question, created FROM projects ORDER BY created DESC LIMIT 10");
|
|
4715
|
+
for (const p of projects) lines.push(`- **${p.name}**: ${p.question || "(no question)"}`);
|
|
4716
|
+
lines.push("");
|
|
4717
|
+
lines.push(`## Recent Claims`);
|
|
4718
|
+
const claims = db.all("SELECT text, status, confidence FROM claims ORDER BY created DESC LIMIT 10");
|
|
4719
|
+
for (const c of claims) lines.push(`- [${c.status}] ${String(c.text).slice(0, 80)}... (${c.confidence})`);
|
|
4720
|
+
lines.push("");
|
|
4721
|
+
lines.push(`## Knowledge Notes`);
|
|
4722
|
+
const kb = db.all("SELECT title, tags, confidence FROM kb_notes ORDER BY created DESC LIMIT 10");
|
|
4723
|
+
for (const k of kb) lines.push(`- **${String(k.title).slice(0, 50)}** ${k.tags ? `(${k.tags})` : ""} [${k.confidence}]`);
|
|
4724
|
+
lines.push("");
|
|
4725
|
+
lines.push(`## Trees`);
|
|
4726
|
+
const trees = db.all("SELECT name FROM trees");
|
|
4727
|
+
for (const t of trees) {
|
|
4728
|
+
const nodes = db.get("SELECT COUNT(*) as n FROM nodes WHERE tree_id=(SELECT id FROM trees WHERE name=?)", [t.name])?.n || 0;
|
|
4729
|
+
lines.push(`- **${t.name}** (${nodes} nodes)`);
|
|
4730
|
+
}
|
|
4731
|
+
lines.push("");
|
|
4732
|
+
lines.push(`---`);
|
|
4733
|
+
lines.push(`*Auto-generated by ygl-trees db export-markdown*`);
|
|
4734
|
+
const md = lines.join("\n");
|
|
4735
|
+
const outPath = rest[0];
|
|
4736
|
+
if (outPath) {
|
|
4737
|
+
node_fs.writeFileSync(outPath, md);
|
|
4738
|
+
ok(ctx, `markdown report saved: ${outPath} (${md.length} chars)`);
|
|
4739
|
+
} else {
|
|
4740
|
+
if (ctx.json) {
|
|
4741
|
+
console.log(JSON.stringify({
|
|
4742
|
+
markdown: md,
|
|
4743
|
+
chars: md.length
|
|
4744
|
+
}, null, 2));
|
|
4745
|
+
return 0;
|
|
4746
|
+
}
|
|
4747
|
+
out(ctx, md);
|
|
4748
|
+
}
|
|
4749
|
+
return 0;
|
|
4750
|
+
}
|
|
4671
4751
|
case "time-travel": {
|
|
4672
4752
|
const when = rest[0];
|
|
4673
4753
|
if (!when) {
|
|
@@ -4736,6 +4816,13 @@ function run$12(env, action, rest, args) {
|
|
|
4736
4816
|
ok(ctx, "no events yet");
|
|
4737
4817
|
return 0;
|
|
4738
4818
|
}
|
|
4819
|
+
if (ctx.json) {
|
|
4820
|
+
console.log(JSON.stringify({
|
|
4821
|
+
timeline: days,
|
|
4822
|
+
total: days.reduce((s2, d) => s2 + d.events, 0)
|
|
4823
|
+
}, null, 2));
|
|
4824
|
+
return 0;
|
|
4825
|
+
}
|
|
4739
4826
|
const maxEvents = Math.max(...days.map((d) => d.events));
|
|
4740
4827
|
head(ctx, "Workspace Timeline");
|
|
4741
4828
|
let totalP = 0, totalS = 0, totalC = 0, totalK = 0, totalN = 0;
|
|
@@ -5495,7 +5582,10 @@ function buildBrief(env) {
|
|
|
5495
5582
|
const { store } = env;
|
|
5496
5583
|
const cur = store.db.metaGet("current_project");
|
|
5497
5584
|
const stats = store.stats();
|
|
5498
|
-
const skills = store.skillList().slice(0, 5)
|
|
5585
|
+
const skills = store.skillList().slice(0, 5).map((sk) => ({
|
|
5586
|
+
...sk,
|
|
5587
|
+
description: String(sk.description || "").slice(0, 60) + (String(sk.description || "").length > 60 ? "…" : "")
|
|
5588
|
+
}));
|
|
5499
5589
|
const notes = store.kbList().slice(0, 5);
|
|
5500
5590
|
const payload = {
|
|
5501
5591
|
workspace: env.home,
|
|
@@ -5619,13 +5709,25 @@ function run$9(env, action, rest, args) {
|
|
|
5619
5709
|
const { execSync } = require("node:child_process");
|
|
5620
5710
|
const self = process.argv[1];
|
|
5621
5711
|
try {
|
|
5622
|
-
|
|
5623
|
-
env: {
|
|
5712
|
+
const result = execSync(`${JSON.stringify(process.execPath)} ${JSON.stringify(self)} --no-color ${cmdParts.map((c) => JSON.stringify(c)).join(" ")}`, {
|
|
5713
|
+
env: {
|
|
5714
|
+
...process.env,
|
|
5715
|
+
YGL_TEST: "1"
|
|
5716
|
+
},
|
|
5624
5717
|
encoding: "utf8",
|
|
5625
5718
|
timeout: 1e4
|
|
5626
|
-
})
|
|
5719
|
+
});
|
|
5720
|
+
if (ctx.json) {
|
|
5721
|
+
console.log(result.trim());
|
|
5722
|
+
return 0;
|
|
5723
|
+
}
|
|
5724
|
+
out(ctx, result.trim());
|
|
5627
5725
|
return 0;
|
|
5628
5726
|
} catch (e) {
|
|
5727
|
+
if (ctx.json) {
|
|
5728
|
+
console.log(JSON.stringify({ error: String(e?.message || e).slice(0, 200) }));
|
|
5729
|
+
return 1;
|
|
5730
|
+
}
|
|
5629
5731
|
bad(ctx, String(e?.stdout || e?.message || e).slice(0, 200));
|
|
5630
5732
|
return 1;
|
|
5631
5733
|
}
|
|
@@ -5708,7 +5810,10 @@ function run$9(env, action, rest, args) {
|
|
|
5708
5810
|
const payload = buildBrief(env);
|
|
5709
5811
|
const cur = payload.current_project;
|
|
5710
5812
|
const stats = payload.counts;
|
|
5711
|
-
const skills = store.skillList().slice(0, 5)
|
|
5813
|
+
const skills = store.skillList().slice(0, 5).map((sk) => ({
|
|
5814
|
+
...sk,
|
|
5815
|
+
description: String(sk.description || "").slice(0, 60) + (String(sk.description || "").length > 60 ? "…" : "")
|
|
5816
|
+
}));
|
|
5712
5817
|
if (ctx.json) {
|
|
5713
5818
|
console.log(JSON.stringify(payload, null, 2));
|
|
5714
5819
|
return 0;
|
|
@@ -8911,7 +9016,7 @@ function run$3(env, action, rest, args) {
|
|
|
8911
9016
|
"confidence",
|
|
8912
9017
|
"reuse",
|
|
8913
9018
|
"updated"
|
|
8914
|
-
]);
|
|
9019
|
+
], "kb add --title ... --body ... | kb import <dir>");
|
|
8915
9020
|
return 0;
|
|
8916
9021
|
case "rm":
|
|
8917
9022
|
case "remove": {
|
|
@@ -10792,6 +10897,22 @@ async function main(argv) {
|
|
|
10792
10897
|
out(ctx, appVersion());
|
|
10793
10898
|
return 0;
|
|
10794
10899
|
}
|
|
10900
|
+
if (!group || group === "help") {
|
|
10901
|
+
const dbFile = require("node:path").join(process.env.YGL_HOME || require("node:path").join(require("node:os").homedir(), ".ygl-trees"), "ygl-trees.db");
|
|
10902
|
+
if (!require("node:fs").existsSync(dbFile)) {
|
|
10903
|
+
console.log("✓ Welcome to ygl-trees! 🐵");
|
|
10904
|
+
console.log();
|
|
10905
|
+
console.log(" This workspace has no database yet. Get started in 3 steps:");
|
|
10906
|
+
console.log();
|
|
10907
|
+
console.log(" 1. ygl-trees db init # create workspace (9 skills auto-imported)");
|
|
10908
|
+
console.log(" 2. ygl-trees agent brief # see current state + next actions");
|
|
10909
|
+
console.log(" 3. ygl-trees project new my-research -q \"your question\"");
|
|
10910
|
+
console.log();
|
|
10911
|
+
console.log(" More: ygl-trees llms (full capability manifest) | ygl-trees (no args, after init)");
|
|
10912
|
+
console.log();
|
|
10913
|
+
return 0;
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10795
10916
|
if (group === "llms") {
|
|
10796
10917
|
const section = String(args.flags.section || "");
|
|
10797
10918
|
if (args.flags.json === true) {
|
|
@@ -72,20 +72,23 @@ function padEnd(s, width) {
|
|
|
72
72
|
return gap > 0 ? s + " ".repeat(gap) : s;
|
|
73
73
|
}
|
|
74
74
|
/** Render rows as an aligned table (falls back to JSON when ctx.json). */
|
|
75
|
-
function table(ctx, rows, headers, keys) {
|
|
75
|
+
function table(ctx, rows, headers, keys, hint) {
|
|
76
76
|
if (ctx.json) {
|
|
77
77
|
console.log(JSON.stringify(rows, null, 2));
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
if (!rows.length) {
|
|
81
81
|
out(ctx, paint(exports.C.dim, " (empty)", ctx.color));
|
|
82
|
+
if (hint)
|
|
83
|
+
out(ctx, paint(exports.C.dim, ` → ${hint}`, ctx.color));
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
84
86
|
const cols = keys || Object.keys(rows[0]).filter((k) => !k.startsWith("_"));
|
|
85
87
|
const hs = headers || cols;
|
|
86
|
-
const widthOf = (k) => Math.max(...rows.map((r) => dispWidth(String(r[k] ?? ""))));
|
|
88
|
+
const widthOf = (k) => Math.min(40, Math.max(...rows.map((r) => dispWidth(String(r[k] ?? "")))));
|
|
87
89
|
const w = cols.map((k, i) => Math.max(dispWidth(hs[i] ?? ""), widthOf(k)));
|
|
88
|
-
const
|
|
90
|
+
const trunc = (s) => s.length > 40 ? s.slice(0, 39) + "…" : s;
|
|
91
|
+
const line = (cells) => cells.map((c, i) => padEnd(trunc(String(c)), w[i])).join(" ");
|
|
89
92
|
out(ctx, paint(exports.C.bold, " " + line(hs), ctx.color));
|
|
90
93
|
out(ctx, paint(exports.C.dim, " " + w.map((n) => "-".repeat(n)).join(" "), ctx.color));
|
|
91
94
|
for (const r of rows)
|
|
@@ -10,7 +10,7 @@ function buildBrief(env) {
|
|
|
10
10
|
const { store } = env;
|
|
11
11
|
const cur = store.db.metaGet("current_project");
|
|
12
12
|
const stats = store.stats();
|
|
13
|
-
const skills = store.skillList().slice(0, 5);
|
|
13
|
+
const skills = store.skillList().slice(0, 5).map((sk) => ({ ...sk, description: String(sk.description || "").slice(0, 60) + (String(sk.description || "").length > 60 ? "…" : "") }));
|
|
14
14
|
const notes = store.kbList().slice(0, 5);
|
|
15
15
|
const payload = {
|
|
16
16
|
workspace: env.home,
|
|
@@ -99,16 +99,27 @@ function run(env, action, rest, args) {
|
|
|
99
99
|
if (r.pattern.test(q)) {
|
|
100
100
|
if (!ctx.json)
|
|
101
101
|
(0, fmt_1.out)(ctx, (0, fmt_1.paint)(fmt_1.C.dim, ` → ${r.label}`, ctx.color));
|
|
102
|
-
//
|
|
102
|
+
// F1 fix (a2a review): parent holds .ygllock → child self-deadlocks.
|
|
103
|
+
// Fix: child gets YGL_TEST=1 to skip lock (parent already holds it —
|
|
104
|
+
// no concurrent write risk for read-only routing).
|
|
103
105
|
const cmdParts = [r.group, r.action, ...(r.extra || [])].filter(Boolean);
|
|
104
106
|
const { execSync } = require("node:child_process");
|
|
105
107
|
const self = process.argv[1];
|
|
106
108
|
try {
|
|
107
|
-
const result = execSync(`${JSON.stringify(process.execPath)} ${JSON.stringify(self)} --no-color ${cmdParts.map(c => JSON.stringify(c)).join(" ")}`, { env: { ...process.env }, encoding: "utf8", timeout: 10000 });
|
|
109
|
+
const result = execSync(`${JSON.stringify(process.execPath)} ${JSON.stringify(self)} --no-color ${cmdParts.map(c => JSON.stringify(c)).join(" ")}`, { env: { ...process.env, YGL_TEST: "1" }, encoding: "utf8", timeout: 10000 });
|
|
110
|
+
// F2 fix: --json mode outputs only the inner result (contract ④)
|
|
111
|
+
if (ctx.json) {
|
|
112
|
+
console.log(result.trim());
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
108
115
|
(0, fmt_1.out)(ctx, result.trim());
|
|
109
116
|
return 0;
|
|
110
117
|
}
|
|
111
118
|
catch (e) {
|
|
119
|
+
if (ctx.json) {
|
|
120
|
+
console.log(JSON.stringify({ error: String(e?.message || e).slice(0, 200) }));
|
|
121
|
+
return 1;
|
|
122
|
+
}
|
|
112
123
|
(0, fmt_1.bad)(ctx, String(e?.stdout || e?.message || e).slice(0, 200));
|
|
113
124
|
return 1;
|
|
114
125
|
}
|
|
@@ -201,7 +212,7 @@ function run(env, action, rest, args) {
|
|
|
201
212
|
const payload = buildBrief(env);
|
|
202
213
|
const cur = payload.current_project;
|
|
203
214
|
const stats = payload.counts;
|
|
204
|
-
const skills = store.skillList().slice(0, 5);
|
|
215
|
+
const skills = store.skillList().slice(0, 5).map((sk) => ({ ...sk, description: String(sk.description || "").slice(0, 60) + (String(sk.description || "").length > 60 ? "…" : "") }));
|
|
205
216
|
if (ctx.json) {
|
|
206
217
|
console.log(JSON.stringify(payload, null, 2));
|
|
207
218
|
return 0;
|
|
@@ -133,6 +133,76 @@ function run(env, action, rest, args) {
|
|
|
133
133
|
(0, fmt_1.show)(ctx, { file, size_kb: (fs.statSync(file).size / 1024).toFixed(1), ...s });
|
|
134
134
|
return 0;
|
|
135
135
|
}
|
|
136
|
+
case "export-markdown": {
|
|
137
|
+
// v2: auto-generated workspace report in markdown
|
|
138
|
+
const lines = [];
|
|
139
|
+
lines.push(`# ygl-trees Workspace Report`);
|
|
140
|
+
lines.push(`> Generated: ${new Date().toISOString()}`);
|
|
141
|
+
lines.push("");
|
|
142
|
+
// Metrics
|
|
143
|
+
const counts = {};
|
|
144
|
+
for (const t of ["projects", "sources", "evidence", "claims", "kb_notes", "skills", "trees", "nodes", "artifacts", "events"]) {
|
|
145
|
+
try {
|
|
146
|
+
counts[t] = db.get(`SELECT COUNT(*) as n FROM ${t}`)?.n || 0;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
counts[t] = 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
lines.push(`## Summary`);
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push(`| Entity | Count |`);
|
|
155
|
+
lines.push(`|---|---|`);
|
|
156
|
+
const labels = { projects: "项目", sources: "来源", evidence: "证据", claims: "断言", kb_notes: "知识", skills: "技能", trees: "树", nodes: "节点", artifacts: "产出", events: "事件" };
|
|
157
|
+
for (const [k, v] of Object.entries(counts))
|
|
158
|
+
lines.push(`| ${labels[k] || k} | ${v} |`);
|
|
159
|
+
lines.push("");
|
|
160
|
+
// Projects
|
|
161
|
+
lines.push(`## Projects`);
|
|
162
|
+
const projects = db.all("SELECT name, question, created FROM projects ORDER BY created DESC LIMIT 10");
|
|
163
|
+
for (const p of projects) {
|
|
164
|
+
lines.push(`- **${p.name}**: ${p.question || "(no question)"}`);
|
|
165
|
+
}
|
|
166
|
+
lines.push("");
|
|
167
|
+
// Recent claims
|
|
168
|
+
lines.push(`## Recent Claims`);
|
|
169
|
+
const claims = db.all("SELECT text, status, confidence FROM claims ORDER BY created DESC LIMIT 10");
|
|
170
|
+
for (const c of claims) {
|
|
171
|
+
lines.push(`- [${c.status}] ${String(c.text).slice(0, 80)}... (${c.confidence})`);
|
|
172
|
+
}
|
|
173
|
+
lines.push("");
|
|
174
|
+
// Knowledge
|
|
175
|
+
lines.push(`## Knowledge Notes`);
|
|
176
|
+
const kb = db.all("SELECT title, tags, confidence FROM kb_notes ORDER BY created DESC LIMIT 10");
|
|
177
|
+
for (const k of kb) {
|
|
178
|
+
lines.push(`- **${String(k.title).slice(0, 50)}** ${k.tags ? `(${k.tags})` : ""} [${k.confidence}]`);
|
|
179
|
+
}
|
|
180
|
+
lines.push("");
|
|
181
|
+
// Trees
|
|
182
|
+
lines.push(`## Trees`);
|
|
183
|
+
const trees = db.all("SELECT name FROM trees");
|
|
184
|
+
for (const t of trees) {
|
|
185
|
+
const nodes = db.get("SELECT COUNT(*) as n FROM nodes WHERE tree_id=(SELECT id FROM trees WHERE name=?)", [t.name])?.n || 0;
|
|
186
|
+
lines.push(`- **${t.name}** (${nodes} nodes)`);
|
|
187
|
+
}
|
|
188
|
+
lines.push("");
|
|
189
|
+
lines.push(`---`);
|
|
190
|
+
lines.push(`*Auto-generated by ygl-trees db export-markdown*`);
|
|
191
|
+
const md = lines.join("\n");
|
|
192
|
+
const outPath = rest[0];
|
|
193
|
+
if (outPath) {
|
|
194
|
+
fs.writeFileSync(outPath, md);
|
|
195
|
+
(0, fmt_1.ok)(ctx, `markdown report saved: ${outPath} (${md.length} chars)`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (ctx.json) {
|
|
199
|
+
console.log(JSON.stringify({ markdown: md, chars: md.length }, null, 2));
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
(0, fmt_1.out)(ctx, md);
|
|
203
|
+
}
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
136
206
|
case "time-travel": {
|
|
137
207
|
// v2: query workspace state at a past moment using entity created_at
|
|
138
208
|
const when = rest[0];
|
|
@@ -190,6 +260,11 @@ function run(env, action, rest, args) {
|
|
|
190
260
|
(0, fmt_1.ok)(ctx, "no events yet");
|
|
191
261
|
return 0;
|
|
192
262
|
}
|
|
263
|
+
// F2 fix: --json outputs pure JSON (frozen contract ④)
|
|
264
|
+
if (ctx.json) {
|
|
265
|
+
console.log(JSON.stringify({ timeline: days, total: days.reduce((s2, d) => s2 + d.events, 0) }, null, 2));
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
193
268
|
const maxEvents = Math.max(...days.map(d => d.events));
|
|
194
269
|
(0, fmt_1.head)(ctx, "Workspace Timeline");
|
|
195
270
|
let totalP = 0, totalS = 0, totalC = 0, totalK = 0, totalN = 0;
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ygl-trees v0.
|
|
1
|
+
# ygl-trees v0.74.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).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ygl-trees",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.74.0",
|
|
4
4
|
"description": "猿光 · 科技树 — database-centric CLI for research → production → knowledge reuse, built for AI agents.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/apps/cli/index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"reflect-metadata": "^0.2.2",
|
|
26
26
|
"typeorm": "^1.1.2",
|
|
27
27
|
"@deepseek-ai/cordis": "~4.0.5-alpha.2",
|
|
28
|
-
"@deepseek-ai/cordis-plugin-timer": "~1.1.7-alpha.2",
|
|
29
28
|
"@deepseek-ai/schemastery": "~3.18.5-alpha.2",
|
|
29
|
+
"@deepseek-ai/cordis-plugin-timer": "~1.1.7-alpha.2",
|
|
30
30
|
"@deepseek-ai/cosmokit": "~1.8.6-alpha.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|