@lumenflow/cli 2.2.2 → 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__/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 +356 -101
- 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 +4 -3
- package/dist/validate-skills-spec.js +4 -3
- package/dist/validate.js +4 -3
- 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 +39 -12
- 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
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { ensureCleanWorktree } from '../wu-done-check.js';
|
|
3
|
+
import * as gitAdapter from '@lumenflow/core/git-adapter';
|
|
4
|
+
import * as errorHandler from '@lumenflow/core/error-handler';
|
|
5
|
+
// Mock dependencies
|
|
6
|
+
vi.mock('@lumenflow/core/git-adapter');
|
|
7
|
+
vi.mock('@lumenflow/core/error-handler');
|
|
8
|
+
describe('wu-done', () => {
|
|
9
|
+
describe('ensureCleanWorktree', () => {
|
|
10
|
+
let mockGit;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.resetAllMocks();
|
|
13
|
+
mockGit = {
|
|
14
|
+
getStatus: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
vi.mocked(gitAdapter.createGitForPath).mockReturnValue(mockGit);
|
|
17
|
+
});
|
|
18
|
+
it('should pass if worktree is clean', async () => {
|
|
19
|
+
mockGit.getStatus.mockResolvedValue(''); // Clean status
|
|
20
|
+
await ensureCleanWorktree('/path/to/worktree');
|
|
21
|
+
expect(mockGit.getStatus).toHaveBeenCalled();
|
|
22
|
+
expect(errorHandler.die).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
it('should die if worktree has uncommitted changes', async () => {
|
|
25
|
+
mockGit.getStatus.mockResolvedValue('M file.ts\n?? new-file.ts'); // Dirty status
|
|
26
|
+
await ensureCleanWorktree('/path/to/worktree');
|
|
27
|
+
expect(mockGit.getStatus).toHaveBeenCalled();
|
|
28
|
+
expect(errorHandler.die).toHaveBeenCalledWith(expect.stringContaining('Worktree has uncommitted changes'));
|
|
29
|
+
});
|
|
30
|
+
it('should use the correct worktree path', async () => {
|
|
31
|
+
mockGit.getStatus.mockResolvedValue('');
|
|
32
|
+
await ensureCleanWorktree('/custom/worktree/path');
|
|
33
|
+
expect(gitAdapter.createGitForPath).toHaveBeenCalledWith('/custom/worktree/path');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-1225: Tests for wu-edit append-by-default behavior
|
|
3
|
+
*
|
|
4
|
+
* Validates that array fields (code_paths, risks, acceptance, etc.)
|
|
5
|
+
* now append by default instead of replacing, making behavior consistent
|
|
6
|
+
* across all array options.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { applyEdits, mergeStringField } from '../wu-edit.js';
|
|
10
|
+
describe('wu-edit applyEdits', () => {
|
|
11
|
+
describe('WU-1225: code_paths append-by-default', () => {
|
|
12
|
+
const baseWU = {
|
|
13
|
+
id: 'WU-1225',
|
|
14
|
+
status: 'ready',
|
|
15
|
+
code_paths: ['existing/path.ts'],
|
|
16
|
+
};
|
|
17
|
+
it('appends code_paths by default (no flags)', () => {
|
|
18
|
+
const opts = { codePaths: ['new/path.ts'] };
|
|
19
|
+
const result = applyEdits(baseWU, opts);
|
|
20
|
+
expect(result.code_paths).toEqual(['existing/path.ts', 'new/path.ts']);
|
|
21
|
+
});
|
|
22
|
+
it('appends code_paths when --append is set (backwards compat)', () => {
|
|
23
|
+
const opts = { codePaths: ['new/path.ts'], append: true };
|
|
24
|
+
const result = applyEdits(baseWU, opts);
|
|
25
|
+
expect(result.code_paths).toEqual(['existing/path.ts', 'new/path.ts']);
|
|
26
|
+
});
|
|
27
|
+
it('replaces code_paths when --replace-code-paths is set', () => {
|
|
28
|
+
const opts = { codePaths: ['new/path.ts'], replaceCodePaths: true };
|
|
29
|
+
const result = applyEdits(baseWU, opts);
|
|
30
|
+
expect(result.code_paths).toEqual(['new/path.ts']);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('WU-1225: risks append-by-default', () => {
|
|
34
|
+
const baseWU = {
|
|
35
|
+
id: 'WU-1225',
|
|
36
|
+
status: 'ready',
|
|
37
|
+
risks: ['existing risk'],
|
|
38
|
+
};
|
|
39
|
+
it('appends risks by default', () => {
|
|
40
|
+
const opts = { risks: ['new risk'] };
|
|
41
|
+
const result = applyEdits(baseWU, opts);
|
|
42
|
+
expect(result.risks).toEqual(['existing risk', 'new risk']);
|
|
43
|
+
});
|
|
44
|
+
it('replaces risks when --replace-risks is set', () => {
|
|
45
|
+
const opts = { risks: ['new risk'], replaceRisks: true };
|
|
46
|
+
const result = applyEdits(baseWU, opts);
|
|
47
|
+
expect(result.risks).toEqual(['new risk']);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('WU-1225: blocked_by append-by-default', () => {
|
|
51
|
+
const baseWU = {
|
|
52
|
+
id: 'WU-1225',
|
|
53
|
+
status: 'ready',
|
|
54
|
+
blocked_by: ['WU-100'],
|
|
55
|
+
};
|
|
56
|
+
it('appends blocked_by by default', () => {
|
|
57
|
+
const opts = { blockedBy: 'WU-200' };
|
|
58
|
+
const result = applyEdits(baseWU, opts);
|
|
59
|
+
expect(result.blocked_by).toEqual(['WU-100', 'WU-200']);
|
|
60
|
+
});
|
|
61
|
+
it('replaces blocked_by when --replace-blocked-by is set', () => {
|
|
62
|
+
const opts = { blockedBy: 'WU-200', replaceBlockedBy: true };
|
|
63
|
+
const result = applyEdits(baseWU, opts);
|
|
64
|
+
expect(result.blocked_by).toEqual(['WU-200']);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('WU-1225: dependencies append-by-default', () => {
|
|
68
|
+
const baseWU = {
|
|
69
|
+
id: 'WU-1225',
|
|
70
|
+
status: 'ready',
|
|
71
|
+
dependencies: ['WU-50'],
|
|
72
|
+
};
|
|
73
|
+
it('appends dependencies by default', () => {
|
|
74
|
+
const opts = { addDep: 'WU-60' };
|
|
75
|
+
const result = applyEdits(baseWU, opts);
|
|
76
|
+
expect(result.dependencies).toEqual(['WU-50', 'WU-60']);
|
|
77
|
+
});
|
|
78
|
+
it('replaces dependencies when --replace-dependencies is set', () => {
|
|
79
|
+
const opts = { addDep: 'WU-60', replaceDependencies: true };
|
|
80
|
+
const result = applyEdits(baseWU, opts);
|
|
81
|
+
expect(result.dependencies).toEqual(['WU-60']);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe('WU-1144: acceptance already appends by default', () => {
|
|
85
|
+
const baseWU = {
|
|
86
|
+
id: 'WU-1225',
|
|
87
|
+
status: 'ready',
|
|
88
|
+
acceptance: ['existing criterion'],
|
|
89
|
+
};
|
|
90
|
+
it('appends acceptance by default', () => {
|
|
91
|
+
const opts = { acceptance: ['new criterion'] };
|
|
92
|
+
const result = applyEdits(baseWU, opts);
|
|
93
|
+
expect(result.acceptance).toEqual(['existing criterion', 'new criterion']);
|
|
94
|
+
});
|
|
95
|
+
it('replaces acceptance when --replace-acceptance is set', () => {
|
|
96
|
+
const opts = { acceptance: ['new criterion'], replaceAcceptance: true };
|
|
97
|
+
const result = applyEdits(baseWU, opts);
|
|
98
|
+
expect(result.acceptance).toEqual(['new criterion']);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('wu-edit mergeStringField', () => {
|
|
103
|
+
it('appends by default', () => {
|
|
104
|
+
const result = mergeStringField('existing', 'new', false);
|
|
105
|
+
expect(result).toBe('existing\n\nnew');
|
|
106
|
+
});
|
|
107
|
+
it('replaces when shouldReplace is true', () => {
|
|
108
|
+
const result = mergeStringField('existing', 'new', true);
|
|
109
|
+
expect(result).toBe('new');
|
|
110
|
+
});
|
|
111
|
+
it('returns new value if existing is empty', () => {
|
|
112
|
+
const result = mergeStringField('', 'new', false);
|
|
113
|
+
expect(result).toBe('new');
|
|
114
|
+
});
|
|
115
|
+
it('returns new value if existing is undefined', () => {
|
|
116
|
+
const result = mergeStringField(undefined, 'new', false);
|
|
117
|
+
expect(result).toBe('new');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as locationResolver from '@lumenflow/core/dist/context/location-resolver.js';
|
|
3
|
+
import * as wuYaml from '@lumenflow/core/dist/wu-yaml.js';
|
|
4
|
+
import { CONTEXT_VALIDATION, WU_STATUS } from '@lumenflow/core/dist/wu-constants.js';
|
|
5
|
+
const { LOCATION_TYPES } = CONTEXT_VALIDATION;
|
|
6
|
+
// Mock dependencies
|
|
7
|
+
vi.mock('@lumenflow/core/dist/context/location-resolver.js');
|
|
8
|
+
vi.mock('@lumenflow/core/dist/error-handler.js');
|
|
9
|
+
vi.mock('@lumenflow/core/dist/wu-yaml.js');
|
|
10
|
+
vi.mock('../gates.js', () => ({
|
|
11
|
+
runGates: vi.fn().mockResolvedValue(true),
|
|
12
|
+
}));
|
|
13
|
+
describe('wu-prep (WU-1223)', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
vi.resetAllMocks();
|
|
16
|
+
});
|
|
17
|
+
describe('location validation', () => {
|
|
18
|
+
it('should error when run from main checkout', async () => {
|
|
19
|
+
// Mock location as main checkout
|
|
20
|
+
vi.mocked(locationResolver.resolveLocation).mockResolvedValue({
|
|
21
|
+
type: LOCATION_TYPES.MAIN,
|
|
22
|
+
cwd: '/repo',
|
|
23
|
+
gitRoot: '/repo',
|
|
24
|
+
mainCheckout: '/repo',
|
|
25
|
+
worktreeName: null,
|
|
26
|
+
worktreeWuId: null,
|
|
27
|
+
});
|
|
28
|
+
// Import after mocks are set up
|
|
29
|
+
const { resolveLocation } = await import('@lumenflow/core/dist/context/location-resolver.js');
|
|
30
|
+
const location = await resolveLocation();
|
|
31
|
+
// Verify the mock returns main
|
|
32
|
+
expect(location.type).toBe(LOCATION_TYPES.MAIN);
|
|
33
|
+
});
|
|
34
|
+
it('should proceed when run from worktree', async () => {
|
|
35
|
+
// Mock location as worktree
|
|
36
|
+
vi.mocked(locationResolver.resolveLocation).mockResolvedValue({
|
|
37
|
+
type: LOCATION_TYPES.WORKTREE,
|
|
38
|
+
cwd: '/repo/worktrees/framework-cli-wu-1223',
|
|
39
|
+
gitRoot: '/repo/worktrees/framework-cli-wu-1223',
|
|
40
|
+
mainCheckout: '/repo',
|
|
41
|
+
worktreeName: 'framework-cli-wu-1223',
|
|
42
|
+
worktreeWuId: 'WU-1223',
|
|
43
|
+
});
|
|
44
|
+
const { resolveLocation } = await import('@lumenflow/core/dist/context/location-resolver.js');
|
|
45
|
+
const location = await resolveLocation();
|
|
46
|
+
// Verify the mock returns worktree
|
|
47
|
+
expect(location.type).toBe(LOCATION_TYPES.WORKTREE);
|
|
48
|
+
expect(location.mainCheckout).toBe('/repo');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('WU status validation', () => {
|
|
52
|
+
it('should only allow in_progress WUs', async () => {
|
|
53
|
+
// Mock WU YAML with wrong status
|
|
54
|
+
const mockDoc = {
|
|
55
|
+
id: 'WU-1223',
|
|
56
|
+
status: WU_STATUS.DONE,
|
|
57
|
+
title: 'Test WU',
|
|
58
|
+
};
|
|
59
|
+
vi.mocked(wuYaml.readWU).mockReturnValue(mockDoc);
|
|
60
|
+
const { readWU } = await import('@lumenflow/core/dist/wu-yaml.js');
|
|
61
|
+
const doc = readWU('path/to/wu.yaml', 'WU-1223');
|
|
62
|
+
expect(doc.status).toBe(WU_STATUS.DONE);
|
|
63
|
+
expect(doc.status).not.toBe(WU_STATUS.IN_PROGRESS);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('success message', () => {
|
|
67
|
+
it('should include copy-paste instruction with main path', async () => {
|
|
68
|
+
// The success message should include:
|
|
69
|
+
// 1. Main checkout path
|
|
70
|
+
// 2. WU ID
|
|
71
|
+
// 3. Copy-paste command: cd <main> && pnpm wu:done --id <WU-ID>
|
|
72
|
+
const mainCheckout = '/repo';
|
|
73
|
+
const wuId = 'WU-1223';
|
|
74
|
+
// Build expected command that would be in the success message
|
|
75
|
+
const expectedCommand = `cd ${mainCheckout} && pnpm wu:done --id ${wuId}`;
|
|
76
|
+
expect(expectedCommand).toBe('cd /repo && pnpm wu:done --id WU-1223');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('wu:done worktree check (WU-1223)', () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
vi.resetAllMocks();
|
|
83
|
+
});
|
|
84
|
+
it('should error when run from worktree with guidance to use wu:prep', async () => {
|
|
85
|
+
// Mock location as worktree
|
|
86
|
+
vi.mocked(locationResolver.resolveLocation).mockResolvedValue({
|
|
87
|
+
type: LOCATION_TYPES.WORKTREE,
|
|
88
|
+
cwd: '/repo/worktrees/framework-cli-wu-1223',
|
|
89
|
+
gitRoot: '/repo/worktrees/framework-cli-wu-1223',
|
|
90
|
+
mainCheckout: '/repo',
|
|
91
|
+
worktreeName: 'framework-cli-wu-1223',
|
|
92
|
+
worktreeWuId: 'WU-1223',
|
|
93
|
+
});
|
|
94
|
+
const { resolveLocation } = await import('@lumenflow/core/dist/context/location-resolver.js');
|
|
95
|
+
const location = await resolveLocation();
|
|
96
|
+
// The error message should guide user to wu:prep workflow
|
|
97
|
+
expect(location.type).toBe(LOCATION_TYPES.WORKTREE);
|
|
98
|
+
// Error message should contain:
|
|
99
|
+
const errorShouldContain = [
|
|
100
|
+
'wu:prep', // Mention the new command
|
|
101
|
+
'main checkout', // Explain where wu:done should run
|
|
102
|
+
'/repo', // Main checkout path
|
|
103
|
+
];
|
|
104
|
+
// Build the expected error content
|
|
105
|
+
const expectedGuidance = `pnpm wu:prep --id WU-1223`;
|
|
106
|
+
expect(expectedGuidance).toContain('wu:prep');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -243,9 +243,10 @@ async function main() {
|
|
|
243
243
|
const issues = await readIssues(baseDir, sinceDate, opts.category, opts.severity);
|
|
244
244
|
displaySummary(issues, opts.since);
|
|
245
245
|
}
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
247
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
248
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
249
|
+
if (import.meta.main) {
|
|
249
250
|
main().catch((err) => {
|
|
250
251
|
die(`Issues query failed: ${err.message}`);
|
|
251
252
|
});
|
package/dist/agent-log-issue.js
CHANGED
|
@@ -6,11 +6,32 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* pnpm agent:log-issue --category workflow --severity minor --title "..." --description "..."
|
|
9
|
+
* pnpm agent:log-issue --category tooling --severity major --title "..." --description "..." \
|
|
10
|
+
* --tag worktree --tag gates --file src/main.ts --file src/utils.ts
|
|
11
|
+
*
|
|
12
|
+
* WU-1182: Uses Commander.js repeatable options pattern for --tag and --file.
|
|
13
|
+
* Use --tag multiple times instead of comma-separated --tags.
|
|
9
14
|
*/
|
|
10
15
|
import { Command } from 'commander';
|
|
11
16
|
import { logIncident, getCurrentSession } from '@lumenflow/agent';
|
|
12
17
|
import { EXIT_CODES, INCIDENT_SEVERITY, LUMENFLOW_PATHS, } from '@lumenflow/core/dist/wu-constants.js';
|
|
13
18
|
import chalk from 'chalk';
|
|
19
|
+
/**
|
|
20
|
+
* WU-1182: Collector function for Commander.js repeatable options.
|
|
21
|
+
* Accumulates multiple flag values into an array.
|
|
22
|
+
*
|
|
23
|
+
* Usage: --tag a --tag b → ['a', 'b']
|
|
24
|
+
*
|
|
25
|
+
* This follows Commander.js best practices - use repeatable pattern for
|
|
26
|
+
* multi-value options instead of comma-separated splits.
|
|
27
|
+
*
|
|
28
|
+
* @param value - New value from CLI
|
|
29
|
+
* @param previous - Previously accumulated values
|
|
30
|
+
* @returns Updated array with new value appended
|
|
31
|
+
*/
|
|
32
|
+
function collectRepeatable(value, previous) {
|
|
33
|
+
return previous.concat([value]);
|
|
34
|
+
}
|
|
14
35
|
const program = new Command()
|
|
15
36
|
.name('agent:log-issue')
|
|
16
37
|
.description('Log a workflow issue or incident')
|
|
@@ -19,9 +40,9 @@ const program = new Command()
|
|
|
19
40
|
.requiredOption('--title <title>', 'Short description (5-100 chars)')
|
|
20
41
|
.requiredOption('--description <desc>', 'Detailed context (10-2000 chars)')
|
|
21
42
|
.option('--resolution <res>', 'How the issue was resolved')
|
|
22
|
-
.option('--
|
|
43
|
+
.option('--tag <tag>', 'Tag for categorization (repeatable)', collectRepeatable, [])
|
|
23
44
|
.option('--step <step>', 'Current workflow step (e.g., wu:done, gates)')
|
|
24
|
-
.option('--
|
|
45
|
+
.option('--file <file>', 'Related file path (repeatable)', collectRepeatable, [])
|
|
25
46
|
.action(async (opts) => {
|
|
26
47
|
try {
|
|
27
48
|
const session = await getCurrentSession();
|
|
@@ -36,10 +57,10 @@ const program = new Command()
|
|
|
36
57
|
title: opts.title,
|
|
37
58
|
description: opts.description,
|
|
38
59
|
resolution: opts.resolution,
|
|
39
|
-
tags: opts.
|
|
60
|
+
tags: opts.tag,
|
|
40
61
|
context: {
|
|
41
62
|
current_step: opts.step,
|
|
42
|
-
related_files: opts.
|
|
63
|
+
related_files: opts.file,
|
|
43
64
|
},
|
|
44
65
|
};
|
|
45
66
|
await logIncident(incident);
|
package/dist/backlog-prune.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - Auto-tagging stale WUs (in_progress/ready too long without activity)
|
|
7
7
|
* - Archiving old completed WUs (done for > N days)
|
|
8
8
|
*
|
|
9
|
-
* WU-1106: INIT-003 Phase 3b - Migrate from PatientPath tools/backlog-prune.
|
|
9
|
+
* WU-1106: INIT-003 Phase 3b - Migrate from PatientPath tools/backlog-prune.ts
|
|
10
10
|
*
|
|
11
11
|
* Usage:
|
|
12
12
|
* pnpm backlog:prune # Dry-run mode (shows what would be done)
|
|
@@ -291,9 +291,10 @@ async function main() {
|
|
|
291
291
|
}
|
|
292
292
|
process.exit(EXIT_CODES.SUCCESS);
|
|
293
293
|
}
|
|
294
|
-
//
|
|
295
|
-
|
|
294
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
295
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
296
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
296
297
|
import { runCLI } from './cli-entry-point.js';
|
|
297
|
-
if (
|
|
298
|
+
if (import.meta.main) {
|
|
298
299
|
runCLI(main);
|
|
299
300
|
}
|
package/dist/cli-entry-point.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-console -- CLI entry point uses console for error output */
|
|
1
2
|
/**
|
|
2
3
|
* Shared CLI entry point wrapper
|
|
3
4
|
*
|
|
@@ -12,6 +13,10 @@
|
|
|
12
13
|
*
|
|
13
14
|
* WU-1085: Initializes color support respecting NO_COLOR/FORCE_COLOR/--no-color
|
|
14
15
|
*
|
|
16
|
+
* WU-1233: Adds EPIPE protection for pipe resilience. When CLI output is piped
|
|
17
|
+
* through head/tail, the pipe may close before all output is written. Without
|
|
18
|
+
* this protection, Node.js throws unhandled EPIPE errors crashing the process.
|
|
19
|
+
*
|
|
15
20
|
* @example
|
|
16
21
|
* ```typescript
|
|
17
22
|
* // At the bottom of each CLI file:
|
|
@@ -23,15 +28,20 @@
|
|
|
23
28
|
* ```
|
|
24
29
|
*/
|
|
25
30
|
import { EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
|
|
26
|
-
import { initColorSupport } from '@lumenflow/core';
|
|
31
|
+
import { initColorSupport, StreamErrorHandler } from '@lumenflow/core';
|
|
27
32
|
/**
|
|
28
33
|
* Wraps an async main function with proper error handling.
|
|
29
34
|
* WU-1085: Also initializes color support based on NO_COLOR/FORCE_COLOR/--no-color
|
|
35
|
+
* WU-1233: Attaches EPIPE handler for graceful pipe closure
|
|
30
36
|
*
|
|
31
37
|
* @param main - The async main function to execute
|
|
32
38
|
* @returns Promise that resolves when main completes (or after error handling)
|
|
33
39
|
*/
|
|
34
40
|
export async function runCLI(main) {
|
|
41
|
+
// WU-1233: Attach EPIPE handler before running command
|
|
42
|
+
// This must be done early to catch any EPIPE errors during execution
|
|
43
|
+
const streamErrorHandler = StreamErrorHandler.createWithDefaults();
|
|
44
|
+
streamErrorHandler.attach();
|
|
35
45
|
// WU-1085: Initialize color support before running command
|
|
36
46
|
initColorSupport();
|
|
37
47
|
try {
|