libero3 0.3.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/COLD_START.md +31 -0
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/data/migrations/001_initial_schema.sql +43 -0
- package/data/migrations/002_seed_categories.sql +18 -0
- package/data/migrations/003_add_rating.sql +4 -0
- package/data/migrations/004_timer_sessions.sql +21 -0
- package/data/migrations/005_profile_settings.sql +5 -0
- package/data/migrations/006_add_timeblock_status.sql +1 -0
- package/data/migrations/007_reminders.sql +17 -0
- package/data/migrations/008_timer_target_ms.sql +5 -0
- package/data/migrations/009_tasks.sql +22 -0
- package/data/migrations/010_timer_task_id.sql +4 -0
- package/data/migrations/011_profile_default_confirmed.sql +4 -0
- package/data/migrations/012_identity_tables.sql +68 -0
- package/data/migrations/013_identity_managed_by.sql +3 -0
- package/data/migrations/014_task_difficulty_priority.sql +6 -0
- package/data/migrations/015_timeblock_asked_at.sql +5 -0
- package/data/migrations/016_intent_columns.sql +8 -0
- package/data/migrations/017_ap_wallet.sql +25 -0
- package/data/migrations/018_ap_rewards.sql +21 -0
- package/data/migrations/019_ap_bounty.sql +10 -0
- package/data/migrations/020_chat_turns.sql +10 -0
- package/data/migrations/021_portrait.sql +4 -0
- package/data/migrations/022_family_emoji_single.sql +3 -0
- package/data/migrations/023_ap_balance_view_log.sql +9 -0
- package/data/schema.sql +75 -0
- package/dist/data/db.js +83 -0
- package/dist/data/migrations/001_initial_schema.sql +43 -0
- package/dist/data/migrations/002_seed_categories.sql +18 -0
- package/dist/data/migrations/003_add_rating.sql +4 -0
- package/dist/data/migrations/004_timer_sessions.sql +21 -0
- package/dist/data/migrations/005_profile_settings.sql +5 -0
- package/dist/data/migrations/006_add_timeblock_status.sql +1 -0
- package/dist/data/migrations/007_reminders.sql +17 -0
- package/dist/data/migrations/008_timer_target_ms.sql +5 -0
- package/dist/data/migrations/009_tasks.sql +22 -0
- package/dist/data/migrations/010_timer_task_id.sql +4 -0
- package/dist/data/migrations/011_profile_default_confirmed.sql +4 -0
- package/dist/data/migrations/012_identity_tables.sql +68 -0
- package/dist/data/migrations/013_identity_managed_by.sql +3 -0
- package/dist/data/migrations/014_task_difficulty_priority.sql +6 -0
- package/dist/data/migrations/015_timeblock_asked_at.sql +5 -0
- package/dist/data/migrations/016_intent_columns.sql +8 -0
- package/dist/data/migrations/017_ap_wallet.sql +25 -0
- package/dist/data/migrations/018_ap_rewards.sql +21 -0
- package/dist/data/migrations/019_ap_bounty.sql +10 -0
- package/dist/data/migrations/020_chat_turns.sql +10 -0
- package/dist/data/migrations/021_portrait.sql +4 -0
- package/dist/data/migrations/022_family_emoji_single.sql +3 -0
- package/dist/data/migrations/023_ap_balance_view_log.sql +9 -0
- package/dist/data/schema.sql +75 -0
- package/dist/knowledge/_registry/dimensions.yaml +10 -0
- package/dist/knowledge/_registry/personas.yaml +7 -0
- package/dist/knowledge/_schema/analysis-model.schema.js +32 -0
- package/dist/knowledge/_schema/entry.schema.js +45 -0
- package/dist/knowledge/_schema/persona.schema.js +23 -0
- package/dist/knowledge/_schema/scenario-pack.schema.js +29 -0
- package/dist/knowledge/analysis-models/review-default.yaml +16 -0
- package/dist/knowledge/analysis-models/review-summer-plan.yaml +15 -0
- package/dist/knowledge/brain-science/brain-deep-warmup.yaml +22 -0
- package/dist/knowledge/brain-science/brain-emotion-granularity.yaml +22 -0
- package/dist/knowledge/brain-science/brain-env-engineering.yaml +22 -0
- package/dist/knowledge/brain-science/brain-extended-mind.yaml +22 -0
- package/dist/knowledge/brain-science/brain-friction-barrier.yaml +22 -0
- package/dist/knowledge/brain-science/brain-peak-end.yaml +29 -0
- package/dist/knowledge/brain-science/brain-pomodoro.yaml +31 -0
- package/dist/knowledge/brain-science/brain-revenge-bedtime.yaml +22 -0
- package/dist/knowledge/brain-science/brain-single-variable.yaml +15 -0
- package/dist/knowledge/brain-science/brain-tiny-habits.yaml +22 -0
- package/dist/knowledge/energy/energy-active-recovery.yaml +22 -0
- package/dist/knowledge/energy/energy-deliberate-rest.yaml +22 -0
- package/dist/knowledge/energy/energy-exercise-embedded.yaml +22 -0
- package/dist/knowledge/energy/energy-fixed-wake-time.yaml +22 -0
- package/dist/knowledge/energy/energy-four-energy-radar.yaml +22 -0
- package/dist/knowledge/energy/energy-intensity-recovery.yaml +22 -0
- package/dist/knowledge/scenario-packs/pack-summer-kid.yaml +12 -0
- package/dist/knowledge/src/repo/fs-yaml.js +252 -0
- package/dist/knowledge/src/repo/types.js +1 -0
- package/dist/knowledge/time/time-batch-windows.yaml +22 -0
- package/dist/knowledge/time/time-conscious-abandon.yaml +22 -0
- package/dist/knowledge/time/time-deep-work-block.yaml +22 -0
- package/dist/knowledge/time/time-finite-commitment.yaml +22 -0
- package/dist/knowledge/time/time-forced-whitespace.yaml +22 -0
- package/dist/knowledge/time/time-gtd-next-action.yaml +22 -0
- package/dist/knowledge/time/time-three-tier-list.yaml +22 -0
- package/dist/mcp/cold-start.js +43 -0
- package/dist/mcp/contract/v1/rest.js +56 -0
- package/dist/mcp/rest-server.js +52 -0
- package/dist/mcp/server-core.js +1334 -0
- package/dist/mcp/server.js +34 -0
- package/dist/mcp/src/ap/index.js +14 -0
- package/dist/mcp/src/ap/rewards.js +44 -0
- package/dist/mcp/src/categories/index.js +12 -0
- package/dist/mcp/src/chat/from-env.js +111 -0
- package/dist/mcp/src/chat/history.js +35 -0
- package/dist/mcp/src/chat/loop.js +131 -0
- package/dist/mcp/src/chat/portrait.js +133 -0
- package/dist/mcp/src/chat/system-prompt.js +23 -0
- package/dist/mcp/src/chat/tool-names.js +27 -0
- package/dist/mcp/src/chat/tool-schema.js +20 -0
- package/dist/mcp/src/chat/types.js +1 -0
- package/dist/mcp/src/clock/index.js +78 -0
- package/dist/mcp/src/coachmod/index.js +135 -0
- package/dist/mcp/src/host-adapters/hermes-cron.js +185 -0
- package/dist/mcp/src/host-adapters/index.js +61 -0
- package/dist/mcp/src/host-adapters/types.js +7 -0
- package/dist/mcp/src/identity/equivalent-ids.js +41 -0
- package/dist/mcp/src/identity/in-memory.js +100 -0
- package/dist/mcp/src/identity/resolve.js +60 -0
- package/dist/mcp/src/identity/types.js +4 -0
- package/dist/mcp/src/matrix/index.js +15 -0
- package/dist/mcp/src/profile/confirm-default.js +48 -0
- package/dist/mcp/src/profile/index.js +298 -0
- package/dist/mcp/src/profile-confirm.js +28 -0
- package/dist/mcp/src/profile-helpers.js +109 -0
- package/dist/mcp/src/reminder/hermes-channel.js +40 -0
- package/dist/mcp/src/reminder/index.js +70 -0
- package/dist/mcp/src/reminder/push-channel.js +10 -0
- package/dist/mcp/src/repo/factory.js +34 -0
- package/dist/mcp/src/repo/in-memory.js +469 -0
- package/dist/mcp/src/repo/sqlite-ap.js +188 -0
- package/dist/mcp/src/repo/sqlite-helpers.js +39 -0
- package/dist/mcp/src/repo/sqlite-identity.js +129 -0
- package/dist/mcp/src/repo/sqlite.js +754 -0
- package/dist/mcp/src/repo/types.js +4 -0
- package/dist/mcp/src/rest/app.js +121 -0
- package/dist/mcp/src/rest/invoke-tool.js +98 -0
- package/dist/mcp/src/rest/short-token.js +38 -0
- package/dist/mcp/src/rest/tokens.js +55 -0
- package/dist/mcp/src/scheduler/index.js +319 -0
- package/dist/mcp/src/stats/index.js +29 -0
- package/dist/mcp/src/stickiness/report.js +174 -0
- package/dist/mcp/src/stickiness/types.js +1 -0
- package/dist/mcp/src/task/index.js +412 -0
- package/dist/mcp/src/timeblock/index.js +206 -0
- package/dist/mcp/src/timeline/index.js +705 -0
- package/dist/mcp/src/timer/index.js +234 -0
- package/dist/mcp/src/user-id-resolver.js +30 -0
- package/dist/mcp/src/weixin/app.js +97 -0
- package/dist/mcp/src/weixin/custom-send.js +82 -0
- package/dist/mcp/src/weixin/handle-text.js +49 -0
- package/dist/mcp/src/weixin/reminder-loop.js +39 -0
- package/dist/mcp/src/weixin/signature.js +13 -0
- package/dist/mcp/src/weixin/tencent-asr.js +101 -0
- package/dist/mcp/src/weixin/xml.js +37 -0
- package/dist/mcp/weixin-server.js +158 -0
- package/knowledge/_registry/dimensions.yaml +10 -0
- package/knowledge/_registry/personas.yaml +7 -0
- package/knowledge/_schema/analysis-model.schema.ts +36 -0
- package/knowledge/_schema/entry.schema.ts +50 -0
- package/knowledge/_schema/persona.schema.ts +30 -0
- package/knowledge/_schema/scenario-pack.schema.ts +33 -0
- package/knowledge/analysis-models/review-default.yaml +16 -0
- package/knowledge/analysis-models/review-summer-plan.yaml +15 -0
- package/knowledge/brain-science/brain-deep-warmup.yaml +22 -0
- package/knowledge/brain-science/brain-emotion-granularity.yaml +22 -0
- package/knowledge/brain-science/brain-env-engineering.yaml +22 -0
- package/knowledge/brain-science/brain-extended-mind.yaml +22 -0
- package/knowledge/brain-science/brain-friction-barrier.yaml +22 -0
- package/knowledge/brain-science/brain-peak-end.yaml +29 -0
- package/knowledge/brain-science/brain-pomodoro.yaml +31 -0
- package/knowledge/brain-science/brain-revenge-bedtime.yaml +22 -0
- package/knowledge/brain-science/brain-single-variable.yaml +15 -0
- package/knowledge/brain-science/brain-tiny-habits.yaml +22 -0
- package/knowledge/energy/energy-active-recovery.yaml +22 -0
- package/knowledge/energy/energy-deliberate-rest.yaml +22 -0
- package/knowledge/energy/energy-exercise-embedded.yaml +22 -0
- package/knowledge/energy/energy-fixed-wake-time.yaml +22 -0
- package/knowledge/energy/energy-four-energy-radar.yaml +22 -0
- package/knowledge/energy/energy-intensity-recovery.yaml +22 -0
- package/knowledge/scenario-packs/pack-summer-kid.yaml +12 -0
- package/knowledge/src/repo/fs-yaml.ts +282 -0
- package/knowledge/src/repo/types.ts +24 -0
- package/knowledge/tests/fixtures/base/_registry/dimensions.yaml +4 -0
- package/knowledge/tests/fixtures/base/time/valid-entry.yaml +24 -0
- package/knowledge/tests/fixtures/dup/_registry/dimensions.yaml +4 -0
- package/knowledge/tests/fixtures/dup/time/entry-a.yaml +13 -0
- package/knowledge/tests/fixtures/dup/time/entry-b.yaml +13 -0
- package/knowledge/tests/fixtures/unregistered/_registry/dimensions.yaml +7 -0
- package/knowledge/tests/fixtures/unregistered/time/anxiety-entry.yaml +13 -0
- package/knowledge/tests/repo/fs-yaml-knowledge-repo.test.ts +333 -0
- package/knowledge/time/time-batch-windows.yaml +22 -0
- package/knowledge/time/time-conscious-abandon.yaml +22 -0
- package/knowledge/time/time-deep-work-block.yaml +22 -0
- package/knowledge/time/time-finite-commitment.yaml +22 -0
- package/knowledge/time/time-forced-whitespace.yaml +22 -0
- package/knowledge/time/time-gtd-next-action.yaml +22 -0
- package/knowledge/time/time-three-tier-list.yaml +22 -0
- package/knowledge/validate.ts +50 -0
- package/package.json +76 -0
- package/scripts/postinstall-cold-start.js +45 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach } from "vitest";
|
|
2
|
+
import { writeFileSync, unlinkSync, rmSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { validateEntry } from "../../_schema/entry.schema.js";
|
|
5
|
+
import { FsYamlKnowledgeRepo } from "../../src/repo/fs-yaml.js";
|
|
6
|
+
|
|
7
|
+
// ── Fixture paths ──
|
|
8
|
+
|
|
9
|
+
const FIXTURES_BASE = join(import.meta.dirname, "..", "fixtures");
|
|
10
|
+
const BASE = join(FIXTURES_BASE, "base");
|
|
11
|
+
const DUP = join(FIXTURES_BASE, "dup");
|
|
12
|
+
const UNREGISTERED = join(FIXTURES_BASE, "unregistered");
|
|
13
|
+
|
|
14
|
+
// ============================================================
|
|
15
|
+
// Schema validation tests (T1-T6) — test validateEntry() directly
|
|
16
|
+
// ============================================================
|
|
17
|
+
|
|
18
|
+
describe("Schema validation (validateEntry)", () => {
|
|
19
|
+
// T1
|
|
20
|
+
test("T1: valid entry passes validation", () => {
|
|
21
|
+
const entry = validateEntry({
|
|
22
|
+
id: "test-valid",
|
|
23
|
+
kind: "prescription",
|
|
24
|
+
dimension: "time",
|
|
25
|
+
title: "测试条目",
|
|
26
|
+
triggers: {
|
|
27
|
+
signals: [{ quadrant: "能耗黑洞" }],
|
|
28
|
+
symptoms: ["测试症状"],
|
|
29
|
+
},
|
|
30
|
+
prescription: {
|
|
31
|
+
one_liner: "测试处方",
|
|
32
|
+
micro_experiment: "测试微实验",
|
|
33
|
+
},
|
|
34
|
+
source: { ref: "测试来源" },
|
|
35
|
+
evidence_level: "book",
|
|
36
|
+
});
|
|
37
|
+
expect(entry.id).toBe("test-valid");
|
|
38
|
+
expect(entry.kind).toBe("prescription");
|
|
39
|
+
expect(entry.dimension).toBe("time");
|
|
40
|
+
// Default values
|
|
41
|
+
expect(entry.tags).toEqual([]);
|
|
42
|
+
expect(entry.applicability.personas_fit).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// T2
|
|
46
|
+
test("T2: missing id throws", () => {
|
|
47
|
+
expect(() =>
|
|
48
|
+
validateEntry({
|
|
49
|
+
kind: "prescription" as const,
|
|
50
|
+
dimension: "time",
|
|
51
|
+
title: "测试",
|
|
52
|
+
triggers: { symptoms: ["测试"] },
|
|
53
|
+
prescription: { one_liner: "t", micro_experiment: "t" },
|
|
54
|
+
source: { ref: "t" },
|
|
55
|
+
evidence_level: "book",
|
|
56
|
+
})
|
|
57
|
+
).toThrow();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// T3
|
|
61
|
+
test("T3: empty triggers (no signals, no symptoms) throws", () => {
|
|
62
|
+
// Both fields absent
|
|
63
|
+
expect(() =>
|
|
64
|
+
validateEntry({
|
|
65
|
+
id: "t3",
|
|
66
|
+
kind: "prescription" as const,
|
|
67
|
+
dimension: "time",
|
|
68
|
+
title: "测试",
|
|
69
|
+
triggers: {},
|
|
70
|
+
prescription: { one_liner: "t", micro_experiment: "t" },
|
|
71
|
+
source: { ref: "t" },
|
|
72
|
+
evidence_level: "book",
|
|
73
|
+
})
|
|
74
|
+
).toThrow();
|
|
75
|
+
|
|
76
|
+
// Both fields explicitly empty arrays
|
|
77
|
+
expect(() =>
|
|
78
|
+
validateEntry({
|
|
79
|
+
id: "t3b",
|
|
80
|
+
kind: "prescription" as const,
|
|
81
|
+
dimension: "time",
|
|
82
|
+
title: "测试",
|
|
83
|
+
triggers: { signals: [], symptoms: [] },
|
|
84
|
+
prescription: { one_liner: "t", micro_experiment: "t" },
|
|
85
|
+
source: { ref: "t" },
|
|
86
|
+
evidence_level: "book",
|
|
87
|
+
})
|
|
88
|
+
).toThrow();
|
|
89
|
+
|
|
90
|
+
// signals present but empty, symptoms absent
|
|
91
|
+
expect(() =>
|
|
92
|
+
validateEntry({
|
|
93
|
+
id: "t3c",
|
|
94
|
+
kind: "prescription" as const,
|
|
95
|
+
dimension: "time",
|
|
96
|
+
title: "测试",
|
|
97
|
+
triggers: { signals: [] },
|
|
98
|
+
prescription: { one_liner: "t", micro_experiment: "t" },
|
|
99
|
+
source: { ref: "t" },
|
|
100
|
+
evidence_level: "book",
|
|
101
|
+
})
|
|
102
|
+
).toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// T4
|
|
106
|
+
test("T4: kind=prescription without one_liner throws", () => {
|
|
107
|
+
// Missing one_liner field entirely
|
|
108
|
+
expect(() =>
|
|
109
|
+
validateEntry({
|
|
110
|
+
id: "t4",
|
|
111
|
+
kind: "prescription" as const,
|
|
112
|
+
dimension: "time",
|
|
113
|
+
title: "测试",
|
|
114
|
+
triggers: { symptoms: ["测试"] },
|
|
115
|
+
prescription: { micro_experiment: "t" } as any,
|
|
116
|
+
source: { ref: "t" },
|
|
117
|
+
evidence_level: "book",
|
|
118
|
+
})
|
|
119
|
+
).toThrow();
|
|
120
|
+
|
|
121
|
+
// Empty one_liner string
|
|
122
|
+
expect(() =>
|
|
123
|
+
validateEntry({
|
|
124
|
+
id: "t4b",
|
|
125
|
+
kind: "prescription" as const,
|
|
126
|
+
dimension: "time",
|
|
127
|
+
title: "测试",
|
|
128
|
+
triggers: { symptoms: ["测试"] },
|
|
129
|
+
prescription: { one_liner: "", micro_experiment: "t" },
|
|
130
|
+
source: { ref: "t" },
|
|
131
|
+
evidence_level: "book",
|
|
132
|
+
})
|
|
133
|
+
).toThrow();
|
|
134
|
+
|
|
135
|
+
// Missing micro_experiment
|
|
136
|
+
expect(() =>
|
|
137
|
+
validateEntry({
|
|
138
|
+
id: "t4c",
|
|
139
|
+
kind: "prescription" as const,
|
|
140
|
+
dimension: "time",
|
|
141
|
+
title: "测试",
|
|
142
|
+
triggers: { symptoms: ["测试"] },
|
|
143
|
+
prescription: { one_liner: "t" },
|
|
144
|
+
source: { ref: "t" },
|
|
145
|
+
evidence_level: "book",
|
|
146
|
+
})
|
|
147
|
+
).toThrow();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// T6
|
|
151
|
+
test("T6: invalid evidence_level throws", () => {
|
|
152
|
+
expect(() =>
|
|
153
|
+
validateEntry({
|
|
154
|
+
id: "t6",
|
|
155
|
+
kind: "prescription" as const,
|
|
156
|
+
dimension: "time",
|
|
157
|
+
title: "测试",
|
|
158
|
+
triggers: { symptoms: ["测试"] },
|
|
159
|
+
prescription: { one_liner: "t", micro_experiment: "t" },
|
|
160
|
+
source: { ref: "t" },
|
|
161
|
+
evidence_level: "unknown" as any,
|
|
162
|
+
})
|
|
163
|
+
).toThrow();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// ============================================================
|
|
168
|
+
// KnowledgeRepo checks — duplicate id & dimension registry (T5, T7)
|
|
169
|
+
// ============================================================
|
|
170
|
+
|
|
171
|
+
describe("KnowledgeRepo — validation at load time", () => {
|
|
172
|
+
// T5
|
|
173
|
+
test("T5: duplicate id in loadAll throws", async () => {
|
|
174
|
+
const repo = new FsYamlKnowledgeRepo(DUP);
|
|
175
|
+
await expect(repo.loadAll()).rejects.toThrow();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// T7
|
|
179
|
+
test("T7: unregistered dimension throws", async () => {
|
|
180
|
+
const repo = new FsYamlKnowledgeRepo(UNREGISTERED);
|
|
181
|
+
await expect(repo.loadAll()).rejects.toThrow(/维度|dimension/i);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ============================================================
|
|
186
|
+
// KnowledgeRepo — base fixture operations (T8-T12, T15, T16)
|
|
187
|
+
// ============================================================
|
|
188
|
+
|
|
189
|
+
describe("KnowledgeRepo — CRUD with base fixtures", () => {
|
|
190
|
+
let repo: FsYamlKnowledgeRepo;
|
|
191
|
+
|
|
192
|
+
beforeEach(() => {
|
|
193
|
+
// Create fresh instance each test
|
|
194
|
+
repo = new FsYamlKnowledgeRepo(BASE);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// T8
|
|
198
|
+
test("T8: loadAll returns correct count", async () => {
|
|
199
|
+
const entries = await repo.loadAll();
|
|
200
|
+
expect(entries.length).toBeGreaterThan(0);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// T9
|
|
204
|
+
test("T9: getById hits existing entry", async () => {
|
|
205
|
+
const entry = await repo.getById("test-valid-entry");
|
|
206
|
+
expect(entry).not.toBeNull();
|
|
207
|
+
expect(entry!.id).toBe("test-valid-entry");
|
|
208
|
+
expect(entry!.dimension).toBe("time");
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// T10
|
|
212
|
+
test("T10: byDimension filters correctly", async () => {
|
|
213
|
+
const time = await repo.byDimension("time");
|
|
214
|
+
expect(time.length).toBeGreaterThan(0);
|
|
215
|
+
for (const e of time) {
|
|
216
|
+
expect(e.dimension).toBe("time");
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// T11
|
|
221
|
+
test("T11: matchBySignal quadrant match returns entries", async () => {
|
|
222
|
+
const matches = await repo.matchBySignal({ quadrant: "能耗黑洞" });
|
|
223
|
+
expect(matches.length).toBeGreaterThan(0);
|
|
224
|
+
for (const e of matches) {
|
|
225
|
+
const hasQuadrant = (e.triggers.signals ?? []).some(
|
|
226
|
+
(s) => s.quadrant === "能耗黑洞"
|
|
227
|
+
);
|
|
228
|
+
expect(hasQuadrant).toBe(true);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// T12
|
|
233
|
+
test("T12: matchBySymptom returns matching entries", async () => {
|
|
234
|
+
const matches = await repo.matchBySymptom("拖延");
|
|
235
|
+
expect(matches.length).toBeGreaterThan(0);
|
|
236
|
+
for (const e of matches) {
|
|
237
|
+
const hasSymptom = (e.triggers.symptoms ?? []).some((s) =>
|
|
238
|
+
s.includes("拖延")
|
|
239
|
+
);
|
|
240
|
+
expect(hasSymptom).toBe(true);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// T15
|
|
245
|
+
test("T15: getById nonexistent returns null", async () => {
|
|
246
|
+
const entry = await repo.getById("nonexistent-id");
|
|
247
|
+
expect(entry).toBeNull();
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// T16
|
|
251
|
+
test("T16: byDimension empty dimension returns empty array", async () => {
|
|
252
|
+
const entries = await repo.byDimension("nonexistent-dim");
|
|
253
|
+
expect(entries).toHaveLength(0);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// ============================================================
|
|
258
|
+
// Equivalence test — real knowledge dir (T13)
|
|
259
|
+
// ============================================================
|
|
260
|
+
|
|
261
|
+
describe("Equivalence — real knowledge directory preserves migrated content", () => {
|
|
262
|
+
// T13: 迁移等价性。初始迁移 21 条;断言下界而非精确数——
|
|
263
|
+
// 知识库可扩展(如番茄钟/论文),硬编码精确数会反扩展性。
|
|
264
|
+
// 守住"不少于初始迁移"即满足"不丢内容"的本意。
|
|
265
|
+
test("T13: loadAll from real knowledge dir returns ≥ 21 entries (initial migration baseline)", async () => {
|
|
266
|
+
const repo = new FsYamlKnowledgeRepo();
|
|
267
|
+
const entries = await repo.loadAll();
|
|
268
|
+
expect(entries.length).toBeGreaterThanOrEqual(21);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// ============================================================
|
|
273
|
+
// Extensibility test (T14)
|
|
274
|
+
// ============================================================
|
|
275
|
+
|
|
276
|
+
describe("Extensibility — new dimension via file addition", () => {
|
|
277
|
+
// T14
|
|
278
|
+
test("T14: adding a new dimension + entry works without code changes", async () => {
|
|
279
|
+
const tmpDir = join(FIXTURES_BASE, "__ext_test__");
|
|
280
|
+
const regDir = join(tmpDir, "_registry");
|
|
281
|
+
const anxDir = join(tmpDir, "anxiety");
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
mkdirSync(regDir, { recursive: true });
|
|
285
|
+
mkdirSync(anxDir, { recursive: true });
|
|
286
|
+
|
|
287
|
+
// Create registry with the new dimension
|
|
288
|
+
writeFileSync(
|
|
289
|
+
join(regDir, "dimensions.yaml"),
|
|
290
|
+
[
|
|
291
|
+
"dimensions:",
|
|
292
|
+
' - id: time',
|
|
293
|
+
' label: 时间',
|
|
294
|
+
' proposition: test',
|
|
295
|
+
' - id: anxiety',
|
|
296
|
+
' label: 焦虑',
|
|
297
|
+
' proposition: test',
|
|
298
|
+
].join("\n"),
|
|
299
|
+
"utf-8"
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
// Create a valid entry in the new dimension
|
|
303
|
+
writeFileSync(
|
|
304
|
+
join(anxDir, "parasympathetic.yaml"),
|
|
305
|
+
[
|
|
306
|
+
'id: anxiety-parasympathetic',
|
|
307
|
+
'kind: prescription',
|
|
308
|
+
'dimension: anxiety',
|
|
309
|
+
'title: 副交感神经激活',
|
|
310
|
+
'triggers:',
|
|
311
|
+
' symptoms:',
|
|
312
|
+
' - 焦虑',
|
|
313
|
+
'prescription:',
|
|
314
|
+
' one_liner: 测试条目',
|
|
315
|
+
' micro_experiment: 测试',
|
|
316
|
+
'source:',
|
|
317
|
+
' ref: 测试',
|
|
318
|
+
'evidence_level: book',
|
|
319
|
+
].join("\n"),
|
|
320
|
+
"utf-8"
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
const repo = new FsYamlKnowledgeRepo(tmpDir);
|
|
324
|
+
const entries = await repo.loadAll();
|
|
325
|
+
expect(entries).toHaveLength(1);
|
|
326
|
+
expect(entries[0].dimension).toBe("anxiety");
|
|
327
|
+
expect(entries[0].id).toBe("anxiety-parasympathetic");
|
|
328
|
+
} finally {
|
|
329
|
+
// Cleanup
|
|
330
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-batch-windows
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 窗口期批处理
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 能耗黑洞
|
|
8
|
+
symptoms:
|
|
9
|
+
- 浅薄工作占比大
|
|
10
|
+
- 消息太多
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 邮件/消息每天只在固定 2 个时段处理,其余时间默认不可达
|
|
13
|
+
steps:
|
|
14
|
+
- 选定 2 个批处理时段(如 11:00 和 16:00)
|
|
15
|
+
- 批处理时段集中回邮件/消息
|
|
16
|
+
- 其余时间关闭通知
|
|
17
|
+
micro_experiment: 明天只在 11:00 和 16:00 各花 15 分钟处理消息
|
|
18
|
+
verify: 一天中被消息打断的次数是否显著下降
|
|
19
|
+
source:
|
|
20
|
+
ref: 《深度工作》(Deep Work, Cal Newport)
|
|
21
|
+
evidence_level: book
|
|
22
|
+
tags: [批处理, 消息管理]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-conscious-abandon
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 有意识的放弃
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 可放弃
|
|
8
|
+
symptoms:
|
|
9
|
+
- 可放弃事项多
|
|
10
|
+
- 做减法
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 选一个低分分类,直接砍掉或砍半。空出来的时间不填新东西——感受"留白"
|
|
13
|
+
steps:
|
|
14
|
+
- 找出可放弃象限中耗时最长但满意度最低的 1 个分类
|
|
15
|
+
- 直接砍掉或砍半
|
|
16
|
+
- 空出来的时间不填新东西
|
|
17
|
+
micro_experiment: 选一个低分分类砍掉,空出来的时间不填新东西
|
|
18
|
+
verify: 一周后看总耗时是否下降、满意度是否提升
|
|
19
|
+
source:
|
|
20
|
+
ref: 《四千周》(Four Thousand Weeks, Oliver Burkeman)
|
|
21
|
+
evidence_level: book
|
|
22
|
+
tags: [做减法, 放弃]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-deep-work-block
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 深度工作块
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 能耗黑洞
|
|
8
|
+
symptoms:
|
|
9
|
+
- 碎片化严重
|
|
10
|
+
- 频繁切换
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 每天锁定 1-1.5 小时无干扰深度时段:关通知、手机放抽屉、只做一件事
|
|
13
|
+
steps:
|
|
14
|
+
- 选定每天的深度时段(如上午 9:00-10:30)
|
|
15
|
+
- 关掉所有通知,手机放抽屉
|
|
16
|
+
- 只做一件需要深度思考的事
|
|
17
|
+
micro_experiment: 明天上午锁定 90 分钟无干扰深度工作,关通知+手机放抽屉
|
|
18
|
+
verify: 深度工作块后的产出感和满意度
|
|
19
|
+
source:
|
|
20
|
+
ref: 《深度工作》(Deep Work, Cal Newport)
|
|
21
|
+
evidence_level: book
|
|
22
|
+
tags: [专注, 深度工作]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-finite-commitment
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 有限承诺清单
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 能耗黑洞
|
|
8
|
+
symptoms:
|
|
9
|
+
- 承诺过多
|
|
10
|
+
- 焦虑忙乱
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 列出 3-5 个核心承诺,新任务进入前必须先挤掉一个旧的
|
|
13
|
+
steps:
|
|
14
|
+
- 写下当前所有在进行的承诺
|
|
15
|
+
- 只保留最重要的 3-5 个为"核心承诺层"
|
|
16
|
+
- 任何新任务进入,必须先从核心层挤掉一个
|
|
17
|
+
micro_experiment: 本周把活跃承诺压到 ≤5 个,新任务来必须先删一个旧的
|
|
18
|
+
verify: 一周后看高投入低满足分类的耗时是否下降
|
|
19
|
+
source:
|
|
20
|
+
ref: 《四千周》(Four Thousand Weeks, Oliver Burkeman)
|
|
21
|
+
evidence_level: book
|
|
22
|
+
tags: [做减法, 承诺管理]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-forced-whitespace
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 强制留白
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- daily_total_minutes: ">600"
|
|
8
|
+
symptoms:
|
|
9
|
+
- 某天经常过载
|
|
10
|
+
- 日程太满
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 日程只排 60-70%,把留白当作系统必需而非懒惰
|
|
13
|
+
steps:
|
|
14
|
+
- 计算你一天的实际可用时间
|
|
15
|
+
- 只排 60-70% 的任务
|
|
16
|
+
- 留出 30-40% 应对意外和恢复
|
|
17
|
+
micro_experiment: 明天日程只排 60%,留白 40%
|
|
18
|
+
verify: 一周后看过载天数是否减少
|
|
19
|
+
source:
|
|
20
|
+
ref: Make Time (Jake Knapp & John Zeratsky)
|
|
21
|
+
evidence_level: book
|
|
22
|
+
tags: [留白, 日程]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-gtd-next-action
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: GTD 下一步物理动作
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 能耗黑洞
|
|
8
|
+
symptoms:
|
|
9
|
+
- 任务反复推迟
|
|
10
|
+
- 不知道从哪开始
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 把模糊目标拆为"打开文档 + 写 3 个小标题"级粒度
|
|
13
|
+
steps:
|
|
14
|
+
- 写下模糊目标
|
|
15
|
+
- 问自己"下一步物理动作是什么"
|
|
16
|
+
- 拆到 ≤5 分钟的粒度
|
|
17
|
+
micro_experiment: 选一个反复推迟的任务,拆成 3 个 ≤5 分钟的物理动作
|
|
18
|
+
verify: 拆解后 24 小时内是否启动了
|
|
19
|
+
source:
|
|
20
|
+
ref: GTD (Getting Things Done, David Allen)
|
|
21
|
+
evidence_level: framework
|
|
22
|
+
tags: [GTD, 任务拆解]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: time-three-tier-list
|
|
2
|
+
kind: prescription
|
|
3
|
+
dimension: time
|
|
4
|
+
title: 三层专注清单
|
|
5
|
+
triggers:
|
|
6
|
+
signals:
|
|
7
|
+
- quadrant: 能耗黑洞
|
|
8
|
+
symptoms:
|
|
9
|
+
- 待办过载焦虑
|
|
10
|
+
- 事情太多
|
|
11
|
+
prescription:
|
|
12
|
+
one_liner: 第一层今日重点 / 第二层本周推进 / 第三层随时杂事
|
|
13
|
+
steps:
|
|
14
|
+
- 列出所有待办
|
|
15
|
+
- 分三层:今日重点 ≤3 项 / 本周推进 / 随时杂事
|
|
16
|
+
- 每天先做第一层
|
|
17
|
+
micro_experiment: 明天只设 3 个今日重点,其他待办归入第二、三层
|
|
18
|
+
verify: 当天完成率是否提升
|
|
19
|
+
source:
|
|
20
|
+
ref: Esor 时间管理方法论
|
|
21
|
+
evidence_level: folk
|
|
22
|
+
tags: [优先级, 清单]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { FsYamlKnowledgeRepo } from "./src/repo/fs-yaml.js";
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
const repo = new FsYamlKnowledgeRepo();
|
|
6
|
+
try {
|
|
7
|
+
const entries = await repo.loadAll();
|
|
8
|
+
console.log(`✅ ${entries.length} 条知识条目校验通过`);
|
|
9
|
+
// dimension breakdown
|
|
10
|
+
const dims = new Map<string, number>();
|
|
11
|
+
for (const e of entries) dims.set(e.dimension, (dims.get(e.dimension) || 0) + 1);
|
|
12
|
+
for (const [dim, count] of dims) console.log(` ${dim}: ${count} 条`);
|
|
13
|
+
|
|
14
|
+
// COACH-MOD §3:复盘模块知识层校验(schema + 引用完整性)
|
|
15
|
+
const models = await repo.loadAnalysisModels();
|
|
16
|
+
const packs = await repo.loadScenarioPacks();
|
|
17
|
+
const personas = await repo.loadPersonas();
|
|
18
|
+
|
|
19
|
+
// 恰好一个默认模型(getDefaultAnalysisModel 内含 0/多 抛错)
|
|
20
|
+
const defaultModel = await repo.getDefaultAnalysisModel();
|
|
21
|
+
|
|
22
|
+
// 场景包引用完整性:persona_id + analysis_model_ids 必须存在
|
|
23
|
+
const modelIds = new Set(models.map((m) => m.id));
|
|
24
|
+
const personaIds = new Set(personas.map((p) => p.id));
|
|
25
|
+
for (const pack of packs) {
|
|
26
|
+
if (!personaIds.has(pack.recommends.persona_id)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`场景包 "${pack.id}" 引用的画像 "${pack.recommends.persona_id}" 不存在`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
for (const mid of pack.recommends.analysis_model_ids) {
|
|
32
|
+
if (!modelIds.has(mid)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`场景包 "${pack.id}" 引用的分析模型 "${mid}" 不存在`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(
|
|
41
|
+
`✅ 复盘模块:${models.length} 分析模型(默认=${defaultModel.id})/ ${packs.length} 场景包 / ${personas.length} 画像,引用完整`,
|
|
42
|
+
);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
console.error(`❌ 校验失败: ${e.message}`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "libero3",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "AI 时间管理教练 MCP 工具集——时间块记录、统计、矩阵诊断、计时、分类、profile",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "amosyuan",
|
|
7
|
+
"homepage": "https://github.com/beimingju-dafu/libero-2",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/beimingju-dafu/libero-2.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"model-context-protocol",
|
|
15
|
+
"time-tracking",
|
|
16
|
+
"productivity",
|
|
17
|
+
"coach",
|
|
18
|
+
"ai"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"packageManager": "pnpm@9.15.9",
|
|
22
|
+
"bin": {
|
|
23
|
+
"libero-mcp": "./dist/mcp/server.js",
|
|
24
|
+
"libero-rest": "./dist/mcp/rest-server.js",
|
|
25
|
+
"libero3": "./dist/mcp/server.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start:rest": "tsx mcp/rest-server.ts",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"lint": "eslint .",
|
|
32
|
+
"lint:fix": "eslint . --fix",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"format": "prettier --write .",
|
|
35
|
+
"format:check": "prettier --check .",
|
|
36
|
+
"db:init": "tsx data/db.ts",
|
|
37
|
+
"build": "tsc -p tsconfig.build.json && cp -r data/migrations dist/data/ && cp data/schema.sql dist/data/ && find knowledge -name '*.yaml' -not -path '*/tests/*' -exec sh -c 'mkdir -p dist/knowledge/$(dirname \"${1#knowledge/}\") && cp \"$1\" dist/knowledge/\"${1#knowledge/}\"' _ {} \\;",
|
|
38
|
+
"postinstall": "node scripts/postinstall-cold-start.js",
|
|
39
|
+
"prepublishOnly": "npm test && npm run build",
|
|
40
|
+
"knowledge:validate": "tsx knowledge/validate.ts",
|
|
41
|
+
"case:panel": "tsx eval/case-panel/server.ts",
|
|
42
|
+
"eval:smoke": "tsx eval/auto-runner.ts --smoke --runs 3",
|
|
43
|
+
"eval:smoke:deterministic": "tsx eval/run-smoke-deterministic.ts",
|
|
44
|
+
"prepare": "husky"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"data/migrations",
|
|
49
|
+
"data/schema.sql",
|
|
50
|
+
"knowledge",
|
|
51
|
+
"COLD_START.md",
|
|
52
|
+
"scripts/postinstall-cold-start.js"
|
|
53
|
+
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@hono/node-server": "^2.1.0",
|
|
56
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
57
|
+
"better-sqlite3": "^12.10.0",
|
|
58
|
+
"hono": "^4.13.1",
|
|
59
|
+
"js-yaml": "^4.2.0",
|
|
60
|
+
"zod": "^4.4.3"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
64
|
+
"@types/js-yaml": "^4.0.9",
|
|
65
|
+
"@types/node": "^20.0.0",
|
|
66
|
+
"cron-parser": "^5.6.2",
|
|
67
|
+
"eslint": "^10.6.0",
|
|
68
|
+
"eslint-config-prettier": "^10.1.8",
|
|
69
|
+
"husky": "^9.1.7",
|
|
70
|
+
"prettier": "^3.9.4",
|
|
71
|
+
"tsx": "^4.0.0",
|
|
72
|
+
"typescript": "^5.4.0",
|
|
73
|
+
"typescript-eslint": "^8.62.1",
|
|
74
|
+
"vitest": "^1.6.0"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall — print cold-start for the *installing host agent* (Hermes/OpenClaw).
|
|
4
|
+
* End users never need to remember this; the host must relay the welcome as its
|
|
5
|
+
* next chat message, then reload MCP. Do not ask the human to coach the agent.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const welcomePath = join(root, "COLD_START.md");
|
|
13
|
+
|
|
14
|
+
const FALLBACK_WELCOME = `你好,我是 libero,帮你管时间和精力。
|
|
15
|
+
|
|
16
|
+
常用这几件事,直接说就行:
|
|
17
|
+
• 计时:「开始计时写代码」/「开个 25 分钟番茄钟」
|
|
18
|
+
• 日程:「明天 9 点到 11 点写报告」
|
|
19
|
+
• 拆任务:「这个太大了,帮我拆一下」
|
|
20
|
+
• 看数据:「这周时间都花哪儿了」
|
|
21
|
+
• 复盘:「帮我复盘一下这周」
|
|
22
|
+
|
|
23
|
+
想先试哪一句?`;
|
|
24
|
+
|
|
25
|
+
function extractWelcome(md) {
|
|
26
|
+
const m = md.match(/```\n([\s\S]*?)\n```/);
|
|
27
|
+
return m ? m[1].trim() : FALLBACK_WELCOME;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const welcome = existsSync(welcomePath)
|
|
31
|
+
? extractWelcome(readFileSync(welcomePath, "utf-8"))
|
|
32
|
+
: FALLBACK_WELCOME;
|
|
33
|
+
|
|
34
|
+
// Markers are for the installing agent reading npm output — never for the human checklist.
|
|
35
|
+
process.stderr.write(`
|
|
36
|
+
========== LIBERO_HOST_ACTION (installing agent only) ==========
|
|
37
|
+
Your NEXT message to the human MUST be the welcome block below (verbatim).
|
|
38
|
+
Do NOT ask for user_id. Do NOT list MCP tools / schemas.
|
|
39
|
+
THEN configure mcp_servers.libero (command: libero-mcp) if needed and /reload-mcp.
|
|
40
|
+
The human must NOT be told to "read a md" or "remind the agent".
|
|
41
|
+
---------- welcome (send to human) ----------
|
|
42
|
+
${welcome}
|
|
43
|
+
---------- end welcome ----------
|
|
44
|
+
===============================================================
|
|
45
|
+
`);
|