@lumenflow/cli 2.4.0 → 2.5.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 +11 -8
- package/dist/__tests__/init-config-lanes.test.js +131 -0
- package/dist/__tests__/init-docs-structure.test.js +119 -0
- package/dist/__tests__/init-lane-inference.test.js +125 -0
- package/dist/__tests__/init-onboarding-docs.test.js +132 -0
- package/dist/__tests__/init-quick-ref.test.js +145 -0
- package/dist/__tests__/init-scripts.test.js +207 -0
- package/dist/__tests__/init-template-portability.test.js +97 -0
- package/dist/__tests__/init.test.js +7 -2
- package/dist/__tests__/initiative-add-wu.test.js +420 -0
- package/dist/__tests__/initiative-plan-replacement.test.js +162 -0
- package/dist/__tests__/initiative-remove-wu.test.js +458 -0
- package/dist/__tests__/onboarding-smoke-test.test.js +211 -0
- package/dist/__tests__/path-centralization-cli.test.js +234 -0
- package/dist/__tests__/plan-create.test.js +126 -0
- package/dist/__tests__/plan-edit.test.js +157 -0
- package/dist/__tests__/plan-link.test.js +239 -0
- package/dist/__tests__/plan-promote.test.js +181 -0
- package/dist/__tests__/templates-sync.test.js +219 -0
- package/dist/__tests__/wu-create-strict.test.js +118 -0
- package/dist/__tests__/wu-edit-strict.test.js +109 -0
- package/dist/__tests__/wu-validate-strict.test.js +113 -0
- package/dist/flow-bottlenecks.js +4 -2
- package/dist/gates.js +22 -0
- package/dist/init.js +670 -87
- package/dist/initiative-add-wu.js +112 -16
- package/dist/initiative-remove-wu.js +248 -0
- package/dist/onboarding-smoke-test.js +400 -0
- package/dist/orchestrate-init-status.js +37 -9
- package/dist/orchestrate-initiative.js +10 -4
- package/dist/plan-create.js +199 -0
- package/dist/plan-edit.js +235 -0
- package/dist/plan-link.js +233 -0
- package/dist/plan-promote.js +231 -0
- package/dist/sync-templates.js +137 -5
- package/dist/wu-block.js +16 -5
- package/dist/wu-claim.js +15 -9
- package/dist/wu-create.js +50 -2
- package/dist/wu-deps.js +3 -1
- package/dist/wu-done.js +14 -5
- package/dist/wu-edit.js +35 -0
- package/dist/wu-prep.js +131 -8
- package/dist/wu-spawn.js +14 -1
- package/dist/wu-unblock.js +34 -2
- package/dist/wu-validate.js +25 -17
- package/package.json +11 -7
- package/templates/core/.lumenflow/constraints.md.template +61 -3
- package/templates/core/AGENTS.md.template +2 -2
- package/templates/core/LUMENFLOW.md.template +85 -23
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +157 -0
- package/templates/core/ai/onboarding/agent-safety-card.md.template +227 -0
- package/templates/core/ai/onboarding/docs-generation.md.template +277 -0
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +49 -7
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +343 -110
- package/templates/core/ai/onboarding/release-process.md.template +8 -2
- package/templates/core/ai/onboarding/starting-prompt.md.template +407 -0
- package/templates/core/ai/onboarding/test-ratchet.md.template +131 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +91 -38
- package/templates/core/ai/onboarding/vendor-support.md.template +219 -0
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +13 -1
- package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +14 -16
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +48 -4
- package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +5 -1
- package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +19 -8
- package/dist/__tests__/init-plan.test.js +0 -340
- package/dist/agent-issues-query.d.ts +0 -16
- package/dist/agent-log-issue.d.ts +0 -10
- package/dist/agent-session-end.d.ts +0 -10
- package/dist/agent-session.d.ts +0 -10
- package/dist/backlog-prune.d.ts +0 -84
- package/dist/cli-entry-point.d.ts +0 -8
- package/dist/deps-add.d.ts +0 -91
- package/dist/deps-remove.d.ts +0 -17
- package/dist/docs-sync.d.ts +0 -50
- package/dist/file-delete.d.ts +0 -84
- package/dist/file-edit.d.ts +0 -82
- package/dist/file-read.d.ts +0 -92
- package/dist/file-write.d.ts +0 -90
- package/dist/flow-bottlenecks.d.ts +0 -16
- package/dist/flow-report.d.ts +0 -16
- package/dist/gates.d.ts +0 -94
- package/dist/git-branch.d.ts +0 -65
- package/dist/git-diff.d.ts +0 -58
- package/dist/git-log.d.ts +0 -69
- package/dist/git-status.d.ts +0 -58
- package/dist/guard-locked.d.ts +0 -62
- package/dist/guard-main-branch.d.ts +0 -50
- package/dist/guard-worktree-commit.d.ts +0 -59
- package/dist/index.d.ts +0 -10
- package/dist/init-plan.d.ts +0 -80
- package/dist/init-plan.js +0 -337
- package/dist/init.d.ts +0 -46
- package/dist/initiative-add-wu.d.ts +0 -22
- package/dist/initiative-bulk-assign-wus.d.ts +0 -16
- package/dist/initiative-create.d.ts +0 -28
- package/dist/initiative-edit.d.ts +0 -34
- package/dist/initiative-list.d.ts +0 -12
- package/dist/initiative-status.d.ts +0 -11
- package/dist/lumenflow-upgrade.d.ts +0 -103
- package/dist/mem-checkpoint.d.ts +0 -16
- package/dist/mem-cleanup.d.ts +0 -29
- package/dist/mem-create.d.ts +0 -17
- package/dist/mem-export.d.ts +0 -10
- package/dist/mem-inbox.d.ts +0 -35
- package/dist/mem-init.d.ts +0 -15
- package/dist/mem-ready.d.ts +0 -16
- package/dist/mem-signal.d.ts +0 -16
- package/dist/mem-start.d.ts +0 -16
- package/dist/mem-summarize.d.ts +0 -22
- package/dist/mem-triage.d.ts +0 -22
- package/dist/metrics-cli.d.ts +0 -90
- package/dist/metrics-snapshot.d.ts +0 -18
- package/dist/orchestrate-init-status.d.ts +0 -11
- package/dist/orchestrate-initiative.d.ts +0 -12
- package/dist/orchestrate-monitor.d.ts +0 -11
- package/dist/release.d.ts +0 -117
- package/dist/rotate-progress.d.ts +0 -48
- package/dist/session-coordinator.d.ts +0 -74
- package/dist/spawn-list.d.ts +0 -16
- package/dist/state-bootstrap.d.ts +0 -92
- package/dist/sync-templates.d.ts +0 -52
- package/dist/trace-gen.d.ts +0 -84
- package/dist/validate-agent-skills.d.ts +0 -50
- package/dist/validate-agent-sync.d.ts +0 -36
- package/dist/validate-backlog-sync.d.ts +0 -37
- package/dist/validate-skills-spec.d.ts +0 -40
- package/dist/validate.d.ts +0 -60
- package/dist/wu-block.d.ts +0 -16
- package/dist/wu-claim.d.ts +0 -74
- package/dist/wu-cleanup.d.ts +0 -35
- package/dist/wu-create.d.ts +0 -69
- package/dist/wu-delete.d.ts +0 -21
- package/dist/wu-deps.d.ts +0 -13
- package/dist/wu-done.d.ts +0 -225
- package/dist/wu-edit.d.ts +0 -63
- package/dist/wu-infer-lane.d.ts +0 -17
- package/dist/wu-preflight.d.ts +0 -47
- package/dist/wu-prune.d.ts +0 -16
- package/dist/wu-recover.d.ts +0 -37
- package/dist/wu-release.d.ts +0 -19
- package/dist/wu-repair.d.ts +0 -60
- package/dist/wu-spawn-completion.d.ts +0 -10
- package/dist/wu-spawn.d.ts +0 -192
- package/dist/wu-status.d.ts +0 -25
- package/dist/wu-unblock.d.ts +0 -16
- package/dist/wu-unlock-lane.d.ts +0 -19
- package/dist/wu-validate.d.ts +0 -16
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for init:plan command (WU-1105)
|
|
3
|
-
*
|
|
4
|
-
* The init:plan command links plan files to initiatives by setting
|
|
5
|
-
* the `related_plan` field in the initiative YAML.
|
|
6
|
-
*
|
|
7
|
-
* TDD: These tests are written BEFORE the implementation.
|
|
8
|
-
*/
|
|
9
|
-
import { describe, it, expect, vi, beforeEach, afterEach, beforeAll } from 'vitest';
|
|
10
|
-
import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'node:fs';
|
|
11
|
-
import { join } from 'node:path';
|
|
12
|
-
import { tmpdir } from 'node:os';
|
|
13
|
-
import { parseYAML, stringifyYAML } from '@lumenflow/core/dist/wu-yaml.js';
|
|
14
|
-
// Pre-import the module to ensure coverage tracking includes the module itself
|
|
15
|
-
let initPlanModule;
|
|
16
|
-
beforeAll(async () => {
|
|
17
|
-
initPlanModule = await import('../init-plan.js');
|
|
18
|
-
});
|
|
19
|
-
// Mock modules before importing the module under test
|
|
20
|
-
const mockGit = {
|
|
21
|
-
branch: vi.fn().mockResolvedValue({ current: 'main' }),
|
|
22
|
-
status: vi.fn().mockResolvedValue({ isClean: () => true }),
|
|
23
|
-
};
|
|
24
|
-
vi.mock('@lumenflow/core/dist/git-adapter.js', () => ({
|
|
25
|
-
getGitForCwd: vi.fn(() => mockGit),
|
|
26
|
-
}));
|
|
27
|
-
vi.mock('@lumenflow/core/dist/wu-helpers.js', () => ({
|
|
28
|
-
ensureOnMain: vi.fn().mockResolvedValue(undefined),
|
|
29
|
-
}));
|
|
30
|
-
vi.mock('@lumenflow/core/dist/micro-worktree.js', () => ({
|
|
31
|
-
withMicroWorktree: vi.fn(async ({ execute }) => {
|
|
32
|
-
// Simulate micro-worktree by executing in temp dir
|
|
33
|
-
const tempDir = join(tmpdir(), `init-plan-test-${Date.now()}`);
|
|
34
|
-
mkdirSync(tempDir, { recursive: true });
|
|
35
|
-
try {
|
|
36
|
-
await execute({ worktreePath: tempDir });
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
// Cleanup handled by test
|
|
40
|
-
}
|
|
41
|
-
}),
|
|
42
|
-
}));
|
|
43
|
-
describe('init:plan command', () => {
|
|
44
|
-
let tempDir;
|
|
45
|
-
let originalCwd;
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
tempDir = join(tmpdir(), `init-plan-test-${Date.now()}`);
|
|
48
|
-
mkdirSync(tempDir, { recursive: true });
|
|
49
|
-
originalCwd = process.cwd();
|
|
50
|
-
});
|
|
51
|
-
afterEach(() => {
|
|
52
|
-
process.chdir(originalCwd);
|
|
53
|
-
if (existsSync(tempDir)) {
|
|
54
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
55
|
-
}
|
|
56
|
-
vi.clearAllMocks();
|
|
57
|
-
});
|
|
58
|
-
describe('validateInitIdFormat', () => {
|
|
59
|
-
it('should accept valid INIT-NNN format', async () => {
|
|
60
|
-
const { validateInitIdFormat } = await import('../init-plan.js');
|
|
61
|
-
// Should not throw
|
|
62
|
-
expect(() => validateInitIdFormat('INIT-001')).not.toThrow();
|
|
63
|
-
expect(() => validateInitIdFormat('INIT-123')).not.toThrow();
|
|
64
|
-
});
|
|
65
|
-
it('should accept valid INIT-NAME format', async () => {
|
|
66
|
-
const { validateInitIdFormat } = await import('../init-plan.js');
|
|
67
|
-
expect(() => validateInitIdFormat('INIT-TOOLING')).not.toThrow();
|
|
68
|
-
expect(() => validateInitIdFormat('INIT-A1')).not.toThrow();
|
|
69
|
-
});
|
|
70
|
-
it('should reject invalid formats', async () => {
|
|
71
|
-
const { validateInitIdFormat } = await import('../init-plan.js');
|
|
72
|
-
expect(() => validateInitIdFormat('init-001')).toThrow();
|
|
73
|
-
expect(() => validateInitIdFormat('INIT001')).toThrow();
|
|
74
|
-
expect(() => validateInitIdFormat('WU-001')).toThrow();
|
|
75
|
-
expect(() => validateInitIdFormat('')).toThrow();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe('validatePlanPath', () => {
|
|
79
|
-
it('should accept existing markdown files', async () => {
|
|
80
|
-
const { validatePlanPath } = await import('../init-plan.js');
|
|
81
|
-
const planPath = join(tempDir, 'test-plan.md');
|
|
82
|
-
writeFileSync(planPath, '# Test Plan');
|
|
83
|
-
// Should not throw
|
|
84
|
-
expect(() => validatePlanPath(planPath)).not.toThrow();
|
|
85
|
-
});
|
|
86
|
-
it('should reject non-existent files when not creating', async () => {
|
|
87
|
-
const { validatePlanPath } = await import('../init-plan.js');
|
|
88
|
-
const planPath = join(tempDir, 'nonexistent.md');
|
|
89
|
-
expect(() => validatePlanPath(planPath)).toThrow();
|
|
90
|
-
});
|
|
91
|
-
it('should reject non-markdown files', async () => {
|
|
92
|
-
const { validatePlanPath } = await import('../init-plan.js');
|
|
93
|
-
const planPath = join(tempDir, 'test-plan.txt');
|
|
94
|
-
writeFileSync(planPath, 'Test Plan');
|
|
95
|
-
expect(() => validatePlanPath(planPath)).toThrow();
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe('formatPlanUri', () => {
|
|
99
|
-
it('should format plan path as lumenflow:// URI', async () => {
|
|
100
|
-
const { formatPlanUri } = await import('../init-plan.js');
|
|
101
|
-
expect(formatPlanUri('docs/04-operations/plans/my-plan.md')).toBe('lumenflow://plans/my-plan.md');
|
|
102
|
-
});
|
|
103
|
-
it('should handle nested paths', async () => {
|
|
104
|
-
const { formatPlanUri } = await import('../init-plan.js');
|
|
105
|
-
expect(formatPlanUri('docs/04-operations/plans/subdir/nested-plan.md')).toBe('lumenflow://plans/subdir/nested-plan.md');
|
|
106
|
-
});
|
|
107
|
-
it('should handle paths not in standard location', async () => {
|
|
108
|
-
const { formatPlanUri } = await import('../init-plan.js');
|
|
109
|
-
// Should still create a URI even for non-standard paths
|
|
110
|
-
expect(formatPlanUri('/absolute/path/custom-plan.md')).toBe('lumenflow://plans/custom-plan.md');
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe('checkInitiativeExists', () => {
|
|
114
|
-
it('should return initiative doc if found', async () => {
|
|
115
|
-
const { checkInitiativeExists } = await import('../init-plan.js');
|
|
116
|
-
// Create a mock initiative file
|
|
117
|
-
const initDir = join(tempDir, 'docs', '04-operations', 'tasks', 'initiatives');
|
|
118
|
-
mkdirSync(initDir, { recursive: true });
|
|
119
|
-
const initPath = join(initDir, 'INIT-001.yaml');
|
|
120
|
-
const initDoc = {
|
|
121
|
-
id: 'INIT-001',
|
|
122
|
-
slug: 'test-initiative',
|
|
123
|
-
title: 'Test Initiative',
|
|
124
|
-
status: 'open',
|
|
125
|
-
created: '2026-01-25',
|
|
126
|
-
};
|
|
127
|
-
writeFileSync(initPath, stringifyYAML(initDoc));
|
|
128
|
-
process.chdir(tempDir);
|
|
129
|
-
const result = checkInitiativeExists('INIT-001');
|
|
130
|
-
expect(result.id).toBe('INIT-001');
|
|
131
|
-
});
|
|
132
|
-
it('should throw if initiative not found', async () => {
|
|
133
|
-
const { checkInitiativeExists } = await import('../init-plan.js');
|
|
134
|
-
process.chdir(tempDir);
|
|
135
|
-
expect(() => checkInitiativeExists('INIT-999')).toThrow();
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe('updateInitiativeWithPlan', () => {
|
|
139
|
-
it('should add related_plan field to initiative', async () => {
|
|
140
|
-
const { updateInitiativeWithPlan } = await import('../init-plan.js');
|
|
141
|
-
// Setup mock initiative
|
|
142
|
-
const initDir = join(tempDir, 'docs', '04-operations', 'tasks', 'initiatives');
|
|
143
|
-
mkdirSync(initDir, { recursive: true });
|
|
144
|
-
const initPath = join(initDir, 'INIT-001.yaml');
|
|
145
|
-
const initDoc = {
|
|
146
|
-
id: 'INIT-001',
|
|
147
|
-
slug: 'test-initiative',
|
|
148
|
-
title: 'Test Initiative',
|
|
149
|
-
status: 'open',
|
|
150
|
-
created: '2026-01-25',
|
|
151
|
-
};
|
|
152
|
-
writeFileSync(initPath, stringifyYAML(initDoc));
|
|
153
|
-
// Update initiative
|
|
154
|
-
const changed = updateInitiativeWithPlan(tempDir, 'INIT-001', 'lumenflow://plans/test-plan.md');
|
|
155
|
-
expect(changed).toBe(true);
|
|
156
|
-
// Verify the file was updated
|
|
157
|
-
const updated = parseYAML(readFileSync(initPath, 'utf-8'));
|
|
158
|
-
expect(updated.related_plan).toBe('lumenflow://plans/test-plan.md');
|
|
159
|
-
});
|
|
160
|
-
it('should return false if plan already linked (idempotent)', async () => {
|
|
161
|
-
const { updateInitiativeWithPlan } = await import('../init-plan.js');
|
|
162
|
-
// Setup mock initiative with existing plan
|
|
163
|
-
const initDir = join(tempDir, 'docs', '04-operations', 'tasks', 'initiatives');
|
|
164
|
-
mkdirSync(initDir, { recursive: true });
|
|
165
|
-
const initPath = join(initDir, 'INIT-001.yaml');
|
|
166
|
-
const initDoc = {
|
|
167
|
-
id: 'INIT-001',
|
|
168
|
-
slug: 'test-initiative',
|
|
169
|
-
title: 'Test Initiative',
|
|
170
|
-
status: 'open',
|
|
171
|
-
created: '2026-01-25',
|
|
172
|
-
related_plan: 'lumenflow://plans/test-plan.md',
|
|
173
|
-
};
|
|
174
|
-
writeFileSync(initPath, stringifyYAML(initDoc));
|
|
175
|
-
// Update initiative with same plan
|
|
176
|
-
const changed = updateInitiativeWithPlan(tempDir, 'INIT-001', 'lumenflow://plans/test-plan.md');
|
|
177
|
-
expect(changed).toBe(false);
|
|
178
|
-
});
|
|
179
|
-
it('should warn but proceed if different plan already linked', async () => {
|
|
180
|
-
const { updateInitiativeWithPlan } = await import('../init-plan.js');
|
|
181
|
-
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
182
|
-
// Setup mock initiative with different plan
|
|
183
|
-
const initDir = join(tempDir, 'docs', '04-operations', 'tasks', 'initiatives');
|
|
184
|
-
mkdirSync(initDir, { recursive: true });
|
|
185
|
-
const initPath = join(initDir, 'INIT-001.yaml');
|
|
186
|
-
const initDoc = {
|
|
187
|
-
id: 'INIT-001',
|
|
188
|
-
slug: 'test-initiative',
|
|
189
|
-
title: 'Test Initiative',
|
|
190
|
-
status: 'open',
|
|
191
|
-
created: '2026-01-25',
|
|
192
|
-
related_plan: 'lumenflow://plans/old-plan.md',
|
|
193
|
-
};
|
|
194
|
-
writeFileSync(initPath, stringifyYAML(initDoc));
|
|
195
|
-
// Update initiative with new plan
|
|
196
|
-
const changed = updateInitiativeWithPlan(tempDir, 'INIT-001', 'lumenflow://plans/new-plan.md');
|
|
197
|
-
expect(changed).toBe(true);
|
|
198
|
-
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Replacing existing related_plan'));
|
|
199
|
-
consoleSpy.mockRestore();
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
describe('createPlanTemplate', () => {
|
|
203
|
-
it('should create a plan template file', async () => {
|
|
204
|
-
const { createPlanTemplate } = await import('../init-plan.js');
|
|
205
|
-
const plansDir = join(tempDir, 'docs', '04-operations', 'plans');
|
|
206
|
-
mkdirSync(plansDir, { recursive: true });
|
|
207
|
-
const planPath = createPlanTemplate(tempDir, 'INIT-001', 'Test Initiative');
|
|
208
|
-
expect(existsSync(planPath)).toBe(true);
|
|
209
|
-
const content = readFileSync(planPath, 'utf-8');
|
|
210
|
-
expect(content).toContain('# INIT-001');
|
|
211
|
-
expect(content).toContain('Test Initiative');
|
|
212
|
-
expect(content).toContain('## Goal');
|
|
213
|
-
expect(content).toContain('## Scope');
|
|
214
|
-
});
|
|
215
|
-
it('should not overwrite existing plan file', async () => {
|
|
216
|
-
const { createPlanTemplate } = await import('../init-plan.js');
|
|
217
|
-
const plansDir = join(tempDir, 'docs', '04-operations', 'plans');
|
|
218
|
-
mkdirSync(plansDir, { recursive: true });
|
|
219
|
-
// Create existing file
|
|
220
|
-
const existingPath = join(plansDir, 'INIT-001-test-initiative.md');
|
|
221
|
-
writeFileSync(existingPath, '# Existing Content');
|
|
222
|
-
expect(() => createPlanTemplate(tempDir, 'INIT-001', 'Test Initiative')).toThrow();
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
describe('LOG_PREFIX', () => {
|
|
226
|
-
it('should use correct log prefix', async () => {
|
|
227
|
-
const { LOG_PREFIX } = await import('../init-plan.js');
|
|
228
|
-
expect(LOG_PREFIX).toBe('[init:plan]');
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
describe('getCommitMessage', () => {
|
|
232
|
-
it('should generate correct commit message', async () => {
|
|
233
|
-
const { getCommitMessage } = await import('../init-plan.js');
|
|
234
|
-
expect(getCommitMessage('INIT-001', 'lumenflow://plans/my-plan.md')).toBe('docs: link plan my-plan.md to init-001');
|
|
235
|
-
});
|
|
236
|
-
it('should handle nested plan paths', async () => {
|
|
237
|
-
const { getCommitMessage } = await import('../init-plan.js');
|
|
238
|
-
expect(getCommitMessage('INIT-TOOLING', 'lumenflow://plans/subdir/nested-plan.md')).toBe('docs: link plan subdir/nested-plan.md to init-tooling');
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
describe('updateInitiativeWithPlan ID mismatch', () => {
|
|
242
|
-
it('should throw if initiative ID does not match', async () => {
|
|
243
|
-
const { updateInitiativeWithPlan } = await import('../init-plan.js');
|
|
244
|
-
// Setup mock initiative with different ID
|
|
245
|
-
const initDir = join(tempDir, 'docs', '04-operations', 'tasks', 'initiatives');
|
|
246
|
-
mkdirSync(initDir, { recursive: true });
|
|
247
|
-
const initPath = join(initDir, 'INIT-001.yaml');
|
|
248
|
-
const initDoc = {
|
|
249
|
-
id: 'INIT-002', // Wrong ID
|
|
250
|
-
slug: 'test-initiative',
|
|
251
|
-
title: 'Test Initiative',
|
|
252
|
-
status: 'open',
|
|
253
|
-
created: '2026-01-25',
|
|
254
|
-
};
|
|
255
|
-
writeFileSync(initPath, stringifyYAML(initDoc));
|
|
256
|
-
expect(() => updateInitiativeWithPlan(tempDir, 'INIT-001', 'lumenflow://plans/test-plan.md')).toThrow();
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
describe('init:plan CLI integration', () => {
|
|
261
|
-
it('should require --initiative flag', async () => {
|
|
262
|
-
// This test verifies that the CLI requires the initiative flag
|
|
263
|
-
// The actual CLI integration is tested via subprocess
|
|
264
|
-
const { WU_OPTIONS } = await import('@lumenflow/core/dist/arg-parser.js');
|
|
265
|
-
expect(WU_OPTIONS.initiative).toBeDefined();
|
|
266
|
-
expect(WU_OPTIONS.initiative.flags).toContain('--initiative');
|
|
267
|
-
});
|
|
268
|
-
it('should export main function for CLI entry', async () => {
|
|
269
|
-
const initPlan = await import('../init-plan.js');
|
|
270
|
-
expect(typeof initPlan.main).toBe('function');
|
|
271
|
-
});
|
|
272
|
-
it('should export all required functions', async () => {
|
|
273
|
-
const initPlan = await import('../init-plan.js');
|
|
274
|
-
expect(typeof initPlan.validateInitIdFormat).toBe('function');
|
|
275
|
-
expect(typeof initPlan.validatePlanPath).toBe('function');
|
|
276
|
-
expect(typeof initPlan.formatPlanUri).toBe('function');
|
|
277
|
-
expect(typeof initPlan.checkInitiativeExists).toBe('function');
|
|
278
|
-
expect(typeof initPlan.updateInitiativeWithPlan).toBe('function');
|
|
279
|
-
expect(typeof initPlan.createPlanTemplate).toBe('function');
|
|
280
|
-
expect(typeof initPlan.getCommitMessage).toBe('function');
|
|
281
|
-
expect(typeof initPlan.LOG_PREFIX).toBe('string');
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
describe('createPlanTemplate edge cases', () => {
|
|
285
|
-
let tempDir;
|
|
286
|
-
let originalCwd;
|
|
287
|
-
beforeEach(() => {
|
|
288
|
-
tempDir = join(tmpdir(), `init-plan-test-${Date.now()}`);
|
|
289
|
-
mkdirSync(tempDir, { recursive: true });
|
|
290
|
-
originalCwd = process.cwd();
|
|
291
|
-
});
|
|
292
|
-
afterEach(() => {
|
|
293
|
-
process.chdir(originalCwd);
|
|
294
|
-
if (existsSync(tempDir)) {
|
|
295
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
296
|
-
}
|
|
297
|
-
vi.clearAllMocks();
|
|
298
|
-
});
|
|
299
|
-
it('should create plans directory if it does not exist', async () => {
|
|
300
|
-
const { createPlanTemplate } = await import('../init-plan.js');
|
|
301
|
-
// Do NOT pre-create the plans directory
|
|
302
|
-
const planPath = createPlanTemplate(tempDir, 'INIT-001', 'Test Initiative');
|
|
303
|
-
expect(existsSync(planPath)).toBe(true);
|
|
304
|
-
expect(planPath).toContain('docs/04-operations/plans');
|
|
305
|
-
});
|
|
306
|
-
it('should truncate long titles in filename', async () => {
|
|
307
|
-
const { createPlanTemplate } = await import('../init-plan.js');
|
|
308
|
-
const longTitle = 'This is an extremely long initiative title that should be truncated in the filename';
|
|
309
|
-
const planPath = createPlanTemplate(tempDir, 'INIT-001', longTitle);
|
|
310
|
-
expect(existsSync(planPath)).toBe(true);
|
|
311
|
-
// Filename should be truncated
|
|
312
|
-
const filename = planPath.split('/').pop() || '';
|
|
313
|
-
// INIT-001- is 9 chars, .md is 3 chars, slug should be max 30 chars
|
|
314
|
-
expect(filename.length).toBeLessThanOrEqual(9 + 30 + 3);
|
|
315
|
-
});
|
|
316
|
-
it('should handle special characters in title', async () => {
|
|
317
|
-
const { createPlanTemplate } = await import('../init-plan.js');
|
|
318
|
-
const specialTitle = "Test's Initiative: (Special) Chars! @#$%";
|
|
319
|
-
const planPath = createPlanTemplate(tempDir, 'INIT-001', specialTitle);
|
|
320
|
-
expect(existsSync(planPath)).toBe(true);
|
|
321
|
-
// Filename should only have kebab-case characters
|
|
322
|
-
expect(planPath).toMatch(/INIT-001-[a-z0-9-]+\.md$/);
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
/**
|
|
326
|
-
* Note on main() function testing:
|
|
327
|
-
*
|
|
328
|
-
* The main() function is intentionally not unit-tested because:
|
|
329
|
-
* 1. It calls die() which invokes process.exit() - difficult to mock without complex test infrastructure
|
|
330
|
-
* 2. It involves micro-worktree operations with git
|
|
331
|
-
* 3. All business logic functions it calls ARE thoroughly tested above
|
|
332
|
-
*
|
|
333
|
-
* The main() function is integration/orchestration code that composes the tested helper functions.
|
|
334
|
-
* Integration testing via subprocess (pnpm init:plan) is the appropriate testing strategy for main().
|
|
335
|
-
*
|
|
336
|
-
* Coverage statistics:
|
|
337
|
-
* - All exported helper functions: ~100% coverage
|
|
338
|
-
* - main() function: Not unit tested (orchestration code)
|
|
339
|
-
* - Overall file coverage: ~50% (acceptable for CLI commands)
|
|
340
|
-
*/
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Agent Issues Query CLI (WU-1018)
|
|
4
|
-
*
|
|
5
|
-
* Query and display logged agent incidents/issues.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* pnpm agent:issues-query summary # Summary of last 7 days
|
|
9
|
-
* pnpm agent:issues-query summary --since 30 # Summary of last 30 days
|
|
10
|
-
* pnpm agent:issues-query summary --category tooling
|
|
11
|
-
* pnpm agent:issues-query summary --severity blocker
|
|
12
|
-
*
|
|
13
|
-
* @module agent-issues-query
|
|
14
|
-
* @see {@link @lumenflow/agent}
|
|
15
|
-
*/
|
|
16
|
-
export {};
|
package/dist/agent-session.d.ts
DELETED
package/dist/backlog-prune.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Backlog Prune Command
|
|
4
|
-
*
|
|
5
|
-
* Maintains backlog hygiene by:
|
|
6
|
-
* - Auto-tagging stale WUs (in_progress/ready too long without activity)
|
|
7
|
-
* - Archiving old completed WUs (done for > N days)
|
|
8
|
-
*
|
|
9
|
-
* WU-1106: INIT-003 Phase 3b - Migrate from PatientPath tools/backlog-prune.mjs
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* pnpm backlog:prune # Dry-run mode (shows what would be done)
|
|
13
|
-
* pnpm backlog:prune --execute # Apply changes
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* Default configuration for backlog pruning
|
|
17
|
-
*/
|
|
18
|
-
export declare const BACKLOG_PRUNE_DEFAULTS: {
|
|
19
|
-
/** Days without activity before in_progress WU is considered stale */
|
|
20
|
-
staleDaysInProgress: number;
|
|
21
|
-
/** Days without activity before ready WU is considered stale */
|
|
22
|
-
staleDaysReady: number;
|
|
23
|
-
/** Days after completion before done WU can be archived */
|
|
24
|
-
archiveDaysDone: number;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Arguments for backlog-prune command
|
|
28
|
-
*/
|
|
29
|
-
export interface BacklogPruneArgs {
|
|
30
|
-
dryRun: boolean;
|
|
31
|
-
staleDaysInProgress: number;
|
|
32
|
-
staleDaysReady: number;
|
|
33
|
-
archiveDaysDone: number;
|
|
34
|
-
help?: boolean;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Minimal WU information needed for prune analysis
|
|
38
|
-
*/
|
|
39
|
-
export interface WuPruneInfo {
|
|
40
|
-
id: string;
|
|
41
|
-
status: string;
|
|
42
|
-
title?: string;
|
|
43
|
-
created?: string;
|
|
44
|
-
updated?: string;
|
|
45
|
-
completed?: string;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Result of WU categorization
|
|
49
|
-
*/
|
|
50
|
-
export interface PruneCategorization {
|
|
51
|
-
stale: WuPruneInfo[];
|
|
52
|
-
archivable: WuPruneInfo[];
|
|
53
|
-
healthy: WuPruneInfo[];
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Parse command line arguments for backlog-prune
|
|
57
|
-
*/
|
|
58
|
-
export declare function parseBacklogPruneArgs(argv: string[]): BacklogPruneArgs;
|
|
59
|
-
/**
|
|
60
|
-
* Calculate days since a date string
|
|
61
|
-
* @returns Number of days since date, or null if invalid
|
|
62
|
-
*/
|
|
63
|
-
export declare function calculateStaleDays(dateStr: string | undefined | null): number | null;
|
|
64
|
-
/**
|
|
65
|
-
* Check if a WU is stale based on its status and last activity date
|
|
66
|
-
*/
|
|
67
|
-
export declare function isWuStale(wu: WuPruneInfo, options: {
|
|
68
|
-
staleDaysInProgress?: number;
|
|
69
|
-
staleDaysReady?: number;
|
|
70
|
-
}): boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Check if a WU is archivable (done for more than N days)
|
|
73
|
-
*/
|
|
74
|
-
export declare function isWuArchivable(wu: WuPruneInfo, options: {
|
|
75
|
-
archiveDaysDone?: number;
|
|
76
|
-
}): boolean;
|
|
77
|
-
/**
|
|
78
|
-
* Categorize WUs into stale, archivable, and healthy
|
|
79
|
-
*/
|
|
80
|
-
export declare function categorizeWus(wus: WuPruneInfo[], options: {
|
|
81
|
-
staleDaysInProgress: number;
|
|
82
|
-
staleDaysReady: number;
|
|
83
|
-
archiveDaysDone: number;
|
|
84
|
-
}): PruneCategorization;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wraps an async main function with proper error handling.
|
|
3
|
-
* WU-1085: Also initializes color support based on NO_COLOR/FORCE_COLOR/--no-color
|
|
4
|
-
*
|
|
5
|
-
* @param main - The async main function to execute
|
|
6
|
-
* @returns Promise that resolves when main completes (or after error handling)
|
|
7
|
-
*/
|
|
8
|
-
export declare function runCLI(main: () => Promise<void>): Promise<void>;
|
package/dist/deps-add.d.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Deps Add CLI Command
|
|
4
|
-
*
|
|
5
|
-
* Safe wrapper for `pnpm add` that enforces worktree discipline.
|
|
6
|
-
* Dependencies can only be added from within a worktree, not from main checkout.
|
|
7
|
-
*
|
|
8
|
-
* WU-1112: INIT-003 Phase 6 - Migrate remaining Tier 1 tools
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* pnpm deps:add react
|
|
12
|
-
* pnpm deps:add --dev vitest
|
|
13
|
-
* pnpm deps:add --filter @lumenflow/cli chalk
|
|
14
|
-
*
|
|
15
|
-
* @see dependency-guard.ts for blocking logic
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* Arguments for deps-add command
|
|
19
|
-
*/
|
|
20
|
-
export interface DepsAddArgs {
|
|
21
|
-
/** Package names to add */
|
|
22
|
-
packages?: string[];
|
|
23
|
-
/** Add as dev dependency */
|
|
24
|
-
dev?: boolean;
|
|
25
|
-
/** Filter to specific workspace package */
|
|
26
|
-
filter?: string;
|
|
27
|
-
/** Use exact version */
|
|
28
|
-
exact?: boolean;
|
|
29
|
-
/** Show help */
|
|
30
|
-
help?: boolean;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Arguments for deps-remove command (also exported from here for convenience)
|
|
34
|
-
*/
|
|
35
|
-
export interface DepsRemoveArgs {
|
|
36
|
-
/** Package names to remove */
|
|
37
|
-
packages?: string[];
|
|
38
|
-
/** Filter to specific workspace package */
|
|
39
|
-
filter?: string;
|
|
40
|
-
/** Show help */
|
|
41
|
-
help?: boolean;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Result of worktree context validation
|
|
45
|
-
*/
|
|
46
|
-
export interface WorktreeValidationResult {
|
|
47
|
-
/** Whether the context is valid (inside worktree) */
|
|
48
|
-
valid: boolean;
|
|
49
|
-
/** Error message if invalid */
|
|
50
|
-
error?: string;
|
|
51
|
-
/** Fix command suggestion */
|
|
52
|
-
fixCommand?: string;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Parse command line arguments for deps-add
|
|
56
|
-
*
|
|
57
|
-
* @param argv - Process argv array
|
|
58
|
-
* @returns Parsed arguments
|
|
59
|
-
*/
|
|
60
|
-
export declare function parseDepsAddArgs(argv: string[]): DepsAddArgs;
|
|
61
|
-
/**
|
|
62
|
-
* Parse command line arguments for deps-remove
|
|
63
|
-
*
|
|
64
|
-
* @param argv - Process argv array
|
|
65
|
-
* @returns Parsed arguments
|
|
66
|
-
*/
|
|
67
|
-
export declare function parseDepsRemoveArgs(argv: string[]): DepsRemoveArgs;
|
|
68
|
-
/**
|
|
69
|
-
* Validate that the current directory is within a worktree
|
|
70
|
-
*
|
|
71
|
-
* Dependencies should only be modified in worktrees to maintain
|
|
72
|
-
* isolation and prevent lockfile conflicts on main checkout.
|
|
73
|
-
*
|
|
74
|
-
* @param cwd - Current working directory to validate
|
|
75
|
-
* @returns Validation result with error and fix command if invalid
|
|
76
|
-
*/
|
|
77
|
-
export declare function validateWorktreeContext(cwd: string): WorktreeValidationResult;
|
|
78
|
-
/**
|
|
79
|
-
* Build pnpm add command string from arguments
|
|
80
|
-
*
|
|
81
|
-
* @param args - Parsed deps-add arguments
|
|
82
|
-
* @returns Command string ready for execution
|
|
83
|
-
*/
|
|
84
|
-
export declare function buildPnpmAddCommand(args: DepsAddArgs): string;
|
|
85
|
-
/**
|
|
86
|
-
* Build pnpm remove command string from arguments
|
|
87
|
-
*
|
|
88
|
-
* @param args - Parsed deps-remove arguments
|
|
89
|
-
* @returns Command string ready for execution
|
|
90
|
-
*/
|
|
91
|
-
export declare function buildPnpmRemoveCommand(args: DepsRemoveArgs): string;
|
package/dist/deps-remove.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Deps Remove CLI Command
|
|
4
|
-
*
|
|
5
|
-
* Safe wrapper for `pnpm remove` that enforces worktree discipline.
|
|
6
|
-
* Dependencies can only be removed from within a worktree, not from main checkout.
|
|
7
|
-
*
|
|
8
|
-
* WU-1112: INIT-003 Phase 6 - Migrate remaining Tier 1 tools
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* pnpm deps:remove lodash
|
|
12
|
-
* pnpm deps:remove --filter @lumenflow/cli chalk
|
|
13
|
-
*
|
|
14
|
-
* @see dependency-guard.ts for blocking logic
|
|
15
|
-
*/
|
|
16
|
-
import { type DepsRemoveArgs } from './deps-add.js';
|
|
17
|
-
export type { DepsRemoveArgs };
|
package/dist/docs-sync.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file docs-sync.ts
|
|
3
|
-
* LumenFlow docs:sync command for syncing agent docs to existing projects (WU-1083)
|
|
4
|
-
* WU-1085: Added createWUParser for proper --help support
|
|
5
|
-
* WU-1124: Refactored to read templates from bundled files (INIT-004 Phase 2)
|
|
6
|
-
*/
|
|
7
|
-
export type VendorType = 'claude' | 'cursor' | 'aider' | 'all' | 'none';
|
|
8
|
-
/**
|
|
9
|
-
* WU-1085: Parse docs-sync command options using createWUParser
|
|
10
|
-
* Provides proper --help, --version, and option parsing
|
|
11
|
-
*/
|
|
12
|
-
export declare function parseDocsSyncOptions(): {
|
|
13
|
-
force: boolean;
|
|
14
|
-
vendor: VendorType;
|
|
15
|
-
};
|
|
16
|
-
export interface SyncOptions {
|
|
17
|
-
force: boolean;
|
|
18
|
-
vendor?: VendorType;
|
|
19
|
-
}
|
|
20
|
-
export interface SyncResult {
|
|
21
|
-
created: string[];
|
|
22
|
-
skipped: string[];
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* WU-1124: Get the templates directory path
|
|
26
|
-
* Templates are bundled with the CLI package at dist/templates/
|
|
27
|
-
* Falls back to src/templates/ for development
|
|
28
|
-
*/
|
|
29
|
-
export declare function getTemplatesDir(): string;
|
|
30
|
-
/**
|
|
31
|
-
* WU-1124: Load a template file from the bundled templates directory
|
|
32
|
-
* @param templatePath - Relative path from templates directory (e.g., 'core/ai/onboarding/quick-ref-commands.md.template')
|
|
33
|
-
* @returns Template content as string
|
|
34
|
-
*/
|
|
35
|
-
export declare function loadTemplate(templatePath: string): string;
|
|
36
|
-
/**
|
|
37
|
-
* Sync agent onboarding docs to an existing project
|
|
38
|
-
* WU-1124: Now reads templates from bundled files instead of hardcoded strings
|
|
39
|
-
*/
|
|
40
|
-
export declare function syncAgentDocs(targetDir: string, options: SyncOptions): Promise<SyncResult>;
|
|
41
|
-
/**
|
|
42
|
-
* Sync Claude skills to an existing project
|
|
43
|
-
* WU-1124: Now reads templates from bundled files instead of hardcoded strings
|
|
44
|
-
*/
|
|
45
|
-
export declare function syncSkills(targetDir: string, options: SyncOptions): Promise<SyncResult>;
|
|
46
|
-
/**
|
|
47
|
-
* CLI entry point for docs:sync command
|
|
48
|
-
* WU-1085: Updated to use parseDocsSyncOptions for proper --help support
|
|
49
|
-
*/
|
|
50
|
-
export declare function main(): Promise<void>;
|