@haaaiawd/loom 0.9.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.
- package/LICENSE +21 -0
- package/README.md +87 -52
- package/cli/bin/loom.js +438 -149
- package/cli/help/concepts.md +93 -72
- package/cli/help/doctor.md +71 -121
- package/cli/help/loop.md +120 -135
- package/cli/help/patch.md +33 -0
- package/cli/help/preview.md +2 -1
- package/cli/help/version.md +92 -16
- package/cli/help/workflow.md +89 -100
- package/cli/src/activate.js +302 -73
- package/cli/src/auto.js +41 -18
- package/cli/src/diagnostics.js +223 -50
- package/cli/src/guide.js +127 -38
- package/cli/src/init.js +50 -29
- package/cli/src/intent-draft.js +303 -0
- package/cli/src/intent-map.js +540 -54
- package/cli/src/patch.js +214 -0
- package/cli/src/philosophy.js +181 -156
- package/cli/src/preview-prompt.md +13 -6
- package/cli/src/preview.js +1 -0
- package/cli/src/shared/intent-ref.js +38 -0
- package/cli/src/shared/proof-reference.js +19 -0
- package/cli/src/shared/verification-method.js +32 -0
- package/cli/src/verify.js +204 -51
- package/cli/src/version.js +5 -4
- package/dimensions/PART_DECOMPOSITION.md +42 -203
- package/dimensions/SEARCH_METHODOLOGY.md +101 -97
- package/dimensions/examples/AGENT_SYSTEM/README.md +1 -1
- package/dimensions/examples/CLI_TOOL/README.md +1 -1
- package/dimensions/universal/COLLABORATION_PHILOSOPHY.md +28 -77
- package/dimensions/universal/ENGINEERING_CREED.md +30 -74
- package/dimensions/universal/PRODUCT_PHILOSOPHY.md +32 -70
- package/meta/BASELINE.md +91 -276
- package/meta/INTENT_LOOP.md +242 -737
- package/meta/PHILOSOPHY_WEAVER.md +110 -343
- package/meta/ROLE_ACTIVATION.md +103 -267
- package/package.json +4 -3
- package/roles/architect.md +71 -111
- package/roles/forge.md +87 -126
- package/roles/keeper.md +99 -223
- package/roles/visionary.md +57 -86
- package/templates/INTENT_MAP_TEMPLATE.json +24 -10
- package/templates/PHILOSOPHY_TEMPLATE.md +44 -75
- package/templates/VISION_TEMPLATE.md +44 -67
package/cli/bin/loom.js
CHANGED
|
@@ -10,17 +10,20 @@ 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,
|
|
15
|
-
import { writeVerification, 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';
|
|
19
19
|
import { doctor, contextSummary, traceIntent, reverseDep, reverseRef } from '../src/diagnostics.js';
|
|
20
20
|
import { getHelpTopic, listHelpTopics } from '../src/help.js';
|
|
21
21
|
import { guideProject } from '../src/guide.js';
|
|
22
|
-
import { isAutoOn, autoOn, autoOff, autoStatus } from '../src/auto.js';
|
|
22
|
+
import { isAutoOn, autoOn, autoOff, autoStatus, getAutoMode } from '../src/auto.js';
|
|
23
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
|
-
|
|
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
|
-
|
|
90
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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': {
|
|
@@ -126,20 +207,55 @@ try {
|
|
|
126
207
|
console.log(`${id} status 已更新为 ${newStatus}`);
|
|
127
208
|
break;
|
|
128
209
|
}
|
|
210
|
+
case 'done': {
|
|
211
|
+
// loom intent done <id> — 自动走 pending→in_progress→completed,检查验证记录
|
|
212
|
+
const id = rest[0];
|
|
213
|
+
if (!id) die('用法: loom intent done <id>');
|
|
214
|
+
const verificationsDir = getVerificationsDir(versionDir);
|
|
215
|
+
// 检查有没有验证记录
|
|
216
|
+
const history = getVerificationHistory(verificationsDir, id);
|
|
217
|
+
if (!history || history.records.length === 0) {
|
|
218
|
+
die(`${id} 没有验证记录。先跑: loom verify pass ${id} --summary "..."`);
|
|
219
|
+
}
|
|
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
|
+
}
|
|
230
|
+
const currentStatus = intent.status;
|
|
231
|
+
if (currentStatus === 'completed') {
|
|
232
|
+
console.log(`${id} 已经是 completed,无需操作`);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
if (currentStatus === 'pending') {
|
|
236
|
+
updateIntentStatus(versionDir, id, 'in_progress');
|
|
237
|
+
}
|
|
238
|
+
updateIntentStatus(versionDir, id, 'completed');
|
|
239
|
+
console.log(`${id} 已完成(${currentStatus} → completed)`);
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
129
242
|
default:
|
|
130
|
-
die(`未知子命令: intent ${sub}\n用法: loom intent [next|status|graph|get
|
|
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]`);
|
|
131
244
|
}
|
|
132
245
|
break;
|
|
133
246
|
}
|
|
134
247
|
|
|
135
|
-
case 'init': {
|
|
136
|
-
|
|
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());
|
|
137
254
|
console.log('LOOM 项目已初始化');
|
|
138
|
-
console.log(`
|
|
139
|
-
for (const
|
|
140
|
-
if (result.skipped.length) {
|
|
141
|
-
console.log(
|
|
142
|
-
for (const s of result.skipped) console.log(` - ${s}`);
|
|
255
|
+
for (const c of result.created) console.log(` [created] ${c}`);
|
|
256
|
+
for (const s of result.skipped) console.log(` [skipped] ${s} (already exists)`);
|
|
257
|
+
if (result.created.length === 0 && result.skipped.length > 0) {
|
|
258
|
+
console.log(' 所有文件已存在,无需操作。');
|
|
143
259
|
}
|
|
144
260
|
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
145
261
|
console.log('To Agent: 运行 loom guide 诊断当前阶段,按引导执行');
|
|
@@ -151,7 +267,7 @@ try {
|
|
|
151
267
|
}
|
|
152
268
|
|
|
153
269
|
case 'activate': {
|
|
154
|
-
const role = sub;
|
|
270
|
+
const role = sub;
|
|
155
271
|
if (!role) die('用法: loom activate <role>\n角色: weaver | visionary | architect | forge | keeper');
|
|
156
272
|
// weaver 不需要 versionDir(项目还没初始化时也能激活)
|
|
157
273
|
let versionDir = null;
|
|
@@ -163,7 +279,10 @@ try {
|
|
|
163
279
|
if (!String(e.message).includes('找不到 .loom')) throw e;
|
|
164
280
|
}
|
|
165
281
|
}
|
|
166
|
-
const
|
|
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);
|
|
167
286
|
output(prompt);
|
|
168
287
|
break;
|
|
169
288
|
}
|
|
@@ -177,15 +296,56 @@ try {
|
|
|
177
296
|
output(getPhilosophy(getPhilosophyDir(versionDir), anchor));
|
|
178
297
|
break;
|
|
179
298
|
}
|
|
180
|
-
case 'list':
|
|
181
|
-
output(listPhilosophyFiles(getPhilosophyDir(versionDir)));
|
|
182
|
-
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
|
+
}
|
|
183
334
|
case 'check': {
|
|
184
|
-
const philDir = getPhilosophyDir(versionDir);
|
|
185
|
-
const inspiration = validateInspirationSources(philDir);
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
335
|
+
const philDir = getPhilosophyDir(versionDir);
|
|
336
|
+
const inspiration = validateInspirationSources(philDir);
|
|
337
|
+
const allIssues = inspiration.issues;
|
|
338
|
+
const allPassed = inspiration.passed;
|
|
339
|
+
|
|
340
|
+
// --json: 结构化输出,供 Agent 程序化消费
|
|
341
|
+
if (argv.includes('--json')) {
|
|
342
|
+
output({
|
|
343
|
+
passed: allPassed,
|
|
344
|
+
issues: allIssues,
|
|
345
|
+
sources: inspiration.sources.map(({ file, sources }) => ({ file, count: sources.length })),
|
|
346
|
+
});
|
|
347
|
+
exit(allPassed ? 0 : 1);
|
|
348
|
+
}
|
|
189
349
|
|
|
190
350
|
if (allPassed) {
|
|
191
351
|
console.log('✓ 哲学文档校验通过');
|
|
@@ -193,10 +353,6 @@ try {
|
|
|
193
353
|
for (const { file, sources } of inspiration.sources) {
|
|
194
354
|
console.log(` ${file}: ${sources.length} 个源`);
|
|
195
355
|
}
|
|
196
|
-
console.log(' 实现部分拆解:');
|
|
197
|
-
for (const part of decomposition.parts) {
|
|
198
|
-
console.log(` - ${part}`);
|
|
199
|
-
}
|
|
200
356
|
} else {
|
|
201
357
|
const high = allIssues.filter((i) => i.severity === 'high').length;
|
|
202
358
|
const medium = allIssues.filter((i) => i.severity === 'medium').length;
|
|
@@ -205,13 +361,13 @@ try {
|
|
|
205
361
|
const icon = issue.severity === 'high' ? '⚠' : '·';
|
|
206
362
|
console.log(` ${icon} [${issue.severity}] ${issue.msg}`);
|
|
207
363
|
}
|
|
208
|
-
console.log('\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/
|
|
364
|
+
console.log('\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。');
|
|
209
365
|
exit(1);
|
|
210
366
|
}
|
|
211
367
|
break;
|
|
212
368
|
}
|
|
213
369
|
default:
|
|
214
|
-
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>]`);
|
|
215
371
|
}
|
|
216
372
|
break;
|
|
217
373
|
}
|
|
@@ -226,11 +382,16 @@ try {
|
|
|
226
382
|
output(getVerificationContract(versionDir, id));
|
|
227
383
|
break;
|
|
228
384
|
}
|
|
229
|
-
case 'history': {
|
|
230
|
-
const id = rest[0];
|
|
231
|
-
if (!id) die('用法: loom verify history <id>');
|
|
232
|
-
|
|
233
|
-
|
|
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
|
+
}
|
|
234
395
|
break;
|
|
235
396
|
}
|
|
236
397
|
case 'pending':
|
|
@@ -260,24 +421,63 @@ try {
|
|
|
260
421
|
} else {
|
|
261
422
|
die('用法: loom verify write --json-file <path> | --json <json-string>');
|
|
262
423
|
}
|
|
263
|
-
const result = writeVerification(verificationsDir, record);
|
|
424
|
+
const result = writeVerification(versionDir, verificationsDir, record);
|
|
264
425
|
console.log(`验证记录已写入: ${result.filePath}`);
|
|
265
426
|
console.log(` 轮次: ${result.round}, verdict: ${record.verdict}`);
|
|
266
|
-
if (record.verdict === 'deviated') {
|
|
267
|
-
console.log(` deviated 累计: ${result.deviated_count} 轮`);
|
|
268
|
-
if (result.should_escalate) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
+
}
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
case 'pass':
|
|
437
|
+
case 'fail': {
|
|
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];
|
|
461
|
+
const verdict = sub === 'pass' ? 'passed' : 'deviated';
|
|
462
|
+
const result = createQuickVerification(versionDir, verificationsDir, id, verdict, summary, extras);
|
|
463
|
+
console.log(`验证记录已写入: ${result.filePath}`);
|
|
464
|
+
console.log(` 轮次: ${result.round}, verdict: ${verdict}`);
|
|
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
|
+
}
|
|
272
472
|
break;
|
|
273
473
|
}
|
|
274
474
|
default:
|
|
275
|
-
die(`未知子命令: verify ${sub}\n用法: loom verify [contract <id>|history <id>|pending|list|write --json-file <path>|--json <string>]`);
|
|
475
|
+
die(`未知子命令: verify ${sub}\n用法: loom verify [contract <id>|history <id>|pending|list|write --json-file <path>|--json <string>|pass <id> --summary "..."|fail <id> --summary "..."]`);
|
|
276
476
|
}
|
|
277
477
|
break;
|
|
278
478
|
}
|
|
279
479
|
|
|
280
|
-
case 'version': {
|
|
480
|
+
case 'version': {
|
|
281
481
|
const loomRoot = findLoomRoot();
|
|
282
482
|
switch (sub) {
|
|
283
483
|
case 'list': {
|
|
@@ -324,8 +524,42 @@ try {
|
|
|
324
524
|
default:
|
|
325
525
|
die(`未知子命令: version ${sub}\n用法: loom version [list|current|new|use <v>|diff <v1> <v2>]`);
|
|
326
526
|
}
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
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
|
+
}
|
|
329
563
|
|
|
330
564
|
case 'doctor': {
|
|
331
565
|
const versionDir = findVersionDir();
|
|
@@ -337,7 +571,11 @@ try {
|
|
|
337
571
|
for (const issue of issues) {
|
|
338
572
|
const icon = issue.severity === 'fatal' ? '☠' : issue.severity === 'high' ? '⚠' : '·';
|
|
339
573
|
console.log(` ${icon} [${issue.severity}] ${issue.type}: ${issue.msg}`);
|
|
574
|
+
if (issue.fix_hint) {
|
|
575
|
+
console.log(` → 修复: ${issue.fix_hint}`);
|
|
576
|
+
}
|
|
340
577
|
}
|
|
578
|
+
console.log(`\n参见 meta/PHILOSOPHY_WEAVER.md + dimensions/SEARCH_METHODOLOGY.md。`);
|
|
341
579
|
}
|
|
342
580
|
break;
|
|
343
581
|
}
|
|
@@ -368,22 +606,39 @@ try {
|
|
|
368
606
|
break;
|
|
369
607
|
}
|
|
370
608
|
|
|
371
|
-
case 'guide': {
|
|
372
|
-
const dryRun = argv.includes('--dry-run');
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log(
|
|
609
|
+
case 'guide': {
|
|
610
|
+
const dryRun = argv.includes('--dry-run');
|
|
611
|
+
const jsonOut = argv.includes('--json');
|
|
612
|
+
const result = guideProject(cwd(), { dryRun });
|
|
613
|
+
if (jsonOut) {
|
|
614
|
+
output(result);
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
console.log(`阶段 ${result.stage_num}: ${result.stage}`);
|
|
618
|
+
if (dryRun) {
|
|
619
|
+
console.log('诊断: dry-run(不写 heartbeat)');
|
|
382
620
|
}
|
|
621
|
+
const modeDesc = {
|
|
622
|
+
'manual': '手动(每步需确认)',
|
|
623
|
+
'auto-loop': 'AUTO auto-loop(设计阶段需 review,Intent Loop 自动)',
|
|
624
|
+
'auto-design': 'AUTO auto-design(全部自动)',
|
|
625
|
+
};
|
|
626
|
+
console.log(`模式: ${modeDesc[result.auto_mode] || result.auto_mode}`);
|
|
383
627
|
console.log(`\n${result.message}`);
|
|
384
628
|
console.log(`\n下一步: ${result.next_action}`);
|
|
385
629
|
console.log(` → ${result.next_command}`);
|
|
386
|
-
if (
|
|
630
|
+
if (result.inputs && result.inputs.length > 0) {
|
|
631
|
+
console.log(`\n需要读取:`);
|
|
632
|
+
for (const f of result.inputs) console.log(` - ${f}`);
|
|
633
|
+
}
|
|
634
|
+
if (result.outputs && result.outputs.length > 0) {
|
|
635
|
+
console.log(`\n需要产出:`);
|
|
636
|
+
for (const f of result.outputs) console.log(` - ${f}`);
|
|
637
|
+
}
|
|
638
|
+
if (result.verify_command) {
|
|
639
|
+
console.log(`\n完成后校验: ${result.verify_command}`);
|
|
640
|
+
}
|
|
641
|
+
if (result.auto_mode === 'manual' && result.stage_num > 0 && result.stage_num < 6) {
|
|
387
642
|
console.log(`\n提示: 开启 AUTO 模式可自动连续执行 — loom auto on`);
|
|
388
643
|
}
|
|
389
644
|
break;
|
|
@@ -392,76 +647,93 @@ try {
|
|
|
392
647
|
case 'auto': {
|
|
393
648
|
const loomRoot = findLoomRoot();
|
|
394
649
|
switch (sub) {
|
|
395
|
-
case 'on':
|
|
396
|
-
|
|
397
|
-
|
|
650
|
+
case 'on': {
|
|
651
|
+
// loom auto on → auto-loop(默认)
|
|
652
|
+
// loom auto on --design → auto-design
|
|
653
|
+
const wantDesign = argv.includes('--design');
|
|
654
|
+
const mode = wantDesign ? 'auto-design' : 'auto-loop';
|
|
655
|
+
autoOn(loomRoot, mode);
|
|
656
|
+
if (mode === 'auto-design') {
|
|
657
|
+
console.log('AUTO 模式: auto-design(全部自动,含哲学/愿景/架构)');
|
|
658
|
+
console.log('Agent 自动连续执行所有阶段,不等人类确认。');
|
|
659
|
+
} else {
|
|
660
|
+
console.log('AUTO 模式: auto-loop(Intent Loop 自动,设计阶段需 review)');
|
|
661
|
+
console.log(' - stage 1-3(哲学/愿景/架构):自动执行但需人类 review');
|
|
662
|
+
console.log(' - stage 4+(Intent Loop):自动连续执行');
|
|
663
|
+
}
|
|
398
664
|
console.log('核心契约: 持续运行,除非出意外否则不允许私自停止。');
|
|
399
665
|
console.log(' - L3 human_review 由 Keeper 自主判定,不停下等人类');
|
|
400
666
|
console.log(' - 唯一允许停下的情况: blocked(依赖阻塞/契约无法判定/连续 3 轮 deviated 升级)');
|
|
401
|
-
console.log('
|
|
667
|
+
console.log('切换: loom auto on --design | loom auto off');
|
|
402
668
|
break;
|
|
669
|
+
}
|
|
403
670
|
case 'off':
|
|
404
671
|
autoOff(loomRoot);
|
|
405
|
-
console.log('AUTO
|
|
672
|
+
console.log('AUTO 模式: manual(每步需人类确认)');
|
|
406
673
|
break;
|
|
407
674
|
case 'status': {
|
|
408
675
|
const status = autoStatus(loomRoot);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
console.log(
|
|
676
|
+
const modeDesc = {
|
|
677
|
+
'manual': 'manual(每步需人类确认)',
|
|
678
|
+
'auto-loop': 'auto-loop(Intent Loop 自动,设计阶段需 review)',
|
|
679
|
+
'auto-design': 'auto-design(全部自动,含哲学/愿景/架构)',
|
|
680
|
+
};
|
|
681
|
+
console.log(`AUTO 模式: ${modeDesc[status.mode] || status.mode}`);
|
|
682
|
+
if (status.mode === 'auto-loop') {
|
|
683
|
+
console.log(' 规则: stage 1-3 需人类 review,stage 4+ 自动执行');
|
|
684
|
+
} else if (status.mode === 'auto-design') {
|
|
685
|
+
console.log(' 规则: 全部阶段自动执行,不需人类 review');
|
|
686
|
+
}
|
|
687
|
+
if (status.heartbeat) {
|
|
688
|
+
const hb = status.heartbeat;
|
|
689
|
+
console.log(` 心跳: ${hb.timestamp}`);
|
|
690
|
+
console.log(` 阶段: ${hb.stage} (stage ${hb.stage_num})`);
|
|
691
|
+
console.log(` 下一步: ${hb.next_action}`);
|
|
692
|
+
console.log(` 命令: ${hb.next_command}`);
|
|
693
|
+
} else if (status.mode !== 'manual') {
|
|
694
|
+
console.log(' 心跳: 尚未记录(运行 loom guide 后生成)');
|
|
423
695
|
}
|
|
424
696
|
break;
|
|
425
697
|
}
|
|
426
698
|
default:
|
|
427
|
-
die(`未知子命令: auto ${sub}\n用法: loom auto [on|off|status]`);
|
|
699
|
+
die(`未知子命令: auto ${sub}\n用法: loom auto [on [--design]|off|status]`);
|
|
428
700
|
}
|
|
429
701
|
break;
|
|
430
702
|
}
|
|
431
703
|
|
|
432
|
-
case 'preview': {
|
|
433
|
-
const previewFile = join(cwd(), 'loom-preview.html');
|
|
434
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
435
|
-
console.log(`用法:
|
|
436
|
-
loom preview 打开新鲜 preview;过期时提示重新生成
|
|
437
|
-
loom preview --regen 输出生成提示词,让 Agent 重写 loom-preview.html
|
|
438
|
-
loom preview status 检查 preview 是否存在、是否新鲜
|
|
439
|
-
loom preview --stale 强行打开过期 preview
|
|
440
|
-
loom preview --help 显示本帮助`);
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
const status = getPreviewStatus(cwd());
|
|
444
|
-
const hasPreview = status.exists;
|
|
445
|
-
const regenOnly = argv.includes('--regen') || argv.includes('-r');
|
|
446
|
-
const openStale = argv.includes('--stale');
|
|
447
|
-
|
|
448
|
-
if (sub === 'status') {
|
|
449
|
-
output(status);
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// 已有 HTML 且没指定 --regen:直接打开浏览器
|
|
454
|
-
if (hasPreview && !regenOnly) {
|
|
455
|
-
if (!status.fresh && !openStale) {
|
|
456
|
-
console.log('preview 已过期:.loom 源文件比 loom-preview.html 更新。');
|
|
457
|
-
console.log(` preview: ${status.preview_mtime || '未知'}`);
|
|
458
|
-
console.log(` 最新源: ${status.source_latest_mtime || '未知'} ${status.latest_source_file ? `(${status.latest_source_file})` : ''}`);
|
|
459
|
-
console.log('\n下一步: loom preview --regen');
|
|
460
|
-
console.log('强行打开旧 preview: loom preview --stale');
|
|
461
|
-
break;
|
|
462
|
-
}
|
|
463
|
-
const { spawn } = await import('node:child_process');
|
|
464
|
-
const target = previewFile.replace(/\\/g, '/');
|
|
704
|
+
case 'preview': {
|
|
705
|
+
const previewFile = join(cwd(), 'loom-preview.html');
|
|
706
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
707
|
+
console.log(`用法:
|
|
708
|
+
loom preview 打开新鲜 preview;过期时提示重新生成
|
|
709
|
+
loom preview --regen 输出生成提示词,让 Agent 重写 loom-preview.html
|
|
710
|
+
loom preview status 检查 preview 是否存在、是否新鲜
|
|
711
|
+
loom preview --stale 强行打开过期 preview
|
|
712
|
+
loom preview --help 显示本帮助`);
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
const status = getPreviewStatus(cwd());
|
|
716
|
+
const hasPreview = status.exists;
|
|
717
|
+
const regenOnly = argv.includes('--regen') || argv.includes('-r');
|
|
718
|
+
const openStale = argv.includes('--stale');
|
|
719
|
+
|
|
720
|
+
if (sub === 'status') {
|
|
721
|
+
output(status);
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// 已有 HTML 且没指定 --regen:直接打开浏览器
|
|
726
|
+
if (hasPreview && !regenOnly) {
|
|
727
|
+
if (!status.fresh && !openStale) {
|
|
728
|
+
console.log('preview 已过期:.loom 源文件比 loom-preview.html 更新。');
|
|
729
|
+
console.log(` preview: ${status.preview_mtime || '未知'}`);
|
|
730
|
+
console.log(` 最新源: ${status.source_latest_mtime || '未知'} ${status.latest_source_file ? `(${status.latest_source_file})` : ''}`);
|
|
731
|
+
console.log('\n下一步: loom preview --regen');
|
|
732
|
+
console.log('强行打开旧 preview: loom preview --stale');
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
const { spawn } = await import('node:child_process');
|
|
736
|
+
const target = previewFile.replace(/\\/g, '/');
|
|
465
737
|
if (process.platform === 'win32') {
|
|
466
738
|
spawn('cmd', ['/c', 'start', target], { detached: true, stdio: 'ignore' }).unref();
|
|
467
739
|
} else if (process.platform === 'darwin') {
|
|
@@ -470,9 +742,9 @@ try {
|
|
|
470
742
|
spawn('xdg-open', [target], { detached: true, stdio: 'ignore' }).unref();
|
|
471
743
|
}
|
|
472
744
|
console.log(`已打开浏览器: ${previewFile}`);
|
|
473
|
-
console.log(status.fresh ? `重新生成: loom preview --regen` : `已打开旧 preview。重新生成: loom preview --regen`);
|
|
474
|
-
break;
|
|
475
|
-
}
|
|
745
|
+
console.log(status.fresh ? `重新生成: loom preview --regen` : `已打开旧 preview。重新生成: loom preview --regen`);
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
476
748
|
|
|
477
749
|
// 没有 HTML 或指定 --regen:输出提示词让 AI 生成
|
|
478
750
|
const prompt = generatePreviewPrompt();
|
|
@@ -508,47 +780,64 @@ To Human:
|
|
|
508
780
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
509
781
|
|
|
510
782
|
用法:
|
|
511
|
-
loom init 初始化项目(创建 .loom/v1/ 骨架 + 模板)
|
|
512
|
-
loom guide 诊断当前阶段,输出下一步引导
|
|
513
|
-
loom guide --dry-run 只读诊断当前阶段,不写 heartbeat
|
|
514
|
-
loom auto on|off|status AUTO
|
|
515
|
-
loom activate <role>
|
|
783
|
+
loom init 初始化项目(创建 .loom/v1/ 骨架 + 模板)
|
|
784
|
+
loom guide 诊断当前阶段,输出下一步引导
|
|
785
|
+
loom guide --dry-run 只读诊断当前阶段,不写 heartbeat
|
|
786
|
+
loom auto on|off|status AUTO 编排协议开关(由 Agent/runtime 消费,不由 CLI 自行执行)
|
|
787
|
+
loom activate <role> [--intent <id>] 输出普通或 Intent-scoped 角色激活提示词
|
|
516
788
|
|
|
517
789
|
loom version list 列出所有版本(* 标记当前)
|
|
518
790
|
loom version current 显示当前版本
|
|
519
791
|
loom version new 创建 v{N+1} + 自动切换为当前
|
|
520
792
|
loom version use <v> 切换当前版本
|
|
521
|
-
loom version diff <v1> <v2> 对比两个版本的文件差异
|
|
522
|
-
|
|
523
|
-
loom
|
|
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 才弃用
|
|
524
806
|
loom intent status 返回进度概览
|
|
525
807
|
loom intent graph 输出 Mermaid 依赖图
|
|
526
808
|
loom intent get <id> 返回某 Intent 完整信息
|
|
527
|
-
loom intent narrative <id> 返回某 Intent 的意图叙事(解析 narrative_ref)
|
|
809
|
+
loom intent narrative <id> 返回某 Intent 的意图叙事(解析 narrative_ref)
|
|
810
|
+
loom intent diff <v1> <v2> 按显式 lineage 对比 Intent 语义
|
|
528
811
|
loom intent validate 校验 Intent Map 结构
|
|
529
812
|
loom intent trace <id> 返回某 Intent 的完整追溯链(依赖+验证+哲学+叙事)
|
|
530
813
|
loom intent reverse-dep <id> 返回依赖某 Intent 的所有 Intent(变更影响评估)
|
|
531
814
|
loom intent reverse-ref <anchor> 返回引用某哲学锚点的所有 Intent
|
|
532
|
-
loom intent update <id> --status <s> 更新 Intent 状态(Keeper 用)
|
|
815
|
+
loom intent update <id> --status <s> 更新 Intent 状态(Keeper 用)
|
|
816
|
+
loom intent done <id> 当前 revision 验证通过后闭合 Intent
|
|
533
817
|
|
|
534
818
|
loom doctor 项目健康检查(一致性+孤儿引用+循环依赖+僵尸)
|
|
535
819
|
loom context 上下文摘要(进度+下一步+待验证+风险)
|
|
536
|
-
loom preview 打开新鲜 HTML;过期则提示重新生成
|
|
537
|
-
loom preview status 检查 preview 是否存在、是否新鲜
|
|
538
|
-
loom preview --regen 强制重新输出提示词(让 AI 重新生成 HTML)
|
|
539
|
-
loom preview --stale 强行打开过期 preview
|
|
540
|
-
loom help <topic>
|
|
541
|
-
|
|
542
|
-
loom philosophy get <anchor> 按锚点加载哲学章节
|
|
543
|
-
loom philosophy list 列出哲学文档文件
|
|
544
|
-
loom philosophy check 校验灵感来源质量(源数量/多样性/理由)
|
|
820
|
+
loom preview 打开新鲜 HTML;过期则提示重新生成
|
|
821
|
+
loom preview status 检查 preview 是否存在、是否新鲜
|
|
822
|
+
loom preview --regen 强制重新输出提示词(让 AI 重新生成 HTML)
|
|
823
|
+
loom preview --stale 强行打开过期 preview
|
|
824
|
+
loom help <topic> 分层指南(含 patch 工作流)
|
|
825
|
+
|
|
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
|
|
545
831
|
|
|
546
832
|
loom verify contract <id> 返回某 Intent 的验收契约(解析引用)
|
|
547
|
-
loom verify history <id> 返回某 Intent
|
|
833
|
+
loom verify history <id> 返回某 Intent 本地验证历史
|
|
834
|
+
loom verify history <ref> --across-versions 递归返回 lineage 各版本的本地历史
|
|
548
835
|
loom verify pending 返回待验证的 Intent
|
|
549
836
|
loom verify list 列出所有验证记录
|
|
550
837
|
loom verify write --json-file <path> 从文件读入并写入验证记录
|
|
551
|
-
loom verify write --json <string> 从命令行字符串写入验证记录
|
|
838
|
+
loom verify write --json <string> 从命令行字符串写入验证记录
|
|
839
|
+
loom verify pass|fail <id> --summary <text> 快捷写入验证结果
|
|
840
|
+
--quality-proof <ref> 声明相对质量提升时,指向基线比较与稳定性证据
|
|
552
841
|
|
|
553
842
|
参数:
|
|
554
843
|
--loom-dir <path> 指定 .loom/v{N} 目录(默认读 .loom/current 指针)`);
|