@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.
- package/LICENSE +21 -0
- package/README.md +87 -52
- package/cli/bin/loom.js +285 -99
- 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/diagnostics.js +138 -41
- package/cli/src/guide.js +41 -19
- 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 +177 -154
- 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 +184 -61
- 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,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,
|
|
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
|
-
|
|
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': {
|
|
@@ -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
|
-
//
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
215
|
-
const
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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 === '
|
|
319
|
-
const summaryIdx = argv.indexOf('--summary');
|
|
320
|
-
const reproIdx = argv.indexOf('--reproduction-command');
|
|
321
|
-
const deviationIdx = argv.indexOf('--deviation');
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
if (
|
|
326
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
618
|
-
loom activate <role>
|
|
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
|
|
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>
|
|
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 指针)`);
|