@haaaiawd/loom 0.10.0 → 1.0.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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -52
  3. package/cli/bin/loom.js +285 -99
  4. package/cli/help/concepts.md +93 -72
  5. package/cli/help/doctor.md +71 -121
  6. package/cli/help/loop.md +120 -135
  7. package/cli/help/patch.md +33 -0
  8. package/cli/help/preview.md +2 -1
  9. package/cli/help/version.md +92 -16
  10. package/cli/help/workflow.md +89 -100
  11. package/cli/src/activate.js +302 -73
  12. package/cli/src/diagnostics.js +138 -41
  13. package/cli/src/guide.js +41 -19
  14. package/cli/src/init.js +50 -29
  15. package/cli/src/intent-draft.js +303 -0
  16. package/cli/src/intent-map.js +540 -54
  17. package/cli/src/patch.js +214 -0
  18. package/cli/src/philosophy.js +177 -154
  19. package/cli/src/preview-prompt.md +13 -6
  20. package/cli/src/preview.js +1 -0
  21. package/cli/src/shared/intent-ref.js +38 -0
  22. package/cli/src/shared/proof-reference.js +19 -0
  23. package/cli/src/shared/verification-method.js +32 -0
  24. package/cli/src/verify.js +184 -61
  25. package/cli/src/version.js +5 -4
  26. package/dimensions/PART_DECOMPOSITION.md +42 -203
  27. package/dimensions/SEARCH_METHODOLOGY.md +101 -97
  28. package/dimensions/examples/AGENT_SYSTEM/README.md +1 -1
  29. package/dimensions/examples/CLI_TOOL/README.md +1 -1
  30. package/dimensions/universal/COLLABORATION_PHILOSOPHY.md +28 -77
  31. package/dimensions/universal/ENGINEERING_CREED.md +30 -74
  32. package/dimensions/universal/PRODUCT_PHILOSOPHY.md +32 -70
  33. package/meta/BASELINE.md +91 -276
  34. package/meta/INTENT_LOOP.md +242 -737
  35. package/meta/PHILOSOPHY_WEAVER.md +110 -343
  36. package/meta/ROLE_ACTIVATION.md +103 -267
  37. package/package.json +4 -3
  38. package/roles/architect.md +71 -111
  39. package/roles/forge.md +87 -126
  40. package/roles/keeper.md +99 -223
  41. package/roles/visionary.md +57 -86
  42. package/templates/INTENT_MAP_TEMPLATE.json +24 -10
  43. package/templates/PHILOSOPHY_TEMPLATE.md +44 -75
  44. package/templates/VISION_TEMPLATE.md +44 -67
package/cli/bin/loom.js CHANGED
@@ -10,9 +10,9 @@ import { findLoomRoot, findVersionDir, readCurrentPointer } from '../src/shared/
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
 
13
- import { getNextIntent, getStatus, getDependencyGraph, getIntent, loadIntentMap, updateIntentStatus, getNarrative } from '../src/intent-map.js';
14
- import { getPhilosophy, listPhilosophyFiles, validateInspirationSources, validatePartDecomposition } from '../src/philosophy.js';
15
- import { writeVerification, createQuickVerification, getVerificationHistory, getPendingVerifications, listVerifications, getVerificationContract } from '../src/verify.js';
13
+ import { deprecateIntent, getNextIntent, getStatus, getDependencyGraph, getIntent, loadIntentMap, updateIntentStatus, getNarrative, diffIntentVersions } from '../src/intent-map.js';
14
+ import { assessPhilosophyImpact, getPhilosophy, listPhilosophyFiles, revisePhilosophy, validateInspirationSources } from '../src/philosophy.js';
15
+ import { writeVerification, createQuickVerification, getVerificationHistory, getAcrossVersionVerificationHistory, getPendingVerifications, listVerifications, getVerificationContract, isVerificationCurrent } from '../src/verify.js';
16
16
  import { initProject } from '../src/init.js';
17
17
  import { activateRole } from '../src/activate.js';
18
18
  import { listVersions, newVersion, useVersion, diffVersions } from '../src/version.js';
@@ -20,7 +20,10 @@ import { doctor, contextSummary, traceIntent, reverseDep, reverseRef } from '../
20
20
  import { getHelpTopic, listHelpTopics } from '../src/help.js';
21
21
  import { guideProject } from '../src/guide.js';
22
22
  import { isAutoOn, autoOn, autoOff, autoStatus, getAutoMode } from '../src/auto.js';
23
- import { generatePreviewPrompt, getPreviewStatus } from '../src/preview.js';
23
+ import { generatePreviewPrompt, getPreviewStatus } from '../src/preview.js';
24
+ import { getPatch, listPatches, recordPatch, validatePatches } from '../src/patch.js';
25
+ import { addIntentDraft, finalizeIntentDraft, getIntentDraft, reviseIntentDraft } from '../src/intent-draft.js';
26
+ import { resolveIntentRef } from '../src/shared/intent-ref.js';
24
27
 
25
28
  // ─── 路径解析 ──────────────────────────────────────────
26
29
  // findLoomRoot / findVersionDir / readCurrentPointer 已提取到 shared/paths.js
@@ -64,9 +67,75 @@ try {
64
67
  break;
65
68
  }
66
69
 
67
- case 'intent': {
68
- const versionDir = findVersionDir();
69
- switch (sub) {
70
+ case 'intent': {
71
+ const versionDir = findVersionDir();
72
+ switch (sub) {
73
+ case 'add': {
74
+ const titleIdx = argv.indexOf('--title');
75
+ const dependsIdx = argv.indexOf('--depends-on');
76
+ const title = titleIdx !== -1 ? argv[titleIdx + 1] : null;
77
+ const dependencies = dependsIdx !== -1 && argv[dependsIdx + 1]
78
+ ? argv[dependsIdx + 1].split(',').map((id) => id.trim()).filter(Boolean)
79
+ : [];
80
+ if (!title) die('用法: loom intent add --title <text> [--depends-on INT-001,INT-002]');
81
+ output(addIntentDraft(versionDir, title, dependencies));
82
+ break;
83
+ }
84
+ case 'revise': {
85
+ const id = rest[0];
86
+ const reasonIdx = argv.indexOf('--reason');
87
+ const reason = reasonIdx !== -1 ? argv[reasonIdx + 1] : null;
88
+ if (!id || !reason) die('用法: loom intent revise <id> --reason <text>');
89
+ output(reviseIntentDraft(versionDir, id, reason));
90
+ break;
91
+ }
92
+ case 'draft': {
93
+ const id = rest[0];
94
+ if (!id) die('用法: loom intent draft <id>');
95
+ output(getIntentDraft(versionDir, id));
96
+ break;
97
+ }
98
+ case 'finalize': {
99
+ const id = rest[0];
100
+ if (!id) die('用法: loom intent finalize <id>');
101
+ const parseIds = (name) => {
102
+ const index = argv.indexOf(name);
103
+ if (index === -1) return [];
104
+ const value = argv[index + 1];
105
+ if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
106
+ return value.split(',').map((item) => item.trim()).filter(Boolean);
107
+ };
108
+ output(finalizeIntentDraft(versionDir, id, {
109
+ review: parseIds('--review'),
110
+ unaffected: parseIds('--unaffected'),
111
+ }));
112
+ break;
113
+ }
114
+ case 'deprecate': {
115
+ const id = rest[0];
116
+ const readFlag = (name) => {
117
+ const index = argv.indexOf(name);
118
+ return index === -1 ? null : argv[index + 1];
119
+ };
120
+ const parseIds = (name) => {
121
+ const value = readFlag(name);
122
+ if (value === null) return [];
123
+ if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
124
+ return value.split(',').map((item) => item.trim()).filter(Boolean);
125
+ };
126
+ const reason = readFlag('--reason');
127
+ if (!id || !reason || reason.startsWith('--')) die('用法: loom intent deprecate <id> --reason <text> [--confirm [--replacement <id>] [--review <ids>] [--unaffected <ids>]]');
128
+ const replacement = readFlag('--replacement');
129
+ if (argv.includes('--replacement') && (!replacement || replacement.startsWith('--'))) die('--replacement 需要当前版本的 Intent ID');
130
+ output(deprecateIntent(versionDir, id, {
131
+ reason,
132
+ confirm: argv.includes('--confirm'),
133
+ replacement,
134
+ review: parseIds('--review'),
135
+ unaffected: parseIds('--unaffected'),
136
+ }));
137
+ break;
138
+ }
70
139
  case 'next':
71
140
  output(getNextIntent(versionDir) ?? '没有可执行的 Intent');
72
141
  break;
@@ -76,25 +145,36 @@ try {
76
145
  console.log(`进度: ${s.counts.completed}/${s.counts.total} 完成`);
77
146
  console.log(` pending: ${s.counts.pending} ${fmt(s.ids.pending)}`);
78
147
  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)}`);
81
- break;
148
+ console.log(` completed: ${s.counts.completed} ${fmt(s.ids.completed)}`);
149
+ console.log(` blocked: ${s.counts.blocked} ${fmt(s.ids.blocked)}`);
150
+ console.log(` needs_review: ${s.counts.needs_review} ${fmt(s.ids.needs_review)}`);
151
+ console.log(` deprecated: ${s.counts.deprecated} ${fmt(s.deprecated)}`);
152
+ break;
82
153
  }
83
154
  case 'graph':
84
155
  output(getDependencyGraph(versionDir));
85
156
  break;
86
- case 'get': {
87
- const id = rest[0];
88
- if (!id) die('用法: loom intent get <id>');
89
- output(getIntent(versionDir, id));
90
- break;
91
- }
157
+ case 'get': {
158
+ const id = rest[0];
159
+ if (!id) die('用法: loom intent get <id>');
160
+ const resolved = resolveIntentRef(versionDir, id);
161
+ output(getIntent(resolved.versionDir, resolved.intentId));
162
+ break;
163
+ }
92
164
  case 'narrative': {
93
165
  const id = rest[0];
94
166
  if (!id) die('用法: loom intent narrative <id>');
95
- output(getNarrative(versionDir, id));
96
- break;
97
- }
167
+ const resolved = resolveIntentRef(versionDir, id);
168
+ output(getNarrative(resolved.versionDir, resolved.intentId));
169
+ break;
170
+ }
171
+ case 'diff': {
172
+ const from = rest[0];
173
+ const to = rest[1];
174
+ if (!from || !to) die('用法: loom intent diff <v1> <v2>');
175
+ output(diffIntentVersions(findLoomRoot(), from, to));
176
+ break;
177
+ }
98
178
  case 'validate':
99
179
  loadIntentMap(versionDir);
100
180
  console.log('Intent Map 校验通过');
@@ -102,7 +182,8 @@ try {
102
182
  case 'trace': {
103
183
  const id = rest[0];
104
184
  if (!id) die('用法: loom intent trace <id>');
105
- output(traceIntent(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir), id));
185
+ const resolved = resolveIntentRef(versionDir, id);
186
+ output(traceIntent(resolved.versionDir, getVerificationsDir(resolved.versionDir), getPhilosophyDir(resolved.versionDir), resolved.intentId));
106
187
  break;
107
188
  }
108
189
  case 'reverse-dep': {
@@ -136,13 +217,16 @@ try {
136
217
  if (!history || history.records.length === 0) {
137
218
  die(`${id} 没有验证记录。先跑: loom verify pass ${id} --summary "..."`);
138
219
  }
139
- // 检查最新验证记录是否 passed
140
- const latest = history.records[history.records.length - 1];
141
- if (latest.verdict !== 'passed') {
142
- die(`${id} 最新验证记录是 ${latest.verdict}(非 passed)。只有 passed 的 Intent 才能 done。`);
143
- }
144
- // 获取当前状态,自动走两步
145
- const intent = getIntent(versionDir, id);
220
+ // 最新记录必须针对当前 revision 且通过,旧 revision 的通过不能闭合 Intent。
221
+ const latest = history.records[history.records.length - 1];
222
+ if (latest.verdict !== 'passed') {
223
+ die(`${id} 最新验证记录是 ${latest.verdict}(非 passed)。只有 passed 的 Intent 才能 done。`);
224
+ }
225
+ // 获取当前状态,自动走两步
226
+ const intent = getIntent(versionDir, id);
227
+ if (!isVerificationCurrent(intent, latest)) {
228
+ die(`${id} 最新 passed 验证不属于当前 Intent revision ${intent.revision ?? 1}。先重新验证。`);
229
+ }
146
230
  const currentStatus = intent.status;
147
231
  if (currentStatus === 'completed') {
148
232
  console.log(`${id} 已经是 completed,无需操作`);
@@ -156,13 +240,17 @@ try {
156
240
  break;
157
241
  }
158
242
  default:
159
- die(`未知子命令: intent ${sub}\n用法: loom intent [next|status|graph|get <id>|narrative <id>|validate|trace <id>|reverse-dep <id>|reverse-ref <anchor>|update <id> --status <...>|done <id>]`);
243
+ die(`未知子命令: intent ${sub}\n用法: loom intent [add|revise|draft|finalize|deprecate|next|status|graph|get|narrative|diff|validate|trace|reverse-dep|reverse-ref|update|done]`);
160
244
  }
161
245
  break;
162
246
  }
163
247
 
164
- case 'init': {
165
- const result = initProject(cwd());
248
+ case 'init': {
249
+ if (sub === '--help' || sub === '-h') {
250
+ console.log('用法: loom init\\n\\n在当前目录创建 LOOM 项目骨架。若目录已有 .loom/,不会覆盖现有文件。');
251
+ break;
252
+ }
253
+ const result = initProject(cwd());
166
254
  console.log('LOOM 项目已初始化');
167
255
  for (const c of result.created) console.log(` [created] ${c}`);
168
256
  for (const s of result.skipped) console.log(` [skipped] ${s} (already exists)`);
@@ -179,7 +267,7 @@ try {
179
267
  }
180
268
 
181
269
  case 'activate': {
182
- const role = sub;
270
+ const role = sub;
183
271
  if (!role) die('用法: loom activate <role>\n角色: weaver | visionary | architect | forge | keeper');
184
272
  // weaver 不需要 versionDir(项目还没初始化时也能激活)
185
273
  let versionDir = null;
@@ -191,7 +279,10 @@ try {
191
279
  if (!String(e.message).includes('找不到 .loom')) throw e;
192
280
  }
193
281
  }
194
- const prompt = activateRole(role, versionDir);
282
+ const intentIdx = argv.indexOf('--intent');
283
+ const intentId = intentIdx !== -1 ? argv[intentIdx + 1] : null;
284
+ if (intentIdx !== -1 && !intentId) die('用法: loom activate <role> --intent <id>');
285
+ const prompt = activateRole(role, versionDir, intentId);
195
286
  output(prompt);
196
287
  break;
197
288
  }
@@ -205,24 +296,54 @@ try {
205
296
  output(getPhilosophy(getPhilosophyDir(versionDir), anchor));
206
297
  break;
207
298
  }
208
- case 'list':
209
- output(listPhilosophyFiles(getPhilosophyDir(versionDir)));
210
- break;
299
+ case 'list':
300
+ output(listPhilosophyFiles(getPhilosophyDir(versionDir)));
301
+ break;
302
+ case 'impact': {
303
+ const anchor = rest[0];
304
+ if (!anchor) die('用法: loom philosophy impact <anchor>');
305
+ output(assessPhilosophyImpact(versionDir, anchor));
306
+ break;
307
+ }
308
+ case 'revise': {
309
+ const anchor = rest[0];
310
+ const readFlag = (name) => {
311
+ const index = argv.indexOf(name);
312
+ return index === -1 ? null : argv[index + 1];
313
+ };
314
+ const parseIds = (name) => {
315
+ const value = readFlag(name);
316
+ if (value === null) return [];
317
+ if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
318
+ return value.split(',').map((item) => item.trim()).filter(Boolean);
319
+ };
320
+ const classification = readFlag('--classification');
321
+ const reason = readFlag('--reason');
322
+ if (!anchor || !classification || classification.startsWith('--') || !reason || reason.startsWith('--')) {
323
+ die('用法: loom philosophy revise <anchor> --classification <clarification|minor|major> --reason <text> [--confirm --review <ids> --unaffected <ids>]');
324
+ }
325
+ output(revisePhilosophy(versionDir, anchor, {
326
+ classification,
327
+ reason,
328
+ confirm: argv.includes('--confirm'),
329
+ review: parseIds('--review'),
330
+ unaffected: parseIds('--unaffected'),
331
+ }));
332
+ break;
333
+ }
211
334
  case 'check': {
212
- const philDir = getPhilosophyDir(versionDir);
213
- const inspiration = validateInspirationSources(philDir);
214
- const decomposition = validatePartDecomposition(philDir);
215
- const allIssues = [...inspiration.issues, ...decomposition.issues];
216
- const allPassed = inspiration.passed && decomposition.passed;
335
+ const philDir = getPhilosophyDir(versionDir);
336
+ const inspiration = validateInspirationSources(philDir);
337
+ const allIssues = inspiration.issues;
338
+ const allPassed = inspiration.passed;
217
339
 
218
340
  // --json: 结构化输出,供 Agent 程序化消费
219
341
  if (argv.includes('--json')) {
220
342
  output({
221
- passed: allPassed,
222
- issues: allIssues,
223
- sources: inspiration.sources.map(({ file, sources }) => ({ file, count: sources.length })),
224
- parts: decomposition.parts,
225
- });
343
+ passed: allPassed,
344
+ issues: allIssues,
345
+ sources: inspiration.sources.map(({ file, sources }) => ({ file, count: sources.length })),
346
+ });
226
347
  exit(allPassed ? 0 : 1);
227
348
  }
228
349
 
@@ -232,10 +353,6 @@ try {
232
353
  for (const { file, sources } of inspiration.sources) {
233
354
  console.log(` ${file}: ${sources.length} 个源`);
234
355
  }
235
- console.log(' 实现部分拆解:');
236
- for (const part of decomposition.parts) {
237
- console.log(` - ${part}`);
238
- }
239
356
  } else {
240
357
  const high = allIssues.filter((i) => i.severity === 'high').length;
241
358
  const medium = allIssues.filter((i) => i.severity === 'medium').length;
@@ -244,13 +361,13 @@ try {
244
361
  const icon = issue.severity === 'high' ? '⚠' : '·';
245
362
  console.log(` ${icon} [${issue.severity}] ${issue.msg}`);
246
363
  }
247
- console.log('\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/PART_DECOMPOSITION.md + dimensions/SEARCH_METHODOLOGY.md。');
364
+ console.log('\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。');
248
365
  exit(1);
249
366
  }
250
367
  break;
251
368
  }
252
369
  default:
253
- die(`未知子命令: philosophy ${sub}\n用法: loom philosophy [get <anchor>|list|check]`);
370
+ die(`未知子命令: philosophy ${sub}\n用法: loom philosophy [get <anchor>|list|check|impact <anchor>|revise <anchor> --classification <clarification|minor|major> --reason <text>]`);
254
371
  }
255
372
  break;
256
373
  }
@@ -265,11 +382,16 @@ try {
265
382
  output(getVerificationContract(versionDir, id));
266
383
  break;
267
384
  }
268
- case 'history': {
269
- const id = rest[0];
270
- if (!id) die('用法: loom verify history <id>');
271
- const history = getVerificationHistory(verificationsDir, id);
272
- output(history ?? `没有 ${id} 的验证记录`);
385
+ case 'history': {
386
+ const id = rest[0];
387
+ if (!id) die('用法: loom verify history <id>');
388
+ if (argv.includes('--across-versions')) {
389
+ output(getAcrossVersionVerificationHistory(versionDir, id));
390
+ } else {
391
+ const resolved = resolveIntentRef(versionDir, id);
392
+ const history = getVerificationHistory(getVerificationsDir(resolved.versionDir), resolved.intentId);
393
+ output(history ?? `没有 ${resolved.ref} 的验证记录`);
394
+ }
273
395
  break;
274
396
  }
275
397
  case 'pending':
@@ -299,41 +421,54 @@ try {
299
421
  } else {
300
422
  die('用法: loom verify write --json-file <path> | --json <json-string>');
301
423
  }
302
- const result = writeVerification(verificationsDir, record);
424
+ const result = writeVerification(versionDir, verificationsDir, record);
303
425
  console.log(`验证记录已写入: ${result.filePath}`);
304
426
  console.log(` 轮次: ${result.round}, verdict: ${record.verdict}`);
305
- if (record.verdict === 'deviated') {
306
- console.log(` deviated 累计: ${result.deviated_count} 轮`);
307
- if (result.should_escalate) {
308
- console.log(` ⚠ 达到 3 轮上限,应升级为 blocked——Keeper 应执行: loom intent update ${record.intent_id} --status blocked`);
309
- }
310
- }
427
+ if (record.verdict === 'deviated') {
428
+ console.log(` deviated 累计: ${result.deviated_count} 轮`);
429
+ if (result.should_escalate) {
430
+ updateIntentStatus(versionDir, record.intent_id, 'blocked');
431
+ console.log(` ⚠ 已达到 3 轮上限,${record.intent_id} 已自动升级为 blocked`);
432
+ }
433
+ }
311
434
  break;
312
435
  }
313
436
  case 'pass':
314
437
  case 'fail': {
315
- // loom verify pass <id> --summary "..." [--reproduction-command "..."]
316
- // loom verify fail <id> --summary "..." [--deviation "..."] [--reproduction-command "..."]
317
- const id = rest[0];
318
- if (!id) die(`用法: loom verify ${sub} <id> --summary "..." [--reproduction-command "..."]${sub === 'fail' ? ' [--deviation "..."]' : ''}`);
319
- const summaryIdx = argv.indexOf('--summary');
320
- const reproIdx = argv.indexOf('--reproduction-command');
321
- const deviationIdx = argv.indexOf('--deviation');
322
- const summary = summaryIdx !== -1 ? argv[summaryIdx + 1] : null;
323
- if (!summary) die(`缺少 --summary: loom verify ${sub} ${id} --summary "..."`);
324
- const extras = {};
325
- if (reproIdx !== -1 && argv[reproIdx + 1]) extras.reproduction_command = argv[reproIdx + 1];
326
- if (sub === 'fail' && deviationIdx !== -1 && argv[deviationIdx + 1]) extras.deviation_detail = argv[deviationIdx + 1];
438
+ // loom verify pass <id> --summary "..." [--reproduction-command "..."] [--preservation-evidence "..."] [--quality-proof "..."]
439
+ // loom verify fail <id> --summary "..." [--deviation "..."] [--reproduction-command "..."]
440
+ const id = rest[0];
441
+ if (!id) die(`用法: loom verify ${sub} <id> --summary "..." [--reproduction-command "..."]${sub === 'pass' ? ' [--preservation-evidence "..."] [--quality-proof "..."]' : ' [--deviation "..."]'}`);
442
+ const summaryIdx = argv.indexOf('--summary');
443
+ const reproIdx = argv.indexOf('--reproduction-command');
444
+ const deviationIdx = argv.indexOf('--deviation');
445
+ const qualityProofIdx = argv.indexOf('--quality-proof');
446
+ const preservationIdx = argv.indexOf('--preservation-evidence');
447
+ const summary = summaryIdx !== -1 ? argv[summaryIdx + 1] : null;
448
+ if (!summary) die(`缺少 --summary: loom verify ${sub} ${id} --summary "..."`);
449
+ const intent = getIntent(versionDir, id);
450
+ if (sub === 'pass' && intent.continuity_required && !(preservationIdx !== -1 && argv[preservationIdx + 1])) {
451
+ die(`Intent ${id} 声明了 continuity_required;通过前必须提供 --preservation-evidence,证明旧状态 → 新操作后的完整序列未发生未授权丢失。`);
452
+ }
453
+ if (sub === 'pass' && intent.quality_contract && !(qualityProofIdx !== -1 && argv[qualityProofIdx + 1])) {
454
+ die(`Intent ${id} 声明了 quality_contract;通过前必须提供 --quality-proof,指向项目内真实的 Quality Proof Markdown 锚点。`);
455
+ }
456
+ const extras = {};
457
+ if (reproIdx !== -1 && argv[reproIdx + 1]) extras.reproduction_command = argv[reproIdx + 1];
458
+ if (sub === 'fail' && deviationIdx !== -1 && argv[deviationIdx + 1]) extras.deviation_detail = argv[deviationIdx + 1];
459
+ if (sub === 'pass' && qualityProofIdx !== -1 && argv[qualityProofIdx + 1]) extras.quality_proof_ref = argv[qualityProofIdx + 1];
460
+ if (sub === 'pass' && preservationIdx !== -1 && argv[preservationIdx + 1]) extras.preservation_evidence = argv[preservationIdx + 1];
327
461
  const verdict = sub === 'pass' ? 'passed' : 'deviated';
328
- const result = createQuickVerification(verificationsDir, id, verdict, summary, extras);
462
+ const result = createQuickVerification(versionDir, verificationsDir, id, verdict, summary, extras);
329
463
  console.log(`验证记录已写入: ${result.filePath}`);
330
464
  console.log(` 轮次: ${result.round}, verdict: ${verdict}`);
331
- if (verdict === 'deviated') {
332
- console.log(` deviated 累计: ${result.deviated_count} 轮`);
333
- if (result.should_escalate) {
334
- console.log(` ⚠ 达到 3 轮上限,应升级为 blocked——Keeper 应执行: loom intent update ${id} --status blocked`);
335
- }
336
- }
465
+ if (verdict === 'deviated') {
466
+ console.log(` deviated 累计: ${result.deviated_count} 轮`);
467
+ if (result.should_escalate) {
468
+ updateIntentStatus(versionDir, id, 'blocked');
469
+ console.log(` ⚠ 已达到 3 轮上限,${id} 已自动升级为 blocked`);
470
+ }
471
+ }
337
472
  break;
338
473
  }
339
474
  default:
@@ -342,7 +477,7 @@ try {
342
477
  break;
343
478
  }
344
479
 
345
- case 'version': {
480
+ case 'version': {
346
481
  const loomRoot = findLoomRoot();
347
482
  switch (sub) {
348
483
  case 'list': {
@@ -389,8 +524,42 @@ try {
389
524
  default:
390
525
  die(`未知子命令: version ${sub}\n用法: loom version [list|current|new|use <v>|diff <v1> <v2>]`);
391
526
  }
392
- break;
393
- }
527
+ break;
528
+ }
529
+
530
+ case 'patch': {
531
+ const versionDir = findVersionDir();
532
+ switch (sub) {
533
+ case 'record': {
534
+ const fileFlagIdx = argv.indexOf('--json-file');
535
+ const inputPath = fileFlagIdx !== -1 ? argv[fileFlagIdx + 1] : null;
536
+ if (!inputPath) die('用法: loom patch record --json-file <path>');
537
+ let record;
538
+ try {
539
+ record = JSON.parse(readFileSync(inputPath, 'utf-8'));
540
+ } catch (e) {
541
+ die(`JSON 文件解析失败: ${inputPath}\n原因: ${e.message}`);
542
+ }
543
+ output(recordPatch(versionDir, record));
544
+ break;
545
+ }
546
+ case 'list':
547
+ output(listPatches(versionDir));
548
+ break;
549
+ case 'get': {
550
+ const id = rest[0];
551
+ if (!id) die('用法: loom patch get <id>');
552
+ output(getPatch(versionDir, id));
553
+ break;
554
+ }
555
+ case 'validate':
556
+ output(validatePatches(versionDir));
557
+ break;
558
+ default:
559
+ die(`未知子命令: patch ${sub}\n用法: loom patch [record --json-file <path>|list|get <id>|validate]`);
560
+ }
561
+ break;
562
+ }
394
563
 
395
564
  case 'doctor': {
396
565
  const versionDir = findVersionDir();
@@ -406,7 +575,7 @@ try {
406
575
  console.log(` → 修复: ${issue.fix_hint}`);
407
576
  }
408
577
  }
409
- console.log(`\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/PART_DECOMPOSITION.md + dimensions/SEARCH_METHODOLOGY.md。`);
578
+ console.log(`\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。`);
410
579
  }
411
580
  break;
412
581
  }
@@ -614,25 +783,37 @@ To Human:
614
783
  loom init 初始化项目(创建 .loom/v1/ 骨架 + 模板)
615
784
  loom guide 诊断当前阶段,输出下一步引导
616
785
  loom guide --dry-run 只读诊断当前阶段,不写 heartbeat
617
- loom auto on|off|status AUTO 模式开关(on Agent 自动连续执行)
618
- loom activate <role> 输出角色激活提示词(weaver|visionary|architect|forge|keeper)
786
+ loom auto on|off|status AUTO 编排协议开关(由 Agent/runtime 消费,不由 CLI 自行执行)
787
+ loom activate <role> [--intent <id>] 输出普通或 Intent-scoped 角色激活提示词
619
788
 
620
789
  loom version list 列出所有版本(* 标记当前)
621
790
  loom version current 显示当前版本
622
791
  loom version new 创建 v{N+1} + 自动切换为当前
623
792
  loom version use <v> 切换当前版本
624
- loom version diff <v1> <v2> 对比两个版本的文件差异
625
-
626
- loom intent next 返回下一个可执行 Intent
793
+ loom version diff <v1> <v2> 对比两个版本的文件差异
794
+
795
+ loom patch record --json-file <path> 记录 Patch 并生成 Markdown 投影
796
+ loom patch list 列出当前版本的 Patch
797
+ loom patch get <id> 返回指定 Patch
798
+ loom patch validate 校验 Patch ledger 和 Markdown 投影
799
+
800
+ loom intent next 返回下一个可执行 Intent
801
+ loom intent add --title <text> [--depends-on <ids>] 创建新增 draft
802
+ loom intent revise <id> --reason <text> 创建修订 draft 并报告反向依赖
803
+ loom intent draft <id> 查看 draft
804
+ loom intent finalize <id> 校验并原子写入官方 Intent Map
805
+ loom intent deprecate <id> --reason <text> 只读评估;加 --confirm 才弃用
627
806
  loom intent status 返回进度概览
628
807
  loom intent graph 输出 Mermaid 依赖图
629
808
  loom intent get <id> 返回某 Intent 完整信息
630
- loom intent narrative <id> 返回某 Intent 的意图叙事(解析 narrative_ref)
809
+ loom intent narrative <id> 返回某 Intent 的意图叙事(解析 narrative_ref)
810
+ loom intent diff <v1> <v2> 按显式 lineage 对比 Intent 语义
631
811
  loom intent validate 校验 Intent Map 结构
632
812
  loom intent trace <id> 返回某 Intent 的完整追溯链(依赖+验证+哲学+叙事)
633
813
  loom intent reverse-dep <id> 返回依赖某 Intent 的所有 Intent(变更影响评估)
634
814
  loom intent reverse-ref <anchor> 返回引用某哲学锚点的所有 Intent
635
- loom intent update <id> --status <s> 更新 Intent 状态(Keeper 用)
815
+ loom intent update <id> --status <s> 更新 Intent 状态(Keeper 用)
816
+ loom intent done <id> 当前 revision 验证通过后闭合 Intent
636
817
 
637
818
  loom doctor 项目健康检查(一致性+孤儿引用+循环依赖+僵尸)
638
819
  loom context 上下文摘要(进度+下一步+待验证+风险)
@@ -640,18 +821,23 @@ To Human:
640
821
  loom preview status 检查 preview 是否存在、是否新鲜
641
822
  loom preview --regen 强制重新输出提示词(让 AI 重新生成 HTML)
642
823
  loom preview --stale 强行打开过期 preview
643
- loom help <topic> 分层指南(workflow|concepts|loop|version|doctor|preview)
824
+ loom help <topic> 分层指南(含 patch 工作流)
644
825
 
645
- loom philosophy get <anchor> 按锚点加载哲学章节
646
- loom philosophy list 列出哲学文档文件
647
- loom philosophy check 校验灵感来源质量(源数量/多样性/理由)
826
+ loom philosophy get <anchor> 按锚点加载哲学章节
827
+ loom philosophy list 列出哲学文档文件
828
+ loom philosophy check 校验灵感来源质量(源数量/多样性/理由)
829
+ loom philosophy impact <anchor> 只读分析直接引用和传递影响
830
+ loom philosophy revise <anchor> --classification <type> --reason <text> 只读评估;加 --confirm 才记录 clarification/minor
648
831
 
649
832
  loom verify contract <id> 返回某 Intent 的验收契约(解析引用)
650
- loom verify history <id> 返回某 Intent 验证历史
833
+ loom verify history <id> 返回某 Intent 本地验证历史
834
+ loom verify history <ref> --across-versions 递归返回 lineage 各版本的本地历史
651
835
  loom verify pending 返回待验证的 Intent
652
836
  loom verify list 列出所有验证记录
653
837
  loom verify write --json-file <path> 从文件读入并写入验证记录
654
- loom verify write --json <string> 从命令行字符串写入验证记录
838
+ loom verify write --json <string> 从命令行字符串写入验证记录
839
+ loom verify pass|fail <id> --summary <text> 快捷写入验证结果
840
+ --quality-proof <ref> 声明相对质量提升时,指向基线比较与稳定性证据
655
841
 
656
842
  参数:
657
843
  --loom-dir <path> 指定 .loom/v{N} 目录(默认读 .loom/current 指针)`);