@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 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, readCurrentPointer, newVersion, useVersion, diffVersions } from '../src/version.js';
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
- // LOOM 项目目录结构: .loom/v{N}/
23
- // 优先读 .loom/current 指针;不存在则回退到自动探测最新版本。
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 findLoomDir() {
37
- const flagIdx = argv.indexOf('--loom-dir');
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(loomDir) {
57
- return join(loomDir, 'verifications');
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 loomDir = findLoomDir();
68
+ const versionDir = findVersionDir();
83
69
  switch (sub) {
84
70
  case 'next':
85
- output(getNextIntent(loomDir) ?? '没有可执行的 Intent');
71
+ output(getNextIntent(versionDir) ?? '没有可执行的 Intent');
86
72
  break;
87
73
  case 'status': {
88
- const s = getStatus(loomDir);
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.join(', ') || '-'}`);
91
- console.log(` in_progress: ${s.counts.in_progress} ${s.ids.in_progress.join(', ') || '-'}`);
92
- console.log(` completed: ${s.counts.completed} ${s.ids.completed.join(', ') || '-'}`);
93
- console.log(` blocked: ${s.counts.blocked} ${s.ids.blocked.join(', ') || '-'}`);
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(loomDir));
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(loomDir, id));
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(loomDir, id));
95
+ output(getNarrative(versionDir, id));
109
96
  break;
110
97
  }
111
98
  case 'validate':
112
- loadIntentMap(loomDir);
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(loomDir, getVerificationsDir(loomDir), getPhilosophyDir(loomDir), id));
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(loomDir, id));
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(loomDir, anchor));
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(loomDir, id, newStatus);
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 不需要 loomDir(项目还没初始化时也能激活)
170
- let loomDir = null;
156
+ // weaver 不需要 versionDir(项目还没初始化时也能激活)
157
+ let versionDir = null;
171
158
  if (role !== 'weaver') {
172
- try { loomDir = findLoomDir(); } catch { /* 项目还没初始化,weaver 之外的角色会缺少项目上下文 */ }
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, loomDir);
166
+ const prompt = activateRole(role, versionDir);
175
167
  output(prompt);
176
168
  break;
177
169
  }
178
170
 
179
171
  case 'philosophy': {
180
- const loomDir = findLoomDir();
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(loomDir), anchor));
177
+ output(getPhilosophy(getPhilosophyDir(versionDir), anchor));
186
178
  break;
187
179
  }
188
180
  case 'list':
189
- output(listPhilosophyFiles(getPhilosophyDir(loomDir)));
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 loomDir = findLoomDir();
199
- const verificationsDir = getVerificationsDir(loomDir);
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(loomDir, id));
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(loomDir, verificationsDir));
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
- record = JSON.parse(readFileSync(argv[fileFlagIdx + 1], 'utf-8'));
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
- record = JSON.parse(argv[jsonFlagIdx + 1]);
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 loomDir = findLoomDir();
302
- const { issues, summary } = doctor(loomDir, getVerificationsDir(loomDir), getPhilosophyDir(loomDir));
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 loomDir = findLoomDir();
317
- output(contextSummary(loomDir, getVerificationsDir(loomDir), getPhilosophyDir(loomDir)));
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
- // 输出提示词,让 AI .loom/ 文件、重组信息、生成 HTML
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 输出提示词+数据,让 AI 生成 HTML 可视化预览
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 不内置备份、审计、回滚——这些是版本控制的职责。
@@ -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
+ \`\`\`