@lumenflow/cli 2.7.0 → 2.9.0

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 (84) hide show
  1. package/README.md +121 -105
  2. package/dist/__tests__/agent-spawn-coordination.test.js +451 -0
  3. package/dist/__tests__/commands/integrate.test.js +165 -0
  4. package/dist/__tests__/commands.test.js +75 -0
  5. package/dist/__tests__/doctor.test.js +510 -0
  6. package/dist/__tests__/gates-config.test.js +0 -1
  7. package/dist/__tests__/hooks/enforcement.test.js +279 -0
  8. package/dist/__tests__/init-greenfield.test.js +247 -0
  9. package/dist/__tests__/init-quick-ref.test.js +0 -1
  10. package/dist/__tests__/init-template-portability.test.js +0 -1
  11. package/dist/__tests__/init.test.js +249 -0
  12. package/dist/__tests__/initiative-e2e.test.js +442 -0
  13. package/dist/__tests__/initiative-plan-replacement.test.js +0 -1
  14. package/dist/__tests__/memory-integration.test.js +333 -0
  15. package/dist/__tests__/release.test.js +1 -1
  16. package/dist/__tests__/safe-git.test.js +0 -1
  17. package/dist/__tests__/state-doctor.test.js +54 -0
  18. package/dist/__tests__/sync-templates.test.js +255 -0
  19. package/dist/__tests__/wu-create-required-fields.test.js +121 -0
  20. package/dist/__tests__/wu-done-auto-cleanup.test.js +135 -0
  21. package/dist/__tests__/wu-lifecycle-integration.test.js +388 -0
  22. package/dist/backlog-prune.js +0 -1
  23. package/dist/cli-entry-point.js +0 -1
  24. package/dist/commands/integrate.js +229 -0
  25. package/dist/commands.js +171 -0
  26. package/dist/docs-sync.js +46 -0
  27. package/dist/doctor.js +479 -10
  28. package/dist/gates.js +0 -7
  29. package/dist/hooks/enforcement-checks.js +209 -0
  30. package/dist/hooks/enforcement-generator.js +365 -0
  31. package/dist/hooks/enforcement-sync.js +243 -0
  32. package/dist/hooks/index.js +7 -0
  33. package/dist/init.js +502 -17
  34. package/dist/initiative-add-wu.js +0 -2
  35. package/dist/initiative-create.js +0 -3
  36. package/dist/initiative-edit.js +0 -5
  37. package/dist/initiative-plan.js +0 -1
  38. package/dist/initiative-remove-wu.js +0 -2
  39. package/dist/lane-health.js +0 -2
  40. package/dist/lane-suggest.js +0 -1
  41. package/dist/mem-checkpoint.js +0 -2
  42. package/dist/mem-cleanup.js +0 -2
  43. package/dist/mem-context.js +0 -3
  44. package/dist/mem-create.js +0 -2
  45. package/dist/mem-delete.js +0 -3
  46. package/dist/mem-inbox.js +0 -2
  47. package/dist/mem-index.js +0 -1
  48. package/dist/mem-init.js +0 -2
  49. package/dist/mem-profile.js +0 -1
  50. package/dist/mem-promote.js +0 -1
  51. package/dist/mem-ready.js +0 -2
  52. package/dist/mem-signal.js +0 -2
  53. package/dist/mem-start.js +0 -2
  54. package/dist/mem-summarize.js +0 -2
  55. package/dist/metrics-cli.js +1 -1
  56. package/dist/metrics-snapshot.js +1 -1
  57. package/dist/onboarding-smoke-test.js +0 -5
  58. package/dist/orchestrate-init-status.js +0 -1
  59. package/dist/orchestrate-initiative.js +0 -1
  60. package/dist/orchestrate-monitor.js +0 -1
  61. package/dist/plan-create.js +0 -2
  62. package/dist/plan-edit.js +0 -2
  63. package/dist/plan-link.js +0 -2
  64. package/dist/plan-promote.js +0 -2
  65. package/dist/signal-cleanup.js +0 -4
  66. package/dist/state-bootstrap.js +0 -1
  67. package/dist/state-cleanup.js +0 -4
  68. package/dist/state-doctor-fix.js +5 -8
  69. package/dist/state-doctor.js +0 -11
  70. package/dist/sync-templates.js +188 -34
  71. package/dist/wu-block.js +100 -48
  72. package/dist/wu-claim.js +1 -22
  73. package/dist/wu-cleanup.js +0 -1
  74. package/dist/wu-create.js +0 -2
  75. package/dist/wu-done-auto-cleanup.js +139 -0
  76. package/dist/wu-done.js +11 -4
  77. package/dist/wu-edit.js +0 -12
  78. package/dist/wu-preflight.js +0 -1
  79. package/dist/wu-prep.js +0 -1
  80. package/dist/wu-proto.js +0 -1
  81. package/dist/wu-spawn.js +0 -3
  82. package/dist/wu-unblock.js +0 -2
  83. package/dist/wu-validate.js +0 -1
  84. package/package.json +9 -7
@@ -491,4 +491,253 @@ describe('lumenflow init', () => {
491
491
  });
492
492
  });
493
493
  });
494
+ // WU-1382: Improved templates for agent clarity
495
+ describe('WU-1382: improved templates for agent clarity', () => {
496
+ describe('CLAUDE.md template enhancements', () => {
497
+ it('should include CLI commands table inline in CLAUDE.md', async () => {
498
+ const options = {
499
+ force: false,
500
+ full: false,
501
+ client: 'claude',
502
+ };
503
+ await scaffoldProject(tempDir, options);
504
+ const claudeMdPath = path.join(tempDir, 'CLAUDE.md');
505
+ expect(fs.existsSync(claudeMdPath)).toBe(true);
506
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
507
+ // Should have CLI commands table with common commands
508
+ expect(content).toContain('| Command');
509
+ expect(content).toContain('wu:claim');
510
+ expect(content).toContain('wu:done');
511
+ expect(content).toContain('wu:status');
512
+ expect(content).toContain('gates');
513
+ });
514
+ it('should include warning about manual YAML editing in CLAUDE.md', async () => {
515
+ const options = {
516
+ force: false,
517
+ full: false,
518
+ client: 'claude',
519
+ };
520
+ await scaffoldProject(tempDir, options);
521
+ const claudeMdPath = path.join(tempDir, 'CLAUDE.md');
522
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
523
+ // Should warn against manual WU YAML edits
524
+ expect(content).toMatch(/do\s+not\s+(manually\s+)?edit|never\s+(manually\s+)?edit/i);
525
+ expect(content).toMatch(/wu.*yaml|yaml.*wu/i);
526
+ });
527
+ });
528
+ describe('config.yaml managed file header', () => {
529
+ it('should include managed file header in .lumenflow.config.yaml', async () => {
530
+ const options = {
531
+ force: false,
532
+ full: false,
533
+ };
534
+ await scaffoldProject(tempDir, options);
535
+ const configPath = path.join(tempDir, '.lumenflow.config.yaml');
536
+ expect(fs.existsSync(configPath)).toBe(true);
537
+ const content = fs.readFileSync(configPath, 'utf-8');
538
+ // Should have managed file header
539
+ expect(content).toMatch(/LUMENFLOW\s+MANAGED\s+FILE/i);
540
+ expect(content).toMatch(/do\s+not\s+(manually\s+)?edit/i);
541
+ });
542
+ });
543
+ describe('lane-inference.yaml managed file header', () => {
544
+ it('should include managed file header in .lumenflow.lane-inference.yaml', async () => {
545
+ const options = {
546
+ force: false,
547
+ full: true,
548
+ };
549
+ await scaffoldProject(tempDir, options);
550
+ const laneInferencePath = path.join(tempDir, '.lumenflow.lane-inference.yaml');
551
+ expect(fs.existsSync(laneInferencePath)).toBe(true);
552
+ const content = fs.readFileSync(laneInferencePath, 'utf-8');
553
+ // Should have managed file header
554
+ expect(content).toMatch(/LUMENFLOW\s+MANAGED\s+FILE/i);
555
+ expect(content).toMatch(/do\s+not\s+(manually\s+)?edit/i);
556
+ });
557
+ });
558
+ });
559
+ // WU-1383: CLI safeguards against manual file editing
560
+ describe('WU-1383: CLI safeguards for Claude client', () => {
561
+ const CONFIG_FILE_NAME = '.lumenflow.config.yaml';
562
+ describe('enforcement hooks enabled by default for --client claude', () => {
563
+ it('should add enforcement hooks config when --client claude is used', async () => {
564
+ const options = {
565
+ force: false,
566
+ full: false,
567
+ client: 'claude',
568
+ };
569
+ await scaffoldProject(tempDir, options);
570
+ const configPath = path.join(tempDir, CONFIG_FILE_NAME);
571
+ expect(fs.existsSync(configPath)).toBe(true);
572
+ const content = fs.readFileSync(configPath, 'utf-8');
573
+ // Should have enforcement hooks enabled for claude-code
574
+ expect(content).toContain('claude-code');
575
+ expect(content).toContain('enforcement');
576
+ expect(content).toContain('hooks: true');
577
+ });
578
+ it('should set block_outside_worktree to true by default for claude client', async () => {
579
+ const options = {
580
+ force: false,
581
+ full: false,
582
+ client: 'claude',
583
+ };
584
+ await scaffoldProject(tempDir, options);
585
+ const configPath = path.join(tempDir, CONFIG_FILE_NAME);
586
+ const content = fs.readFileSync(configPath, 'utf-8');
587
+ expect(content).toContain('block_outside_worktree: true');
588
+ });
589
+ it('should NOT add enforcement hooks for other clients like cursor', async () => {
590
+ const options = {
591
+ force: false,
592
+ full: false,
593
+ client: 'cursor',
594
+ };
595
+ await scaffoldProject(tempDir, options);
596
+ const configPath = path.join(tempDir, CONFIG_FILE_NAME);
597
+ const content = fs.readFileSync(configPath, 'utf-8');
598
+ // Should NOT have claude-code enforcement section (check for the nested enforcement block)
599
+ // Note: The default config has agents.defaultClient: claude-code, but no enforcement section
600
+ expect(content).not.toContain('block_outside_worktree');
601
+ expect(content).not.toMatch(/claude-code:\s*\n\s*enforcement/);
602
+ });
603
+ });
604
+ describe('warning when config already exists', () => {
605
+ it('should add warning to result when config yaml already exists', async () => {
606
+ // Create existing config file
607
+ fs.writeFileSync(path.join(tempDir, CONFIG_FILE_NAME), '# Existing config\ndirectories:\n tasksDir: docs/tasks\n');
608
+ const options = {
609
+ force: false,
610
+ full: false,
611
+ };
612
+ const result = await scaffoldProject(tempDir, options);
613
+ // Should have warning about existing config
614
+ expect(result.warnings).toBeDefined();
615
+ expect(result.warnings?.some((w) => w.includes('already exists'))).toBe(true);
616
+ // Warning should suggest CLI commands
617
+ expect(result.warnings?.some((w) => w.includes('CLI') || w.includes('lumenflow'))).toBe(true);
618
+ });
619
+ it('should skip config file when it already exists (not force)', async () => {
620
+ const existingContent = '# My custom config\n';
621
+ fs.writeFileSync(path.join(tempDir, CONFIG_FILE_NAME), existingContent);
622
+ const options = {
623
+ force: false,
624
+ full: false,
625
+ };
626
+ const result = await scaffoldProject(tempDir, options);
627
+ expect(result.skipped).toContain(CONFIG_FILE_NAME);
628
+ // Content should not be changed
629
+ const content = fs.readFileSync(path.join(tempDir, CONFIG_FILE_NAME), 'utf-8');
630
+ expect(content).toBe(existingContent);
631
+ });
632
+ });
633
+ describe('post-init output shows CLI commands prominently', () => {
634
+ // Note: These test the ScaffoldResult which contains info for the CLI output
635
+ // The main() function uses these to print output
636
+ it('should include CLI usage guidance in warnings when config exists', async () => {
637
+ fs.writeFileSync(path.join(tempDir, CONFIG_FILE_NAME), '# Existing\n');
638
+ const options = {
639
+ force: false,
640
+ full: false,
641
+ };
642
+ const result = await scaffoldProject(tempDir, options);
643
+ // Warning should mention CLI commands for editing config
644
+ expect(result.warnings?.some((w) => /pnpm|lumenflow|CLI/i.test(w))).toBe(true);
645
+ });
646
+ });
647
+ describe('warning message suggests CLI commands not manual editing', () => {
648
+ it('should warn users to use CLI commands instead of manual editing', async () => {
649
+ fs.writeFileSync(path.join(tempDir, CONFIG_FILE_NAME), '# Existing config\n');
650
+ const options = {
651
+ force: false,
652
+ full: false,
653
+ };
654
+ const result = await scaffoldProject(tempDir, options);
655
+ // Should have a warning that mentions not to manually edit
656
+ expect(result.warnings?.some((w) => w.includes('manual') || w.includes('CLI') || w.includes('lumenflow'))).toBe(true);
657
+ });
658
+ });
659
+ });
660
+ // WU-1362: Branch guard tests for init.ts
661
+ describe('WU-1362: branch guard for tracked file writes', () => {
662
+ it('should block scaffold when on main branch and targeting main checkout', async () => {
663
+ // This test verifies that scaffoldProject checks branch before writing
664
+ // Note: This test uses a temp directory (not on main), so it should pass
665
+ // The actual blocking only applies when targeting main checkout on main branch
666
+ const options = {
667
+ force: false,
668
+ full: false,
669
+ };
670
+ // Since we're in a temp dir, not on main branch, this should work
671
+ const result = await scaffoldProject(tempDir, options);
672
+ expect(result.created.length).toBeGreaterThan(0);
673
+ });
674
+ it('should allow scaffold in worktree directory', async () => {
675
+ // Simulate worktree-like path by creating directory structure
676
+ const worktreePath = path.join(tempDir, 'worktrees', 'operations-wu-999');
677
+ fs.mkdirSync(worktreePath, { recursive: true });
678
+ const options = {
679
+ force: false,
680
+ full: false,
681
+ };
682
+ // Should succeed when in worktree-like path
683
+ const result = await scaffoldProject(worktreePath, options);
684
+ expect(result.created.length).toBeGreaterThan(0);
685
+ });
686
+ });
687
+ // WU-1385: Include wu-sizing-guide.md in lumenflow init onboarding docs
688
+ describe('WU-1385: wu-sizing-guide.md scaffolding', () => {
689
+ describe('wu-sizing-guide.md creation with --full', () => {
690
+ it('should scaffold wu-sizing-guide.md in onboarding docs with --full', async () => {
691
+ const options = {
692
+ force: false,
693
+ full: true,
694
+ docsStructure: 'arc42',
695
+ };
696
+ await scaffoldProject(tempDir, options);
697
+ const onboardingDir = path.join(tempDir, ONBOARDING_DOCS_PATH);
698
+ const sizingGuidePath = path.join(onboardingDir, 'wu-sizing-guide.md');
699
+ expect(fs.existsSync(sizingGuidePath)).toBe(true);
700
+ });
701
+ it('should include key sizing guide content', async () => {
702
+ const options = {
703
+ force: false,
704
+ full: true,
705
+ docsStructure: 'arc42',
706
+ };
707
+ await scaffoldProject(tempDir, options);
708
+ const onboardingDir = path.join(tempDir, ONBOARDING_DOCS_PATH);
709
+ const sizingGuidePath = path.join(onboardingDir, 'wu-sizing-guide.md');
710
+ const content = fs.readFileSync(sizingGuidePath, 'utf-8');
711
+ // Should have key content from the sizing guide
712
+ expect(content).toContain('Complexity');
713
+ expect(content).toContain('Tool Calls');
714
+ expect(content).toContain('Context');
715
+ });
716
+ it('should not scaffold wu-sizing-guide.md with --minimal (full=false)', async () => {
717
+ const options = {
718
+ force: false,
719
+ full: false,
720
+ };
721
+ await scaffoldProject(tempDir, options);
722
+ const onboardingDir = path.join(tempDir, ONBOARDING_DOCS_PATH);
723
+ const sizingGuidePath = path.join(onboardingDir, 'wu-sizing-guide.md');
724
+ expect(fs.existsSync(sizingGuidePath)).toBe(false);
725
+ });
726
+ });
727
+ describe('starting-prompt.md references sizing guide', () => {
728
+ it('should reference wu-sizing-guide.md in starting-prompt.md', async () => {
729
+ const options = {
730
+ force: false,
731
+ full: true,
732
+ docsStructure: 'arc42',
733
+ };
734
+ await scaffoldProject(tempDir, options);
735
+ const onboardingDir = path.join(tempDir, ONBOARDING_DOCS_PATH);
736
+ const startingPromptPath = path.join(onboardingDir, 'starting-prompt.md');
737
+ const content = fs.readFileSync(startingPromptPath, 'utf-8');
738
+ // Should reference the sizing guide
739
+ expect(content).toContain('wu-sizing-guide.md');
740
+ });
741
+ });
742
+ });
494
743
  });