@haaaiawd/loom 1.3.1 → 2.0.1

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 +16 -86
  2. package/CONTRIBUTING.md +37 -0
  3. package/EVIL_EVAL.md +112 -0
  4. package/README.md +156 -403
  5. package/README.zh-CN.md +176 -0
  6. package/SECURITY.md +11 -0
  7. package/cli/bin/loom.js +171 -998
  8. package/cli/src/protocol.js +411 -0
  9. package/cli/src/store.js +628 -0
  10. package/design.md +201 -0
  11. package/docs/PROMPT_CATALOG.md +106 -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 +27 -24
  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/src/guide.js DELETED
@@ -1,548 +0,0 @@
1
- // guide — 状态感知动态引导
2
- // 检测项目当前在哪个阶段,输出"你在阶段 X,下一步做 Y"。
3
- // 面向 Agent(主)和人类(辅)。比 help workflow(静态)智能。
4
-
5
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { readCurrentPointer } from './version.js';
8
- import { loadIntentMap } from './intent-map.js';
9
- import { getCapabilityCoverage } from './capability-graph.js';
10
- import { listCapabilityProposals } from './capability-proposals.js';
11
- import { isAutoOn, getAutoMode, writeHeartbeat, needsHumanReview } from './auto.js';
12
- import { doctor } from './diagnostics.js';
13
- import { getExpertisePackState } from './expertise-pack.js';
14
- import { validateAtlas } from './atlas.js';
15
-
16
- /**
17
- * 检测文件是否还是模板(未填充真实内容)。
18
- * MD 文件检查 <!-- LOOM_TEMPLATE --> 标记——但如果文件已经超过模板大小(>2KB),
19
- * 认为用户已经填充了真实内容只是忘了删标记,忽略标记。
20
- * JSON 文件检查 _meta._template 字段。
21
- */
22
- function isTemplate(filePath) {
23
- if (!existsSync(filePath)) return true;
24
- const content = readFileSync(filePath, 'utf-8');
25
- if (filePath.endsWith('.json')) {
26
- try {
27
- const data = JSON.parse(content);
28
- return data._meta?._template === true;
29
- } catch {
30
- return false; // 损坏文件不算模板
31
- }
32
- }
33
- // MD 文件:有 LOOM_TEMPLATE 标记 且 文件较小(<2KB)才算模板。
34
- // 如果文件已经 >2KB,说明用户填充了内容只是忘了删标记——忽略标记。
35
- if (content.includes('<!-- LOOM_TEMPLATE -->')) {
36
- return content.length < 2048;
37
- }
38
- return false;
39
- }
40
-
41
- /**
42
- * 诊断项目当前阶段。
43
- * @param {string} projectDir — 项目根目录
44
- * @returns {{ stage: string, stage_num: number, details: object, auto: boolean, next_action: string, next_command: string, message: string, needs_human_review: boolean }}
45
- */
46
- export function guideProject(projectDir, options = {}) {
47
- const cwd = projectDir || process.cwd();
48
- const loomRoot = join(cwd, '.loom');
49
- const autoMode = getAutoMode(loomRoot);
50
- const auto = autoMode !== 'manual'; // 向后兼容 boolean
51
- const result = diagnoseStage(cwd, loomRoot, auto);
52
- result.auto_mode = autoMode;
53
-
54
- // 按 stage 补充可执行信息:要读什么、要产出什么、完成后跑什么校验
55
- const current = result.details.version || 'v1';
56
- const stageMeta = {
57
- not_initialized: {
58
- inputs: [],
59
- outputs: ['.loom/v1/'],
60
- verify_command: 'loom guide',
61
- },
62
- no_version: {
63
- inputs: [],
64
- outputs: ['.loom/v1/'],
65
- verify_command: 'loom guide',
66
- },
67
- need_philosophy: {
68
- inputs: ['meta/PHILOSOPHY_WEAVER.md', 'meta/BASELINE.md', 'dimensions/SEARCH_METHODOLOGY.md'],
69
- outputs: [`.loom/${current}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`, `.loom/${current}/00_PHILOSOPHY/ENGINEERING_CREED.md`, `.loom/${current}/00_PHILOSOPHY/DECISION_RUBRIC.md`],
70
- verify_command: 'loom philosophy check',
71
- input_delivery: 'context_pack',
72
- },
73
- need_vision: {
74
- inputs: ['roles/visionary.md', `.loom/${current}/00_PHILOSOPHY/`],
75
- outputs: [`.loom/${current}/01_VISION.md`],
76
- verify_command: 'loom guide',
77
- input_delivery: 'context_pack',
78
- },
79
- need_architecture: {
80
- inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
81
- outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
82
- verify_command: 'loom doctor',
83
- input_delivery: 'context_pack',
84
- },
85
- need_capability_graph: {
86
- inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
87
- outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
88
- verify_command: 'loom capability coverage',
89
- input_delivery: 'context_pack',
90
- },
91
- capability_graph_incomplete: {
92
- inputs: ['roles/architect.md', `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
93
- outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
94
- verify_command: 'loom capability coverage',
95
- input_delivery: 'context_pack',
96
- },
97
- capability_graph_proposals_pending: {
98
- inputs: ['roles/architect.md', `.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`],
99
- outputs: [`.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
100
- verify_command: 'loom capability proposal list',
101
- input_delivery: 'context_pack',
102
- },
103
- intent_map_broken: {
104
- inputs: [`.loom/${current}/04_INTENT_MAP.json`],
105
- outputs: [`.loom/${current}/04_INTENT_MAP.json`],
106
- verify_command: 'loom intent validate',
107
- },
108
- in_loop: {
109
- inputs: ['roles/forge.md', 'roles/keeper.md', `.loom/${current}/04_INTENT_MAP.json`],
110
- outputs: ['代码文件', `.loom/${current}/verifications/INT-*.json`],
111
- verify_command: 'loom verify pending',
112
- },
113
- ready_for_loop: {
114
- inputs: ['roles/forge.md', 'roles/keeper.md', `.loom/${current}/04_INTENT_MAP.json`],
115
- outputs: ['代码文件', `.loom/${current}/verifications/INT-*.json`],
116
- verify_command: 'loom verify pending',
117
- },
118
- done: {
119
- inputs: [`.loom/${current}/04_INTENT_MAP.json`, `.loom/${current}/verifications/`],
120
- outputs: [`.loom/${current}/06_CHANGELOG.json`, `.loom/${current}/06_CHANGELOG.md`],
121
- verify_command: 'loom doctor',
122
- },
123
- unknown: {
124
- inputs: [],
125
- outputs: [],
126
- verify_command: 'loom doctor',
127
- },
128
- };
129
- const meta = stageMeta[result.stage] || {};
130
- result.inputs = meta.inputs || [];
131
- result.outputs = meta.outputs || [];
132
- result.verify_command = meta.verify_command || null;
133
- result.input_delivery = meta.input_delivery || null;
134
- // 统一后处理:写心跳 + 加 AUTO 提示词 + 判断是否需要人类 review
135
- if (existsSync(loomRoot) && !options.dryRun) {
136
- try {
137
- writeHeartbeat(loomRoot, {
138
- stage: result.stage,
139
- stage_num: result.stage_num,
140
- next_command: result.next_command,
141
- next_action: result.next_action,
142
- });
143
- } catch { /* 心跳写入失败不阻塞 guide */ }
144
- }
145
- result.needs_human_review = needsHumanReview(loomRoot, result.stage_num);
146
- // 在 message 末尾加 AUTO 提示
147
- if (result.stage_num > 0 && result.stage_num < 4) {
148
- if (autoMode === 'auto-design') {
149
- result.message += '\n\n> AUTO 模式 auto-design——设计阶段也自动执行,无需人类确认。';
150
- } else if (autoMode === 'auto-loop') {
151
- result.message += '\n\n> ⚠ 这是设计阶段(哲学/愿景/架构),auto-loop 模式下需要人类 review 后再继续。';
152
- } else {
153
- result.message += '\n\n> ⚠ AUTO 模式 manual——每步都需要人类确认后再继续。';
154
- }
155
- } else if (result.stage_num >= 4) {
156
- if (auto) {
157
- result.message += '\n\n> AUTO 模式开启——可继续进入下一阶段;契约、证据与 Keeper 门禁仍不会被跳过。';
158
- } else {
159
- result.message += '\n\n> ⚠ AUTO 模式关闭——执行 next_command 后等人类确认再继续。';
160
- }
161
- }
162
- return result;
163
- }
164
-
165
- /**
166
- * 内部函数:诊断阶段(不含心跳和 AUTO 提示词)。
167
- */
168
- function diagnoseStage(cwd, loomRoot, auto) {
169
-
170
- // 状态 0: 没有 .loom/
171
- if (!existsSync(loomRoot)) {
172
- return {
173
- stage: 'not_initialized',
174
- stage_num: 0,
175
- details: {},
176
- auto,
177
- next_action: '初始化 LOOM 项目',
178
- next_command: 'loom init',
179
- message: '项目尚未初始化。运行 loom init 创建 .loom/v1/ 骨架。',
180
- };
181
- }
182
-
183
- const current = readCurrentPointer(loomRoot);
184
- if (!current) {
185
- return {
186
- stage: 'no_version',
187
- stage_num: 0,
188
- details: {},
189
- auto,
190
- next_action: '初始化第一个版本',
191
- next_command: 'loom init',
192
- message: '.loom/ 存在但没有版本目录。运行 loom init 创建 v1。',
193
- };
194
- }
195
-
196
- const versionDir = join(loomRoot, current);
197
- const philosophyDir = join(versionDir, '00_PHILOSOPHY');
198
- const visionPath = join(versionDir, '01_VISION.md');
199
- const intentMapPath = join(versionDir, '04_INTENT_MAP.json');
200
- const capabilityGraphPath = join(versionDir, '07_CAPABILITY_GRAPH.json');
201
-
202
- // 状态 1: 哲学未织造
203
- const philosophyFile = join(philosophyDir, 'PRODUCT_PHILOSOPHY.md');
204
- if (isTemplate(philosophyFile)) {
205
- return {
206
- stage: 'need_philosophy',
207
- stage_num: 1,
208
- details: { version: current },
209
- auto,
210
- next_action: '织造产品哲学',
211
- next_command: 'loom activate weaver',
212
- message: `当前版本 ${current}:哲学还是模板,需要 Weaver 织造。`,
213
- };
214
- }
215
-
216
- // 状态 2: 哲学已织造,愿景未定义
217
- if (isTemplate(visionPath)) {
218
- return {
219
- stage: 'need_vision',
220
- stage_num: 2,
221
- details: { version: current },
222
- auto,
223
- next_action: '定义产品愿景',
224
- next_command: 'loom activate visionary',
225
- message: `当前版本 ${current}:哲学已织造,愿景还是模板,需要 Visionary 定义。`,
226
- };
227
- }
228
-
229
- // 状态 3: 愿景已定义,先展开项目问题面与能力缺口,再承诺 Intent。
230
- if (existsSync(capabilityGraphPath) && isTemplate(capabilityGraphPath)) {
231
- return {
232
- stage: 'need_capability_graph',
233
- stage_num: 3,
234
- details: { version: current },
235
- auto,
236
- next_action: '展开 Capability Graph,路由高影响问题与能力缺口',
237
- next_command: 'loom activate architect',
238
- message: `当前版本 ${current}:愿景已定义,但 Capability Graph 还是模板。Architect 必须先判断哪些体验、系统、资产、风险与能力适用,再创建正式 Intent。`,
239
- };
240
- }
241
-
242
- // 图谱一旦存在,就不能把不完整路由悄悄跨过去。旧项目缺少该文件仍保留兼容路径。
243
- if (existsSync(capabilityGraphPath)) {
244
- try {
245
- const pendingProposals = listCapabilityProposals(versionDir, { unresolvedOnly: true });
246
- if (pendingProposals.length) {
247
- return {
248
- stage: 'capability_graph_proposals_pending',
249
- stage_num: 3,
250
- details: { version: current, proposals: pendingProposals.map((proposal) => proposal.id) },
251
- auto,
252
- next_action: '审计新信息对 Capability Graph、Intent 和契约的影响',
253
- next_command: 'loom activate architect',
254
- message: `当前版本 ${current} 有 ${pendingProposals.length} 个未闭合的 Capability Graph proposal。它们是新要求、研究或实现发现,不得由 Forge 静默变成当前 Intent 范围。`,
255
- };
256
- }
257
- } catch (error) {
258
- return {
259
- stage: 'capability_graph_proposals_pending', stage_num: 3, details: { version: current, error: error.message }, auto,
260
- next_action: '修复 Capability Graph proposal', next_command: 'loom activate architect',
261
- message: `Capability Graph proposal 无法审计: ${error.message}`,
262
- };
263
- }
264
- try {
265
- const coverage = getCapabilityCoverage(versionDir);
266
- if (!coverage.summary.ready) {
267
- return {
268
- stage: 'capability_graph_incomplete',
269
- stage_num: 3.1,
270
- details: { version: current, coverage: coverage.summary },
271
- auto,
272
- next_action: '补齐 Capability Graph 的透镜审视、路由、可观察验证入口与 Intent 回链',
273
- next_command: 'loom activate architect',
274
- message: `当前版本 ${current}:Capability Graph 尚未闭合(透镜契约缺口 ${coverage.summary.lens_contract_gaps || 0}、能力领域缺口 ${coverage.summary.capability_domain_gaps || 0}、高影响前沿 ${coverage.summary.high_unrouted}、路由缺口 ${coverage.summary.routing_gaps}、不可观察 outcome ${coverage.summary.high_outcomes_without_observable_evidence}、未映射 Intent ${coverage.summary.unmapped_intents})。先由 Architect 补图谱,再继续设计 Intent。`,
275
- };
276
- }
277
- } catch (error) {
278
- const impactGateError = /impact_assessment|impact 必须为 high|外部获取门禁/.test(error.message);
279
- return {
280
- stage: 'capability_graph_incomplete',
281
- stage_num: 3.1,
282
- details: { version: current, error: error.message },
283
- auto,
284
- next_action: impactGateError
285
- ? '先完成每个 capability 的 Impact Gate,再决定哪些必须外部获取'
286
- : '修复 Capability Graph',
287
- next_command: 'loom activate architect',
288
- message: impactGateError
289
- ? `Capability Graph 的 Impact Gate 尚未通过:${error.message} 先由 Architect 判断影响的用户结果、错判代价与外部知识是否会改变决定,不能靠降级 impact 继续。`
290
- : `Capability Graph 无法通过校验: ${error.message}`,
291
- };
292
- }
293
- }
294
-
295
- // 状态 3.5: 图谱已建立,Intent Map 未设计。
296
- if (isTemplate(intentMapPath)) {
297
- return {
298
- stage: 'need_architecture',
299
- stage_num: 3.5,
300
- details: { version: current },
301
- auto,
302
- next_action: '从 Capability Graph 编译系统架构 + Intent Map',
303
- next_command: 'loom activate architect',
304
- message: `当前版本 ${current}:Capability Graph 已建立,Intent Map 还是模板。Architect 需要将已路由的问题面编译为系统架构与可闭合 Intent。`,
305
- };
306
- }
307
-
308
- // 状态 4-7: Intent Map 已设计,根据 Intent 状态判断
309
- let intentMap;
310
- let intents;
311
- try {
312
- intentMap = loadIntentMap(versionDir);
313
- intents = intentMap.intents;
314
- } catch (e) {
315
- return {
316
- stage: 'intent_map_broken',
317
- stage_num: 3,
318
- details: { version: current, error: e.message },
319
- auto,
320
- next_action: '修复 Intent Map',
321
- next_command: 'loom intent validate',
322
- message: `Intent Map 格式错误: ${e.message}`,
323
- };
324
- }
325
-
326
- const allIntents = Object.values(intents);
327
- const incompleteDesignDocuments = ['02_ARCHITECTURE.md', '05_VERIFICATION.md']
328
- .filter((filename) => isTemplate(join(versionDir, filename)));
329
- if (incompleteDesignDocuments.length) {
330
- return {
331
- stage: 'need_architecture',
332
- stage_num: 3.5,
333
- details: { version: current, incomplete_documents: incompleteDesignDocuments },
334
- auto,
335
- next_action: '完成当前版本的系统架构与验证契约文档',
336
- next_command: 'loom activate architect',
337
- message: `当前版本 ${current} 的 Intent Map 已存在,但 ${incompleteDesignDocuments.join('、')} 仍缺失或是模板。Architect 必须先把系统边界与完成契约落到真实文档,再进入 Intent Loop。`,
338
- };
339
- }
340
- const documentIntegrityIssues = doctor(versionDir, join(versionDir, 'verifications'), philosophyDir).issues
341
- .filter((issue) => [
342
- 'project_document_missing',
343
- 'project_document_template',
344
- 'orphan_philosophy_ref',
345
- 'orphan_philosophy_anchor',
346
- 'intent_narrative_invalid',
347
- 'intent_contract_invalid',
348
- 'capability_impact_gate_missing',
349
- ].includes(issue.type));
350
- if (documentIntegrityIssues.length) {
351
- const issueTypes = [...new Set(documentIntegrityIssues.map((issue) => issue.type))];
352
- return {
353
- stage: 'document_integrity_incomplete',
354
- stage_num: 3.6,
355
- details: { version: current, issue_types: issueTypes },
356
- auto,
357
- next_action: '修复 LOOM 文档引用与结构完整性,再进入 Intent Loop',
358
- next_command: 'loom activate architect',
359
- message: `当前版本 ${current} 的 LOOM 文档完整性尚未通过(${issueTypes.join('、')})。先让 Architect 修复真实文档、章节引用或 Impact Gate;不要带着断开的叙事/契约进入 Intent Loop。`,
360
- };
361
- }
362
- const counts = {
363
- pending: allIntents.filter((i) => i.status === 'pending').length,
364
- in_progress: allIntents.filter((i) => i.status === 'in_progress').length,
365
- completed: allIntents.filter((i) => i.status === 'completed').length,
366
- blocked: allIntents.filter((i) => i.status === 'blocked').length,
367
- needs_review: allIntents.filter((i) => i.status === 'needs_review').length,
368
- };
369
- const total = allIntents.length;
370
-
371
- // 状态 7: 有 blocked(优先报告)
372
- if (counts.blocked > 0) {
373
- const blockedIds = allIntents.filter((i) => i.status === 'blocked').map((i) => i.id);
374
- const msg = auto
375
- ? `有 ${counts.blocked} 个 Intent 阻塞: ${blockedIds.join(', ')}。AUTO 模式下这是唯一允许停下的情况——需要人工介入解决阻塞后才能继续。`
376
- : `有 ${counts.blocked} 个 Intent 阻塞: ${blockedIds.join(', ')}。需要人工介入。`;
377
- return {
378
- stage: 'blocked',
379
- stage_num: 7,
380
- details: { version: current, counts, blocked_ids: blockedIds },
381
- auto,
382
- next_action: '人工介入解决阻塞',
383
- next_command: 'loom intent get ' + blockedIds[0],
384
- message: msg,
385
- };
386
- }
387
-
388
- // 状态 6: 全部 completed
389
- if (counts.completed === total && total > 0) {
390
- const atlas = validateAtlas(join(versionDir, '..', '..'), versionDir);
391
- if (!atlas.valid) {
392
- return {
393
- stage: 'need_decision_atlas',
394
- stage_num: 5.8,
395
- details: { version: current, atlas_errors: atlas.errors },
396
- auto,
397
- next_action: '生成并校验当前版本的决策图谱交付物',
398
- next_command: 'loom atlas --regen',
399
- message: `当前版本 ${current} 的 Intent 已全部完成,但决策图谱尚未交付。它只说明当前架构与决策结构,不展示项目进度;生成 loom-atlas.html 后运行 loom atlas validate。`,
400
- };
401
- }
402
- const health = doctor(versionDir, join(versionDir, 'verifications'), philosophyDir);
403
- const blocking = health.issues.filter((issue) => issue.severity === 'fatal' || issue.severity === 'high');
404
- if (blocking.length) {
405
- return {
406
- stage: 'needs_review',
407
- stage_num: 5.5,
408
- details: { version: current, counts, blocking_issues: blocking.map((issue) => issue.type) },
409
- auto,
410
- next_action: '修复完成门或验证证据后重新运行 doctor',
411
- next_command: 'loom doctor',
412
- message: `当前版本 ${current} 的 Intent 均标为 completed,但健康检查仍有 ${blocking.length} 个高风险问题;不能宣告阶段完成。先运行 loom doctor。`,
413
- };
414
- }
415
- const doneMessage = [
416
- `当前版本 ${current}:全部 ${total} 个 Intent 已完成。`,
417
- '',
418
- '如果有新需求,先判断变更档位:',
419
- '- Patch:不触及 Intent,只修 bug / 样式 / 实现细节;跑验证并记录 changelog。',
420
- '- Minor:新增或修改 Intent,但不改变哲学前提、愿景北极星、架构边界;在当前版本内变更并重验受影响 Intent。',
421
- '- Major:哲学前提、愿景北极星或架构边界变化;运行 loom version new。',
422
- ].join('\n');
423
- return {
424
- stage: 'done',
425
- stage_num: 6,
426
- details: { version: current, counts, total },
427
- auto,
428
- next_action: '项目阶段完成,按 Patch / Minor / Major 判断下一步',
429
- next_command: 'loom help version',
430
- message: doneMessage,
431
- };
432
- }
433
-
434
- // 状态 5: 有 in_progress
435
- if (counts.in_progress > 0) {
436
- const inProgressIds = allIntents.filter((i) => i.status === 'in_progress').map((i) => i.id);
437
- const expertiseOpen = allIntents
438
- .filter((intent) => intent.status === 'in_progress')
439
- .map((intent) => ({ intent, state: getExpertisePackState(versionDir, intent.id) }))
440
- .find(({ state }) => state.required && !state.ready);
441
- if (expertiseOpen) {
442
- const missing = expertiseOpen.state.reason === 'missing';
443
- const blocked = expertiseOpen.state.reason?.startsWith('blocked:');
444
- return {
445
- stage: 'in_loop',
446
- stage_num: 5,
447
- details: {
448
- version: current,
449
- counts,
450
- in_progress_ids: inProgressIds,
451
- expertise_intent: expertiseOpen.intent.id,
452
- expertise_reason: expertiseOpen.state.reason,
453
- },
454
- auto,
455
- next_action: missing
456
- ? '创建搜索计划并执行外部能力获取'
457
- : blocked
458
- ? '解决 Expertise Pack 记录的外部获取阻塞'
459
- : '补齐来源化 Expertise Pack',
460
- next_command: missing
461
- ? `loom expertise init ${expertiseOpen.intent.id}`
462
- : blocked
463
- ? `loom expertise get ${expertiseOpen.intent.id}`
464
- : `loom expertise validate ${expertiseOpen.intent.id}`,
465
- message: blocked
466
- ? `${expertiseOpen.intent.id} 的外部能力获取已明确 blocked:${expertiseOpen.state.reason.slice('blocked: '.length)}。满足 Pack 中的 recovery_condition 后再继续;不能让模型补齐空白。`
467
- : `${expertiseOpen.intent.id} 需要外部能力获取。先由任务信号派生搜索词,实际使用 find skill、网络搜索、官方文档或研究资料,再把可回查来源编译为 Capability Capsules;模型临时生成内容不能代替来源。`,
468
- };
469
- }
470
- const atelierWithoutRecord = allIntents.find((intent) => intent.status === 'in_progress'
471
- && intent.quality_strategy === 'atelier'
472
- && !existsSync(join(versionDir, '09_ATELIER', `${intent.id}.json`)));
473
- if (atelierWithoutRecord) {
474
- return {
475
- stage: 'in_loop',
476
- stage_num: 5,
477
- details: { version: current, counts, in_progress_ids: inProgressIds, atelier_intent: atelierWithoutRecord.id },
478
- auto,
479
- next_action: '创建 Atelier Record 并冻结基线',
480
- next_command: `loom atelier init ${atelierWithoutRecord.id}`,
481
- message: `${atelierWithoutRecord.id} 已进入 Atelier Path,但还没有唯一创作记录。先创建 Record,再按 Forge Context Pack 形成 Authorial Stance。`,
482
- };
483
- }
484
- return {
485
- stage: 'in_loop',
486
- stage_num: 5,
487
- details: { version: current, counts, in_progress_ids: inProgressIds },
488
- auto,
489
- next_action: '继续 Intent Loop',
490
- next_command: 'loom context',
491
- message: `当前版本 ${current}:Intent Loop 进行中(${inProgressIds.join(', ')})。运行 loom context 查看状态。`,
492
- };
493
- }
494
-
495
- // 状态 5.5: 有 needs_review(收敛趟)——已完成但需要重新验证的 Intent
496
- if (counts.needs_review > 0) {
497
- const reviewIds = allIntents.filter((i) => i.status === 'needs_review').map((i) => i.id);
498
- // 读 _meta.pass_count 收敛趟计数(最大 3 趟)
499
- const passCount = intentMap._meta?.pass_count || 1;
500
- const MAX_PASSES = 3;
501
- const isOverLimit = passCount > MAX_PASSES;
502
- const passMsg = ` [Pass ${passCount}/${MAX_PASSES}]`;
503
- if (isOverLimit) {
504
- return {
505
- stage: 'cannot_converge',
506
- stage_num: 7,
507
- details: { version: current, counts, needs_review_ids: reviewIds, pass_count: passCount },
508
- auto,
509
- next_action: '收敛失败——超过最大趟数,需 Architect 介入',
510
- next_command: 'loom intent update ' + reviewIds[0] + ' --status blocked',
511
- message: `当前版本 ${current}:收敛失败,已超过最大 ${MAX_PASSES} 趟限制(当前 Pass ${passCount})。${counts.needs_review} 个 Intent 仍需重新验证(${reviewIds.join(', ')})。这是系统性问题——需 Architect 介入重新设计。`,
512
- };
513
- }
514
- return {
515
- stage: 'converging',
516
- stage_num: 5.5,
517
- details: { version: current, counts, needs_review_ids: reviewIds, pass_count: passCount },
518
- auto,
519
- next_action: `进入收敛趟 Pass ${passCount}——重验 needs_review 的 Intent`,
520
- next_command: 'loom intent update ' + reviewIds[0] + ' --status in_progress',
521
- message: `当前版本 ${current}${passMsg}:${counts.needs_review} 个 Intent 需要重新验证(${reviewIds.join(', ')})。这是不动点收敛的第 ${passCount} 趟——重验这些 Intent,通过则 completed,偏离则修正。一趟无新 needs_review 即收敛达成。最大 ${MAX_PASSES} 趟,超过判定为系统性问题。`,
522
- };
523
- }
524
-
525
- // 状态 4: 有 pending,进入 Intent Loop
526
- if (counts.pending > 0) {
527
- return {
528
- stage: 'ready_for_loop',
529
- stage_num: 4,
530
- details: { version: current, counts, total },
531
- auto,
532
- next_action: '进入 Intent Loop',
533
- next_command: 'loom intent next',
534
- message: `当前版本 ${current}:${counts.pending} 个 Intent 待执行。运行 loom intent next 开始。`,
535
- };
536
- }
537
-
538
- // 兜底
539
- return {
540
- stage: 'unknown',
541
- stage_num: -1,
542
- details: { version: current, counts },
543
- auto,
544
- next_action: '运行健康检查',
545
- next_command: 'loom doctor',
546
- message: '项目状态不明确,运行 loom doctor 诊断。',
547
- };
548
- }
package/cli/src/help.js DELETED
@@ -1,41 +0,0 @@
1
- // help — LOOM CLI 分层指南
2
- // loom help <topic> 输出结构化工作指南,不是 man page 参数说明。
3
- // 指南内容是 agent 能直接理解的:"做什么、用什么命令、怎么判断做对了"。
4
- //
5
- // 内容存储在 cli/help/*.md 文件中,运行时读取。
6
- // 这样改帮助内容不用改代码,未来支持多语言也方便。
7
-
8
- import { readFileSync, existsSync, readdirSync } from 'node:fs';
9
- import { join, dirname } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
-
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const HELP_DIR = join(__dirname, '..', 'help');
14
-
15
- /** topic 列表(从 cli/help/ 目录扫描) */
16
- export function listHelpTopics() {
17
- if (!existsSync(HELP_DIR)) return [];
18
- return readdirSync(HELP_DIR)
19
- .filter((f) => f.endsWith('.md'))
20
- .map((f) => f.replace('.md', ''))
21
- .sort();
22
- }
23
-
24
- /**
25
- * 获取某个 topic 的帮助内容。
26
- * @param {string} topic — topic 名(如 workflow / concepts / loop / version / doctor)
27
- * @returns {string} 帮助内容(MD 格式)
28
- * @throws {Error} topic 不存在时抛错
29
- */
30
- export function getHelpTopic(topic) {
31
- if (!topic) {
32
- const topics = listHelpTopics();
33
- throw new Error(`请指定 topic。可用: ${topics.join(', ')}`);
34
- }
35
- const filePath = join(HELP_DIR, `${topic}.md`);
36
- if (!existsSync(filePath)) {
37
- const topics = listHelpTopics();
38
- throw new Error(`未知 topic: ${topic}\n可用: ${topics.join(', ')}`);
39
- }
40
- return readFileSync(filePath, 'utf-8');
41
- }