@haaaiawd/loom 0.1.0 → 0.7.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/cli/bin/loom.js +106 -71
- package/cli/help/concepts.md +72 -0
- package/cli/help/doctor.md +70 -0
- package/cli/help/loop.md +135 -0
- package/cli/help/version.md +60 -0
- package/cli/help/workflow.md +94 -0
- package/cli/src/activate.js +21 -12
- package/cli/src/auto.js +54 -5
- package/cli/src/diagnostics.js +63 -23
- package/cli/src/guide.js +74 -5
- package/cli/src/help.js +29 -398
- package/cli/src/init.js +42 -8
- package/cli/src/intent-map.js +42 -85
- package/cli/src/philosophy.js +2 -73
- package/cli/src/preview-prompt.md +1 -0
- package/cli/src/shared/md-utils.js +125 -0
- package/cli/src/shared/paths.js +73 -0
- package/cli/src/verify.js +62 -70
- package/cli/src/version.js +1 -1
- package/meta/INTENT_LOOP.md +159 -18
- package/package.json +2 -1
- package/roles/architect.md +12 -0
- package/roles/keeper.md +28 -1
- package/templates/INTENT_MAP_TEMPLATE.json +4 -1
- package/templates/VISION_TEMPLATE.md +4 -2
package/cli/bin/loom.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
// loom — LOOM 框架的 CLI 传感器层
|
|
3
3
|
// Agent 通过这个 CLI 访问 Intent Map / 哲学 / 验证记录,不直接读文件。
|
|
4
4
|
|
|
5
5
|
import { argv, cwd, exit } from 'node:process';
|
|
6
|
-
import { resolve, join } from 'node:path';
|
|
6
|
+
import { resolve, join, dirname } from 'node:path';
|
|
7
7
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { findLoomRoot, findVersionDir, readCurrentPointer } from '../src/shared/paths.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
12
|
|
|
9
13
|
import { getNextIntent, getStatus, getDependencyGraph, getIntent, loadIntentMap, updateIntentStatus, getNarrative } from '../src/intent-map.js';
|
|
10
14
|
import { getPhilosophy, listPhilosophyFiles } from '../src/philosophy.js';
|
|
11
15
|
import { writeVerification, getVerificationHistory, getPendingVerifications, listVerifications, getVerificationContract } from '../src/verify.js';
|
|
12
16
|
import { initProject } from '../src/init.js';
|
|
13
17
|
import { activateRole } from '../src/activate.js';
|
|
14
|
-
import { listVersions,
|
|
18
|
+
import { listVersions, newVersion, useVersion, diffVersions } from '../src/version.js';
|
|
15
19
|
import { doctor, contextSummary, traceIntent, reverseDep, reverseRef } from '../src/diagnostics.js';
|
|
16
20
|
import { getHelpTopic, listHelpTopics } from '../src/help.js';
|
|
17
21
|
import { guideProject } from '../src/guide.js';
|
|
@@ -19,42 +23,15 @@ import { isAutoOn, autoOn, autoOff, autoStatus } from '../src/auto.js';
|
|
|
19
23
|
import { generatePreviewPrompt } from '../src/preview.js';
|
|
20
24
|
|
|
21
25
|
// ─── 路径解析 ──────────────────────────────────────────
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// 也接受 --loom-dir 参数直接指定版本目录。
|
|
25
|
-
|
|
26
|
-
function findLoomRoot() {
|
|
27
|
-
const flagIdx = argv.indexOf('--loom-dir');
|
|
28
|
-
if (flagIdx !== -1 && argv[flagIdx + 1]) {
|
|
29
|
-
// --loom-dir 直接指向版本目录,反推 .loom root
|
|
30
|
-
const dir = resolve(argv[flagIdx + 1]);
|
|
31
|
-
return resolve(dir, '..');
|
|
32
|
-
}
|
|
33
|
-
return join(cwd(), '.loom');
|
|
34
|
-
}
|
|
26
|
+
// findLoomRoot / findVersionDir / readCurrentPointer 已提取到 shared/paths.js
|
|
27
|
+
// 这里只保留目录辅助函数。
|
|
35
28
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
if (flagIdx !== -1 && argv[flagIdx + 1]) {
|
|
39
|
-
return resolve(argv[flagIdx + 1]);
|
|
40
|
-
}
|
|
41
|
-
const loomRoot = join(cwd(), '.loom');
|
|
42
|
-
if (!existsSync(loomRoot)) {
|
|
43
|
-
die(`找不到 .loom 目录: ${loomRoot}`);
|
|
44
|
-
}
|
|
45
|
-
const current = readCurrentPointer(loomRoot);
|
|
46
|
-
if (!current) {
|
|
47
|
-
die(`.loom 下没有版本目录 (v1, v2, ...)`);
|
|
48
|
-
}
|
|
49
|
-
return join(loomRoot, current);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getPhilosophyDir(loomDir) {
|
|
53
|
-
return join(loomDir, '00_PHILOSOPHY');
|
|
29
|
+
function getPhilosophyDir(versionDir) {
|
|
30
|
+
return join(versionDir, '00_PHILOSOPHY');
|
|
54
31
|
}
|
|
55
32
|
|
|
56
|
-
function getVerificationsDir(
|
|
57
|
-
return join(
|
|
33
|
+
function getVerificationsDir(versionDir) {
|
|
34
|
+
return join(versionDir, 'verifications');
|
|
58
35
|
}
|
|
59
36
|
|
|
60
37
|
// ─── 输出工具 ──────────────────────────────────────────
|
|
@@ -78,56 +55,66 @@ const [cmd, sub, ...rest] = argv.slice(2);
|
|
|
78
55
|
|
|
79
56
|
try {
|
|
80
57
|
switch (cmd) {
|
|
58
|
+
case '--version':
|
|
59
|
+
case '-v': {
|
|
60
|
+
// 从根 package.json 读版本号(cli/bin -> cli -> LOOM root)
|
|
61
|
+
const pkgPath = resolve(__dirname, '..', '..', 'package.json');
|
|
62
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
63
|
+
console.log(`loom ${pkg.version}`);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
|
|
81
67
|
case 'intent': {
|
|
82
|
-
const
|
|
68
|
+
const versionDir = findVersionDir();
|
|
83
69
|
switch (sub) {
|
|
84
70
|
case 'next':
|
|
85
|
-
output(getNextIntent(
|
|
71
|
+
output(getNextIntent(versionDir) ?? '没有可执行的 Intent');
|
|
86
72
|
break;
|
|
87
73
|
case 'status': {
|
|
88
|
-
const s = getStatus(
|
|
74
|
+
const s = getStatus(versionDir);
|
|
75
|
+
const fmt = (ids) => ids.map((id) => s.titles[id] ? `${id}(${s.titles[id]})` : id).join(', ') || '-';
|
|
89
76
|
console.log(`进度: ${s.counts.completed}/${s.counts.total} 完成`);
|
|
90
|
-
console.log(` pending: ${s.counts.pending} ${s.ids.pending
|
|
91
|
-
console.log(` in_progress: ${s.counts.in_progress} ${s.ids.in_progress
|
|
92
|
-
console.log(` completed: ${s.counts.completed} ${s.ids.completed
|
|
93
|
-
console.log(` blocked: ${s.counts.blocked} ${s.ids.blocked
|
|
77
|
+
console.log(` pending: ${s.counts.pending} ${fmt(s.ids.pending)}`);
|
|
78
|
+
console.log(` in_progress: ${s.counts.in_progress} ${fmt(s.ids.in_progress)}`);
|
|
79
|
+
console.log(` completed: ${s.counts.completed} ${fmt(s.ids.completed)}`);
|
|
80
|
+
console.log(` blocked: ${s.counts.blocked} ${fmt(s.ids.blocked)}`);
|
|
94
81
|
break;
|
|
95
82
|
}
|
|
96
83
|
case 'graph':
|
|
97
|
-
output(getDependencyGraph(
|
|
84
|
+
output(getDependencyGraph(versionDir));
|
|
98
85
|
break;
|
|
99
86
|
case 'get': {
|
|
100
87
|
const id = rest[0];
|
|
101
88
|
if (!id) die('用法: loom intent get <id>');
|
|
102
|
-
output(getIntent(
|
|
89
|
+
output(getIntent(versionDir, id));
|
|
103
90
|
break;
|
|
104
91
|
}
|
|
105
92
|
case 'narrative': {
|
|
106
93
|
const id = rest[0];
|
|
107
94
|
if (!id) die('用法: loom intent narrative <id>');
|
|
108
|
-
output(getNarrative(
|
|
95
|
+
output(getNarrative(versionDir, id));
|
|
109
96
|
break;
|
|
110
97
|
}
|
|
111
98
|
case 'validate':
|
|
112
|
-
loadIntentMap(
|
|
99
|
+
loadIntentMap(versionDir);
|
|
113
100
|
console.log('Intent Map 校验通过');
|
|
114
101
|
break;
|
|
115
102
|
case 'trace': {
|
|
116
103
|
const id = rest[0];
|
|
117
104
|
if (!id) die('用法: loom intent trace <id>');
|
|
118
|
-
output(traceIntent(
|
|
105
|
+
output(traceIntent(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir), id));
|
|
119
106
|
break;
|
|
120
107
|
}
|
|
121
108
|
case 'reverse-dep': {
|
|
122
109
|
const id = rest[0];
|
|
123
110
|
if (!id) die('用法: loom intent reverse-dep <id>');
|
|
124
|
-
output(reverseDep(
|
|
111
|
+
output(reverseDep(versionDir, id));
|
|
125
112
|
break;
|
|
126
113
|
}
|
|
127
114
|
case 'reverse-ref': {
|
|
128
115
|
const anchor = rest[0];
|
|
129
116
|
if (!anchor) die('用法: loom intent reverse-ref <anchor>\n例: loom intent reverse-ref PRODUCT_PHILOSOPHY.md#core-belief');
|
|
130
|
-
output(reverseRef(
|
|
117
|
+
output(reverseRef(versionDir, anchor));
|
|
131
118
|
break;
|
|
132
119
|
}
|
|
133
120
|
case 'update': {
|
|
@@ -135,7 +122,7 @@ try {
|
|
|
135
122
|
const statusFlagIdx = argv.indexOf('--status');
|
|
136
123
|
const newStatus = statusFlagIdx !== -1 ? argv[statusFlagIdx + 1] : null;
|
|
137
124
|
if (!id || !newStatus) die('用法: loom intent update <id> --status <pending|in_progress|completed|blocked|needs_review>');
|
|
138
|
-
updateIntentStatus(
|
|
125
|
+
updateIntentStatus(versionDir, id, newStatus);
|
|
139
126
|
console.log(`${id} status 已更新为 ${newStatus}`);
|
|
140
127
|
break;
|
|
141
128
|
}
|
|
@@ -166,27 +153,32 @@ try {
|
|
|
166
153
|
case 'activate': {
|
|
167
154
|
const role = sub;
|
|
168
155
|
if (!role) die('用法: loom activate <role>\n角色: weaver | visionary | architect | forge | keeper');
|
|
169
|
-
// weaver 不需要
|
|
170
|
-
let
|
|
156
|
+
// weaver 不需要 versionDir(项目还没初始化时也能激活)
|
|
157
|
+
let versionDir = null;
|
|
171
158
|
if (role !== 'weaver') {
|
|
172
|
-
try {
|
|
159
|
+
try {
|
|
160
|
+
versionDir = findVersionDir();
|
|
161
|
+
} catch (e) {
|
|
162
|
+
// 只吞"找不到 .loom 目录"——其他错误(权限、磁盘)向上抛
|
|
163
|
+
if (!String(e.message).includes('找不到 .loom')) throw e;
|
|
164
|
+
}
|
|
173
165
|
}
|
|
174
|
-
const prompt = activateRole(role,
|
|
166
|
+
const prompt = activateRole(role, versionDir);
|
|
175
167
|
output(prompt);
|
|
176
168
|
break;
|
|
177
169
|
}
|
|
178
170
|
|
|
179
171
|
case 'philosophy': {
|
|
180
|
-
const
|
|
172
|
+
const versionDir = findVersionDir();
|
|
181
173
|
switch (sub) {
|
|
182
174
|
case 'get': {
|
|
183
175
|
const anchor = rest[0];
|
|
184
176
|
if (!anchor) die('用法: loom philosophy get <anchor>\n例: loom philosophy get PRODUCT_PHILOSOPHY.md#core-belief');
|
|
185
|
-
output(getPhilosophy(getPhilosophyDir(
|
|
177
|
+
output(getPhilosophy(getPhilosophyDir(versionDir), anchor));
|
|
186
178
|
break;
|
|
187
179
|
}
|
|
188
180
|
case 'list':
|
|
189
|
-
output(listPhilosophyFiles(getPhilosophyDir(
|
|
181
|
+
output(listPhilosophyFiles(getPhilosophyDir(versionDir)));
|
|
190
182
|
break;
|
|
191
183
|
default:
|
|
192
184
|
die(`未知子命令: philosophy ${sub}\n用法: loom philosophy [get <anchor>|list]`);
|
|
@@ -195,13 +187,13 @@ try {
|
|
|
195
187
|
}
|
|
196
188
|
|
|
197
189
|
case 'verify': {
|
|
198
|
-
const
|
|
199
|
-
const verificationsDir = getVerificationsDir(
|
|
190
|
+
const versionDir = findVersionDir();
|
|
191
|
+
const verificationsDir = getVerificationsDir(versionDir);
|
|
200
192
|
switch (sub) {
|
|
201
193
|
case 'contract': {
|
|
202
194
|
const id = rest[0];
|
|
203
195
|
if (!id) die('用法: loom verify contract <id>');
|
|
204
|
-
output(getVerificationContract(
|
|
196
|
+
output(getVerificationContract(versionDir, id));
|
|
205
197
|
break;
|
|
206
198
|
}
|
|
207
199
|
case 'history': {
|
|
@@ -212,7 +204,7 @@ try {
|
|
|
212
204
|
break;
|
|
213
205
|
}
|
|
214
206
|
case 'pending':
|
|
215
|
-
output(getPendingVerifications(
|
|
207
|
+
output(getPendingVerifications(versionDir, verificationsDir));
|
|
216
208
|
break;
|
|
217
209
|
case 'list':
|
|
218
210
|
output(listVerifications(verificationsDir));
|
|
@@ -224,9 +216,17 @@ try {
|
|
|
224
216
|
const jsonFlagIdx = argv.indexOf('--json');
|
|
225
217
|
let record;
|
|
226
218
|
if (fileFlagIdx !== -1 && argv[fileFlagIdx + 1]) {
|
|
227
|
-
|
|
219
|
+
try {
|
|
220
|
+
record = JSON.parse(readFileSync(argv[fileFlagIdx + 1], 'utf-8'));
|
|
221
|
+
} catch (e) {
|
|
222
|
+
die(`JSON 文件解析失败: ${argv[fileFlagIdx + 1]}\n原因: ${e.message}`);
|
|
223
|
+
}
|
|
228
224
|
} else if (jsonFlagIdx !== -1 && argv[jsonFlagIdx + 1]) {
|
|
229
|
-
|
|
225
|
+
try {
|
|
226
|
+
record = JSON.parse(argv[jsonFlagIdx + 1]);
|
|
227
|
+
} catch (e) {
|
|
228
|
+
die(`JSON 字符串解析失败: ${e.message}`);
|
|
229
|
+
}
|
|
230
230
|
} else {
|
|
231
231
|
die('用法: loom verify write --json-file <path> | --json <json-string>');
|
|
232
232
|
}
|
|
@@ -298,8 +298,8 @@ try {
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
case 'doctor': {
|
|
301
|
-
const
|
|
302
|
-
const { issues, summary } = doctor(
|
|
301
|
+
const versionDir = findVersionDir();
|
|
302
|
+
const { issues, summary } = doctor(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir));
|
|
303
303
|
if (summary.healthy) {
|
|
304
304
|
console.log('✓ 项目健康,未发现问题');
|
|
305
305
|
} else {
|
|
@@ -313,8 +313,8 @@ try {
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
case 'context': {
|
|
316
|
-
const
|
|
317
|
-
output(contextSummary(
|
|
316
|
+
const versionDir = findVersionDir();
|
|
317
|
+
output(contextSummary(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir)));
|
|
318
318
|
break;
|
|
319
319
|
}
|
|
320
320
|
|
|
@@ -361,6 +361,9 @@ try {
|
|
|
361
361
|
case 'on':
|
|
362
362
|
autoOn(loomRoot);
|
|
363
363
|
console.log('AUTO 模式已开启。Agent 将自动连续执行,不等用户确认。');
|
|
364
|
+
console.log('核心契约: 持续运行,除非出意外否则不允许私自停止。');
|
|
365
|
+
console.log(' - L3 human_review 由 Keeper 自主判定,不停下等人类');
|
|
366
|
+
console.log(' - 唯一允许停下的情况: blocked(依赖阻塞/契约无法判定/连续 3 轮 deviated 升级)');
|
|
364
367
|
console.log('关闭: loom auto off');
|
|
365
368
|
break;
|
|
366
369
|
case 'off':
|
|
@@ -371,8 +374,18 @@ try {
|
|
|
371
374
|
const status = autoStatus(loomRoot);
|
|
372
375
|
if (status.on) {
|
|
373
376
|
console.log(`AUTO 模式: 开启(自 ${status.since})`);
|
|
377
|
+
console.log(' 规则: stage 1-3(哲学/愿景/架构)需人类 review,stage 4+(Intent Loop)自动执行');
|
|
378
|
+
if (status.heartbeat) {
|
|
379
|
+
const hb = status.heartbeat;
|
|
380
|
+
console.log(` 心跳: ${hb.timestamp}`);
|
|
381
|
+
console.log(` 阶段: ${hb.stage} (stage ${hb.stage_num})`);
|
|
382
|
+
console.log(` 下一步: ${hb.next_action}`);
|
|
383
|
+
console.log(` 命令: ${hb.next_command}`);
|
|
384
|
+
} else {
|
|
385
|
+
console.log(' 心跳: 尚未记录(运行 loom guide 后生成)');
|
|
386
|
+
}
|
|
374
387
|
} else {
|
|
375
|
-
console.log('AUTO 模式:
|
|
388
|
+
console.log('AUTO 模式: 关闭(所有阶段都需人类确认)');
|
|
376
389
|
}
|
|
377
390
|
break;
|
|
378
391
|
}
|
|
@@ -383,10 +396,31 @@ try {
|
|
|
383
396
|
}
|
|
384
397
|
|
|
385
398
|
case 'preview': {
|
|
386
|
-
|
|
399
|
+
const previewFile = join(cwd(), 'loom-preview.html');
|
|
400
|
+
const hasPreview = existsSync(previewFile);
|
|
401
|
+
const regenOnly = argv.includes('--regen') || argv.includes('-r');
|
|
402
|
+
|
|
403
|
+
// 已有 HTML 且没指定 --regen:直接打开浏览器
|
|
404
|
+
if (hasPreview && !regenOnly) {
|
|
405
|
+
const { spawn } = await import('node:child_process');
|
|
406
|
+
const target = previewFile.replace(/\\/g, '/');
|
|
407
|
+
if (process.platform === 'win32') {
|
|
408
|
+
spawn('cmd', ['/c', 'start', target], { detached: true, stdio: 'ignore' }).unref();
|
|
409
|
+
} else if (process.platform === 'darwin') {
|
|
410
|
+
spawn('open', [target], { detached: true, stdio: 'ignore' }).unref();
|
|
411
|
+
} else {
|
|
412
|
+
spawn('xdg-open', [target], { detached: true, stdio: 'ignore' }).unref();
|
|
413
|
+
}
|
|
414
|
+
console.log(`已打开浏览器: ${previewFile}`);
|
|
415
|
+
console.log(`重新生成: loom preview --regen`);
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// 没有 HTML 或指定 --regen:输出提示词让 AI 生成
|
|
387
420
|
const prompt = generatePreviewPrompt();
|
|
388
421
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
389
422
|
console.log('To Agent: 按以下提示词读 .loom/ 文件并生成 loom-preview.html');
|
|
423
|
+
console.log(' 生成完成后再次运行 loom preview 会自动打开浏览器');
|
|
390
424
|
console.log('To Human: 把以下内容给你的 AI agent');
|
|
391
425
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
392
426
|
console.log('');
|
|
@@ -440,7 +474,8 @@ To Human:
|
|
|
440
474
|
|
|
441
475
|
loom doctor 项目健康检查(一致性+孤儿引用+循环依赖+僵尸)
|
|
442
476
|
loom context 上下文摘要(进度+下一步+待验证+风险)
|
|
443
|
-
loom preview
|
|
477
|
+
loom preview 已有 HTML 则打开浏览器,否则输出提示词让 AI 生成
|
|
478
|
+
loom preview --regen 强制重新输出提示词(让 AI 重新生成 HTML)
|
|
444
479
|
loom help <topic> 分层指南(workflow|concepts|loop|version|doctor)
|
|
445
480
|
|
|
446
481
|
loom philosophy get <anchor> 按锚点加载哲学章节
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
## LOOM 核心概念
|
|
2
|
+
|
|
3
|
+
## 哲学
|
|
4
|
+
|
|
5
|
+
项目的价值观和工程原则——为什么存在、什么不做、冲突时谁优先。
|
|
6
|
+
由 Philosophy Weaver 从真实思想体系织造,不是模板填空。
|
|
7
|
+
所有角色激活时强制加载哲学作为共同锚点。
|
|
8
|
+
|
|
9
|
+
相关命令:
|
|
10
|
+
- \`loom philosophy get <anchor>\` — 加载哲学章节
|
|
11
|
+
- \`loom philosophy list\` — 列出哲学文档
|
|
12
|
+
- \`loom intent reverse-ref <anchor>\` — 哪些 Intent 引用了这个哲学锚点
|
|
13
|
+
|
|
14
|
+
## Intent
|
|
15
|
+
|
|
16
|
+
一个意图单元——不是"做什么"(任务),是"为什么做"(意图)。
|
|
17
|
+
每个 Intent 携带:
|
|
18
|
+
- narrative_ref — 意图叙事引用(为什么存在)
|
|
19
|
+
- depends_on — 依赖的 Intent(拓扑序)
|
|
20
|
+
- acceptance — 验收契约(Keeper 据此判定)
|
|
21
|
+
- philosophy_anchors — 哲学锚点(引用哪些哲学原则)
|
|
22
|
+
- status — 状态(pending|in_progress|completed|blocked|needs_review)
|
|
23
|
+
- verification_method — 验证方式(L1 静态|L2 运行时|L3 人类反馈,可选)
|
|
24
|
+
|
|
25
|
+
相关命令:
|
|
26
|
+
- \`loom intent next\` — 下一个可执行 Intent
|
|
27
|
+
- \`loom intent get <id>\` — Intent 详情
|
|
28
|
+
- \`loom intent narrative <id>\` — 意图叙事
|
|
29
|
+
- \`loom intent trace <id>\` — 完整追溯链
|
|
30
|
+
- \`loom intent reverse-dep <id>\` — 谁依赖这个 Intent
|
|
31
|
+
|
|
32
|
+
## Intent Map
|
|
33
|
+
|
|
34
|
+
所有 Intent 的依赖图(JSON)。Architect 绘制,定义拓扑序和依赖关系。
|
|
35
|
+
必须是 DAG(有向无环图),不能有循环依赖。
|
|
36
|
+
|
|
37
|
+
相关命令:
|
|
38
|
+
- \`loom intent validate\` — 校验结构 + 依赖一致性
|
|
39
|
+
- \`loom intent graph\` — Mermaid 依赖图
|
|
40
|
+
- \`loom intent status\` — 进度概览
|
|
41
|
+
|
|
42
|
+
## Intent Loop
|
|
43
|
+
|
|
44
|
+
核心循环:Keeper 选 Intent → Forge 实现 → Keeper 验证 → 闭合或修正。
|
|
45
|
+
每个 Intent 独立走一圈。详细流程见 \`loom help loop\`。
|
|
46
|
+
|
|
47
|
+
## Keeper
|
|
48
|
+
|
|
49
|
+
独立验证子代理——不继承 Forge 的实现上下文,从磁盘重新加载意图和契约。
|
|
50
|
+
判定四维度:意图忠实度 / 哲学一致性 / 底线合规 / 验收达成。
|
|
51
|
+
判定结果:passed / deviated / blocked / pending_human。
|
|
52
|
+
|
|
53
|
+
## 底线
|
|
54
|
+
|
|
55
|
+
不可妥协的约束(BASELINE.md 5 条 + 项目特定底线)。
|
|
56
|
+
角色激活时强制加载,哲学不能覆盖。违反底线必须立即停止。
|
|
57
|
+
|
|
58
|
+
## 验证记录
|
|
59
|
+
|
|
60
|
+
Keeper 每次验证写入一条记录(追加模式),包含:
|
|
61
|
+
- verdict(passed/deviated/blocked/pending_human)
|
|
62
|
+
- 四维度判定
|
|
63
|
+
- 证据
|
|
64
|
+
- 偏离说明(如果 deviated)
|
|
65
|
+
|
|
66
|
+
deviated 连续 3 轮升级 blocked。pending_human 默认 7 天超时升级 blocked。
|
|
67
|
+
|
|
68
|
+
相关命令:
|
|
69
|
+
- \`loom verify contract <id>\` — 获取验收契约
|
|
70
|
+
- \`loom verify write --json-file <path>\` — 写入验证记录
|
|
71
|
+
- \`loom verify history <id>\` — 验证历史
|
|
72
|
+
- \`loom verify pending\` — 待验证的 Intent
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
## 诊断与恢复指南
|
|
2
|
+
|
|
3
|
+
## 健康检查
|
|
4
|
+
|
|
5
|
+
\`\`\`bash
|
|
6
|
+
loom doctor
|
|
7
|
+
\`\`\`
|
|
8
|
+
|
|
9
|
+
检测 6 类问题:
|
|
10
|
+
|
|
11
|
+
| 问题类型 | 严重度 | 说明 |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| cycle | fatal | 循环依赖(Intent Map 有环) |
|
|
14
|
+
| orphan_philosophy_ref | high | 哲学锚点指向不存在的文件 |
|
|
15
|
+
| orphan_dependency | high | depends_on 引用不存在的 Intent |
|
|
16
|
+
| completed_no_record | high | completed 但无验证记录 |
|
|
17
|
+
| completed_depends_blocked | high | completed 依赖 blocked 的 Intent |
|
|
18
|
+
| in_progress_no_record | medium | in_progress 但无验证记录(可能中断) |
|
|
19
|
+
| zombie | medium | in_progress/blocked 超过 7 天无活动 |
|
|
20
|
+
|
|
21
|
+
## 上下文摘要
|
|
22
|
+
|
|
23
|
+
\`\`\`bash
|
|
24
|
+
loom context
|
|
25
|
+
\`\`\`
|
|
26
|
+
|
|
27
|
+
一条命令获取:进度 + 下一个 Intent + 待验证 + 不一致项 + 风险。
|
|
28
|
+
Agent 重启后先跑这个,快速知道"我在哪、接下来做什么"。
|
|
29
|
+
|
|
30
|
+
## 崩溃恢复
|
|
31
|
+
|
|
32
|
+
### Forge 崩溃(Intent 留在 in_progress)
|
|
33
|
+
|
|
34
|
+
1. 跑 \`loom doctor\` 确认哪些 Intent 状态不一致
|
|
35
|
+
2. 跑 \`loom context\` 看整体状态
|
|
36
|
+
3. 用户决定:
|
|
37
|
+
- 继续:重新激活 Forge,从当前代码接着做
|
|
38
|
+
- 重置:\`loom intent update <id> --status pending\`,从头来
|
|
39
|
+
|
|
40
|
+
### Intent Map 文件损坏
|
|
41
|
+
|
|
42
|
+
1. \`loom intent validate\` 会检测到格式错误
|
|
43
|
+
2. 从 Git 恢复(.loom/ 应纳入版本控制)
|
|
44
|
+
|
|
45
|
+
### 验证记录丢失
|
|
46
|
+
|
|
47
|
+
1. \`loom doctor\` 会检测到 completed 无记录
|
|
48
|
+
2. 重新验证该 Intent,或从 Git 恢复
|
|
49
|
+
|
|
50
|
+
## 追溯工具
|
|
51
|
+
|
|
52
|
+
\`\`\`bash
|
|
53
|
+
# Intent 完整追溯链(依赖+验证+哲学+叙事)
|
|
54
|
+
loom intent trace <id>
|
|
55
|
+
|
|
56
|
+
# 反向依赖(谁依赖这个 Intent → 变更影响评估)
|
|
57
|
+
loom intent reverse-dep <id>
|
|
58
|
+
|
|
59
|
+
# 反向哲学引用(哪些 Intent 引用这个锚点 → 哲学变更影响评估)
|
|
60
|
+
loom intent reverse-ref <anchor>
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
## 版本控制是前提
|
|
64
|
+
|
|
65
|
+
LOOM 假设项目使用 Git。所有 .loom/ 下的文件都应纳入版本控制:
|
|
66
|
+
- 文件损坏 → 从 Git 恢复
|
|
67
|
+
- 误操作 → 从 Git 回滚
|
|
68
|
+
- 变更追溯 → Git log 就是审计日志
|
|
69
|
+
|
|
70
|
+
LOOM 不内置备份、审计、回滚——这些是版本控制的职责。
|
package/cli/help/loop.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
## Intent Loop 详细流程
|
|
2
|
+
|
|
3
|
+
每个 Intent 独立走一圈。Loop 终止条件:所有 Intent 为 completed 且无 needs_review(不动点达成)。
|
|
4
|
+
|
|
5
|
+
## Step 1:Keeper 选 Intent
|
|
6
|
+
|
|
7
|
+
\`\`\`bash
|
|
8
|
+
loom intent next # 返回下一个可执行 Intent(pending 且依赖都 completed)
|
|
9
|
+
loom context # 当前状态摘要(进度+下一步+风险)
|
|
10
|
+
\`\`\`
|
|
11
|
+
|
|
12
|
+
如果没有可执行的 Intent:
|
|
13
|
+
- 全部 completed → 项目阶段完成
|
|
14
|
+
- 有 blocked → 需要人工介入
|
|
15
|
+
- 有 in_progress 但无验证记录 → 可能上次中断,跑 \`loom doctor\` 诊断
|
|
16
|
+
|
|
17
|
+
## Step 2:更新状态
|
|
18
|
+
|
|
19
|
+
\`\`\`bash
|
|
20
|
+
loom intent update <id> --status in_progress
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
## Step 3:Forge 实现
|
|
24
|
+
|
|
25
|
+
\`\`\`bash
|
|
26
|
+
loom activate forge
|
|
27
|
+
\`\`\`
|
|
28
|
+
|
|
29
|
+
Forge 加载:意图叙事 + 哲学锚点 + 验收契约,在约束下自主实现代码。
|
|
30
|
+
辅助命令(三个命令的分工):
|
|
31
|
+
- \`loom intent narrative <id>\` — 读意图叙事("为什么做")
|
|
32
|
+
- \`loom verify contract <id>\` — 读验收契约("做成什么样才算数")
|
|
33
|
+
- \`loom intent trace <id>\` — 完整追溯链(叙事+契约+哲学锚点一次性加载,最常用)
|
|
34
|
+
- \`loom philosophy get <anchor>\` — 读哲学原则(遇到取舍时查)
|
|
35
|
+
|
|
36
|
+
## Step 4:Keeper 验证
|
|
37
|
+
|
|
38
|
+
\`\`\`bash
|
|
39
|
+
loom activate keeper
|
|
40
|
+
loom verify contract <id> # 重新加载验收契约
|
|
41
|
+
\`\`\`
|
|
42
|
+
|
|
43
|
+
Keeper 独立验证四维度:
|
|
44
|
+
1. 意图忠实度 — 实现是否忠于原始意图叙事
|
|
45
|
+
2. 哲学一致性 — 实现是否符合哲学原则
|
|
46
|
+
3. 底线合规 — 是否违反 BASELINE
|
|
47
|
+
4. 验收达成 — 是否满足验收契约
|
|
48
|
+
|
|
49
|
+
写入验证记录:
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
loom verify write --json-file verification.json
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
验证记录格式(\`loom verify write\` 的输入):
|
|
55
|
+
\`\`\`json
|
|
56
|
+
{
|
|
57
|
+
"intent_id": "INT-001",
|
|
58
|
+
"verdict": "passed",
|
|
59
|
+
"timestamp": "2026-06-28T12:00:00.000Z",
|
|
60
|
+
"summary": "具体证据描述——不是'看起来没问题'",
|
|
61
|
+
"reproduction_command": "LLM_API_KEY=mock npm test",
|
|
62
|
+
"dimensions": {
|
|
63
|
+
"intent_fidelity": {
|
|
64
|
+
"verdict": "passed",
|
|
65
|
+
"evidence": "对照意图叙事第 2 段,extract.js 实现了完整编排"
|
|
66
|
+
},
|
|
67
|
+
"philosophy_consistency": {
|
|
68
|
+
"verdict": "passed",
|
|
69
|
+
"evidence": "AI_PHILOSOPHY 反模式逐条对照:JSON.parse 有 try/catch、fetch 有超时、无硬编码密钥"
|
|
70
|
+
},
|
|
71
|
+
"baseline_compliance": {
|
|
72
|
+
"verdict": "passed",
|
|
73
|
+
"evidence": "B1-B5 逐条合规"
|
|
74
|
+
},
|
|
75
|
+
"acceptance_achievement": {
|
|
76
|
+
"verdict": "passed",
|
|
77
|
+
"evidence": "6 条契约全部达成,npm test 6/6 pass"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
\`\`\`
|
|
82
|
+
CLI 自动包装成 \`{ intent_id, records: [{ round, ... }] }\` 追加到验证文件。
|
|
83
|
+
\`dimensions\` 每个维度必须是 \`{ verdict, evidence }\` 对象——不允许只写"合规",必须写具体证据。
|
|
84
|
+
\`reproduction_command\` 是复现验证的命令——别人跑这个命令能复现你的验证结果。L2 必填。
|
|
85
|
+
|
|
86
|
+
**evidence 写法参考**:
|
|
87
|
+
- 长度:每条 evidence 50-300 字符为宜。太短("合规")不达标,太长难读。
|
|
88
|
+
- 哲学一致性维度:按哲学锚点逐条对照反模式(见 keeper.md 的"承诺验证法")
|
|
89
|
+
- 其他维度:写"对照了什么 + 在代码哪里看到/没看到"
|
|
90
|
+
- \`reproduction_command\` 注意平台差异:
|
|
91
|
+
- Unix/Mac: \`LLM_API_KEY=mock npm test\`
|
|
92
|
+
- Windows PowerShell: \`$env:LLM_API_KEY='mock'; npm test\`
|
|
93
|
+
- Windows cmd: \`set LLM_API_KEY=mock && npm test\`
|
|
94
|
+
- \`node --test\` 在 Windows 上不能用目录路径(\`node --test test/\` 会报错),用 glob:\`node --test test/*.test.js\`
|
|
95
|
+
|
|
96
|
+
## Step 5:根据判定结果
|
|
97
|
+
|
|
98
|
+
| verdict | 处理 |
|
|
99
|
+
|---|---|
|
|
100
|
+
| passed | \`loom intent update <id> --status completed\`,回到 Step 1 |
|
|
101
|
+
| deviated | 与 Forge 对话修正,重新实现重新验证。连续 3 轮升级 blocked |
|
|
102
|
+
| blocked | \`loom intent update <id> --status blocked\`,停下报告用户 |
|
|
103
|
+
| pending_human | **手动模式**:等用户补充判定(L3 人类反馈)。7 天超时升级 blocked |
|
|
104
|
+
|
|
105
|
+
**AUTO 模式下的判定规则**(关键差异):
|
|
106
|
+
- AUTO 模式开启时(\`loom auto on\`),**不允许 pending_human**
|
|
107
|
+
- Keeper 遇到 L3 verification_method 时,用 L1+L2 能耐自主判定所有维度
|
|
108
|
+
- 要么 passed(有证据),要么 deviated(有偏离说明),不停下等人类
|
|
109
|
+
- **持续运行,除非出意外否则不允许私自停止**
|
|
110
|
+
- "出意外" = blocked(依赖阻塞/契约无法判定/连续 3 轮 deviated 升级)、fatal 错误
|
|
111
|
+
|
|
112
|
+
## 变更回流
|
|
113
|
+
|
|
114
|
+
如果 Forge 发现验收契约不合理、或 Architect 的设计需要调整:
|
|
115
|
+
1. Keeper 评估变更范围(微调 vs 结构性变更)
|
|
116
|
+
2. 微调(验收措辞、验证方式)→ Keeper 直接改
|
|
117
|
+
3. 结构性变更(增减 Intent、改依赖)→ 重新激活 Architect
|
|
118
|
+
4. 受影响的已完成 Intent 标记为 needs_review
|
|
119
|
+
|
|
120
|
+
## 不动点收敛
|
|
121
|
+
|
|
122
|
+
默认单趟:所有 Intent 按拓扑序验证完毕且全 passed → done。
|
|
123
|
+
|
|
124
|
+
触发收敛:Pass 1 结束后还有 needs_review 的 Intent → 自动进入 Pass 2。
|
|
125
|
+
- Pass 2: 重验所有 needs_review 的 Intent
|
|
126
|
+
- deviated → 修 → 重验
|
|
127
|
+
- 修的时候又影响别的 → 标记 needs_review
|
|
128
|
+
- passed → completed
|
|
129
|
+
- Pass 2 结束还有 needs_review → Pass 3
|
|
130
|
+
- Pass 3 结束还有 needs_review → blocked,报告"无法收敛"
|
|
131
|
+
|
|
132
|
+
收敛达成 = 一趟完整 pass 没有产生任何新的 needs_review(不动点)。
|
|
133
|
+
最大 3 趟,超过判定为系统性问题,需 Architect 介入。
|
|
134
|
+
|
|
135
|
+
详细规则见 .loom/v{N}/ 下的 INTENT_LOOP.md。
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
## 版本演进指南
|
|
2
|
+
|
|
3
|
+
LOOM 用 .loom/v{N}/ 目录支持多版本共存与演进。
|
|
4
|
+
|
|
5
|
+
## 什么时候升级版本
|
|
6
|
+
|
|
7
|
+
| 变更类型 | 判定标准 | 处理方式 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Minor | 不改哲学前提、不改愿景北极星、不改架构边界 | 当前版本内改(变更回流机制) |
|
|
10
|
+
| Major | 哲学前提变了、愿景北极星变了、架构边界变了 | 创建新版本 |
|
|
11
|
+
|
|
12
|
+
判定由用户 + Agent 对话完成,CLI 不做决策。
|
|
13
|
+
|
|
14
|
+
## Major 升级流程
|
|
15
|
+
|
|
16
|
+
\`\`\`bash
|
|
17
|
+
# 1. 创建新版本(空目录 + 模板,自动切换为当前)
|
|
18
|
+
loom version new
|
|
19
|
+
|
|
20
|
+
# 2. 看看旧版本有什么(Agent 决定参考什么)
|
|
21
|
+
loom version diff v1 v2
|
|
22
|
+
|
|
23
|
+
# 3. Weaver 读旧哲学,织造新哲学
|
|
24
|
+
loom activate weaver
|
|
25
|
+
# → 必须读 .loom/v1/00_PHILOSOPHY/,记录"相对 v1 变了什么"
|
|
26
|
+
|
|
27
|
+
# 4. Visionary 读旧愿景,定义新愿景
|
|
28
|
+
loom activate visionary
|
|
29
|
+
# → 必须读 .loom/v1/01_VISION.md
|
|
30
|
+
|
|
31
|
+
# 5. Architect 读旧架构,设计新架构
|
|
32
|
+
loom activate architect
|
|
33
|
+
# → 必须读 .loom/v1/02_ARCHITECTURE.md + 04_INTENT_MAP.json
|
|
34
|
+
|
|
35
|
+
# 6. 进入新版本的 Intent Loop
|
|
36
|
+
\`\`\`
|
|
37
|
+
|
|
38
|
+
## 关键设计
|
|
39
|
+
|
|
40
|
+
- **空目录 + 模板**:\`loom version new\` 不自动复制旧版本内容。强制重新思考——参考 ≠ 复制。
|
|
41
|
+
- **旧版本只读**:当前指针指向的版本是当前真相,旧版本保留作历史参考。
|
|
42
|
+
- **Intent ID 重新编号**:v2 的 INT-001 和 v1 的 INT-001 没有关系。追溯靠 Git history 和 \`loom version diff\`。
|
|
43
|
+
|
|
44
|
+
## 版本管理命令
|
|
45
|
+
|
|
46
|
+
\`\`\`bash
|
|
47
|
+
loom version list # 列出所有版本(* 标记当前)
|
|
48
|
+
loom version current # 显示当前版本
|
|
49
|
+
loom version new # 创建 v{N+1} + 自动切换
|
|
50
|
+
loom version use <v> # 切换当前版本
|
|
51
|
+
loom version diff <v1> <v2> # 对比文件差异
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
## 切换回旧版本
|
|
55
|
+
|
|
56
|
+
\`\`\`bash
|
|
57
|
+
loom version use v1 # 切回 v1 查看历史
|
|
58
|
+
loom intent trace <id> # 在 v1 中追溯 Intent 历史
|
|
59
|
+
loom version use v2 # 切回 v2 继续
|
|
60
|
+
\`\`\`
|