@haaaiawd/loom 1.3.1 → 2.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/CHANGELOG.md +11 -86
- package/CONTRIBUTING.md +37 -0
- package/EVIL_EVAL.md +112 -0
- package/README.md +193 -445
- package/README.zh-CN.md +174 -0
- package/SECURITY.md +11 -0
- package/cli/bin/loom.js +171 -998
- package/cli/src/protocol.js +367 -0
- package/cli/src/store.js +626 -0
- package/design.md +194 -0
- package/docs/PROMPT_CATALOG.md +99 -0
- package/docs/RELEASE_CHECKLIST.md +53 -0
- package/docs/UX_FLOW.md +171 -0
- package/docs/brand/loom-mark.svg +18 -0
- package/docs/brand/loom-readme-header.svg +34 -0
- package/docs/brand/loom-readme-header.zh-CN.svg +29 -0
- package/docs/loom-eval-loop.drawio +21 -0
- package/docs/loom-eval-loop.svg +56 -0
- package/docs/loom-production-loop.drawio +41 -0
- package/docs/loom-production-loop.svg +92 -0
- package/package.json +43 -40
- package/EXTERNAL_ACQUISITION_DESIGN.md +0 -143
- package/cli/help/asset.md +0 -36
- package/cli/help/atelier.md +0 -37
- package/cli/help/atlas.md +0 -48
- package/cli/help/capability.md +0 -118
- package/cli/help/concepts.md +0 -105
- package/cli/help/doctor.md +0 -80
- package/cli/help/expertise.md +0 -52
- package/cli/help/loop.md +0 -134
- package/cli/help/patch.md +0 -33
- package/cli/help/proposals.md +0 -21
- package/cli/help/version.md +0 -136
- package/cli/help/workflow.md +0 -116
- package/cli/src/activate.js +0 -505
- package/cli/src/asset-library.js +0 -384
- package/cli/src/atelier.js +0 -331
- package/cli/src/atlas.js +0 -282
- package/cli/src/auto.js +0 -116
- package/cli/src/capability-graph.js +0 -724
- package/cli/src/capability-proposals.js +0 -225
- package/cli/src/diagnostics.js +0 -859
- package/cli/src/expertise-pack.js +0 -336
- package/cli/src/guide.js +0 -548
- package/cli/src/help.js +0 -41
- package/cli/src/init.js +0 -187
- package/cli/src/intent-draft.js +0 -303
- package/cli/src/intent-map.js +0 -747
- package/cli/src/patch.js +0 -214
- package/cli/src/philosophy.js +0 -331
- package/cli/src/shared/intent-ref.js +0 -38
- package/cli/src/shared/md-utils.js +0 -125
- package/cli/src/shared/paths.js +0 -73
- package/cli/src/shared/proof-reference.js +0 -19
- package/cli/src/shared/verification-method.js +0 -32
- package/cli/src/verify.js +0 -394
- package/cli/src/version.js +0 -134
- package/dimensions/AUTHORSHIP.md +0 -45
- package/dimensions/PART_DECOMPOSITION.md +0 -42
- package/dimensions/SEARCH_METHODOLOGY.md +0 -101
- package/dimensions/examples/AGENT_SYSTEM/README.md +0 -219
- package/dimensions/examples/CLI_TOOL/README.md +0 -163
- package/dimensions/universal/COLLABORATION_PHILOSOPHY.md +0 -28
- package/dimensions/universal/ENGINEERING_CREED.md +0 -30
- package/dimensions/universal/PRODUCT_PHILOSOPHY.md +0 -32
- package/meta/BASELINE.md +0 -91
- package/meta/INTENT_LOOP.md +0 -296
- package/meta/PHILOSOPHY_WEAVER.md +0 -110
- package/meta/ROLE_ACTIVATION.md +0 -114
- package/roles/architect.md +0 -92
- package/roles/forge.md +0 -110
- package/roles/impact-reviewer.md +0 -37
- package/roles/keeper.md +0 -113
- package/roles/visionary.md +0 -57
- package/templates/ASSET_LIBRARY_MANIFEST_TEMPLATE.json +0 -10
- package/templates/ATELIER_RECORD_TEMPLATE.json +0 -48
- package/templates/ATLAS_TEMPLATE.html +0 -104
- package/templates/CAPABILITY_BRIEF_TEMPLATE.md +0 -36
- package/templates/CAPABILITY_GRAPH_EXAMPLE.json +0 -188
- package/templates/CAPABILITY_GRAPH_TEMPLATE.json +0 -78
- package/templates/EXPERTISE_PACK_TEMPLATE.json +0 -22
- package/templates/INTENT_MAP_TEMPLATE.json +0 -85
- package/templates/PHILOSOPHY_TEMPLATE.md +0 -44
- package/templates/VISION_TEMPLATE.md +0 -44
package/cli/bin/loom.js
CHANGED
|
@@ -1,1033 +1,206 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// loom — LOOM 框架的 CLI 传感器层
|
|
3
|
-
// Agent 通过这个 CLI 访问 Intent Map / 哲学 / 验证记录,不直接读文件。
|
|
4
2
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, resolve } from 'node:path';
|
|
8
5
|
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
6
|
+
import {
|
|
7
|
+
blockTask,
|
|
8
|
+
checkProject,
|
|
9
|
+
compileContext,
|
|
10
|
+
completeTask,
|
|
11
|
+
createCapability,
|
|
12
|
+
createDesign,
|
|
13
|
+
getCapability,
|
|
14
|
+
getDesign,
|
|
15
|
+
getKeeperPrompt,
|
|
16
|
+
getTask,
|
|
17
|
+
importTasks,
|
|
18
|
+
initProject,
|
|
19
|
+
listCapabilities,
|
|
20
|
+
listDesigns,
|
|
21
|
+
loadProject,
|
|
22
|
+
markReady,
|
|
23
|
+
recordKeeper,
|
|
24
|
+
recordUnderstanding,
|
|
25
|
+
reopenTask,
|
|
26
|
+
scaffoldEval,
|
|
27
|
+
skipKeeper,
|
|
28
|
+
startTask,
|
|
29
|
+
taskSummary,
|
|
30
|
+
updateTask,
|
|
31
|
+
} from '../src/store.js';
|
|
32
|
+
import { promptCatalog } from '../src/protocol.js';
|
|
33
|
+
|
|
34
|
+
const argv = process.argv.slice(2);
|
|
35
|
+
const [command, subcommand, ...rest] = argv;
|
|
36
|
+
|
|
37
|
+
function output(value) {
|
|
38
|
+
console.log(typeof value === 'string' ? value : JSON.stringify(value, null, 2));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
42
|
-
|
|
41
|
+
function fail(message) {
|
|
42
|
+
console.error(`Error: ${message}`);
|
|
43
|
+
process.exitCode = 1;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
function option(name) {
|
|
47
|
+
const index = argv.indexOf(name);
|
|
48
|
+
return index === -1 ? undefined : argv[index + 1];
|
|
49
|
+
}
|
|
46
50
|
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
function jsonFile() {
|
|
52
|
+
const path = option('--json-file');
|
|
53
|
+
if (!path) throw new Error('This command requires --json-file <path>');
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(readFileSync(resolve(path), 'utf8'));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new Error(`Cannot read JSON file ${path}: ${error.message}`);
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
function help() {
|
|
62
|
+
return `loom — continuity infrastructure for Agents
|
|
63
|
+
|
|
64
|
+
The human talks naturally to the Agent. The Agent uses these commands silently.
|
|
65
|
+
|
|
66
|
+
Start and resume
|
|
67
|
+
loom init
|
|
68
|
+
loom context [--task TASK-001] [--keeper]
|
|
69
|
+
loom check
|
|
70
|
+
loom prompts
|
|
71
|
+
|
|
72
|
+
Preserve understanding
|
|
73
|
+
loom record --json-file <update.json>
|
|
74
|
+
loom project ready
|
|
75
|
+
loom design add <slug> --title <text> --kind <product|experience|system|contract|verification|operations|research>
|
|
76
|
+
loom design list|get <slug>
|
|
77
|
+
loom capability add <slug> --title <professional-field>
|
|
78
|
+
loom capability list|get <slug>
|
|
79
|
+
|
|
80
|
+
Maintain the work map
|
|
81
|
+
loom task plan --json-file <tasks.json>
|
|
82
|
+
loom task status|next|get <id>
|
|
83
|
+
loom task start <id>
|
|
84
|
+
loom task update <id> --json-file <patch.json>
|
|
85
|
+
loom task block <id> --json-file <block.json>
|
|
86
|
+
loom task reopen <id> [--reason <reason>]
|
|
87
|
+
loom task done <id> --json-file <evidence.json>
|
|
88
|
+
|
|
89
|
+
One-time independent handoff
|
|
90
|
+
loom keeper prompt
|
|
91
|
+
loom keeper record --json-file <result.json>
|
|
92
|
+
loom keeper skip --reason <reason>
|
|
93
|
+
|
|
94
|
+
Evaluate LOOM itself
|
|
95
|
+
loom eval scaffold --json-file <scenario.json>
|
|
96
|
+
|
|
97
|
+
Use JSON files for structured writes so long content and shell quoting remain auditable.
|
|
98
|
+
Task completion JSON includes evidence plus one check for every exact done_when criterion.`;
|
|
58
99
|
}
|
|
59
100
|
|
|
60
|
-
// ─── 命令路由 ──────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
const [cmd, sub, ...rest] = argv.slice(2);
|
|
63
|
-
|
|
64
101
|
try {
|
|
65
|
-
switch (
|
|
66
|
-
case '--version':
|
|
102
|
+
switch (command) {
|
|
103
|
+
case '--version':
|
|
67
104
|
case '-v': {
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
console.log(`loom ${pkg.version}`);
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
case 'capability': {
|
|
76
|
-
const versionDir = findVersionDir();
|
|
77
|
-
switch (sub) {
|
|
78
|
-
case 'graph':
|
|
79
|
-
output(getCapabilityGraphProjection(versionDir));
|
|
80
|
-
break;
|
|
81
|
-
case 'frontier':
|
|
82
|
-
output(getCapabilityFrontier(versionDir));
|
|
83
|
-
break;
|
|
84
|
-
case 'get': {
|
|
85
|
-
const id = rest[0];
|
|
86
|
-
if (!id) die('用法: loom capability get <node-id>');
|
|
87
|
-
output(getCapabilityNode(versionDir, id));
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
case 'coverage':
|
|
91
|
-
output(getCapabilityCoverage(versionDir));
|
|
92
|
-
break;
|
|
93
|
-
case 'compile': {
|
|
94
|
-
const intentId = rest[0];
|
|
95
|
-
if (!intentId) die('用法: loom capability compile <intent-id>');
|
|
96
|
-
output(compileCapabilityInputs(versionDir, intentId));
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
case 'proposal': {
|
|
100
|
-
const action = rest[0];
|
|
101
|
-
if (action === 'list') output(listCapabilityProposals(versionDir));
|
|
102
|
-
else if (action === 'get') {
|
|
103
|
-
if (!rest[1]) die('用法: loom capability proposal get <CGP-ID>');
|
|
104
|
-
output(getCapabilityProposal(versionDir, rest[1]));
|
|
105
|
-
} else if (action === 'submit') {
|
|
106
|
-
const fileIndex = argv.indexOf('--json-file');
|
|
107
|
-
const path = fileIndex === -1 ? null : argv[fileIndex + 1];
|
|
108
|
-
if (!path) die('用法: loom capability proposal submit --json-file <proposal.json>');
|
|
109
|
-
output(submitCapabilityProposal(versionDir, JSON.parse(readFileSync(path, 'utf-8'))));
|
|
110
|
-
} else if (action === 'decide') {
|
|
111
|
-
const rationaleIndex = argv.indexOf('--rationale');
|
|
112
|
-
if (!rest[1] || !rest[2] || rationaleIndex === -1 || !argv[rationaleIndex + 1]) die('用法: loom capability proposal decide <CGP-ID> <decision> --rationale <text>');
|
|
113
|
-
output(decideCapabilityProposal(versionDir, rest[1], rest[2], argv[rationaleIndex + 1]));
|
|
114
|
-
} else if (action === 'close') {
|
|
115
|
-
const resolutionIndex = argv.indexOf('--resolution-file');
|
|
116
|
-
if (!rest[1] || resolutionIndex === -1 || !argv[resolutionIndex + 1]) die('用法: loom capability proposal close <CGP-ID> --resolution-file <resolution.json>');
|
|
117
|
-
output(closeCapabilityProposal(versionDir, rest[1], JSON.parse(readFileSync(argv[resolutionIndex + 1], 'utf-8'))));
|
|
118
|
-
} else die('用法: loom capability proposal [list|get|submit|decide|close]');
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
default:
|
|
122
|
-
die(`未知 capability 子命令: ${sub}\n用法: loom capability [graph|frontier|get|coverage|compile|proposal]`);
|
|
123
|
-
}
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
case 'asset': {
|
|
128
|
-
const versionDir = findVersionDir();
|
|
129
|
-
switch (sub) {
|
|
130
|
-
case 'import': {
|
|
131
|
-
const filePath = rest[0];
|
|
132
|
-
const option = (name) => {
|
|
133
|
-
const index = argv.indexOf(name);
|
|
134
|
-
return index === -1 ? undefined : argv[index + 1];
|
|
135
|
-
};
|
|
136
|
-
if (!filePath || filePath.startsWith('--')) die('用法: loom asset import <本地文件> --tags <标签,...> --source <来源> --author <作者> --license <许可> --approval approved [--kind image] [--evidence <节点,...>]');
|
|
137
|
-
output(importAsset(versionDir, filePath, {
|
|
138
|
-
tags: option('--tags'), source: option('--source'), author: option('--author'), license: option('--license'),
|
|
139
|
-
approval: option('--approval'), kind: option('--kind'), evidenceRefs: option('--evidence'),
|
|
140
|
-
failureInjection: env.NODE_ENV === 'test' && option('--test-fail-after') === 'manifest' ? 'after_manifest'
|
|
141
|
-
: env.NODE_ENV === 'test' && option('--test-fail-after') === 'crash-manifest' ? 'crash_after_manifest' : undefined,
|
|
142
|
-
}));
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
case 'list': output(listAssets(versionDir)); break;
|
|
146
|
-
case 'search': {
|
|
147
|
-
const query = rest[0];
|
|
148
|
-
if (!query) die('用法: loom asset search <查询>');
|
|
149
|
-
output(searchAssets(versionDir, query));
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
case 'get': {
|
|
153
|
-
const id = rest[0];
|
|
154
|
-
if (!id) die('用法: loom asset get <asset-id>');
|
|
155
|
-
output(getAsset(versionDir, id));
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
case 'validate': {
|
|
159
|
-
const recovery = recoverAssetImportTransaction(versionDir);
|
|
160
|
-
output({ valid: true, assets: Object.keys(validateAssetLibrary(versionDir).assets).length, recovered_transaction: recovery.recovered ? recovery : null });
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
default: die(`未知 asset 子命令: ${sub}\n用法: loom asset [import|list|search|get|validate]`);
|
|
164
|
-
}
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
case 'atelier': {
|
|
169
|
-
const versionDir = findVersionDir();
|
|
170
|
-
const intentId = rest[0];
|
|
171
|
-
if (!intentId) die(`用法: loom atelier ${sub || '<init|get|validate>'} <intent-id>`);
|
|
172
|
-
switch (sub) {
|
|
173
|
-
case 'init':
|
|
174
|
-
output(initAtelierRecord(versionDir, intentId));
|
|
175
|
-
break;
|
|
176
|
-
case 'get':
|
|
177
|
-
output(getAtelierRecord(versionDir, intentId));
|
|
178
|
-
break;
|
|
179
|
-
case 'validate':
|
|
180
|
-
output(validateAtelierRecord(versionDir, intentId));
|
|
181
|
-
break;
|
|
182
|
-
default:
|
|
183
|
-
die(`未知 atelier 子命令: ${sub}\n用法: loom atelier [init|get|validate] <intent-id>`);
|
|
184
|
-
}
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
case 'expertise': {
|
|
189
|
-
const versionDir = findVersionDir();
|
|
190
|
-
const intentId = rest[0];
|
|
191
|
-
if (!intentId) die(`用法: loom expertise ${sub || '<init|get|validate>'} <intent-id>`);
|
|
192
|
-
switch (sub) {
|
|
193
|
-
case 'init':
|
|
194
|
-
output(initExpertisePack(versionDir, intentId));
|
|
195
|
-
break;
|
|
196
|
-
case 'get':
|
|
197
|
-
output(getExpertisePack(versionDir, intentId));
|
|
198
|
-
break;
|
|
199
|
-
case 'validate':
|
|
200
|
-
output(validateExpertisePack(versionDir, intentId));
|
|
201
|
-
break;
|
|
202
|
-
default:
|
|
203
|
-
die(`未知 expertise 子命令: ${sub}\n用法: loom expertise [init|get|validate] <intent-id>`);
|
|
204
|
-
}
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
case 'intent': {
|
|
209
|
-
const versionDir = findVersionDir();
|
|
210
|
-
switch (sub) {
|
|
211
|
-
case 'add': {
|
|
212
|
-
const titleIdx = argv.indexOf('--title');
|
|
213
|
-
const dependsIdx = argv.indexOf('--depends-on');
|
|
214
|
-
const title = titleIdx !== -1 ? argv[titleIdx + 1] : null;
|
|
215
|
-
const dependencies = dependsIdx !== -1 && argv[dependsIdx + 1]
|
|
216
|
-
? argv[dependsIdx + 1].split(',').map((id) => id.trim()).filter(Boolean)
|
|
217
|
-
: [];
|
|
218
|
-
if (!title) die('用法: loom intent add --title <text> [--depends-on INT-001,INT-002]');
|
|
219
|
-
output(addIntentDraft(versionDir, title, dependencies));
|
|
220
|
-
break;
|
|
221
|
-
}
|
|
222
|
-
case 'revise': {
|
|
223
|
-
const id = rest[0];
|
|
224
|
-
const reasonIdx = argv.indexOf('--reason');
|
|
225
|
-
const reason = reasonIdx !== -1 ? argv[reasonIdx + 1] : null;
|
|
226
|
-
if (!id || !reason) die('用法: loom intent revise <id> --reason <text>');
|
|
227
|
-
output(reviseIntentDraft(versionDir, id, reason));
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
case 'draft': {
|
|
231
|
-
const id = rest[0];
|
|
232
|
-
if (!id) die('用法: loom intent draft <id>');
|
|
233
|
-
output(getIntentDraft(versionDir, id));
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
case 'finalize': {
|
|
237
|
-
const id = rest[0];
|
|
238
|
-
if (!id) die('用法: loom intent finalize <id>');
|
|
239
|
-
const parseIds = (name) => {
|
|
240
|
-
const index = argv.indexOf(name);
|
|
241
|
-
if (index === -1) return [];
|
|
242
|
-
const value = argv[index + 1];
|
|
243
|
-
if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
|
|
244
|
-
return value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
245
|
-
};
|
|
246
|
-
output(finalizeIntentDraft(versionDir, id, {
|
|
247
|
-
review: parseIds('--review'),
|
|
248
|
-
unaffected: parseIds('--unaffected'),
|
|
249
|
-
}));
|
|
250
|
-
break;
|
|
251
|
-
}
|
|
252
|
-
case 'deprecate': {
|
|
253
|
-
const id = rest[0];
|
|
254
|
-
const readFlag = (name) => {
|
|
255
|
-
const index = argv.indexOf(name);
|
|
256
|
-
return index === -1 ? null : argv[index + 1];
|
|
257
|
-
};
|
|
258
|
-
const parseIds = (name) => {
|
|
259
|
-
const value = readFlag(name);
|
|
260
|
-
if (value === null) return [];
|
|
261
|
-
if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
|
|
262
|
-
return value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
263
|
-
};
|
|
264
|
-
const reason = readFlag('--reason');
|
|
265
|
-
if (!id || !reason || reason.startsWith('--')) die('用法: loom intent deprecate <id> --reason <text> [--confirm [--replacement <id>] [--review <ids>] [--unaffected <ids>]]');
|
|
266
|
-
const replacement = readFlag('--replacement');
|
|
267
|
-
if (argv.includes('--replacement') && (!replacement || replacement.startsWith('--'))) die('--replacement 需要当前版本的 Intent ID');
|
|
268
|
-
output(deprecateIntent(versionDir, id, {
|
|
269
|
-
reason,
|
|
270
|
-
confirm: argv.includes('--confirm'),
|
|
271
|
-
replacement,
|
|
272
|
-
review: parseIds('--review'),
|
|
273
|
-
unaffected: parseIds('--unaffected'),
|
|
274
|
-
}));
|
|
275
|
-
break;
|
|
276
|
-
}
|
|
277
|
-
case 'next':
|
|
278
|
-
{
|
|
279
|
-
const next = getNextIntent(versionDir);
|
|
280
|
-
output(next ? {
|
|
281
|
-
...next,
|
|
282
|
-
suggested_flow: {
|
|
283
|
-
start: `loom intent update ${next.id} --status in_progress`,
|
|
284
|
-
forge_context: `loom activate forge --intent ${next.id}`,
|
|
285
|
-
note: '先显式开始 Intent,再由 activate 命令装配该 Intent 的叙事、契约、图谱路由与专业输入。',
|
|
286
|
-
},
|
|
287
|
-
} : '没有可执行的 Intent');
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
case 'status': {
|
|
291
|
-
const s = getStatus(versionDir);
|
|
292
|
-
const fmt = (ids) => ids.map((id) => s.titles[id] ? `${id}(${s.titles[id]})` : id).join(', ') || '-';
|
|
293
|
-
console.log(`进度: ${s.counts.completed}/${s.counts.total} 完成`);
|
|
294
|
-
console.log(` pending: ${s.counts.pending} ${fmt(s.ids.pending)}`);
|
|
295
|
-
console.log(` in_progress: ${s.counts.in_progress} ${fmt(s.ids.in_progress)}`);
|
|
296
|
-
console.log(` completed: ${s.counts.completed} ${fmt(s.ids.completed)}`);
|
|
297
|
-
console.log(` blocked: ${s.counts.blocked} ${fmt(s.ids.blocked)}`);
|
|
298
|
-
console.log(` needs_review: ${s.counts.needs_review} ${fmt(s.ids.needs_review)}`);
|
|
299
|
-
console.log(` deprecated: ${s.counts.deprecated} ${fmt(s.deprecated)}`);
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
case 'graph':
|
|
303
|
-
output(getDependencyGraph(versionDir));
|
|
304
|
-
break;
|
|
305
|
-
case 'get': {
|
|
306
|
-
const id = rest[0];
|
|
307
|
-
if (!id) die('用法: loom intent get <id>');
|
|
308
|
-
const resolved = resolveIntentRef(versionDir, id);
|
|
309
|
-
output(getIntent(resolved.versionDir, resolved.intentId));
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
case 'narrative': {
|
|
313
|
-
const id = rest[0];
|
|
314
|
-
if (!id) die('用法: loom intent narrative <id>');
|
|
315
|
-
const resolved = resolveIntentRef(versionDir, id);
|
|
316
|
-
output(getNarrative(resolved.versionDir, resolved.intentId));
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case 'diff': {
|
|
320
|
-
const from = rest[0];
|
|
321
|
-
const to = rest[1];
|
|
322
|
-
if (!from || !to) die('用法: loom intent diff <v1> <v2>');
|
|
323
|
-
output(diffIntentVersions(findLoomRoot(), from, to));
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
326
|
-
case 'validate':
|
|
327
|
-
loadIntentMap(versionDir);
|
|
328
|
-
console.log('Intent Map 校验通过');
|
|
329
|
-
break;
|
|
330
|
-
case 'trace': {
|
|
331
|
-
const id = rest[0];
|
|
332
|
-
if (!id) die('用法: loom intent trace <id>');
|
|
333
|
-
const resolved = resolveIntentRef(versionDir, id);
|
|
334
|
-
output(traceIntent(resolved.versionDir, getVerificationsDir(resolved.versionDir), getPhilosophyDir(resolved.versionDir), resolved.intentId));
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
case 'reverse-dep': {
|
|
338
|
-
const id = rest[0];
|
|
339
|
-
if (!id) die('用法: loom intent reverse-dep <id>');
|
|
340
|
-
output(reverseDep(versionDir, id));
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
case 'reverse-ref': {
|
|
344
|
-
const anchor = rest[0];
|
|
345
|
-
if (!anchor) die('用法: loom intent reverse-ref <anchor>\n例: loom intent reverse-ref PRODUCT_PHILOSOPHY.md#core-belief');
|
|
346
|
-
output(reverseRef(versionDir, anchor));
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
case 'update': {
|
|
350
|
-
const id = rest[0];
|
|
351
|
-
const statusFlagIdx = argv.indexOf('--status');
|
|
352
|
-
const newStatus = statusFlagIdx !== -1 ? argv[statusFlagIdx + 1] : null;
|
|
353
|
-
if (!id || !newStatus) die('用法: loom intent update <id> --status <pending|in_progress|completed|blocked|needs_review>');
|
|
354
|
-
updateIntentStatus(versionDir, id, newStatus);
|
|
355
|
-
console.log(`${id} status 已更新为 ${newStatus}`);
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
case 'done': {
|
|
359
|
-
// loom intent done <id> — 自动走 pending→in_progress→completed,检查验证记录
|
|
360
|
-
const id = rest[0];
|
|
361
|
-
if (!id) die('用法: loom intent done <id>');
|
|
362
|
-
const verificationsDir = getVerificationsDir(versionDir);
|
|
363
|
-
// 检查有没有验证记录
|
|
364
|
-
const history = getVerificationHistory(verificationsDir, id);
|
|
365
|
-
if (!history || history.records.length === 0) {
|
|
366
|
-
die(`${id} 没有验证记录。先跑: loom verify pass ${id} --summary "..."`);
|
|
367
|
-
}
|
|
368
|
-
// 最新记录必须针对当前 revision 且通过,旧 revision 的通过不能闭合 Intent。
|
|
369
|
-
const latest = history.records[history.records.length - 1];
|
|
370
|
-
if (latest.verdict !== 'passed') {
|
|
371
|
-
die(`${id} 最新验证记录是 ${latest.verdict}(非 passed)。只有 passed 的 Intent 才能 done。`);
|
|
372
|
-
}
|
|
373
|
-
// 获取当前状态,自动走两步
|
|
374
|
-
const intent = getIntent(versionDir, id);
|
|
375
|
-
if (!isVerificationCurrent(intent, latest)) {
|
|
376
|
-
die(`${id} 最新 passed 验证不属于当前 Intent revision ${intent.revision ?? 1}。先重新验证。`);
|
|
377
|
-
}
|
|
378
|
-
const expertise = assertExpertiseReady(versionDir, id);
|
|
379
|
-
if (expertise && (latest.expertise?.record_ref !== `10_EXPERTISE_PACKS/${id}.json`
|
|
380
|
-
|| latest.expertise?.intent_revision !== expertise.intent_revision
|
|
381
|
-
|| latest.expertise?.source_count !== expertise.source_count
|
|
382
|
-
|| latest.expertise?.capsule_count !== expertise.capsule_count
|
|
383
|
-
|| latest.expertise?.pack_digest !== expertise.pack_digest)) {
|
|
384
|
-
die(`${id} 最新 passed 未绑定当前 Expertise Pack。请在新的 Keeper task 中重新打开来源并验证。`);
|
|
385
|
-
}
|
|
386
|
-
if (intent.quality_strategy === 'atelier') {
|
|
387
|
-
const atelier = validateAtelierRecord(versionDir, id);
|
|
388
|
-
if (!['selected', 'baseline_retained'].includes(atelier.status)) {
|
|
389
|
-
die(`${id} 的 Atelier Record 尚未完成选择(当前: ${atelier.status})`);
|
|
390
|
-
}
|
|
391
|
-
if (latest.atelier?.record_ref !== `09_ATELIER/${id}.json`
|
|
392
|
-
|| latest.atelier?.stance_revision !== atelier.stance_revision
|
|
393
|
-
|| latest.atelier?.status !== atelier.status) {
|
|
394
|
-
die(`${id} 最新 passed 未绑定当前 Atelier Record 与 stance_revision。请在新的 Keeper task 中重新验证。`);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
const currentStatus = intent.status;
|
|
398
|
-
if (currentStatus === 'completed') {
|
|
399
|
-
console.log(`${id} 已经是 completed,无需操作`);
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
if (currentStatus === 'pending') {
|
|
403
|
-
updateIntentStatus(versionDir, id, 'in_progress');
|
|
404
|
-
}
|
|
405
|
-
updateIntentStatus(versionDir, id, 'completed');
|
|
406
|
-
console.log(`${id} 已完成(${currentStatus} → completed)`);
|
|
407
|
-
break;
|
|
408
|
-
}
|
|
409
|
-
default:
|
|
410
|
-
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]`);
|
|
411
|
-
}
|
|
105
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
106
|
+
const pkg = JSON.parse(readFileSync(resolve(here, '..', '..', 'package.json'), 'utf8'));
|
|
107
|
+
output(`loom ${pkg.version}`);
|
|
412
108
|
break;
|
|
413
109
|
}
|
|
414
|
-
|
|
415
|
-
case '
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
const result = initProject(cwd());
|
|
421
|
-
console.log('LOOM 项目已初始化');
|
|
422
|
-
for (const c of result.created) console.log(` [created] ${c}`);
|
|
423
|
-
for (const s of result.skipped) console.log(` [skipped] ${s} (already exists)`);
|
|
424
|
-
if (result.created.length === 0 && result.skipped.length > 0) {
|
|
425
|
-
console.log(' 所有文件已存在,无需操作。');
|
|
426
|
-
}
|
|
427
|
-
console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
428
|
-
console.log('To Agent: 运行 loom guide 诊断当前阶段,按引导执行');
|
|
429
|
-
console.log('To Human: 把以下指令给你的 AI agent:');
|
|
430
|
-
console.log(' "项目已用 LOOM 初始化。请运行 loom guide 看下一步,');
|
|
431
|
-
console.log(' 然后激活 Weaver 角色织造产品哲学。"');
|
|
432
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
110
|
+
case '--help':
|
|
111
|
+
case '-h':
|
|
112
|
+
case undefined:
|
|
113
|
+
output(help());
|
|
433
114
|
break;
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
case 'activate': {
|
|
437
|
-
const role = sub;
|
|
438
|
-
if (!role) die('用法: loom activate <role>\n角色: weaver | visionary | architect | impact-reviewer | forge | keeper');
|
|
439
|
-
// weaver 不需要 versionDir(项目还没初始化时也能激活)
|
|
440
|
-
let versionDir = null;
|
|
441
|
-
if (role !== 'weaver') {
|
|
442
|
-
try {
|
|
443
|
-
versionDir = findVersionDir();
|
|
444
|
-
} catch (e) {
|
|
445
|
-
// 只吞"找不到 .loom 目录"——其他错误(权限、磁盘)向上抛
|
|
446
|
-
if (!String(e.message).includes('找不到 .loom')) throw e;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
const intentIdx = argv.indexOf('--intent');
|
|
450
|
-
const intentId = intentIdx !== -1 ? argv[intentIdx + 1] : null;
|
|
451
|
-
if (intentIdx !== -1 && !intentId) die('用法: loom activate <role> --intent <id>');
|
|
452
|
-
const prompt = activateRole(role, versionDir, intentId);
|
|
453
|
-
output(prompt);
|
|
115
|
+
case 'init':
|
|
116
|
+
output(initProject());
|
|
454
117
|
break;
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
const versionDir = findVersionDir();
|
|
459
|
-
switch (sub) {
|
|
460
|
-
case 'get': {
|
|
461
|
-
const anchor = rest[0];
|
|
462
|
-
if (!anchor) die('用法: loom philosophy get <anchor>\n例: loom philosophy get PRODUCT_PHILOSOPHY.md#core-belief');
|
|
463
|
-
output(getPhilosophy(getPhilosophyDir(versionDir), anchor));
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
case 'list':
|
|
467
|
-
output(listPhilosophyFiles(getPhilosophyDir(versionDir)));
|
|
468
|
-
break;
|
|
469
|
-
case 'impact': {
|
|
470
|
-
const anchor = rest[0];
|
|
471
|
-
if (!anchor) die('用法: loom philosophy impact <anchor>');
|
|
472
|
-
output(assessPhilosophyImpact(versionDir, anchor));
|
|
473
|
-
break;
|
|
474
|
-
}
|
|
475
|
-
case 'revise': {
|
|
476
|
-
const anchor = rest[0];
|
|
477
|
-
const readFlag = (name) => {
|
|
478
|
-
const index = argv.indexOf(name);
|
|
479
|
-
return index === -1 ? null : argv[index + 1];
|
|
480
|
-
};
|
|
481
|
-
const parseIds = (name) => {
|
|
482
|
-
const value = readFlag(name);
|
|
483
|
-
if (value === null) return [];
|
|
484
|
-
if (!value || value.startsWith('--')) die(`${name} 需要逗号分隔的 Intent ID`);
|
|
485
|
-
return value.split(',').map((item) => item.trim()).filter(Boolean);
|
|
486
|
-
};
|
|
487
|
-
const classification = readFlag('--classification');
|
|
488
|
-
const reason = readFlag('--reason');
|
|
489
|
-
if (!anchor || !classification || classification.startsWith('--') || !reason || reason.startsWith('--')) {
|
|
490
|
-
die('用法: loom philosophy revise <anchor> --classification <clarification|minor|major> --reason <text> [--confirm --review <ids> --unaffected <ids>]');
|
|
491
|
-
}
|
|
492
|
-
output(revisePhilosophy(versionDir, anchor, {
|
|
493
|
-
classification,
|
|
494
|
-
reason,
|
|
495
|
-
confirm: argv.includes('--confirm'),
|
|
496
|
-
review: parseIds('--review'),
|
|
497
|
-
unaffected: parseIds('--unaffected'),
|
|
498
|
-
}));
|
|
499
|
-
break;
|
|
500
|
-
}
|
|
501
|
-
case 'check': {
|
|
502
|
-
const philDir = getPhilosophyDir(versionDir);
|
|
503
|
-
const inspiration = validateInspirationSources(philDir);
|
|
504
|
-
const allIssues = inspiration.issues;
|
|
505
|
-
const allPassed = inspiration.passed;
|
|
506
|
-
|
|
507
|
-
// --json: 结构化输出,供 Agent 程序化消费
|
|
508
|
-
if (argv.includes('--json')) {
|
|
509
|
-
output({
|
|
510
|
-
passed: allPassed,
|
|
511
|
-
issues: allIssues,
|
|
512
|
-
sources: inspiration.sources.map(({ file, sources }) => ({ file, count: sources.length })),
|
|
513
|
-
});
|
|
514
|
-
exit(allPassed ? 0 : 1);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (allPassed) {
|
|
518
|
-
console.log('✓ 哲学文档校验通过');
|
|
519
|
-
console.log(' 灵感来源:');
|
|
520
|
-
for (const { file, sources } of inspiration.sources) {
|
|
521
|
-
console.log(` ${file}: ${sources.length} 个源`);
|
|
522
|
-
}
|
|
523
|
-
} else {
|
|
524
|
-
const high = allIssues.filter((i) => i.severity === 'high').length;
|
|
525
|
-
const medium = allIssues.filter((i) => i.severity === 'medium').length;
|
|
526
|
-
console.log(`✗ 哲学文档校验未通过(${allIssues.length} 个问题: ${high} high, ${medium} medium)`);
|
|
527
|
-
for (const issue of allIssues) {
|
|
528
|
-
const icon = issue.severity === 'high' ? '⚠' : '·';
|
|
529
|
-
console.log(` ${icon} [${issue.severity}] ${issue.msg}`);
|
|
530
|
-
}
|
|
531
|
-
console.log('\n下一步: 运行 loom activate weaver;命令会重新输出哲学阶段所需的 Context Pack。');
|
|
532
|
-
exit(1);
|
|
533
|
-
}
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
default:
|
|
537
|
-
die(`未知子命令: philosophy ${sub}\n用法: loom philosophy [get <anchor>|list|check|impact <anchor>|revise <anchor> --classification <clarification|minor|major> --reason <text>]`);
|
|
538
|
-
}
|
|
118
|
+
case 'context':
|
|
119
|
+
case 'resume':
|
|
120
|
+
output(compileContext({ taskId: option('--task'), keeper: argv.includes('--keeper') }));
|
|
539
121
|
break;
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
case 'verify': {
|
|
543
|
-
const versionDir = findVersionDir();
|
|
544
|
-
const verificationsDir = getVerificationsDir(versionDir);
|
|
545
|
-
switch (sub) {
|
|
546
|
-
case 'contract': {
|
|
547
|
-
const id = rest[0];
|
|
548
|
-
if (!id) die('用法: loom verify contract <id>');
|
|
549
|
-
output(getVerificationContract(versionDir, id));
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
case 'history': {
|
|
553
|
-
const id = rest[0];
|
|
554
|
-
if (!id) die('用法: loom verify history <id>');
|
|
555
|
-
if (argv.includes('--across-versions')) {
|
|
556
|
-
output(getAcrossVersionVerificationHistory(versionDir, id));
|
|
557
|
-
} else {
|
|
558
|
-
const resolved = resolveIntentRef(versionDir, id);
|
|
559
|
-
const history = getVerificationHistory(getVerificationsDir(resolved.versionDir), resolved.intentId);
|
|
560
|
-
output(history ?? `没有 ${resolved.ref} 的验证记录`);
|
|
561
|
-
}
|
|
562
|
-
break;
|
|
563
|
-
}
|
|
564
|
-
case 'pending':
|
|
565
|
-
output(getPendingVerifications(versionDir, verificationsDir));
|
|
566
|
-
break;
|
|
567
|
-
case 'list':
|
|
568
|
-
output(listVerifications(verificationsDir));
|
|
569
|
-
break;
|
|
570
|
-
case 'write': {
|
|
571
|
-
// 支持两种输入方式:--json-file <path>(推荐)或 --json <string>
|
|
572
|
-
// verdict 合法值: passed | deviated | blocked | pending_human
|
|
573
|
-
const fileFlagIdx = argv.indexOf('--json-file');
|
|
574
|
-
const jsonFlagIdx = argv.indexOf('--json');
|
|
575
|
-
let record;
|
|
576
|
-
if (fileFlagIdx !== -1 && argv[fileFlagIdx + 1]) {
|
|
577
|
-
try {
|
|
578
|
-
record = JSON.parse(readFileSync(argv[fileFlagIdx + 1], 'utf-8'));
|
|
579
|
-
} catch (e) {
|
|
580
|
-
die(`JSON 文件解析失败: ${argv[fileFlagIdx + 1]}\n原因: ${e.message}`);
|
|
581
|
-
}
|
|
582
|
-
} else if (jsonFlagIdx !== -1 && argv[jsonFlagIdx + 1]) {
|
|
583
|
-
try {
|
|
584
|
-
record = JSON.parse(argv[jsonFlagIdx + 1]);
|
|
585
|
-
} catch (e) {
|
|
586
|
-
die(`JSON 字符串解析失败: ${e.message}`);
|
|
587
|
-
}
|
|
588
|
-
} else {
|
|
589
|
-
die('用法: loom verify write --json-file <path> | --json <json-string>');
|
|
590
|
-
}
|
|
591
|
-
const result = writeVerification(versionDir, verificationsDir, record);
|
|
592
|
-
console.log(`验证记录已写入: ${result.filePath}`);
|
|
593
|
-
console.log(` 轮次: ${result.round}, verdict: ${record.verdict}`);
|
|
594
|
-
if (record.verdict === 'deviated') {
|
|
595
|
-
console.log(` deviated 累计: ${result.deviated_count} 轮`);
|
|
596
|
-
if (result.should_escalate) {
|
|
597
|
-
updateIntentStatus(versionDir, record.intent_id, 'blocked');
|
|
598
|
-
console.log(` ⚠ 已达到 3 轮上限,${record.intent_id} 已自动升级为 blocked`);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
break;
|
|
602
|
-
}
|
|
603
|
-
case 'pass':
|
|
604
|
-
case 'fail': {
|
|
605
|
-
// loom verify pass <id> --summary "..." --verified-by <id> --verification-context <independent_thread|human_review>
|
|
606
|
-
// loom verify fail <id> --summary "..." [--deviation "..."] [--reproduction-command "..."]
|
|
607
|
-
const id = rest[0];
|
|
608
|
-
if (!id) die(`用法: loom verify ${sub} <id> --summary "..." [--reproduction-command "..."]${sub === 'pass' ? ' [--preservation-evidence "..."] [--quality-proof "..."]' : ' [--deviation "..."]'}`);
|
|
609
|
-
const summaryIdx = argv.indexOf('--summary');
|
|
610
|
-
const reproIdx = argv.indexOf('--reproduction-command');
|
|
611
|
-
const deviationIdx = argv.indexOf('--deviation');
|
|
612
|
-
const qualityProofIdx = argv.indexOf('--quality-proof');
|
|
613
|
-
const preservationIdx = argv.indexOf('--preservation-evidence');
|
|
614
|
-
const verifiedByIdx = argv.indexOf('--verified-by');
|
|
615
|
-
const verificationContextIdx = argv.indexOf('--verification-context');
|
|
616
|
-
const summary = summaryIdx !== -1 ? argv[summaryIdx + 1] : null;
|
|
617
|
-
if (!summary) die(`缺少 --summary: loom verify ${sub} ${id} --summary "..."`);
|
|
618
|
-
const intent = getIntent(versionDir, id);
|
|
619
|
-
if (sub === 'pass' && intent.continuity_required && !(preservationIdx !== -1 && argv[preservationIdx + 1])) {
|
|
620
|
-
die(`Intent ${id} 声明了 continuity_required;通过前必须提供 --preservation-evidence,证明旧状态 → 新操作后的完整序列未发生未授权丢失。`);
|
|
621
|
-
}
|
|
622
|
-
if (sub === 'pass' && intent.quality_contract && !(qualityProofIdx !== -1 && argv[qualityProofIdx + 1])) {
|
|
623
|
-
die(`Intent ${id} 声明了 quality_contract;通过前必须提供 --quality-proof,指向项目内真实的 Quality Proof Markdown 锚点。`);
|
|
624
|
-
}
|
|
625
|
-
if (sub === 'pass' && !(verifiedByIdx !== -1 && argv[verifiedByIdx + 1] && verificationContextIdx !== -1 && argv[verificationContextIdx + 1])) {
|
|
626
|
-
die(`Intent ${id} 通过前必须声明独立验证来源:--verified-by <thread/run/人类标识> --verification-context <independent_thread|human_review>。同一会话自检请记录为自检,不得写 passed。`);
|
|
627
|
-
}
|
|
628
|
-
const extras = {};
|
|
629
|
-
if (reproIdx !== -1 && argv[reproIdx + 1]) extras.reproduction_command = argv[reproIdx + 1];
|
|
630
|
-
if (sub === 'fail' && deviationIdx !== -1 && argv[deviationIdx + 1]) extras.deviation_detail = argv[deviationIdx + 1];
|
|
631
|
-
if (sub === 'pass' && qualityProofIdx !== -1 && argv[qualityProofIdx + 1]) extras.quality_proof_ref = argv[qualityProofIdx + 1];
|
|
632
|
-
if (sub === 'pass' && preservationIdx !== -1 && argv[preservationIdx + 1]) extras.preservation_evidence = argv[preservationIdx + 1];
|
|
633
|
-
if (sub === 'pass') extras.verification_provenance = { verified_by: argv[verifiedByIdx + 1], context: argv[verificationContextIdx + 1] };
|
|
634
|
-
const verdict = sub === 'pass' ? 'passed' : 'deviated';
|
|
635
|
-
const result = createQuickVerification(versionDir, verificationsDir, id, verdict, summary, extras);
|
|
636
|
-
console.log(`验证记录已写入: ${result.filePath}`);
|
|
637
|
-
console.log(` 轮次: ${result.round}, verdict: ${verdict}`);
|
|
638
|
-
if (verdict === 'deviated') {
|
|
639
|
-
console.log(` deviated 累计: ${result.deviated_count} 轮`);
|
|
640
|
-
if (result.should_escalate) {
|
|
641
|
-
updateIntentStatus(versionDir, id, 'blocked');
|
|
642
|
-
console.log(` ⚠ 已达到 3 轮上限,${id} 已自动升级为 blocked`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
break;
|
|
646
|
-
}
|
|
647
|
-
default:
|
|
648
|
-
die(`未知子命令: verify ${sub}\n用法: loom verify [contract <id>|history <id>|pending|list|write --json-file <path>|--json <string>|pass <id> --summary "..."|fail <id> --summary "..."]`);
|
|
649
|
-
}
|
|
122
|
+
case 'prompts':
|
|
123
|
+
output(promptCatalog());
|
|
650
124
|
break;
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
for (const v of versions) {
|
|
659
|
-
const mark = v === current ? ' *' : ' ';
|
|
660
|
-
console.log(`${mark}${v}`);
|
|
661
|
-
}
|
|
662
|
-
if (current) console.log(`\n当前版本: ${current}`);
|
|
663
|
-
break;
|
|
664
|
-
}
|
|
665
|
-
case 'current': {
|
|
666
|
-
const current = readCurrentPointer(loomRoot);
|
|
667
|
-
output(current ?? '没有版本目录');
|
|
668
|
-
break;
|
|
669
|
-
}
|
|
670
|
-
case 'new': {
|
|
671
|
-
const result = newVersion(cwd());
|
|
672
|
-
console.log(`已创建新版本: ${result.version}`);
|
|
673
|
-
console.log(` 创建: ${result.created.length} 项`);
|
|
674
|
-
for (const c of result.created) console.log(` + ${c}`);
|
|
675
|
-
if (result.skipped.length) {
|
|
676
|
-
console.log(` 跳过(已存在): ${result.skipped.length} 项`);
|
|
677
|
-
for (const s of result.skipped) console.log(` - ${s}`);
|
|
678
|
-
}
|
|
679
|
-
console.log(`\n当前版本已切换为 ${result.version}`);
|
|
680
|
-
console.log('下一步: loom activate weaver(参考上一版本哲学织造新哲学)');
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
case 'use': {
|
|
684
|
-
const v = rest[0];
|
|
685
|
-
if (!v) die('用法: loom version use <v1|v2|...>');
|
|
686
|
-
const switched = useVersion(loomRoot, v);
|
|
687
|
-
console.log(`当前版本已切换为 ${switched}`);
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
case 'diff': {
|
|
691
|
-
const v1 = rest[0];
|
|
692
|
-
const v2 = rest[1];
|
|
693
|
-
if (!v1 || !v2) die('用法: loom version diff <v1> <v2>');
|
|
694
|
-
output(diffVersions(loomRoot, v1, v2));
|
|
695
|
-
break;
|
|
696
|
-
}
|
|
697
|
-
default:
|
|
698
|
-
die(`未知子命令: version ${sub}\n用法: loom version [list|current|new|use <v>|diff <v1> <v2>]`);
|
|
699
|
-
}
|
|
700
|
-
break;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
case 'patch': {
|
|
704
|
-
const versionDir = findVersionDir();
|
|
705
|
-
switch (sub) {
|
|
706
|
-
case 'record': {
|
|
707
|
-
const fileFlagIdx = argv.indexOf('--json-file');
|
|
708
|
-
const inputPath = fileFlagIdx !== -1 ? argv[fileFlagIdx + 1] : null;
|
|
709
|
-
if (!inputPath) die('用法: loom patch record --json-file <path>');
|
|
710
|
-
let record;
|
|
711
|
-
try {
|
|
712
|
-
record = JSON.parse(readFileSync(inputPath, 'utf-8'));
|
|
713
|
-
} catch (e) {
|
|
714
|
-
die(`JSON 文件解析失败: ${inputPath}\n原因: ${e.message}`);
|
|
715
|
-
}
|
|
716
|
-
output(recordPatch(versionDir, record));
|
|
717
|
-
break;
|
|
718
|
-
}
|
|
719
|
-
case 'list':
|
|
720
|
-
output(listPatches(versionDir));
|
|
721
|
-
break;
|
|
722
|
-
case 'get': {
|
|
723
|
-
const id = rest[0];
|
|
724
|
-
if (!id) die('用法: loom patch get <id>');
|
|
725
|
-
output(getPatch(versionDir, id));
|
|
726
|
-
break;
|
|
727
|
-
}
|
|
728
|
-
case 'validate':
|
|
729
|
-
output(validatePatches(versionDir));
|
|
730
|
-
break;
|
|
731
|
-
default:
|
|
732
|
-
die(`未知子命令: patch ${sub}\n用法: loom patch [record --json-file <path>|list|get <id>|validate]`);
|
|
733
|
-
}
|
|
734
|
-
break;
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
case 'doctor': {
|
|
738
|
-
const versionDir = findVersionDir();
|
|
739
|
-
const { issues, summary } = doctor(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir));
|
|
740
|
-
if (summary.healthy) {
|
|
741
|
-
console.log('✓ 项目健康,未发现问题');
|
|
742
|
-
} else {
|
|
743
|
-
console.log(`发现 ${summary.total_issues} 个问题(fatal: ${summary.fatal}, high: ${summary.high}, medium: ${summary.medium})`);
|
|
744
|
-
for (const issue of issues) {
|
|
745
|
-
const icon = issue.severity === 'fatal' ? '☠' : issue.severity === 'high' ? '⚠' : '·';
|
|
746
|
-
console.log(` ${icon} [${issue.severity}] ${issue.type}: ${issue.msg}`);
|
|
747
|
-
if (issue.fix_hint) {
|
|
748
|
-
console.log(` → 修复: ${issue.fix_hint}`);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
console.log('\n下一步: 运行 loom guide;它会根据当前状态给出可执行动作与所需 Context Pack。');
|
|
752
|
-
}
|
|
125
|
+
case 'record':
|
|
126
|
+
output(recordUnderstanding(jsonFile()));
|
|
127
|
+
break;
|
|
128
|
+
case 'check': {
|
|
129
|
+
const result = checkProject();
|
|
130
|
+
output(result);
|
|
131
|
+
if (!result.healthy) process.exitCode = 1;
|
|
753
132
|
break;
|
|
754
133
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
134
|
+
case 'project':
|
|
135
|
+
if (subcommand !== 'ready') throw new Error('Usage: loom project ready');
|
|
136
|
+
output(markReady());
|
|
137
|
+
break;
|
|
138
|
+
case 'design': {
|
|
139
|
+
if (subcommand === 'add') {
|
|
140
|
+
const slug = rest[0];
|
|
141
|
+
const title = option('--title');
|
|
142
|
+
const kind = option('--kind');
|
|
143
|
+
if (!slug) throw new Error('Usage: loom design add <slug> --title <text> --kind <kind>');
|
|
144
|
+
output(createDesign(slug, { title, kind }));
|
|
145
|
+
} else if (subcommand === 'list') output(listDesigns());
|
|
146
|
+
else if (subcommand === 'get') {
|
|
147
|
+
if (!rest[0]) throw new Error('Usage: loom design get <slug>');
|
|
148
|
+
output(getDesign(rest[0]));
|
|
149
|
+
} else throw new Error('Usage: loom design add|list|get');
|
|
759
150
|
break;
|
|
760
151
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
} else {
|
|
773
|
-
const content = getHelpTopic(topic);
|
|
774
|
-
if (!content) {
|
|
775
|
-
die(`未知 topic: ${topic}\n可用 topics: ${listHelpTopics().join(', ')}`);
|
|
776
|
-
}
|
|
777
|
-
console.log(content);
|
|
778
|
-
}
|
|
152
|
+
case 'capability': {
|
|
153
|
+
if (subcommand === 'add') {
|
|
154
|
+
const slug = rest[0];
|
|
155
|
+
const title = option('--title');
|
|
156
|
+
if (!slug) throw new Error('Usage: loom capability add <slug> --title <text>');
|
|
157
|
+
output(createCapability(slug, { title }));
|
|
158
|
+
} else if (subcommand === 'list') output(listCapabilities());
|
|
159
|
+
else if (subcommand === 'get') {
|
|
160
|
+
if (!rest[0]) throw new Error('Usage: loom capability get <slug>');
|
|
161
|
+
output(getCapability(rest[0]));
|
|
162
|
+
} else throw new Error('Usage: loom capability add|list|get');
|
|
779
163
|
break;
|
|
780
164
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
output(
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
if (
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
'
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
console.log('\n上下文: 直接运行这条命令;它会输出当前阶段所需的 Context Pack。');
|
|
805
|
-
}
|
|
806
|
-
if (result.verify_command) {
|
|
807
|
-
console.log(`\n完成后校验: ${result.verify_command}`);
|
|
808
|
-
}
|
|
809
|
-
if (result.auto_mode === 'manual' && result.stage_num > 0 && result.stage_num < 6) {
|
|
810
|
-
console.log(`\n提示: 开启 AUTO 模式可自动连续执行 — loom auto on`);
|
|
811
|
-
}
|
|
165
|
+
case 'task': {
|
|
166
|
+
if (subcommand === 'plan') output(importTasks(jsonFile()));
|
|
167
|
+
else if (subcommand === 'status') output(taskSummary(loadProject().taskStore.tasks));
|
|
168
|
+
else if (subcommand === 'next') output(getTask());
|
|
169
|
+
else if (subcommand === 'get') {
|
|
170
|
+
if (!rest[0]) throw new Error('Usage: loom task get <id>');
|
|
171
|
+
output(getTask(rest[0]));
|
|
172
|
+
} else if (subcommand === 'start') {
|
|
173
|
+
if (!rest[0]) throw new Error('Usage: loom task start <id>');
|
|
174
|
+
output(startTask(rest[0]));
|
|
175
|
+
} else if (subcommand === 'update') {
|
|
176
|
+
if (!rest[0]) throw new Error('Usage: loom task update <id> --json-file <patch.json>');
|
|
177
|
+
output(updateTask(rest[0], jsonFile()));
|
|
178
|
+
} else if (subcommand === 'block') {
|
|
179
|
+
if (!rest[0]) throw new Error('Usage: loom task block <id> --json-file <block.json>');
|
|
180
|
+
output(blockTask(rest[0], jsonFile()));
|
|
181
|
+
} else if (subcommand === 'reopen') {
|
|
182
|
+
if (!rest[0]) throw new Error('Usage: loom task reopen <id>');
|
|
183
|
+
output(reopenTask(rest[0], { reason: option('--reason') }));
|
|
184
|
+
} else if (subcommand === 'done') {
|
|
185
|
+
if (!rest[0]) throw new Error('Usage: loom task done <id> --json-file <evidence.json>');
|
|
186
|
+
output(completeTask(rest[0], jsonFile()));
|
|
187
|
+
} else throw new Error('Usage: loom task plan|status|next|get|start|update|block|reopen|done');
|
|
812
188
|
break;
|
|
813
189
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
// loom auto on → auto-loop(默认)
|
|
820
|
-
// loom auto on --design → auto-design
|
|
821
|
-
const wantDesign = argv.includes('--design');
|
|
822
|
-
const mode = wantDesign ? 'auto-design' : 'auto-loop';
|
|
823
|
-
autoOn(loomRoot, mode);
|
|
824
|
-
if (mode === 'auto-design') {
|
|
825
|
-
console.log('AUTO 模式: auto-design(全部自动,含哲学/愿景/架构)');
|
|
826
|
-
console.log('Agent 自动连续执行所有阶段,不等人类确认。');
|
|
827
|
-
} else {
|
|
828
|
-
console.log('AUTO 模式: auto-loop(Intent Loop 自动,设计阶段需 review)');
|
|
829
|
-
console.log(' - stage 1-3(哲学/愿景/架构):自动执行但需人类 review');
|
|
830
|
-
console.log(' - stage 4+(Intent Loop):自动连续执行');
|
|
831
|
-
}
|
|
832
|
-
console.log('核心契约: 持续运行,除非出意外否则不允许私自停止。');
|
|
833
|
-
console.log(' - L3 human_review 由 Keeper 自主判定,不停下等人类');
|
|
834
|
-
console.log(' - 唯一允许停下的情况: blocked(依赖阻塞/契约无法判定/连续 3 轮 deviated 升级)');
|
|
835
|
-
console.log('切换: loom auto on --design | loom auto off');
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
case 'off':
|
|
839
|
-
autoOff(loomRoot);
|
|
840
|
-
console.log('AUTO 模式: manual(每步需人类确认)');
|
|
841
|
-
break;
|
|
842
|
-
case 'status': {
|
|
843
|
-
const status = autoStatus(loomRoot);
|
|
844
|
-
const modeDesc = {
|
|
845
|
-
'manual': 'manual(每步需人类确认)',
|
|
846
|
-
'auto-loop': 'auto-loop(Intent Loop 自动,设计阶段需 review)',
|
|
847
|
-
'auto-design': 'auto-design(全部自动,含哲学/愿景/架构)',
|
|
848
|
-
};
|
|
849
|
-
console.log(`AUTO 模式: ${modeDesc[status.mode] || status.mode}`);
|
|
850
|
-
if (status.mode === 'auto-loop') {
|
|
851
|
-
console.log(' 规则: stage 1-3 需人类 review,stage 4+ 自动执行');
|
|
852
|
-
} else if (status.mode === 'auto-design') {
|
|
853
|
-
console.log(' 规则: 全部阶段自动执行,不需人类 review');
|
|
854
|
-
}
|
|
855
|
-
if (status.heartbeat) {
|
|
856
|
-
const hb = status.heartbeat;
|
|
857
|
-
console.log(` 心跳: ${hb.timestamp}`);
|
|
858
|
-
console.log(` 阶段: ${hb.stage} (stage ${hb.stage_num})`);
|
|
859
|
-
console.log(` 下一步: ${hb.next_action}`);
|
|
860
|
-
console.log(` 命令: ${hb.next_command}`);
|
|
861
|
-
} else if (status.mode !== 'manual') {
|
|
862
|
-
console.log(' 心跳: 尚未记录(运行 loom guide 后生成)');
|
|
863
|
-
}
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
default:
|
|
867
|
-
die(`未知子命令: auto ${sub}\n用法: loom auto [on [--design]|off|status]`);
|
|
868
|
-
}
|
|
190
|
+
case 'keeper': {
|
|
191
|
+
if (subcommand === 'prompt') output(getKeeperPrompt());
|
|
192
|
+
else if (subcommand === 'record') output(recordKeeper(jsonFile()));
|
|
193
|
+
else if (subcommand === 'skip') output(skipKeeper(option('--reason')));
|
|
194
|
+
else throw new Error('Usage: loom keeper prompt|record|skip');
|
|
869
195
|
break;
|
|
870
196
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
const projectDir = cwd();
|
|
875
|
-
if (argv.includes('--help') || argv.includes('-h')) {
|
|
876
|
-
console.log(`用法:
|
|
877
|
-
loom atlas build 编译当前版本的 Atlas Model
|
|
878
|
-
loom atlas --regen 输出 command-assembled Composer Pack,让 Agent 生成 loom-atlas.html
|
|
879
|
-
loom atlas status 检查 loom-atlas.html 与 Model 是否齐全、新鲜
|
|
880
|
-
loom atlas validate 校验 Atlas 是否为当前版本的必交付物
|
|
881
|
-
loom atlas 打开通过校验的 loom-atlas.html
|
|
882
|
-
loom atlas --help 显示本帮助`);
|
|
883
|
-
break;
|
|
884
|
-
}
|
|
885
|
-
if (sub === 'status') {
|
|
886
|
-
output(getAtlasStatus(projectDir, versionDir));
|
|
887
|
-
break;
|
|
888
|
-
}
|
|
889
|
-
if (sub === 'build') {
|
|
890
|
-
output(writeAtlasModel(versionDir));
|
|
891
|
-
break;
|
|
892
|
-
}
|
|
893
|
-
if (sub === 'validate') {
|
|
894
|
-
const result = validateAtlas(projectDir, versionDir);
|
|
895
|
-
if (!result.valid) die(`Atlas 校验失败:\n - ${result.errors.join('\n - ')}`);
|
|
896
|
-
output(result);
|
|
897
|
-
break;
|
|
898
|
-
}
|
|
899
|
-
if (argv.includes('--regen') || argv.includes('-r')) {
|
|
900
|
-
writeAtlasModel(versionDir);
|
|
901
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
902
|
-
console.log('To Agent: 以下 Composer Pack 已直接装配当前架构与决策资料;生成项目根目录的 loom-atlas.html。');
|
|
903
|
-
console.log('To Human: 把以下内容交给负责视觉实现的 Agent。');
|
|
904
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
905
|
-
console.log(generateAtlasComposerPack(versionDir));
|
|
906
|
-
break;
|
|
907
|
-
}
|
|
908
|
-
const validation = validateAtlas(projectDir, versionDir);
|
|
909
|
-
if (!validation.valid) {
|
|
910
|
-
console.log('Atlas 尚未成为当前版本的合格交付物:');
|
|
911
|
-
validation.errors.forEach((error) => console.log(` - ${error}`));
|
|
912
|
-
console.log('\n下一步: loom atlas --regen');
|
|
913
|
-
break;
|
|
914
|
-
}
|
|
915
|
-
{
|
|
916
|
-
const { spawn } = await import('node:child_process');
|
|
917
|
-
const target = validation.atlas_path.replace(/\\/g, '/');
|
|
918
|
-
if (process.platform === 'win32') {
|
|
919
|
-
spawn('cmd', ['/c', 'start', target], { detached: true, stdio: 'ignore' }).unref();
|
|
920
|
-
} else if (process.platform === 'darwin') {
|
|
921
|
-
spawn('open', [target], { detached: true, stdio: 'ignore' }).unref();
|
|
922
|
-
} else {
|
|
923
|
-
spawn('xdg-open', [target], { detached: true, stdio: 'ignore' }).unref();
|
|
924
|
-
}
|
|
925
|
-
console.log(`已打开决策图谱: ${validation.atlas_path}`);
|
|
926
|
-
}
|
|
927
|
-
break;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
case '--help':
|
|
931
|
-
case '-h':
|
|
932
|
-
case undefined:
|
|
933
|
-
console.log(`loom — LOOM 框架 CLI
|
|
934
|
-
哲学驱动开发:哲学 → 愿景 → 架构 → Intent Loop
|
|
935
|
-
|
|
936
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
937
|
-
To Agent:
|
|
938
|
-
你是 AI agent。本项目使用 LOOM 框架。
|
|
939
|
-
第一步: 运行 loom guide 诊断当前阶段
|
|
940
|
-
第二步: 按引导执行对应命令
|
|
941
|
-
学习: loom help <topic> 获取详细指南
|
|
942
|
-
状态: loom context 获取当前进度
|
|
943
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
944
|
-
To Human:
|
|
945
|
-
请把以下指令给你的 AI agent:
|
|
946
|
-
"请用 LOOM 框架初始化并推进这个项目。
|
|
947
|
-
先运行 loom --help 了解命令,再运行 loom guide 看当前阶段,
|
|
948
|
-
然后按引导一步步执行。"
|
|
949
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
950
|
-
|
|
951
|
-
用法:
|
|
952
|
-
loom init 初始化项目(创建 .loom/v1/ 骨架 + 模板)
|
|
953
|
-
loom guide 诊断当前阶段,输出下一步引导
|
|
954
|
-
loom guide --dry-run 只读诊断当前阶段,不写 heartbeat
|
|
955
|
-
loom auto on|off|status AUTO 编排协议开关(由 Agent/runtime 消费,不由 CLI 自行执行)
|
|
956
|
-
loom activate <role> [--intent <id>] 输出普通或 Intent-scoped 角色激活提示词
|
|
957
|
-
|
|
958
|
-
loom version list 列出所有版本(* 标记当前)
|
|
959
|
-
loom version current 显示当前版本
|
|
960
|
-
loom version new 创建 v{N+1} + 自动切换为当前
|
|
961
|
-
loom version use <v> 切换当前版本
|
|
962
|
-
loom version diff <v1> <v2> 对比两个版本的文件差异
|
|
963
|
-
|
|
964
|
-
loom patch record --json-file <path> 记录 Patch 并生成 Markdown 投影
|
|
965
|
-
loom patch list 列出当前版本的 Patch
|
|
966
|
-
loom patch get <id> 返回指定 Patch
|
|
967
|
-
loom patch validate 校验 Patch ledger 和 Markdown 投影
|
|
968
|
-
|
|
969
|
-
loom capability graph 输出 Capability Graph 的 Mermaid 投影与摘要
|
|
970
|
-
loom capability frontier 列出尚未路由的高影响节点
|
|
971
|
-
loom capability get <id> 返回节点、关系、Brief 与 Intent 回链
|
|
972
|
-
loom capability coverage 检查图谱覆盖、Brief 和 Intent 回链
|
|
973
|
-
loom capability compile <id> 只读显示会进入该 Intent 的能力输入
|
|
974
|
-
loom expertise init <id> 创建当前 Intent 的外部能力获取记录
|
|
975
|
-
loom expertise get <id> 返回并校验当前 Expertise Pack
|
|
976
|
-
loom expertise validate <id> 校验检索、来源、Capsule 与 revision
|
|
977
|
-
loom atelier init <id> 为 atelier Intent 创建唯一创作记录
|
|
978
|
-
loom atelier get <id> 返回并校验当前 Atelier Record
|
|
979
|
-
loom atelier validate <id> 校验 Record、revision、候选与证据引用
|
|
980
|
-
|
|
981
|
-
loom intent next 返回下一个可执行 Intent
|
|
982
|
-
loom intent add --title <text> [--depends-on <ids>] 创建新增 draft
|
|
983
|
-
loom intent revise <id> --reason <text> 创建修订 draft 并报告反向依赖
|
|
984
|
-
loom intent draft <id> 查看 draft
|
|
985
|
-
loom intent finalize <id> 校验并原子写入官方 Intent Map
|
|
986
|
-
loom intent deprecate <id> --reason <text> 只读评估;加 --confirm 才弃用
|
|
987
|
-
loom intent status 返回进度概览
|
|
988
|
-
loom intent graph 输出 Mermaid 依赖图
|
|
989
|
-
loom intent get <id> 返回某 Intent 完整信息
|
|
990
|
-
loom intent narrative <id> 返回某 Intent 的意图叙事(解析 narrative_ref)
|
|
991
|
-
loom intent diff <v1> <v2> 按显式 lineage 对比 Intent 语义
|
|
992
|
-
loom intent validate 校验 Intent Map 结构
|
|
993
|
-
loom intent trace <id> 返回某 Intent 的完整追溯链(依赖+验证+哲学+叙事)
|
|
994
|
-
loom intent reverse-dep <id> 返回依赖某 Intent 的所有 Intent(变更影响评估)
|
|
995
|
-
loom intent reverse-ref <anchor> 返回引用某哲学锚点的所有 Intent
|
|
996
|
-
loom intent update <id> --status <s> 更新 Intent 状态(Keeper 用)
|
|
997
|
-
loom intent done <id> 当前 revision 验证通过后闭合 Intent
|
|
998
|
-
|
|
999
|
-
loom doctor 项目健康检查(一致性+孤儿引用+循环依赖+僵尸)
|
|
1000
|
-
loom context 上下文摘要(进度+下一步+待验证+风险)
|
|
1001
|
-
loom atlas build 编译当前版本的决策图谱资料模型
|
|
1002
|
-
loom atlas --regen 输出已装配资料的 Composer Pack,生成 loom-atlas.html
|
|
1003
|
-
loom atlas status 检查决策图谱资料与 HTML 是否新鲜
|
|
1004
|
-
loom atlas validate 校验决策图谱是否为当前版本合格交付物
|
|
1005
|
-
loom atlas 打开通过校验的 loom-atlas.html
|
|
1006
|
-
loom help <topic> 分层指南(含 patch 工作流)
|
|
1007
|
-
|
|
1008
|
-
loom philosophy get <anchor> 按锚点加载哲学章节
|
|
1009
|
-
loom philosophy list 列出哲学文档文件
|
|
1010
|
-
loom philosophy check 校验灵感来源质量(源数量/多样性/理由)
|
|
1011
|
-
loom philosophy impact <anchor> 只读分析直接引用和传递影响
|
|
1012
|
-
loom philosophy revise <anchor> --classification <type> --reason <text> 只读评估;加 --confirm 才记录 clarification/minor
|
|
1013
|
-
|
|
1014
|
-
loom verify contract <id> 返回某 Intent 的验收契约(解析引用)
|
|
1015
|
-
loom verify history <id> 返回某 Intent 本地验证历史
|
|
1016
|
-
loom verify history <ref> --across-versions 递归返回 lineage 各版本的本地历史
|
|
1017
|
-
loom verify pending 返回待验证的 Intent
|
|
1018
|
-
loom verify list 列出所有验证记录
|
|
1019
|
-
loom verify write --json-file <path> 从文件读入并写入验证记录
|
|
1020
|
-
loom verify write --json <string> 从命令行字符串写入验证记录
|
|
1021
|
-
loom verify pass|fail <id> --summary <text> 快捷写入验证结果
|
|
1022
|
-
--quality-proof <ref> 声明相对质量提升时,指向基线比较与稳定性证据
|
|
1023
|
-
|
|
1024
|
-
参数:
|
|
1025
|
-
--loom-dir <path> 指定 .loom/v{N} 目录(默认读 .loom/current 指针)`);
|
|
197
|
+
case 'eval':
|
|
198
|
+
if (subcommand !== 'scaffold') throw new Error('Usage: loom eval scaffold --json-file <scenario.json>');
|
|
199
|
+
output(scaffoldEval(jsonFile()));
|
|
1026
200
|
break;
|
|
1027
|
-
|
|
1028
201
|
default:
|
|
1029
|
-
|
|
202
|
+
throw new Error(`Unknown command: ${command}\n\n${help()}`);
|
|
1030
203
|
}
|
|
1031
|
-
} catch (
|
|
1032
|
-
|
|
204
|
+
} catch (error) {
|
|
205
|
+
fail(error.message);
|
|
1033
206
|
}
|