@lumenflow/cli 2.20.1 → 2.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +8 -4
  2. package/dist/hooks/enforcement-checks.js +120 -0
  3. package/dist/hooks/enforcement-checks.js.map +1 -1
  4. package/dist/init-lane-validation.js +141 -0
  5. package/dist/init-lane-validation.js.map +1 -0
  6. package/dist/init-templates.js +36 -8
  7. package/dist/init-templates.js.map +1 -1
  8. package/dist/init.js +27 -58
  9. package/dist/init.js.map +1 -1
  10. package/dist/initiative-create.js +35 -4
  11. package/dist/initiative-create.js.map +1 -1
  12. package/dist/lane-lifecycle-process.js +364 -0
  13. package/dist/lane-lifecycle-process.js.map +1 -0
  14. package/dist/lane-lock.js +41 -0
  15. package/dist/lane-lock.js.map +1 -0
  16. package/dist/lane-setup.js +55 -0
  17. package/dist/lane-setup.js.map +1 -0
  18. package/dist/lane-status.js +38 -0
  19. package/dist/lane-status.js.map +1 -0
  20. package/dist/lane-validate.js +43 -0
  21. package/dist/lane-validate.js.map +1 -0
  22. package/dist/onboarding-smoke-test.js +17 -0
  23. package/dist/onboarding-smoke-test.js.map +1 -1
  24. package/dist/public-manifest.js +28 -0
  25. package/dist/public-manifest.js.map +1 -1
  26. package/dist/wu-claim-cloud.js +16 -0
  27. package/dist/wu-claim-cloud.js.map +1 -1
  28. package/dist/wu-claim.js +12 -2
  29. package/dist/wu-claim.js.map +1 -1
  30. package/dist/wu-create-content.js +8 -2
  31. package/dist/wu-create-content.js.map +1 -1
  32. package/dist/wu-create-validation.js +5 -3
  33. package/dist/wu-create-validation.js.map +1 -1
  34. package/dist/wu-create.js +21 -1
  35. package/dist/wu-create.js.map +1 -1
  36. package/dist/wu-done.js +57 -8
  37. package/dist/wu-done.js.map +1 -1
  38. package/dist/wu-prep.js +22 -0
  39. package/dist/wu-prep.js.map +1 -1
  40. package/package.json +15 -11
  41. package/dist/__tests__/agent-log-issue.test.js +0 -56
  42. package/dist/__tests__/agent-spawn-coordination.test.js +0 -451
  43. package/dist/__tests__/backlog-prune.test.js +0 -478
  44. package/dist/__tests__/cli-entry-point.test.js +0 -160
  45. package/dist/__tests__/cli-subprocess.test.js +0 -89
  46. package/dist/__tests__/commands/integrate.test.js +0 -165
  47. package/dist/__tests__/commands.test.js +0 -271
  48. package/dist/__tests__/deps-operations.test.js +0 -206
  49. package/dist/__tests__/doctor.test.js +0 -510
  50. package/dist/__tests__/file-operations.test.js +0 -906
  51. package/dist/__tests__/flow-report.test.js +0 -24
  52. package/dist/__tests__/gates-config.test.js +0 -303
  53. package/dist/__tests__/gates-integration-tests.test.js +0 -112
  54. package/dist/__tests__/git-operations.test.js +0 -668
  55. package/dist/__tests__/guard-main-branch.test.js +0 -79
  56. package/dist/__tests__/guards-validation.test.js +0 -416
  57. package/dist/__tests__/hooks/enforcement.test.js +0 -279
  58. package/dist/__tests__/init-config-lanes.test.js +0 -131
  59. package/dist/__tests__/init-docs-structure.test.js +0 -152
  60. package/dist/__tests__/init-greenfield.test.js +0 -247
  61. package/dist/__tests__/init-lane-inference.test.js +0 -125
  62. package/dist/__tests__/init-onboarding-docs.test.js +0 -132
  63. package/dist/__tests__/init-quick-ref.test.js +0 -144
  64. package/dist/__tests__/init-scripts.test.js +0 -207
  65. package/dist/__tests__/init-template-portability.test.js +0 -96
  66. package/dist/__tests__/init.test.js +0 -968
  67. package/dist/__tests__/initiative-add-wu.test.js +0 -490
  68. package/dist/__tests__/initiative-e2e.test.js +0 -442
  69. package/dist/__tests__/initiative-plan-replacement.test.js +0 -161
  70. package/dist/__tests__/initiative-plan.test.js +0 -340
  71. package/dist/__tests__/initiative-remove-wu.test.js +0 -458
  72. package/dist/__tests__/lumenflow-upgrade.test.js +0 -260
  73. package/dist/__tests__/mem-cleanup-execution.test.js +0 -19
  74. package/dist/__tests__/memory-integration.test.js +0 -333
  75. package/dist/__tests__/merge-block.test.js +0 -220
  76. package/dist/__tests__/metrics-cli.test.js +0 -619
  77. package/dist/__tests__/metrics-snapshot.test.js +0 -24
  78. package/dist/__tests__/no-beacon-references-docs.test.js +0 -30
  79. package/dist/__tests__/no-beacon-references.test.js +0 -39
  80. package/dist/__tests__/onboarding-smoke-test.test.js +0 -211
  81. package/dist/__tests__/path-centralization-cli.test.js +0 -234
  82. package/dist/__tests__/plan-create.test.js +0 -126
  83. package/dist/__tests__/plan-edit.test.js +0 -157
  84. package/dist/__tests__/plan-link.test.js +0 -239
  85. package/dist/__tests__/plan-promote.test.js +0 -181
  86. package/dist/__tests__/release.test.js +0 -372
  87. package/dist/__tests__/rotate-progress.test.js +0 -127
  88. package/dist/__tests__/safe-git.test.js +0 -190
  89. package/dist/__tests__/session-coordinator.test.js +0 -109
  90. package/dist/__tests__/state-bootstrap.test.js +0 -432
  91. package/dist/__tests__/state-doctor.test.js +0 -328
  92. package/dist/__tests__/sync-templates.test.js +0 -255
  93. package/dist/__tests__/templates-sync.test.js +0 -219
  94. package/dist/__tests__/trace-gen.test.js +0 -115
  95. package/dist/__tests__/wu-create-required-fields.test.js +0 -143
  96. package/dist/__tests__/wu-create-strict.test.js +0 -118
  97. package/dist/__tests__/wu-create.test.js +0 -121
  98. package/dist/__tests__/wu-done-auto-cleanup.test.js +0 -135
  99. package/dist/__tests__/wu-done-docs-only-policy.test.js +0 -20
  100. package/dist/__tests__/wu-done-staging-whitelist.test.js +0 -35
  101. package/dist/__tests__/wu-done.test.js +0 -36
  102. package/dist/__tests__/wu-edit-strict.test.js +0 -109
  103. package/dist/__tests__/wu-edit.test.js +0 -119
  104. package/dist/__tests__/wu-lifecycle-integration.test.js +0 -388
  105. package/dist/__tests__/wu-prep-default-exec.test.js +0 -35
  106. package/dist/__tests__/wu-prep.test.js +0 -140
  107. package/dist/__tests__/wu-proto.test.js +0 -97
  108. package/dist/__tests__/wu-validate-strict.test.js +0 -113
  109. package/dist/__tests__/wu-validate.test.js +0 -36
  110. package/dist/spawn-list.js +0 -143
  111. package/dist/spawn-list.js.map +0 -1
@@ -1,247 +0,0 @@
1
- /**
2
- * @file init-greenfield.test.ts
3
- * Tests for greenfield onboarding with initiative-first workflow (WU-1364)
4
- *
5
- * Verifies:
6
- * - Init output includes initiative-first workflow guidance
7
- * - starting-prompt.md has 'When Starting From Product Vision' section
8
- * - Init auto-creates initial commit when git repo has no commits
9
- * - Init auto-sets git.requireRemote=false when no remote configured
10
- * - Default lane-inference template includes Core and Feature as parent lanes
11
- * - LUMENFLOW.md mentions initiatives and when to use them
12
- */
13
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
14
- import * as fs from 'node:fs';
15
- import * as path from 'node:path';
16
- import * as os from 'node:os';
17
- import { execFileSync } from 'node:child_process';
18
- import { scaffoldProject } from '../init.js';
19
- // Constants to avoid duplicate strings
20
- const ARC42_DOCS_STRUCTURE = 'arc42';
21
- const STARTING_PROMPT_FILE = 'starting-prompt.md';
22
- const LUMENFLOW_CONFIG_FILE = '.lumenflow.config.yaml';
23
- const LANE_INFERENCE_FILE = '.lumenflow.lane-inference.yaml';
24
- describe('greenfield onboarding (WU-1364)', () => {
25
- let tempDir;
26
- beforeEach(() => {
27
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-greenfield-test-'));
28
- });
29
- afterEach(() => {
30
- fs.rmSync(tempDir, { recursive: true, force: true });
31
- });
32
- function getOnboardingDir() {
33
- return path.join(tempDir, 'docs', '04-operations', '_frameworks', 'lumenflow', 'agent', 'onboarding');
34
- }
35
- function getArc42Options() {
36
- return {
37
- force: false,
38
- full: true,
39
- docsStructure: ARC42_DOCS_STRUCTURE,
40
- };
41
- }
42
- /**
43
- * Initialize a git repo without commits (empty repo state)
44
- * Uses execFileSync for safety (no shell injection)
45
- */
46
- function initEmptyGitRepo() {
47
- execFileSync('git', ['init'], { cwd: tempDir, stdio: 'pipe' });
48
- // Configure git user for commit (required in some environments)
49
- execFileSync('git', ['config', 'user.email', 'test@example.com'], {
50
- cwd: tempDir,
51
- stdio: 'pipe',
52
- });
53
- execFileSync('git', ['config', 'user.name', 'Test User'], { cwd: tempDir, stdio: 'pipe' });
54
- }
55
- /**
56
- * Initialize a git repo with an initial commit
57
- */
58
- function initGitRepoWithCommit() {
59
- initEmptyGitRepo();
60
- fs.writeFileSync(path.join(tempDir, '.gitkeep'), '');
61
- execFileSync('git', ['add', '.gitkeep'], { cwd: tempDir, stdio: 'pipe' });
62
- execFileSync('git', ['commit', '-m', 'Initial commit'], { cwd: tempDir, stdio: 'pipe' });
63
- }
64
- describe('AC: starting-prompt.md has initiative-first workflow section', () => {
65
- it('should include "When Starting From Product Vision" section in starting-prompt.md', async () => {
66
- await scaffoldProject(tempDir, getArc42Options());
67
- const startingPromptPath = path.join(getOnboardingDir(), STARTING_PROMPT_FILE);
68
- expect(fs.existsSync(startingPromptPath)).toBe(true);
69
- const content = fs.readFileSync(startingPromptPath, 'utf-8');
70
- expect(content).toContain('When Starting From Product Vision');
71
- });
72
- it('should describe 4-step initiative workflow in product vision section', async () => {
73
- await scaffoldProject(tempDir, getArc42Options());
74
- const startingPromptPath = path.join(getOnboardingDir(), STARTING_PROMPT_FILE);
75
- const content = fs.readFileSync(startingPromptPath, 'utf-8');
76
- // Should mention initiative creation
77
- expect(content).toContain('initiative:create');
78
- // Should mention phased work
79
- expect(content).toMatch(/phase|INIT-/i);
80
- // Should mention WU organization under initiatives
81
- expect(content).toMatch(/initiative.*WU|WU.*initiative/i);
82
- });
83
- it('should warn against creating orphan WUs without initiative structure', async () => {
84
- await scaffoldProject(tempDir, getArc42Options());
85
- const startingPromptPath = path.join(getOnboardingDir(), STARTING_PROMPT_FILE);
86
- const content = fs.readFileSync(startingPromptPath, 'utf-8');
87
- // Should have guidance about when NOT to create standalone WUs
88
- expect(content).toMatch(/don't|avoid|instead.*initiative/i);
89
- });
90
- });
91
- describe('AC: Init auto-creates initial commit when git repo has no commits', () => {
92
- it('should create initial commit in empty git repo', async () => {
93
- initEmptyGitRepo();
94
- // Verify no commits exist
95
- try {
96
- execFileSync('git', ['rev-parse', 'HEAD'], { cwd: tempDir, stdio: 'pipe' });
97
- throw new Error('Expected HEAD to not exist');
98
- }
99
- catch {
100
- // Expected: fatal: ambiguous argument 'HEAD'
101
- }
102
- await scaffoldProject(tempDir, getArc42Options());
103
- // Now HEAD should exist
104
- const result = execFileSync('git', ['rev-parse', 'HEAD'], {
105
- cwd: tempDir,
106
- encoding: 'utf-8',
107
- stdio: 'pipe',
108
- });
109
- expect(result.trim()).toMatch(/^[a-f0-9]{40}$/);
110
- });
111
- it('should not create extra commit if repo already has commits', async () => {
112
- initGitRepoWithCommit();
113
- // Get initial commit count
114
- const beforeCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
115
- cwd: tempDir,
116
- encoding: 'utf-8',
117
- stdio: 'pipe',
118
- }).trim();
119
- await scaffoldProject(tempDir, getArc42Options());
120
- // Commit count should be the same (init doesn't auto-commit if commits exist)
121
- const afterCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
122
- cwd: tempDir,
123
- encoding: 'utf-8',
124
- stdio: 'pipe',
125
- }).trim();
126
- expect(afterCount).toBe(beforeCount);
127
- });
128
- it('should skip auto-commit if not in a git repo', async () => {
129
- // Not a git repo - just a plain directory
130
- await scaffoldProject(tempDir, getArc42Options());
131
- // Should not fail, just skip the git operations
132
- expect(fs.existsSync(path.join(tempDir, LUMENFLOW_CONFIG_FILE))).toBe(true);
133
- });
134
- });
135
- describe('AC: Init auto-sets git.requireRemote=false when no remote configured', () => {
136
- it('should set requireRemote=false in config when no origin remote', async () => {
137
- initGitRepoWithCommit();
138
- // No remote added
139
- await scaffoldProject(tempDir, getArc42Options());
140
- const configPath = path.join(tempDir, LUMENFLOW_CONFIG_FILE);
141
- const content = fs.readFileSync(configPath, 'utf-8');
142
- expect(content).toContain('requireRemote: false');
143
- });
144
- it('should not set requireRemote=false if origin remote exists', async () => {
145
- initGitRepoWithCommit();
146
- // Add a remote
147
- execFileSync('git', ['remote', 'add', 'origin', 'https://github.com/test/repo.git'], {
148
- cwd: tempDir,
149
- stdio: 'pipe',
150
- });
151
- await scaffoldProject(tempDir, getArc42Options());
152
- const configPath = path.join(tempDir, LUMENFLOW_CONFIG_FILE);
153
- const content = fs.readFileSync(configPath, 'utf-8');
154
- // Should not have requireRemote: false (remote exists)
155
- expect(content).not.toContain('requireRemote: false');
156
- });
157
- it('should skip remote check if not in a git repo', async () => {
158
- // Not a git repo
159
- await scaffoldProject(tempDir, getArc42Options());
160
- const configPath = path.join(tempDir, LUMENFLOW_CONFIG_FILE);
161
- const content = fs.readFileSync(configPath, 'utf-8');
162
- // When not in a git repo, should default to requireRemote: false for safety
163
- expect(content).toContain('requireRemote: false');
164
- });
165
- });
166
- describe('AC: Default lane-inference template includes Core and Feature parent lanes', () => {
167
- it('should include Core as a parent lane', async () => {
168
- await scaffoldProject(tempDir, getArc42Options());
169
- const laneInferencePath = path.join(tempDir, LANE_INFERENCE_FILE);
170
- expect(fs.existsSync(laneInferencePath)).toBe(true);
171
- const content = fs.readFileSync(laneInferencePath, 'utf-8');
172
- // Should have Core as a top-level parent lane (not just Framework: Core)
173
- expect(content).toMatch(/^Core:/m);
174
- });
175
- it('should include Feature as a parent lane', async () => {
176
- await scaffoldProject(tempDir, getArc42Options());
177
- const laneInferencePath = path.join(tempDir, LANE_INFERENCE_FILE);
178
- const content = fs.readFileSync(laneInferencePath, 'utf-8');
179
- // Should have Feature as a top-level parent lane
180
- expect(content).toMatch(/^Feature:/m);
181
- });
182
- it('should support intuitive lane names like "Core: Platform"', async () => {
183
- await scaffoldProject(tempDir, getArc42Options());
184
- const laneInferencePath = path.join(tempDir, LANE_INFERENCE_FILE);
185
- const content = fs.readFileSync(laneInferencePath, 'utf-8');
186
- // Should have sublanes under Core and Feature
187
- // e.g., Core: followed by sublanes like Platform, Library, etc.
188
- expect(content).toMatch(/Core:\n\s+\w+:/m);
189
- expect(content).toMatch(/Feature:\n\s+\w+:/m);
190
- });
191
- });
192
- describe('AC: LUMENFLOW.md mentions initiatives and when to use them', () => {
193
- it('should mention initiatives in generated LUMENFLOW.md', async () => {
194
- await scaffoldProject(tempDir, getArc42Options());
195
- const lumenflowPath = path.join(tempDir, 'LUMENFLOW.md');
196
- const content = fs.readFileSync(lumenflowPath, 'utf-8');
197
- expect(content).toMatch(/initiative/i);
198
- });
199
- it('should explain when to use initiatives vs standalone WUs', async () => {
200
- await scaffoldProject(tempDir, getArc42Options());
201
- const lumenflowPath = path.join(tempDir, 'LUMENFLOW.md');
202
- const content = fs.readFileSync(lumenflowPath, 'utf-8');
203
- // Should mention when to use initiatives
204
- expect(content).toMatch(/multi-phase|product vision|larger|complex/i);
205
- });
206
- it('should reference initiative:create command', async () => {
207
- await scaffoldProject(tempDir, getArc42Options());
208
- const lumenflowPath = path.join(tempDir, 'LUMENFLOW.md');
209
- const content = fs.readFileSync(lumenflowPath, 'utf-8');
210
- expect(content).toContain('initiative:create');
211
- });
212
- });
213
- describe('AC: Init output includes initiative-first workflow guidance', () => {
214
- // This test verifies the console output, which requires capturing stdout
215
- // We'll mock console.log to capture the output
216
- it('should print initiative-first guidance in init output', async () => {
217
- const consoleLogs = [];
218
- const originalLog = console.log;
219
- console.log = (...args) => {
220
- consoleLogs.push(args.join(' '));
221
- };
222
- try {
223
- // Import and run main() to capture console output
224
- const { main } = await import('../init.js');
225
- // Change to temp directory and run init
226
- const originalCwd = process.cwd();
227
- process.chdir(tempDir);
228
- // Mock process.argv for parseInitOptions
229
- const originalArgv = process.argv;
230
- process.argv = ['node', 'init', '--full'];
231
- try {
232
- await main();
233
- }
234
- finally {
235
- process.argv = originalArgv;
236
- process.chdir(originalCwd);
237
- }
238
- const output = consoleLogs.join('\n');
239
- // Should mention initiatives in the "Next steps" or guidance section
240
- expect(output).toMatch(/initiative|product vision|INIT-/i);
241
- }
242
- finally {
243
- console.log = originalLog;
244
- }
245
- });
246
- });
247
- });
@@ -1,125 +0,0 @@
1
- /**
2
- * @file init-lane-inference.test.ts
3
- * Test: .lumenflow.lane-inference.yaml is generated in hierarchical Parent→Sublane format
4
- *
5
- * WU-1307: Fix lumenflow-init scaffolding
6
- *
7
- * The generated lane inference config must use the hierarchical format that
8
- * lane-inference.ts/lane-checker.ts expect:
9
- *
10
- * Parent:
11
- * Sublane:
12
- * code_paths:
13
- * - pattern
14
- * keywords:
15
- * - keyword
16
- *
17
- * NOT the flat format:
18
- * lanes:
19
- * - name: "Parent: Sublane"
20
- * patterns:
21
- * - pattern
22
- */
23
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
24
- import * as fs from 'node:fs';
25
- import * as path from 'node:path';
26
- import * as os from 'node:os';
27
- import YAML from 'yaml';
28
- import { scaffoldProject } from '../init.js';
29
- /** Lane inference file name - extracted to avoid duplicate string lint errors */
30
- const LANE_INFERENCE_FILE_NAME = '.lumenflow.lane-inference.yaml';
31
- describe('init lane inference generation (WU-1307)', () => {
32
- let tempDir;
33
- beforeEach(() => {
34
- // Create a temporary directory for each test
35
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-init-lane-inference-'));
36
- });
37
- afterEach(() => {
38
- // Clean up temporary directory
39
- if (tempDir && fs.existsSync(tempDir)) {
40
- fs.rmSync(tempDir, { recursive: true, force: true });
41
- }
42
- });
43
- /** Helper to read and parse lane inference config from temp directory */
44
- function readLaneInference() {
45
- const laneInferencePath = path.join(tempDir, LANE_INFERENCE_FILE_NAME);
46
- const laneInferenceContent = fs.readFileSync(laneInferencePath, 'utf-8');
47
- return YAML.parse(laneInferenceContent);
48
- }
49
- it('should generate .lumenflow.lane-inference.yaml in hierarchical format', async () => {
50
- // Arrange
51
- const laneInferencePath = path.join(tempDir, LANE_INFERENCE_FILE_NAME);
52
- // Act
53
- await scaffoldProject(tempDir, { force: true, full: true });
54
- // Assert
55
- expect(fs.existsSync(laneInferencePath)).toBe(true);
56
- const laneInference = readLaneInference();
57
- // Should NOT have a flat 'lanes' array
58
- expect(laneInference.lanes).toBeUndefined();
59
- // Should have hierarchical Parent -> Sublane structure
60
- // At minimum, should have Framework, Operations, Content parents
61
- expect(laneInference.Framework).toBeDefined();
62
- expect(laneInference.Operations).toBeDefined();
63
- expect(laneInference.Content).toBeDefined();
64
- });
65
- it('should include sublanes under parent lanes', async () => {
66
- // Act
67
- await scaffoldProject(tempDir, { force: true, full: true });
68
- // Assert
69
- const laneInference = readLaneInference();
70
- // Framework parent should have sublanes like Core, CLI
71
- expect(laneInference.Framework?.Core).toBeDefined();
72
- expect(laneInference.Framework?.CLI).toBeDefined();
73
- // Operations parent should have sublanes like Infrastructure, CI/CD
74
- expect(laneInference.Operations?.Infrastructure).toBeDefined();
75
- expect(laneInference.Operations?.['CI/CD']).toBeDefined();
76
- // Content parent should have Documentation sublane
77
- expect(laneInference.Content?.Documentation).toBeDefined();
78
- });
79
- it('should have code_paths in sublane config (not patterns)', async () => {
80
- // Act
81
- await scaffoldProject(tempDir, { force: true, full: true });
82
- // Assert
83
- const laneInference = readLaneInference();
84
- // Sublanes should have code_paths (not patterns)
85
- const frameworkCore = laneInference.Framework?.Core;
86
- expect(frameworkCore).toBeDefined();
87
- expect(frameworkCore?.code_paths).toBeDefined();
88
- expect(Array.isArray(frameworkCore?.code_paths)).toBe(true);
89
- expect(frameworkCore?.patterns).toBeUndefined();
90
- });
91
- it('should include keywords in sublane config', async () => {
92
- // Act
93
- await scaffoldProject(tempDir, { force: true, full: true });
94
- // Assert
95
- const laneInference = readLaneInference();
96
- // Sublanes should have keywords array
97
- const contentDocs = laneInference.Content?.Documentation;
98
- expect(contentDocs).toBeDefined();
99
- expect(contentDocs?.keywords).toBeDefined();
100
- expect(Array.isArray(contentDocs?.keywords)).toBe(true);
101
- expect(contentDocs?.keywords?.length).toBeGreaterThan(0);
102
- });
103
- it('should generate Experience parent lane for frontend projects', async () => {
104
- // Act
105
- await scaffoldProject(tempDir, { force: true, full: true });
106
- // Assert
107
- const laneInference = readLaneInference();
108
- // Should have Experience parent for frontend work
109
- expect(laneInference.Experience).toBeDefined();
110
- expect(laneInference.Experience?.UI || laneInference.Experience?.Web).toBeDefined();
111
- });
112
- it('should add framework-specific lanes when --framework is provided', async () => {
113
- // Act
114
- await scaffoldProject(tempDir, {
115
- force: true,
116
- full: true,
117
- framework: 'nextjs',
118
- });
119
- // Assert
120
- const laneInference = readLaneInference();
121
- // Should still have base lanes
122
- expect(laneInference.Framework).toBeDefined();
123
- expect(laneInference.Content).toBeDefined();
124
- });
125
- });
@@ -1,132 +0,0 @@
1
- /**
2
- * @file init-onboarding-docs.test.ts
3
- * Tests for onboarding docs scaffold (WU-1309)
4
- * Verifies: starting-prompt, first-15-mins, local-only, lane-inference
5
- */
6
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
- import * as fs from 'node:fs';
8
- import * as path from 'node:path';
9
- import * as os from 'node:os';
10
- import { scaffoldProject } from '../init.js';
11
- describe('onboarding docs scaffold', () => {
12
- let tempDir;
13
- beforeEach(() => {
14
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-onboarding-test-'));
15
- });
16
- afterEach(() => {
17
- fs.rmSync(tempDir, { recursive: true, force: true });
18
- });
19
- // Constants to avoid duplicate strings (sonarjs/no-duplicate-string)
20
- const ARC42_DOCS_STRUCTURE = 'arc42';
21
- const STARTING_PROMPT_FILE = 'starting-prompt.md';
22
- const FIRST_15_MINS_FILE = 'first-15-mins.md';
23
- const LOCAL_ONLY_FILE = 'local-only.md';
24
- const LANE_INFERENCE_FILE = 'lane-inference.md';
25
- function getOnboardingDir(docsStructure = ARC42_DOCS_STRUCTURE) {
26
- if (docsStructure === 'simple') {
27
- return path.join(tempDir, 'docs', '_frameworks', 'lumenflow', 'agent', 'onboarding');
28
- }
29
- return path.join(tempDir, 'docs', '04-operations', '_frameworks', 'lumenflow', 'agent', 'onboarding');
30
- }
31
- function getArc42Options() {
32
- return {
33
- force: false,
34
- full: true,
35
- docsStructure: ARC42_DOCS_STRUCTURE,
36
- };
37
- }
38
- describe('required onboarding docs', () => {
39
- it('should scaffold starting-prompt.md', async () => {
40
- await scaffoldProject(tempDir, getArc42Options());
41
- const docPath = path.join(getOnboardingDir(), STARTING_PROMPT_FILE);
42
- expect(fs.existsSync(docPath)).toBe(true);
43
- const content = fs.readFileSync(docPath, 'utf-8');
44
- expect(content).toContain('Starting Prompt');
45
- expect(content).toContain('LUMENFLOW.md');
46
- });
47
- it('should scaffold first-15-mins.md', async () => {
48
- await scaffoldProject(tempDir, getArc42Options());
49
- const docPath = path.join(getOnboardingDir(), FIRST_15_MINS_FILE);
50
- expect(fs.existsSync(docPath)).toBe(true);
51
- const content = fs.readFileSync(docPath, 'utf-8');
52
- expect(content).toContain('First 15 Minutes');
53
- });
54
- it('should scaffold local-only.md', async () => {
55
- await scaffoldProject(tempDir, getArc42Options());
56
- const docPath = path.join(getOnboardingDir(), LOCAL_ONLY_FILE);
57
- expect(fs.existsSync(docPath)).toBe(true);
58
- const content = fs.readFileSync(docPath, 'utf-8');
59
- expect(content).toContain('requireRemote');
60
- expect(content).toContain('local');
61
- });
62
- it('should scaffold lane-inference.md', async () => {
63
- await scaffoldProject(tempDir, getArc42Options());
64
- const docPath = path.join(getOnboardingDir(), LANE_INFERENCE_FILE);
65
- expect(fs.existsSync(docPath)).toBe(true);
66
- const content = fs.readFileSync(docPath, 'utf-8');
67
- expect(content).toContain('lane');
68
- expect(content).toContain('.lumenflow.lane-inference.yaml');
69
- });
70
- });
71
- describe('onboarding docs with simple structure', () => {
72
- it('should scaffold onboarding docs in simple structure', async () => {
73
- const options = {
74
- force: false,
75
- full: true,
76
- docsStructure: 'simple',
77
- };
78
- await scaffoldProject(tempDir, options);
79
- const onboardingDir = getOnboardingDir('simple');
80
- expect(fs.existsSync(path.join(onboardingDir, STARTING_PROMPT_FILE))).toBe(true);
81
- expect(fs.existsSync(path.join(onboardingDir, FIRST_15_MINS_FILE))).toBe(true);
82
- expect(fs.existsSync(path.join(onboardingDir, LOCAL_ONLY_FILE))).toBe(true);
83
- expect(fs.existsSync(path.join(onboardingDir, LANE_INFERENCE_FILE))).toBe(true);
84
- });
85
- });
86
- describe('complete onboarding docs set', () => {
87
- it('should scaffold all required onboarding docs with --full', async () => {
88
- await scaffoldProject(tempDir, getArc42Options());
89
- const onboardingDir = getOnboardingDir();
90
- // Required docs from WU-1309
91
- const requiredDocs = [
92
- STARTING_PROMPT_FILE,
93
- FIRST_15_MINS_FILE,
94
- LOCAL_ONLY_FILE,
95
- LANE_INFERENCE_FILE,
96
- // Previously existing docs
97
- 'quick-ref-commands.md',
98
- 'first-wu-mistakes.md',
99
- 'troubleshooting-wu-done.md',
100
- 'agent-safety-card.md',
101
- 'wu-create-checklist.md',
102
- ];
103
- for (const doc of requiredDocs) {
104
- const docPath = path.join(onboardingDir, doc);
105
- expect(fs.existsSync(docPath), `Expected ${doc} to exist`).toBe(true);
106
- }
107
- });
108
- it('should not scaffold onboarding docs without --full', async () => {
109
- const options = {
110
- force: false,
111
- full: false,
112
- };
113
- await scaffoldProject(tempDir, options);
114
- const onboardingDir = getOnboardingDir();
115
- // Onboarding docs should not exist without --full (unless --client claude)
116
- expect(fs.existsSync(path.join(onboardingDir, STARTING_PROMPT_FILE))).toBe(false);
117
- });
118
- });
119
- describe('onboarding docs content quality', () => {
120
- it('should have consistent date placeholder in all docs', async () => {
121
- await scaffoldProject(tempDir, getArc42Options());
122
- const onboardingDir = getOnboardingDir();
123
- const docs = fs.readdirSync(onboardingDir).filter((f) => f.endsWith('.md'));
124
- for (const doc of docs) {
125
- const content = fs.readFileSync(path.join(onboardingDir, doc), 'utf-8');
126
- // Should have a date in YYYY-MM-DD format (not {{DATE}} placeholder)
127
- expect(content).toMatch(/\d{4}-\d{2}-\d{2}/);
128
- expect(content).not.toContain('{{DATE}}');
129
- }
130
- });
131
- });
132
- });
@@ -1,144 +0,0 @@
1
- /**
2
- * @file init-quick-ref.test.ts
3
- * Tests for quick-ref commands content (WU-1309)
4
- * Verifies: correct init command, complete wu:create example
5
- */
6
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
- import * as fs from 'node:fs';
8
- import * as path from 'node:path';
9
- import * as os from 'node:os';
10
- import { scaffoldProject } from '../init.js';
11
- // Constants to avoid duplicate strings (sonarjs/no-duplicate-string)
12
- const ARC42_DOCS_STRUCTURE = 'arc42';
13
- describe('quick-ref commands', () => {
14
- let tempDir;
15
- beforeEach(() => {
16
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lumenflow-quickref-test-'));
17
- });
18
- afterEach(() => {
19
- fs.rmSync(tempDir, { recursive: true, force: true });
20
- });
21
- function getQuickRefPath(docsStructure = ARC42_DOCS_STRUCTURE) {
22
- if (docsStructure === 'simple') {
23
- return path.join(tempDir, 'docs', '_frameworks', 'lumenflow', 'agent', 'onboarding', 'quick-ref-commands.md');
24
- }
25
- return path.join(tempDir, 'docs', '04-operations', '_frameworks', 'lumenflow', 'agent', 'onboarding', 'quick-ref-commands.md');
26
- }
27
- function getArc42Options() {
28
- return {
29
- force: false,
30
- full: true,
31
- docsStructure: ARC42_DOCS_STRUCTURE,
32
- };
33
- }
34
- describe('init command documentation', () => {
35
- it('should show correct lumenflow init command', async () => {
36
- await scaffoldProject(tempDir, getArc42Options());
37
- const quickRefPath = getQuickRefPath();
38
- const content = fs.readFileSync(quickRefPath, 'utf-8');
39
- // Should document the init command correctly
40
- expect(content).toContain('lumenflow init');
41
- // Should show the various init flags
42
- expect(content).toContain('--full');
43
- });
44
- it('should document --docs-structure flag in quick-ref', async () => {
45
- await scaffoldProject(tempDir, getArc42Options());
46
- const quickRefPath = getQuickRefPath();
47
- const content = fs.readFileSync(quickRefPath, 'utf-8');
48
- // Should document the docs-structure option
49
- expect(content).toContain('--docs-structure');
50
- expect(content).toMatch(/simple|arc42/);
51
- });
52
- });
53
- describe('wu:create example', () => {
54
- it('should include a complete wu:create example with all required fields', async () => {
55
- await scaffoldProject(tempDir, getArc42Options());
56
- const quickRefPath = getQuickRefPath();
57
- const content = fs.readFileSync(quickRefPath, 'utf-8');
58
- // Should have a complete wu:create example
59
- expect(content).toContain('wu:create');
60
- expect(content).toContain('--lane');
61
- expect(content).toContain('--title');
62
- expect(content).toContain('--description');
63
- expect(content).toContain('--acceptance');
64
- expect(content).toContain('--code-paths');
65
- expect(content).toContain('--exposure');
66
- });
67
- it('should show test-paths in wu:create example', async () => {
68
- await scaffoldProject(tempDir, getArc42Options());
69
- const quickRefPath = getQuickRefPath();
70
- const content = fs.readFileSync(quickRefPath, 'utf-8');
71
- // Should include test paths
72
- expect(content).toMatch(/--test-paths-(unit|e2e)/);
73
- });
74
- it('should show spec-refs in wu:create example for feature WUs', async () => {
75
- await scaffoldProject(tempDir, getArc42Options());
76
- const quickRefPath = getQuickRefPath();
77
- const content = fs.readFileSync(quickRefPath, 'utf-8');
78
- // Should include spec-refs for feature WUs
79
- expect(content).toContain('--spec-refs');
80
- });
81
- });
82
- describe('AGENTS.md quick-ref link', () => {
83
- it('should have correct quick-ref link for arc42 structure', async () => {
84
- await scaffoldProject(tempDir, getArc42Options());
85
- const agentsContent = fs.readFileSync(path.join(tempDir, 'AGENTS.md'), 'utf-8');
86
- // Should point to arc42 path
87
- expect(agentsContent).toContain('docs/04-operations/_frameworks/lumenflow/agent/onboarding/quick-ref-commands.md');
88
- });
89
- it('should have correct quick-ref link for simple structure', async () => {
90
- const options = {
91
- force: false,
92
- full: true,
93
- docsStructure: 'simple',
94
- };
95
- await scaffoldProject(tempDir, options);
96
- const agentsContent = fs.readFileSync(path.join(tempDir, 'AGENTS.md'), 'utf-8');
97
- // Should point to simple path (without 04-operations)
98
- expect(agentsContent).toContain('docs/_frameworks/lumenflow/agent/onboarding/quick-ref-commands.md');
99
- expect(agentsContent).not.toContain('04-operations');
100
- });
101
- });
102
- describe('quick-ref command tables', () => {
103
- it('should have project setup commands including init', async () => {
104
- await scaffoldProject(tempDir, getArc42Options());
105
- const quickRefPath = getQuickRefPath();
106
- const content = fs.readFileSync(quickRefPath, 'utf-8');
107
- // Should have a project setup section
108
- expect(content).toMatch(/##.*?Setup|##.*?Project/i);
109
- expect(content).toContain('lumenflow init');
110
- });
111
- it('should have WU management commands', async () => {
112
- await scaffoldProject(tempDir, getArc42Options());
113
- const quickRefPath = getQuickRefPath();
114
- const content = fs.readFileSync(quickRefPath, 'utf-8');
115
- // Should have WU commands
116
- expect(content).toContain('wu:create');
117
- expect(content).toContain('wu:claim');
118
- expect(content).toContain('wu:done');
119
- expect(content).toContain('wu:block');
120
- });
121
- it('should have gates commands', async () => {
122
- await scaffoldProject(tempDir, getArc42Options());
123
- const quickRefPath = getQuickRefPath();
124
- const content = fs.readFileSync(quickRefPath, 'utf-8');
125
- // Should have gates commands
126
- expect(content).toContain('pnpm gates');
127
- expect(content).toContain('--docs-only');
128
- });
129
- });
130
- describe('workflow sequence', () => {
131
- it('should have a complete workflow sequence example', async () => {
132
- await scaffoldProject(tempDir, getArc42Options());
133
- const quickRefPath = getQuickRefPath();
134
- const content = fs.readFileSync(quickRefPath, 'utf-8');
135
- // Should have workflow sequence
136
- expect(content).toMatch(/workflow|sequence/i);
137
- // Should show the full flow: create -> claim -> work -> commit -> gates -> done
138
- expect(content).toContain('wu:create');
139
- expect(content).toContain('wu:claim');
140
- expect(content).toContain('pnpm gates');
141
- expect(content).toContain('wu:done');
142
- });
143
- });
144
- });