@haaaiawd/loom 1.2.1 → 1.2.2
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 +9 -0
- package/README.md +3 -0
- package/cli/bin/loom.js +21 -16
- package/cli/help/workflow.md +3 -0
- package/cli/src/activate.js +93 -5
- package/cli/src/guide.js +23 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.2 — 2026-07-31
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- `guide` 只输出下一步命令、Context Pack 提示与完成校验,不再展示文件输入/输出清单;当下一步是角色激活时,由该命令装配 Context Pack。
|
|
8
|
+
- `activate weaver` 直接注入检索方法与哲学维度目录;`activate architect` 在无 Intent 的设计阶段直接注入愿景、当前 Capability Graph、Intent Map 与未闭合 proposal。
|
|
9
|
+
- Capability Graph 未闭合或 proposal 待裁决时,`guide` 重新指向可推进工作的 Architect Context Pack,而不是重复运行只读检查。
|
|
10
|
+
- `loom intent next` 返回开始该 Intent 和获取 Forge Context Pack 的明确后续命令。
|
|
11
|
+
|
|
3
12
|
## 1.2.1 — 2026-07-31
|
|
4
13
|
|
|
5
14
|
### Fixed
|
package/README.md
CHANGED
|
@@ -87,6 +87,9 @@ loom guide --dry-run # 只读诊断,不写 heartbeat
|
|
|
87
87
|
guide 检测项目当前在哪个阶段,输出"你在阶段 X,下一步做 Y"。
|
|
88
88
|
Agent 每完成一步都跑 guide 确认下一步。
|
|
89
89
|
如果只是审计或探测,不希望产生任何状态写入,用 `loom guide --dry-run`。
|
|
90
|
+
|
|
91
|
+
当 `guide` 的下一步是 `loom activate <role>` 时,先运行这条命令:它会把该阶段需要的
|
|
92
|
+
规范与版本化输入编译为 Context Pack。`guide` 只负责导航,不输出待阅读文件清单。
|
|
90
93
|
|
|
91
94
|
### AUTO 模式
|
|
92
95
|
|
package/cli/bin/loom.js
CHANGED
|
@@ -274,9 +274,19 @@ try {
|
|
|
274
274
|
}));
|
|
275
275
|
break;
|
|
276
276
|
}
|
|
277
|
-
case 'next':
|
|
278
|
-
|
|
279
|
-
|
|
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;
|
|
280
290
|
case 'status': {
|
|
281
291
|
const s = getStatus(versionDir);
|
|
282
292
|
const fmt = (ids) => ids.map((id) => s.titles[id] ? `${id}(${s.titles[id]})` : id).join(', ') || '-';
|
|
@@ -518,7 +528,7 @@ try {
|
|
|
518
528
|
const icon = issue.severity === 'high' ? '⚠' : '·';
|
|
519
529
|
console.log(` ${icon} [${issue.severity}] ${issue.msg}`);
|
|
520
530
|
}
|
|
521
|
-
console.log('\n
|
|
531
|
+
console.log('\n下一步: 运行 loom activate weaver;命令会重新输出哲学阶段所需的 Context Pack。');
|
|
522
532
|
exit(1);
|
|
523
533
|
}
|
|
524
534
|
break;
|
|
@@ -738,7 +748,7 @@ try {
|
|
|
738
748
|
console.log(` → 修复: ${issue.fix_hint}`);
|
|
739
749
|
}
|
|
740
750
|
}
|
|
741
|
-
console.log(
|
|
751
|
+
console.log('\n下一步: 运行 loom guide;它会根据当前状态给出可执行动作与所需 Context Pack。');
|
|
742
752
|
}
|
|
743
753
|
break;
|
|
744
754
|
}
|
|
@@ -788,17 +798,12 @@ try {
|
|
|
788
798
|
};
|
|
789
799
|
console.log(`模式: ${modeDesc[result.auto_mode] || result.auto_mode}`);
|
|
790
800
|
console.log(`\n${result.message}`);
|
|
791
|
-
console.log(`\n下一步: ${result.next_action}`);
|
|
792
|
-
console.log(` → ${result.next_command}`);
|
|
793
|
-
if (result.
|
|
794
|
-
console.log(
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
if (result.outputs && result.outputs.length > 0) {
|
|
798
|
-
console.log(`\n需要产出:`);
|
|
799
|
-
for (const f of result.outputs) console.log(` - ${f}`);
|
|
800
|
-
}
|
|
801
|
-
if (result.verify_command) {
|
|
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) {
|
|
802
807
|
console.log(`\n完成后校验: ${result.verify_command}`);
|
|
803
808
|
}
|
|
804
809
|
if (result.auto_mode === 'manual' && result.stage_num > 0 && result.stage_num < 6) {
|
package/cli/help/workflow.md
CHANGED
package/cli/src/activate.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// activate — compile a role- and intent-scoped Context Pack.
|
|
2
2
|
|
|
3
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
3
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { join, relative } from 'node:path';
|
|
5
5
|
import { getLoomRoot } from './shared/paths.js';
|
|
6
6
|
import { getIntent, getNarrative } from './intent-map.js';
|
|
7
7
|
import { getIntentDraft } from './intent-draft.js';
|
|
@@ -11,6 +11,7 @@ import { extractMdSection } from './shared/md-utils.js';
|
|
|
11
11
|
import { compileCapabilityInputs } from './capability-graph.js';
|
|
12
12
|
import { getAtelierRecord } from './atelier.js';
|
|
13
13
|
import { formatExpertisePackForPrompt, getExpertisePack, getExpertisePackState } from './expertise-pack.js';
|
|
14
|
+
import { listCapabilityProposals } from './capability-proposals.js';
|
|
14
15
|
|
|
15
16
|
const VALID_ROLES = ['weaver', 'visionary', 'architect', 'forge', 'keeper'];
|
|
16
17
|
|
|
@@ -354,6 +355,92 @@ function compileWorkingFacts(versionDir, objective) {
|
|
|
354
355
|
return refs.join('\n');
|
|
355
356
|
}
|
|
356
357
|
|
|
358
|
+
function readStageFile(path, label) {
|
|
359
|
+
if (!existsSync(path)) return `> ${label} 当前不存在;不要猜测其内容。按角色契约创建或回流。`;
|
|
360
|
+
return readFileSync(path, 'utf-8').trim();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function listMarkdownFiles(root, current = root) {
|
|
364
|
+
if (!existsSync(current)) return [];
|
|
365
|
+
return readdirSync(current, { withFileTypes: true })
|
|
366
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
367
|
+
.flatMap((entry) => {
|
|
368
|
+
const path = join(current, entry.name);
|
|
369
|
+
if (entry.isDirectory()) return listMarkdownFiles(root, path);
|
|
370
|
+
return entry.isFile() && entry.name.endsWith('.md')
|
|
371
|
+
? [relative(root, path).replaceAll('\\', '/')]
|
|
372
|
+
: [];
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Compile the stage inputs that used to be merely named by `loom guide`.
|
|
378
|
+
* These are intentionally separate from Working Facts: they are the source
|
|
379
|
+
* material for a role's immediate decision, not a request to hunt for paths.
|
|
380
|
+
*/
|
|
381
|
+
function compileStageInputs(role, versionDir, objective) {
|
|
382
|
+
if (role === 'weaver') {
|
|
383
|
+
const dimensionsRoot = join(getLoomRoot(), 'dimensions');
|
|
384
|
+
const catalog = listMarkdownFiles(dimensionsRoot)
|
|
385
|
+
.filter((path) => path !== 'SEARCH_METHODOLOGY.md')
|
|
386
|
+
.map((path) => `- dimensions/${path}`);
|
|
387
|
+
return [
|
|
388
|
+
'本节由 `loom activate weaver` 直接装配。不要为了取得这些输入而搜索 CLI 安装目录。',
|
|
389
|
+
'',
|
|
390
|
+
'### Search Methodology',
|
|
391
|
+
'',
|
|
392
|
+
readStageFile(join(dimensionsRoot, 'SEARCH_METHODOLOGY.md'), 'SEARCH_METHODOLOGY.md'),
|
|
393
|
+
'',
|
|
394
|
+
'### Available Dimension Catalog',
|
|
395
|
+
'',
|
|
396
|
+
catalog.length
|
|
397
|
+
? `${catalog.join('\n')}\n\n只打开会实质改变当前项目原则或取舍的维度;目录本身不是要求逐个阅读。`
|
|
398
|
+
: '> 当前没有可用的 dimensions 文档。',
|
|
399
|
+
].join('\n');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (role === 'architect' && versionDir && !objective.intent && !objective.draft) {
|
|
403
|
+
let proposals = [];
|
|
404
|
+
let proposalsError = null;
|
|
405
|
+
try {
|
|
406
|
+
proposals = listCapabilityProposals(versionDir, { unresolvedOnly: true });
|
|
407
|
+
} catch (error) {
|
|
408
|
+
proposalsError = error.message;
|
|
409
|
+
}
|
|
410
|
+
return [
|
|
411
|
+
'本节由 `loom activate architect` 直接装配。以下是当前设计决策所需的版本化输入,不要先手动查找路径。',
|
|
412
|
+
'',
|
|
413
|
+
'### Vision Input (01_VISION.md)',
|
|
414
|
+
'',
|
|
415
|
+
readStageFile(join(versionDir, '01_VISION.md'), '01_VISION.md'),
|
|
416
|
+
'',
|
|
417
|
+
'### Capability Graph Input (07_CAPABILITY_GRAPH.json)',
|
|
418
|
+
'',
|
|
419
|
+
readStageFile(join(versionDir, '07_CAPABILITY_GRAPH.json'), '07_CAPABILITY_GRAPH.json'),
|
|
420
|
+
'',
|
|
421
|
+
'### Intent Map Input (04_INTENT_MAP.json)',
|
|
422
|
+
'',
|
|
423
|
+
readStageFile(join(versionDir, '04_INTENT_MAP.json'), '04_INTENT_MAP.json'),
|
|
424
|
+
'',
|
|
425
|
+
'### Open Capability Graph Proposals',
|
|
426
|
+
'',
|
|
427
|
+
proposalsError
|
|
428
|
+
? `> Proposal 记录无法读取:${proposalsError}。先修复这份记录,不要猜测其状态。`
|
|
429
|
+
: proposals.length
|
|
430
|
+
? `\`\`\`json\n${JSON.stringify(proposals, null, 2)}\n\`\`\``
|
|
431
|
+
: '无。',
|
|
432
|
+
].join('\n');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (role === 'visionary') {
|
|
436
|
+
return '产品哲学与决策准则已在上方 Project Judgment 中装配;不要重新搜索 `.loom` 目录。';
|
|
437
|
+
}
|
|
438
|
+
if (role === 'forge' || role === 'keeper') {
|
|
439
|
+
return '当前 Intent 的叙事、契约、图谱路由与已就绪的 Expertise 输入已在上方装配;只有产物本身需要按 Working Facts 检查。';
|
|
440
|
+
}
|
|
441
|
+
return '当前角色没有额外的阶段输入。';
|
|
442
|
+
}
|
|
443
|
+
|
|
357
444
|
/**
|
|
358
445
|
* Compile a role-scoped Context Pack.
|
|
359
446
|
* @param {string} role
|
|
@@ -377,9 +464,10 @@ export function activateRole(role, versionDir, intentId = null) {
|
|
|
377
464
|
section('3. Hard Invariants', compileInvariants(role, versionDir)),
|
|
378
465
|
section('4. Success Contracts', compileContracts(role, versionDir, objective)),
|
|
379
466
|
section('5. Project Judgment', compileProjectJudgment(role, versionDir, objective)),
|
|
380
|
-
section('6.
|
|
381
|
-
section('7.
|
|
382
|
-
section('8.
|
|
467
|
+
section('6. Stage Inputs (command-assembled)', compileStageInputs(role, versionDir, objective)),
|
|
468
|
+
section('7. Expertise Inputs', compileExpertiseInputs(role, versionDir, objective)),
|
|
469
|
+
section('8. Working Facts', compileWorkingFacts(versionDir, objective)),
|
|
470
|
+
section('9. Role Contract / Output / Reflow / Stop', readRole(role)),
|
|
383
471
|
];
|
|
384
472
|
return `${parts.join('\n\n---\n\n')}\n`;
|
|
385
473
|
}
|
package/cli/src/guide.js
CHANGED
|
@@ -63,35 +63,41 @@ export function guideProject(projectDir, options = {}) {
|
|
|
63
63
|
outputs: ['.loom/v1/'],
|
|
64
64
|
verify_command: 'loom guide',
|
|
65
65
|
},
|
|
66
|
-
need_philosophy: {
|
|
66
|
+
need_philosophy: {
|
|
67
67
|
inputs: ['meta/PHILOSOPHY_WEAVER.md', 'meta/BASELINE.md', 'dimensions/SEARCH_METHODOLOGY.md'],
|
|
68
|
-
outputs: [`.loom/${current}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`, `.loom/${current}/00_PHILOSOPHY/ENGINEERING_CREED.md`, `.loom/${current}/00_PHILOSOPHY/DECISION_RUBRIC.md`],
|
|
69
|
-
verify_command: 'loom philosophy check',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
outputs: [`.loom/${current}/00_PHILOSOPHY/PRODUCT_PHILOSOPHY.md`, `.loom/${current}/00_PHILOSOPHY/ENGINEERING_CREED.md`, `.loom/${current}/00_PHILOSOPHY/DECISION_RUBRIC.md`],
|
|
69
|
+
verify_command: 'loom philosophy check',
|
|
70
|
+
input_delivery: 'context_pack',
|
|
71
|
+
},
|
|
72
|
+
need_vision: {
|
|
73
|
+
inputs: ['roles/visionary.md', `.loom/${current}/00_PHILOSOPHY/`],
|
|
74
|
+
outputs: [`.loom/${current}/01_VISION.md`],
|
|
75
|
+
verify_command: 'loom guide',
|
|
76
|
+
input_delivery: 'context_pack',
|
|
77
|
+
},
|
|
76
78
|
need_architecture: {
|
|
77
79
|
inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
|
|
78
80
|
outputs: [`.loom/${current}/02_ARCHITECTURE.md`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
79
81
|
verify_command: 'loom doctor',
|
|
82
|
+
input_delivery: 'context_pack',
|
|
80
83
|
},
|
|
81
84
|
need_capability_graph: {
|
|
82
85
|
inputs: ['roles/architect.md', `.loom/${current}/01_VISION.md`],
|
|
83
86
|
outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
|
|
84
87
|
verify_command: 'loom capability coverage',
|
|
88
|
+
input_delivery: 'context_pack',
|
|
85
89
|
},
|
|
86
90
|
capability_graph_incomplete: {
|
|
87
91
|
inputs: ['roles/architect.md', `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
88
92
|
outputs: [`.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/07_CAPABILITY_BRIEFS/`],
|
|
89
93
|
verify_command: 'loom capability coverage',
|
|
94
|
+
input_delivery: 'context_pack',
|
|
90
95
|
},
|
|
91
96
|
capability_graph_proposals_pending: {
|
|
92
97
|
inputs: ['roles/architect.md', `.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`],
|
|
93
98
|
outputs: [`.loom/${current}/07_GRAPH_PROPOSALS/`, `.loom/${current}/07_CAPABILITY_GRAPH.json`, `.loom/${current}/04_INTENT_MAP.json`],
|
|
94
99
|
verify_command: 'loom capability proposal list',
|
|
100
|
+
input_delivery: 'context_pack',
|
|
95
101
|
},
|
|
96
102
|
intent_map_broken: {
|
|
97
103
|
inputs: [`.loom/${current}/04_INTENT_MAP.json`],
|
|
@@ -120,9 +126,10 @@ export function guideProject(projectDir, options = {}) {
|
|
|
120
126
|
},
|
|
121
127
|
};
|
|
122
128
|
const meta = stageMeta[result.stage] || {};
|
|
123
|
-
result.inputs = meta.inputs || [];
|
|
124
|
-
result.outputs = meta.outputs || [];
|
|
125
|
-
result.verify_command = meta.verify_command || null;
|
|
129
|
+
result.inputs = meta.inputs || [];
|
|
130
|
+
result.outputs = meta.outputs || [];
|
|
131
|
+
result.verify_command = meta.verify_command || null;
|
|
132
|
+
result.input_delivery = meta.input_delivery || null;
|
|
126
133
|
// 统一后处理:写心跳 + 加 AUTO 提示词 + 判断是否需要人类 review
|
|
127
134
|
if (existsSync(loomRoot) && !options.dryRun) {
|
|
128
135
|
try {
|
|
@@ -242,14 +249,14 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
242
249
|
details: { version: current, proposals: pendingProposals.map((proposal) => proposal.id) },
|
|
243
250
|
auto,
|
|
244
251
|
next_action: '审计新信息对 Capability Graph、Intent 和契约的影响',
|
|
245
|
-
next_command: 'loom
|
|
252
|
+
next_command: 'loom activate architect',
|
|
246
253
|
message: `当前版本 ${current} 有 ${pendingProposals.length} 个未闭合的 Capability Graph proposal。它们是新要求、研究或实现发现,不得由 Forge 静默变成当前 Intent 范围。`,
|
|
247
254
|
};
|
|
248
255
|
}
|
|
249
256
|
} catch (error) {
|
|
250
257
|
return {
|
|
251
258
|
stage: 'capability_graph_proposals_pending', stage_num: 3, details: { version: current, error: error.message }, auto,
|
|
252
|
-
next_action: '修复 Capability Graph proposal', next_command: 'loom
|
|
259
|
+
next_action: '修复 Capability Graph proposal', next_command: 'loom activate architect',
|
|
253
260
|
message: `Capability Graph proposal 无法审计: ${error.message}`,
|
|
254
261
|
};
|
|
255
262
|
}
|
|
@@ -262,7 +269,7 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
262
269
|
details: { version: current, coverage: coverage.summary },
|
|
263
270
|
auto,
|
|
264
271
|
next_action: '补齐 Capability Graph 的路由、可观察验证入口与 Intent 回链',
|
|
265
|
-
next_command: 'loom
|
|
272
|
+
next_command: 'loom activate architect',
|
|
266
273
|
message: `当前版本 ${current}:Capability Graph 尚未闭合(高影响前沿 ${coverage.summary.high_unrouted}、路由缺口 ${coverage.summary.routing_gaps}、不可观察 outcome ${coverage.summary.high_outcomes_without_observable_evidence}、未映射 Intent ${coverage.summary.unmapped_intents})。先由 Architect 补图谱,再继续设计 Intent。`,
|
|
267
274
|
};
|
|
268
275
|
}
|
|
@@ -273,7 +280,7 @@ function diagnoseStage(cwd, loomRoot, auto) {
|
|
|
273
280
|
details: { version: current, error: error.message },
|
|
274
281
|
auto,
|
|
275
282
|
next_action: '修复 Capability Graph',
|
|
276
|
-
next_command: 'loom
|
|
283
|
+
next_command: 'loom activate architect',
|
|
277
284
|
message: `Capability Graph 无法通过校验: ${error.message}`,
|
|
278
285
|
};
|
|
279
286
|
}
|