@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.
Files changed (84) hide show
  1. package/CHANGELOG.md +11 -86
  2. package/CONTRIBUTING.md +37 -0
  3. package/EVIL_EVAL.md +112 -0
  4. package/README.md +193 -445
  5. package/README.zh-CN.md +174 -0
  6. package/SECURITY.md +11 -0
  7. package/cli/bin/loom.js +171 -998
  8. package/cli/src/protocol.js +367 -0
  9. package/cli/src/store.js +626 -0
  10. package/design.md +194 -0
  11. package/docs/PROMPT_CATALOG.md +99 -0
  12. package/docs/RELEASE_CHECKLIST.md +53 -0
  13. package/docs/UX_FLOW.md +171 -0
  14. package/docs/brand/loom-mark.svg +18 -0
  15. package/docs/brand/loom-readme-header.svg +34 -0
  16. package/docs/brand/loom-readme-header.zh-CN.svg +29 -0
  17. package/docs/loom-eval-loop.drawio +21 -0
  18. package/docs/loom-eval-loop.svg +56 -0
  19. package/docs/loom-production-loop.drawio +41 -0
  20. package/docs/loom-production-loop.svg +92 -0
  21. package/package.json +43 -40
  22. package/EXTERNAL_ACQUISITION_DESIGN.md +0 -143
  23. package/cli/help/asset.md +0 -36
  24. package/cli/help/atelier.md +0 -37
  25. package/cli/help/atlas.md +0 -48
  26. package/cli/help/capability.md +0 -118
  27. package/cli/help/concepts.md +0 -105
  28. package/cli/help/doctor.md +0 -80
  29. package/cli/help/expertise.md +0 -52
  30. package/cli/help/loop.md +0 -134
  31. package/cli/help/patch.md +0 -33
  32. package/cli/help/proposals.md +0 -21
  33. package/cli/help/version.md +0 -136
  34. package/cli/help/workflow.md +0 -116
  35. package/cli/src/activate.js +0 -505
  36. package/cli/src/asset-library.js +0 -384
  37. package/cli/src/atelier.js +0 -331
  38. package/cli/src/atlas.js +0 -282
  39. package/cli/src/auto.js +0 -116
  40. package/cli/src/capability-graph.js +0 -724
  41. package/cli/src/capability-proposals.js +0 -225
  42. package/cli/src/diagnostics.js +0 -859
  43. package/cli/src/expertise-pack.js +0 -336
  44. package/cli/src/guide.js +0 -548
  45. package/cli/src/help.js +0 -41
  46. package/cli/src/init.js +0 -187
  47. package/cli/src/intent-draft.js +0 -303
  48. package/cli/src/intent-map.js +0 -747
  49. package/cli/src/patch.js +0 -214
  50. package/cli/src/philosophy.js +0 -331
  51. package/cli/src/shared/intent-ref.js +0 -38
  52. package/cli/src/shared/md-utils.js +0 -125
  53. package/cli/src/shared/paths.js +0 -73
  54. package/cli/src/shared/proof-reference.js +0 -19
  55. package/cli/src/shared/verification-method.js +0 -32
  56. package/cli/src/verify.js +0 -394
  57. package/cli/src/version.js +0 -134
  58. package/dimensions/AUTHORSHIP.md +0 -45
  59. package/dimensions/PART_DECOMPOSITION.md +0 -42
  60. package/dimensions/SEARCH_METHODOLOGY.md +0 -101
  61. package/dimensions/examples/AGENT_SYSTEM/README.md +0 -219
  62. package/dimensions/examples/CLI_TOOL/README.md +0 -163
  63. package/dimensions/universal/COLLABORATION_PHILOSOPHY.md +0 -28
  64. package/dimensions/universal/ENGINEERING_CREED.md +0 -30
  65. package/dimensions/universal/PRODUCT_PHILOSOPHY.md +0 -32
  66. package/meta/BASELINE.md +0 -91
  67. package/meta/INTENT_LOOP.md +0 -296
  68. package/meta/PHILOSOPHY_WEAVER.md +0 -110
  69. package/meta/ROLE_ACTIVATION.md +0 -114
  70. package/roles/architect.md +0 -92
  71. package/roles/forge.md +0 -110
  72. package/roles/impact-reviewer.md +0 -37
  73. package/roles/keeper.md +0 -113
  74. package/roles/visionary.md +0 -57
  75. package/templates/ASSET_LIBRARY_MANIFEST_TEMPLATE.json +0 -10
  76. package/templates/ATELIER_RECORD_TEMPLATE.json +0 -48
  77. package/templates/ATLAS_TEMPLATE.html +0 -104
  78. package/templates/CAPABILITY_BRIEF_TEMPLATE.md +0 -36
  79. package/templates/CAPABILITY_GRAPH_EXAMPLE.json +0 -188
  80. package/templates/CAPABILITY_GRAPH_TEMPLATE.json +0 -78
  81. package/templates/EXPERTISE_PACK_TEMPLATE.json +0 -22
  82. package/templates/INTENT_MAP_TEMPLATE.json +0 -85
  83. package/templates/PHILOSOPHY_TEMPLATE.md +0 -44
  84. 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 { argv, cwd, env, exit } from 'node:process';
6
- import { resolve, join, dirname } from 'node:path';
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 { findLoomRoot, findVersionDir, readCurrentPointer } from '../src/shared/paths.js';
10
-
11
- const __dirname = dirname(fileURLToPath(import.meta.url));
12
-
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
- import { initProject } from '../src/init.js';
17
- import { activateRole } from '../src/activate.js';
18
- import { listVersions, newVersion, useVersion, diffVersions } from '../src/version.js';
19
- import { doctor, contextSummary, traceIntent, reverseDep, reverseRef } from '../src/diagnostics.js';
20
- import { getHelpTopic, listHelpTopics } from '../src/help.js';
21
- import { guideProject } from '../src/guide.js';
22
- import { isAutoOn, autoOn, autoOff, autoStatus, getAutoMode } from '../src/auto.js';
23
- import { generateAtlasComposerPack, getAtlasStatus, validateAtlas, writeAtlasModel } from '../src/atlas.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';
27
- import { compileCapabilityInputs, getCapabilityCoverage, getCapabilityFrontier, getCapabilityGraphProjection, getCapabilityNode } from '../src/capability-graph.js';
28
- import { getAsset, importAsset, listAssets, recoverAssetImportTransaction, searchAssets, validateAssetLibrary } from '../src/asset-library.js';
29
- import { closeCapabilityProposal, decideCapabilityProposal, getCapabilityProposal, listCapabilityProposals, submitCapabilityProposal } from '../src/capability-proposals.js';
30
- import { getAtelierRecord, initAtelierRecord, validateAtelierRecord } from '../src/atelier.js';
31
- import { assertExpertiseReady, getExpertisePack, initExpertisePack, validateExpertisePack } from '../src/expertise-pack.js';
32
-
33
- // ─── 路径解析 ──────────────────────────────────────────
34
- // findLoomRoot / findVersionDir / readCurrentPointer 已提取到 shared/paths.js
35
- // 这里只保留目录辅助函数。
36
-
37
- function getPhilosophyDir(versionDir) {
38
- return join(versionDir, '00_PHILOSOPHY');
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 getVerificationsDir(versionDir) {
42
- return join(versionDir, 'verifications');
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 output(data) {
48
- if (typeof data === 'string') {
49
- console.log(data);
50
- } else {
51
- console.log(JSON.stringify(data, null, 2));
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 die(msg, code = 1) {
56
- console.error(`错误: ${msg}`);
57
- exit(code);
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 (cmd) {
66
- case '--version':
102
+ switch (command) {
103
+ case '--version':
67
104
  case '-v': {
68
- // 从根 package.json 读版本号(cli/bin -> cli -> LOOM root)
69
- const pkgPath = resolve(__dirname, '..', '..', 'package.json');
70
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
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 'init': {
416
- if (sub === '--help' || sub === '-h') {
417
- console.log('用法: loom init\\n\\n在当前目录创建 LOOM 项目骨架。若目录已有 .loom/,不会覆盖现有文件。');
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
- case 'philosophy': {
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
- case 'version': {
654
- const loomRoot = findLoomRoot();
655
- switch (sub) {
656
- case 'list': {
657
- const { versions, current } = listVersions(loomRoot);
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
- case 'context': {
757
- const versionDir = findVersionDir();
758
- output(contextSummary(versionDir, getVerificationsDir(versionDir), getPhilosophyDir(versionDir)));
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
- case 'help': {
763
- // sub topic(loom help workflow → sub='workflow')
764
- // 过滤掉 --help/-h 这种被路由进来的情况
765
- const topic = (sub && !sub.startsWith('-')) ? sub : null;
766
- if (!topic) {
767
- console.log('LOOM 指南 topics:');
768
- for (const t of listHelpTopics()) {
769
- console.log(` loom help ${t}`);
770
- }
771
- console.log('\n运行 loom --help 查看所有命令。');
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
- case 'guide': {
783
- const dryRun = argv.includes('--dry-run');
784
- const jsonOut = argv.includes('--json');
785
- const result = guideProject(cwd(), { dryRun });
786
- if (jsonOut) {
787
- output(result);
788
- break;
789
- }
790
- console.log(`阶段 ${result.stage_num}: ${result.stage}`);
791
- if (dryRun) {
792
- console.log('诊断: dry-run(不写 heartbeat)');
793
- }
794
- const modeDesc = {
795
- 'manual': '手动(每步需确认)',
796
- 'auto-loop': 'AUTO auto-loop(设计阶段需 review,Intent Loop 自动)',
797
- 'auto-design': 'AUTO auto-design(全部自动)',
798
- };
799
- console.log(`模式: ${modeDesc[result.auto_mode] || result.auto_mode}`);
800
- console.log(`\n${result.message}`);
801
- console.log(`\n下一步: ${result.next_action}`);
802
- console.log(` → ${result.next_command}`);
803
- if (result.input_delivery === 'context_pack') {
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
- case 'auto': {
816
- const loomRoot = findLoomRoot();
817
- switch (sub) {
818
- case 'on': {
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
- case 'atlas': {
873
- const versionDir = findVersionDir();
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
- die(`未知命令: ${cmd}\n运行 loom --help 查看用法`);
202
+ throw new Error(`Unknown command: ${command}\n\n${help()}`);
1030
203
  }
1031
- } catch (e) {
1032
- die(e.message);
204
+ } catch (error) {
205
+ fail(error.message);
1033
206
  }