@lumenflow/cli 2.2.1 → 2.3.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.
- package/README.md +147 -57
- package/dist/__tests__/agent-log-issue.test.js +56 -0
- package/dist/__tests__/cli-entry-point.test.js +66 -17
- package/dist/__tests__/cli-subprocess.test.js +25 -0
- package/dist/__tests__/init.test.js +298 -0
- package/dist/__tests__/initiative-plan.test.js +340 -0
- package/dist/__tests__/mem-cleanup-execution.test.js +19 -0
- package/dist/__tests__/merge-block.test.js +220 -0
- package/dist/__tests__/release.test.js +28 -0
- package/dist/__tests__/safe-git.test.js +191 -0
- package/dist/__tests__/state-doctor.test.js +274 -0
- package/dist/__tests__/wu-done.test.js +36 -0
- package/dist/__tests__/wu-edit.test.js +119 -0
- package/dist/__tests__/wu-prep.test.js +108 -0
- package/dist/agent-issues-query.js +4 -3
- package/dist/agent-log-issue.js +25 -4
- package/dist/backlog-prune.js +5 -4
- package/dist/cli-entry-point.js +11 -1
- package/dist/doctor.js +368 -0
- package/dist/flow-bottlenecks.js +6 -5
- package/dist/flow-report.js +4 -3
- package/dist/gates.js +468 -116
- package/dist/guard-locked.js +4 -3
- package/dist/guard-worktree-commit.js +4 -3
- package/dist/init.js +508 -86
- package/dist/initiative-add-wu.js +4 -3
- package/dist/initiative-bulk-assign-wus.js +8 -5
- package/dist/initiative-create.js +73 -37
- package/dist/initiative-edit.js +37 -21
- package/dist/initiative-list.js +4 -3
- package/dist/initiative-plan.js +337 -0
- package/dist/initiative-status.js +4 -3
- package/dist/lane-health.js +377 -0
- package/dist/lane-suggest.js +382 -0
- package/dist/mem-checkpoint.js +2 -2
- package/dist/mem-cleanup.js +2 -2
- package/dist/mem-context.js +306 -0
- package/dist/mem-create.js +2 -2
- package/dist/mem-delete.js +293 -0
- package/dist/mem-inbox.js +2 -2
- package/dist/mem-index.js +211 -0
- package/dist/mem-init.js +1 -1
- package/dist/mem-profile.js +207 -0
- package/dist/mem-promote.js +254 -0
- package/dist/mem-ready.js +2 -2
- package/dist/mem-signal.js +2 -2
- package/dist/mem-start.js +2 -2
- package/dist/mem-summarize.js +2 -2
- package/dist/mem-triage.js +2 -2
- package/dist/merge-block.js +222 -0
- package/dist/metrics-cli.js +7 -4
- package/dist/metrics-snapshot.js +4 -3
- package/dist/orchestrate-initiative.js +10 -4
- package/dist/orchestrate-monitor.js +379 -31
- package/dist/signal-cleanup.js +296 -0
- package/dist/spawn-list.js +6 -5
- package/dist/state-bootstrap.js +5 -4
- package/dist/state-cleanup.js +360 -0
- package/dist/state-doctor-fix.js +196 -0
- package/dist/state-doctor.js +501 -0
- package/dist/validate-agent-skills.js +4 -3
- package/dist/validate-agent-sync.js +4 -3
- package/dist/validate-backlog-sync.js +7 -84
- package/dist/validate-skills-spec.js +4 -3
- package/dist/validate.js +7 -107
- package/dist/wu-block.js +3 -3
- package/dist/wu-claim.js +208 -98
- package/dist/wu-cleanup.js +5 -4
- package/dist/wu-create.js +71 -46
- package/dist/wu-delete.js +88 -60
- package/dist/wu-deps.js +6 -5
- package/dist/wu-done-check.js +34 -0
- package/dist/wu-done.js +60 -24
- package/dist/wu-edit.js +63 -28
- package/dist/wu-infer-lane.js +7 -6
- package/dist/wu-preflight.js +23 -81
- package/dist/wu-prep.js +125 -0
- package/dist/wu-prune.js +4 -3
- package/dist/wu-recover.js +88 -22
- package/dist/wu-repair.js +7 -6
- package/dist/wu-spawn.js +226 -270
- package/dist/wu-status.js +4 -3
- package/dist/wu-unblock.js +5 -5
- package/dist/wu-unlock-lane.js +4 -3
- package/dist/wu-validate.js +5 -4
- package/package.json +16 -7
- package/templates/core/.lumenflow/constraints.md.template +192 -0
- package/templates/core/.lumenflow/rules/git-safety.md.template +27 -0
- package/templates/core/.lumenflow/rules/wu-workflow.md.template +48 -0
- package/templates/core/AGENTS.md.template +60 -0
- package/templates/core/LUMENFLOW.md.template +255 -0
- package/templates/core/UPGRADING.md.template +121 -0
- package/templates/core/ai/onboarding/agent-safety-card.md.template +106 -0
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +198 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +186 -0
- package/templates/core/ai/onboarding/release-process.md.template +362 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +159 -0
- package/templates/core/ai/onboarding/wu-create-checklist.md.template +117 -0
- package/templates/vendors/aider/.aider.conf.yml.template +27 -0
- package/templates/vendors/claude/.claude/CLAUDE.md.template +52 -0
- package/templates/vendors/claude/.claude/settings.json.template +49 -0
- package/templates/vendors/claude/.claude/skills/bug-classification/SKILL.md.template +192 -0
- package/templates/vendors/claude/.claude/skills/code-quality/SKILL.md.template +152 -0
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +155 -0
- package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +304 -0
- package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +131 -0
- package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +164 -0
- package/templates/vendors/claude/.claude/skills/library-first/SKILL.md.template +98 -0
- package/templates/vendors/claude/.claude/skills/lumenflow-gates/SKILL.md.template +87 -0
- package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +84 -0
- package/templates/vendors/claude/.claude/skills/ops-maintenance/SKILL.md.template +254 -0
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +189 -0
- package/templates/vendors/claude/.claude/skills/tdd-workflow/SKILL.md.template +139 -0
- package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +138 -0
- package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +106 -0
- package/templates/vendors/cline/.clinerules.template +53 -0
- package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +34 -0
- package/templates/vendors/cursor/.cursor/rules.md.template +28 -0
- package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +34 -0
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/* eslint-disable no-console -- CLI command uses console for status output */
|
|
2
|
+
/**
|
|
3
|
+
* @file doctor.ts
|
|
4
|
+
* LumenFlow health check command (WU-1177)
|
|
5
|
+
* WU-1191: Lane health check integration
|
|
6
|
+
* Verifies all safety components are installed and configured correctly
|
|
7
|
+
*/
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { createWUParser } from '@lumenflow/core';
|
|
12
|
+
import { loadLaneDefinitions, detectLaneOverlaps } from './lane-health.js';
|
|
13
|
+
/**
|
|
14
|
+
* CLI option definitions for doctor command
|
|
15
|
+
*/
|
|
16
|
+
const DOCTOR_OPTIONS = {
|
|
17
|
+
verbose: {
|
|
18
|
+
name: 'verbose',
|
|
19
|
+
flags: '-v, --verbose',
|
|
20
|
+
description: 'Show detailed output including all checks',
|
|
21
|
+
},
|
|
22
|
+
json: {
|
|
23
|
+
name: 'json',
|
|
24
|
+
flags: '--json',
|
|
25
|
+
description: 'Output results as JSON',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Parse doctor command options
|
|
30
|
+
*/
|
|
31
|
+
export function parseDoctorOptions() {
|
|
32
|
+
const opts = createWUParser({
|
|
33
|
+
name: 'lumenflow-doctor',
|
|
34
|
+
description: 'Check LumenFlow safety components and configuration',
|
|
35
|
+
options: Object.values(DOCTOR_OPTIONS),
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
verbose: opts.verbose ?? false,
|
|
39
|
+
json: opts.json ?? false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if Husky hooks are installed
|
|
44
|
+
*/
|
|
45
|
+
function checkHusky(projectDir) {
|
|
46
|
+
const huskyDir = path.join(projectDir, '.husky');
|
|
47
|
+
const preCommit = path.join(huskyDir, 'pre-commit');
|
|
48
|
+
if (!fs.existsSync(huskyDir)) {
|
|
49
|
+
return {
|
|
50
|
+
passed: false,
|
|
51
|
+
message: 'Husky hooks not installed',
|
|
52
|
+
details: 'Run: pnpm install && pnpm prepare',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (!fs.existsSync(preCommit)) {
|
|
56
|
+
return {
|
|
57
|
+
passed: false,
|
|
58
|
+
message: 'Husky pre-commit hook missing',
|
|
59
|
+
details: 'Run: pnpm prepare',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
passed: true,
|
|
64
|
+
message: 'Husky hooks installed (.husky/pre-commit)',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if safe-git wrapper is present
|
|
69
|
+
*/
|
|
70
|
+
function checkSafeGit(projectDir) {
|
|
71
|
+
const safeGitPath = path.join(projectDir, 'scripts', 'safe-git');
|
|
72
|
+
if (!fs.existsSync(safeGitPath)) {
|
|
73
|
+
return {
|
|
74
|
+
passed: false,
|
|
75
|
+
message: 'Safe-git wrapper missing',
|
|
76
|
+
details: 'The scripts/safe-git file should exist to block destructive git commands',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
passed: true,
|
|
81
|
+
message: 'Safe-git wrapper present (scripts/safe-git)',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if AGENTS.md exists
|
|
86
|
+
*/
|
|
87
|
+
function checkAgentsMd(projectDir) {
|
|
88
|
+
const agentsMdPath = path.join(projectDir, 'AGENTS.md');
|
|
89
|
+
if (!fs.existsSync(agentsMdPath)) {
|
|
90
|
+
return {
|
|
91
|
+
passed: false,
|
|
92
|
+
message: 'AGENTS.md missing',
|
|
93
|
+
details: 'Run: lumenflow init to create universal agent instructions',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
passed: true,
|
|
98
|
+
message: 'AGENTS.md exists',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if .lumenflow.config.yaml exists
|
|
103
|
+
*/
|
|
104
|
+
function checkLumenflowConfig(projectDir) {
|
|
105
|
+
const configPath = path.join(projectDir, '.lumenflow.config.yaml');
|
|
106
|
+
if (!fs.existsSync(configPath)) {
|
|
107
|
+
return {
|
|
108
|
+
passed: false,
|
|
109
|
+
message: 'LumenFlow config missing',
|
|
110
|
+
details: 'Run: lumenflow init to create configuration',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
passed: true,
|
|
115
|
+
message: 'LumenFlow config present (.lumenflow.config.yaml)',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check vendor-specific config files
|
|
120
|
+
*/
|
|
121
|
+
function checkVendorConfigs(projectDir) {
|
|
122
|
+
const claudeMdPath = path.join(projectDir, 'CLAUDE.md');
|
|
123
|
+
const cursorRulesPath = path.join(projectDir, '.cursor', 'rules', 'lumenflow.md');
|
|
124
|
+
const windsurfRulesPath = path.join(projectDir, '.windsurf', 'rules', 'lumenflow.md');
|
|
125
|
+
const clineRulesPath = path.join(projectDir, '.clinerules');
|
|
126
|
+
const agentsMdPath = path.join(projectDir, 'AGENTS.md');
|
|
127
|
+
return {
|
|
128
|
+
claude: {
|
|
129
|
+
present: fs.existsSync(claudeMdPath),
|
|
130
|
+
path: 'CLAUDE.md',
|
|
131
|
+
},
|
|
132
|
+
cursor: {
|
|
133
|
+
present: fs.existsSync(cursorRulesPath),
|
|
134
|
+
path: '.cursor/rules/lumenflow.md',
|
|
135
|
+
},
|
|
136
|
+
windsurf: {
|
|
137
|
+
present: fs.existsSync(windsurfRulesPath),
|
|
138
|
+
path: '.windsurf/rules/lumenflow.md',
|
|
139
|
+
},
|
|
140
|
+
cline: {
|
|
141
|
+
present: fs.existsSync(clineRulesPath),
|
|
142
|
+
path: '.clinerules',
|
|
143
|
+
},
|
|
144
|
+
codex: {
|
|
145
|
+
// Codex reads AGENTS.md directly
|
|
146
|
+
present: fs.existsSync(agentsMdPath),
|
|
147
|
+
path: 'AGENTS.md',
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get command version safely using execFileSync (no shell injection risk)
|
|
153
|
+
*/
|
|
154
|
+
function getCommandVersion(command, args) {
|
|
155
|
+
try {
|
|
156
|
+
const output = execFileSync(command, args, {
|
|
157
|
+
encoding: 'utf-8',
|
|
158
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
159
|
+
}).trim();
|
|
160
|
+
return output;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return 'not found';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Parse semver version string to compare
|
|
168
|
+
*/
|
|
169
|
+
function parseVersion(versionStr) {
|
|
170
|
+
// eslint-disable-next-line sonarjs/slow-regex, sonarjs/prefer-regexp-exec -- Simple semver extraction, no backtracking risk
|
|
171
|
+
const match = versionStr.match(/(\d+)\.(\d+)\.?(\d+)?/);
|
|
172
|
+
if (!match) {
|
|
173
|
+
return [0, 0, 0];
|
|
174
|
+
}
|
|
175
|
+
return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3] || '0', 10)];
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Compare versions: returns true if actual >= required
|
|
179
|
+
*/
|
|
180
|
+
function compareVersions(actual, required) {
|
|
181
|
+
const actualParts = parseVersion(actual);
|
|
182
|
+
const requiredParts = parseVersion(required);
|
|
183
|
+
for (let i = 0; i < 3; i++) {
|
|
184
|
+
if (actualParts[i] > requiredParts[i]) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (actualParts[i] < requiredParts[i]) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* WU-1191: Check lane health configuration
|
|
195
|
+
* Integrates lane:health overlap detection into lumenflow doctor
|
|
196
|
+
*/
|
|
197
|
+
function checkLaneHealth(projectDir) {
|
|
198
|
+
const lanes = loadLaneDefinitions(projectDir);
|
|
199
|
+
// No lanes configured - considered healthy (nothing to check)
|
|
200
|
+
if (lanes.length === 0) {
|
|
201
|
+
return {
|
|
202
|
+
passed: true,
|
|
203
|
+
message: 'No lane definitions found - skipping lane health check',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
// Check for overlapping code_paths
|
|
207
|
+
const overlapResult = detectLaneOverlaps(lanes);
|
|
208
|
+
if (overlapResult.hasOverlaps) {
|
|
209
|
+
const overlapCount = overlapResult.overlaps.length;
|
|
210
|
+
const firstOverlap = overlapResult.overlaps[0];
|
|
211
|
+
const laneNames = firstOverlap.lanes.join(' <-> ');
|
|
212
|
+
return {
|
|
213
|
+
passed: false,
|
|
214
|
+
message: `Lane overlap detected: ${overlapCount} overlap(s) found`,
|
|
215
|
+
details: `First overlap: ${laneNames}. Run 'pnpm lane:health' for full report.`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
passed: true,
|
|
220
|
+
message: `Lane configuration healthy (${lanes.length} lanes, no overlaps)`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Check prerequisite versions
|
|
225
|
+
*/
|
|
226
|
+
function checkPrerequisites() {
|
|
227
|
+
const nodeVersion = getCommandVersion('node', ['--version']);
|
|
228
|
+
const pnpmVersion = getCommandVersion('pnpm', ['--version']);
|
|
229
|
+
const gitVersion = getCommandVersion('git', ['--version']);
|
|
230
|
+
const requiredNode = '22.0.0';
|
|
231
|
+
const requiredPnpm = '9.0.0';
|
|
232
|
+
const requiredGit = '2.0.0';
|
|
233
|
+
const nodeOk = nodeVersion !== 'not found' && compareVersions(nodeVersion, requiredNode);
|
|
234
|
+
const pnpmOk = pnpmVersion !== 'not found' && compareVersions(pnpmVersion, requiredPnpm);
|
|
235
|
+
const gitOk = gitVersion !== 'not found' && compareVersions(gitVersion, requiredGit);
|
|
236
|
+
return {
|
|
237
|
+
node: {
|
|
238
|
+
passed: nodeOk,
|
|
239
|
+
version: nodeVersion,
|
|
240
|
+
required: `>=${requiredNode}`,
|
|
241
|
+
message: nodeOk ? undefined : `Node.js ${requiredNode}+ required`,
|
|
242
|
+
},
|
|
243
|
+
pnpm: {
|
|
244
|
+
passed: pnpmOk,
|
|
245
|
+
version: pnpmVersion,
|
|
246
|
+
required: `>=${requiredPnpm}`,
|
|
247
|
+
message: pnpmOk ? undefined : `pnpm ${requiredPnpm}+ required`,
|
|
248
|
+
},
|
|
249
|
+
git: {
|
|
250
|
+
passed: gitOk,
|
|
251
|
+
version: gitVersion,
|
|
252
|
+
required: `>=${requiredGit}`,
|
|
253
|
+
message: gitOk ? undefined : `Git ${requiredGit}+ required`,
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Run all doctor checks
|
|
259
|
+
*/
|
|
260
|
+
export async function runDoctor(projectDir) {
|
|
261
|
+
const checks = {
|
|
262
|
+
husky: checkHusky(projectDir),
|
|
263
|
+
safeGit: checkSafeGit(projectDir),
|
|
264
|
+
agentsMd: checkAgentsMd(projectDir),
|
|
265
|
+
lumenflowConfig: checkLumenflowConfig(projectDir),
|
|
266
|
+
// WU-1191: Lane health check
|
|
267
|
+
laneHealth: checkLaneHealth(projectDir),
|
|
268
|
+
};
|
|
269
|
+
const vendorConfigs = checkVendorConfigs(projectDir);
|
|
270
|
+
const prerequisites = checkPrerequisites();
|
|
271
|
+
// Determine overall status
|
|
272
|
+
// Note: laneHealth is advisory (not included in critical checks)
|
|
273
|
+
const criticalChecks = [checks.husky, checks.safeGit, checks.agentsMd];
|
|
274
|
+
const allCriticalPassed = criticalChecks.every((check) => check.passed);
|
|
275
|
+
return {
|
|
276
|
+
status: allCriticalPassed ? 'ACTIVE' : 'INCOMPLETE',
|
|
277
|
+
checks,
|
|
278
|
+
vendorConfigs,
|
|
279
|
+
prerequisites,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Format doctor output for terminal display
|
|
284
|
+
*/
|
|
285
|
+
export function formatDoctorOutput(result) {
|
|
286
|
+
const lines = [];
|
|
287
|
+
lines.push('');
|
|
288
|
+
lines.push('LumenFlow Health Check');
|
|
289
|
+
lines.push('─'.repeat(45));
|
|
290
|
+
// Core safety checks
|
|
291
|
+
lines.push('');
|
|
292
|
+
lines.push('Core Safety Components:');
|
|
293
|
+
const formatCheck = (check) => {
|
|
294
|
+
const symbol = check.passed ? '✓' : '✗';
|
|
295
|
+
return ` ${symbol} ${check.message}`;
|
|
296
|
+
};
|
|
297
|
+
lines.push(formatCheck(result.checks.husky));
|
|
298
|
+
lines.push(formatCheck(result.checks.safeGit));
|
|
299
|
+
lines.push(formatCheck(result.checks.agentsMd));
|
|
300
|
+
lines.push(formatCheck(result.checks.lumenflowConfig));
|
|
301
|
+
// WU-1191: Lane Health section
|
|
302
|
+
lines.push('');
|
|
303
|
+
lines.push('Lane Health:');
|
|
304
|
+
lines.push(formatCheck(result.checks.laneHealth));
|
|
305
|
+
// Vendor configs
|
|
306
|
+
lines.push('');
|
|
307
|
+
lines.push('Vendor Configs:');
|
|
308
|
+
const vendors = ['claude', 'cursor', 'windsurf', 'cline', 'codex'];
|
|
309
|
+
for (const vendor of vendors) {
|
|
310
|
+
const config = result.vendorConfigs[vendor];
|
|
311
|
+
const symbol = config.present ? '✓' : '○';
|
|
312
|
+
const status = config.present ? 'present' : 'not configured';
|
|
313
|
+
lines.push(` ${symbol} ${vendor}: ${status} (${config.path})`);
|
|
314
|
+
}
|
|
315
|
+
// Prerequisites
|
|
316
|
+
lines.push('');
|
|
317
|
+
lines.push('Prerequisites:');
|
|
318
|
+
const prereqs = ['node', 'pnpm', 'git'];
|
|
319
|
+
for (const prereq of prereqs) {
|
|
320
|
+
const check = result.prerequisites[prereq];
|
|
321
|
+
const symbol = check.passed ? '✓' : '✗';
|
|
322
|
+
const versionDisplay = check.version === 'not found' ? 'not found' : check.version;
|
|
323
|
+
lines.push(` ${symbol} ${prereq}: ${versionDisplay} (required: ${check.required})`);
|
|
324
|
+
}
|
|
325
|
+
// Summary
|
|
326
|
+
lines.push('');
|
|
327
|
+
lines.push('─'.repeat(45));
|
|
328
|
+
lines.push(`LumenFlow safety: ${result.status}`);
|
|
329
|
+
if (result.status === 'INCOMPLETE') {
|
|
330
|
+
lines.push('');
|
|
331
|
+
lines.push('To fix missing components:');
|
|
332
|
+
lines.push(' pnpm install && pnpm prepare # Install Husky hooks');
|
|
333
|
+
lines.push(' lumenflow init # Create missing config files');
|
|
334
|
+
}
|
|
335
|
+
lines.push('');
|
|
336
|
+
return lines.join('\n');
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Format doctor output as JSON
|
|
340
|
+
*/
|
|
341
|
+
export function formatDoctorJson(result) {
|
|
342
|
+
return JSON.stringify(result, null, 2);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* CLI entry point
|
|
346
|
+
*/
|
|
347
|
+
export async function main() {
|
|
348
|
+
const opts = parseDoctorOptions();
|
|
349
|
+
const projectDir = process.cwd();
|
|
350
|
+
const result = await runDoctor(projectDir);
|
|
351
|
+
if (opts.json) {
|
|
352
|
+
console.log(formatDoctorJson(result));
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.log(formatDoctorOutput(result));
|
|
356
|
+
}
|
|
357
|
+
// Exit with error code if incomplete
|
|
358
|
+
if (result.status === 'INCOMPLETE') {
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
// CLI invocation when run directly
|
|
363
|
+
if (import.meta.main) {
|
|
364
|
+
main().catch((error) => {
|
|
365
|
+
console.error('Doctor failed:', error);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
});
|
|
368
|
+
}
|
package/dist/flow-bottlenecks.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { Command } from 'commander';
|
|
17
17
|
import { getBottleneckAnalysis, } from '@lumenflow/metrics';
|
|
18
|
-
import {
|
|
18
|
+
import { buildDependencyGraphAsync, renderMermaid, } from '@lumenflow/core/dist/dependency-graph.js';
|
|
19
19
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
20
20
|
/** Log prefix for console output */
|
|
21
21
|
const LOG_PREFIX = '[flow:bottlenecks]';
|
|
@@ -144,7 +144,7 @@ async function main() {
|
|
|
144
144
|
const limit = parseInt(opts.limit, 10);
|
|
145
145
|
console.log(`${LOG_PREFIX} Building dependency graph...`);
|
|
146
146
|
// Build dependency graph from WU YAML files
|
|
147
|
-
const coreGraph =
|
|
147
|
+
const coreGraph = await buildDependencyGraphAsync();
|
|
148
148
|
if (coreGraph.size === 0) {
|
|
149
149
|
console.log(`${LOG_PREFIX} No WUs found in dependency graph.`);
|
|
150
150
|
console.log(`${LOG_PREFIX} Ensure WU YAML files exist in docs/04-operations/tasks/wu/ with blocked_by/blocks fields.`);
|
|
@@ -174,9 +174,10 @@ async function main() {
|
|
|
174
174
|
console.log(JSON.stringify(analysis, null, 2));
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
178
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
179
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
180
|
+
if (import.meta.main) {
|
|
180
181
|
main().catch((err) => {
|
|
181
182
|
die(`Bottleneck analysis failed: ${err.message}`);
|
|
182
183
|
});
|
package/dist/flow-report.js
CHANGED
|
@@ -302,9 +302,10 @@ async function main() {
|
|
|
302
302
|
console.log(JSON.stringify(report, null, 2));
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
306
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
307
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
308
|
+
if (import.meta.main) {
|
|
308
309
|
main().catch((err) => {
|
|
309
310
|
die(`Flow report failed: ${err.message}`);
|
|
310
311
|
});
|