@lumenflow/cli 2.8.0 → 2.10.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.
package/dist/init.js CHANGED
@@ -12,13 +12,15 @@ import * as path from 'node:path';
12
12
  import * as yaml from 'yaml';
13
13
  import { execFileSync } from 'node:child_process';
14
14
  import { fileURLToPath } from 'node:url';
15
- import { getDefaultConfig, createWUParser, WU_OPTIONS } from '@lumenflow/core';
15
+ import { getDefaultConfig, createWUParser, WU_OPTIONS, CLAUDE_HOOKS } from '@lumenflow/core';
16
16
  // WU-1067: Import GATE_PRESETS for --preset support
17
17
  import { GATE_PRESETS } from '@lumenflow/core/dist/gates-config.js';
18
18
  // WU-1171: Import merge block utilities
19
19
  import { updateMergeBlock } from './merge-block.js';
20
20
  // WU-1362: Import worktree guard utilities for branch checking
21
21
  import { isMainBranch, isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
22
+ // WU-1386: Import doctor for auto-run after init
23
+ import { runDoctorForInit } from './doctor.js';
22
24
  /**
23
25
  * WU-1085: CLI option definitions for init command
24
26
  * WU-1171: Added --merge and --client options
@@ -70,9 +72,12 @@ const INIT_OPTIONS = {
70
72
  * Provides proper --help, --version, and option parsing
71
73
  */
72
74
  export function parseInitOptions() {
75
+ // WU-1378: Description includes subcommand hint
73
76
  const opts = createWUParser({
74
77
  name: 'lumenflow-init',
75
- description: 'Initialize LumenFlow in a project',
78
+ description: 'Initialize LumenFlow in a project\n\n' +
79
+ 'Subcommands:\n' +
80
+ ' lumenflow commands List all available CLI commands',
76
81
  options: Object.values(INIT_OPTIONS),
77
82
  });
78
83
  // WU-1171: --client takes precedence, --vendor is alias
@@ -321,9 +326,24 @@ const DEFAULT_LANE_DEFINITIONS = [
321
326
  * WU-1067: Supports --preset option for config-driven gates
322
327
  * WU-1307: Includes default lane definitions for onboarding
323
328
  * WU-1364: Supports git config overrides (requireRemote)
329
+ * WU-1383: Adds enforcement hooks config for Claude client by default
324
330
  */
325
- function generateLumenflowConfigYaml(gatePreset, gitConfigOverride) {
326
- const header = `# LumenFlow Configuration\n# Generated by: lumenflow init\n# Customize paths based on your project structure\n\n`;
331
+ function generateLumenflowConfigYaml(gatePreset, gitConfigOverride, client) {
332
+ // WU-1382: Add managed file header to prevent manual edits
333
+ const header = `# ============================================================================
334
+ # LUMENFLOW MANAGED FILE - DO NOT EDIT MANUALLY
335
+ # ============================================================================
336
+ # Generated by: lumenflow init
337
+ # Regenerate with: pnpm exec lumenflow init --force
338
+ #
339
+ # This file is managed by LumenFlow tooling. Manual edits may be overwritten.
340
+ # To customize, use the CLI commands or edit the appropriate source templates.
341
+ # ============================================================================
342
+
343
+ # LumenFlow Configuration
344
+ # Customize paths based on your project structure
345
+
346
+ `;
327
347
  const config = getDefaultConfig();
328
348
  config.directories.agentsDir = LUMENFLOW_AGENTS_DIR;
329
349
  // WU-1067: Add gates.execution section with preset if specified
@@ -344,6 +364,22 @@ function generateLumenflowConfigYaml(gatePreset, gitConfigOverride) {
344
364
  requireRemote: gitConfigOverride.requireRemote,
345
365
  };
346
366
  }
367
+ // WU-1383: Add enforcement hooks for Claude client by default
368
+ // This prevents agents from working on main and editing config files manually
369
+ if (client === 'claude') {
370
+ config.agents = {
371
+ clients: {
372
+ 'claude-code': {
373
+ enforcement: {
374
+ hooks: true,
375
+ block_outside_worktree: true,
376
+ require_wu_for_edits: true,
377
+ warn_on_stop_without_wu_done: true,
378
+ },
379
+ },
380
+ },
381
+ };
382
+ }
347
383
  return header + yaml.stringify(config);
348
384
  }
349
385
  /**
@@ -453,7 +489,78 @@ const LUMENFLOW_MD_TEMPLATE = `# LumenFlow Workflow Guide\n\n**Last updated:** {
453
489
  const CONSTRAINTS_MD_TEMPLATE = `# LumenFlow Constraints Capsule\n\n**Version:** 1.0\n**Last updated:** {{DATE}}\n\n## The 6 Non-Negotiable Constraints\n\n### 1. Worktree Discipline and Git Safety\nWork only in worktrees, treat main as read-only, never run destructive git commands on main.\n\n### 2. WUs Are Specs, Not Code\nRespect code_paths boundaries, no feature creep, no code blocks in WU YAML files.\n\n### 3. Docs-Only vs Code WUs\nDocumentation WUs use \`--docs-only\` gates, code WUs run full gates.\n\n### 4. LLM-First, Zero-Fallback Inference\nUse LLMs for semantic tasks, fall back to safe defaults (never regex/keywords).\n\n### 5. Gates and Skip-Gates\nComplete via \`pnpm wu:done\`; skip-gates only for pre-existing failures with \`--reason\` and \`--fix-wu\`.\n\n### 6. Safety and Governance\nRespect privacy rules, approved sources, security policies; when uncertain, choose safer path.\n\n---\n\n## Mini Audit Checklist\n\nBefore running \`wu:done\`, verify:\n\n- [ ] Working in worktree (not main)\n- [ ] Only modified files in \`code_paths\`\n- [ ] Gates pass\n- [ ] No forbidden git commands used\n- [ ] Acceptance criteria satisfied\n\n---\n\n## Escalation Triggers\n\nStop and ask a human when:\n- Same error repeats 3 times\n- Auth or permissions changes required\n- PII/PHI/safety issues discovered\n- Cloud spend or secrets involved\n`;
454
490
  // Template for root CLAUDE.md
455
491
  // WU-1309: Use <project-root> placeholder for portability
456
- const CLAUDE_MD_TEMPLATE = `# Claude Code Instructions\n\n**Last updated:** {{DATE}}\n\nThis project uses LumenFlow workflow. For workflow documentation, see [LUMENFLOW.md](LUMENFLOW.md).\n\n---\n\n## Quick Start\n\n\`\`\`bash\n# 1. Claim a WU\npnpm wu:claim --id WU-XXXX --lane <Lane>\ncd worktrees/<lane>-wu-xxxx\n\n# 2. Work in worktree, run gates\npnpm gates\n\n# 3. Complete (ALWAYS run this!)\ncd <project-root>\npnpm wu:done --id WU-XXXX\n\`\`\`\n\n---\n\n## Critical: Always wu:done\n\nAfter completing work, ALWAYS run \`pnpm wu:done --id WU-XXXX\`.\n\nSee [LUMENFLOW.md](LUMENFLOW.md) for full workflow documentation.\n`;
492
+ // WU-1382: Expanded with CLI commands table and warning about manual YAML editing
493
+ const CLAUDE_MD_TEMPLATE = `# Claude Code Instructions
494
+
495
+ **Last updated:** {{DATE}}
496
+
497
+ This project uses LumenFlow workflow. For workflow documentation, see [LUMENFLOW.md](LUMENFLOW.md).
498
+
499
+ ---
500
+
501
+ ## Quick Start
502
+
503
+ \`\`\`bash
504
+ # 1. Claim a WU
505
+ pnpm wu:claim --id WU-XXXX --lane <Lane>
506
+ cd worktrees/<lane>-wu-xxxx
507
+
508
+ # 2. Work in worktree, run gates
509
+ pnpm gates
510
+
511
+ # 3. Complete (ALWAYS run this!)
512
+ cd <project-root>
513
+ pnpm wu:done --id WU-XXXX
514
+ \`\`\`
515
+
516
+ ---
517
+
518
+ ## CLI Commands Reference
519
+
520
+ ### WU Lifecycle
521
+
522
+ | Command | Description |
523
+ | ----------------------------------------- | ---------------------------------------- |
524
+ | \`pnpm wu:status --id WU-XXX\` | Show WU status, location, valid commands |
525
+ | \`pnpm wu:claim --id WU-XXX --lane <Lane>\` | Claim WU and create worktree |
526
+ | \`pnpm wu:prep --id WU-XXX\` | Run gates in worktree, prep for wu:done |
527
+ | \`pnpm wu:done --id WU-XXX\` | Complete WU (from main checkout) |
528
+ | \`pnpm wu:block --id WU-XXX --reason "..."\`| Block WU with reason |
529
+ | \`pnpm wu:unblock --id WU-XXX\` | Unblock WU |
530
+
531
+ ### Gates & Quality
532
+
533
+ | Command | Description |
534
+ | ------------------------ | -------------------------- |
535
+ | \`pnpm gates\` | Run all quality gates |
536
+ | \`pnpm gates --docs-only\` | Run gates for docs changes |
537
+ | \`pnpm format\` | Format all files |
538
+ | \`pnpm lint\` | Run linter |
539
+ | \`pnpm typecheck\` | Run TypeScript check |
540
+ | \`pnpm test\` | Run tests |
541
+
542
+ ---
543
+
544
+ ## Critical: Always wu:done
545
+
546
+ After completing work, ALWAYS run \`pnpm wu:done --id WU-XXXX\` from the main checkout.
547
+
548
+ See [LUMENFLOW.md](LUMENFLOW.md) for full workflow documentation.
549
+
550
+ ---
551
+
552
+ ## Warning: Do Not Edit WU YAML Files Manually
553
+
554
+ **Never manually edit WU YAML files** in \`docs/.../tasks/wu/WU-XXX.yaml\`.
555
+
556
+ Use CLI commands instead:
557
+
558
+ - \`pnpm wu:create ...\` to create new WUs
559
+ - \`pnpm wu:edit --id WU-XXX ...\` to modify WU fields
560
+ - \`pnpm wu:claim\` / \`wu:block\` / \`wu:done\` for status changes
561
+
562
+ Manual edits bypass validation and can corrupt workflow state.
563
+ `;
457
564
  // Template for .claude/settings.json
458
565
  const CLAUDE_SETTINGS_TEMPLATE = `{
459
566
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
@@ -1095,9 +1202,19 @@ Choose the safer path:
1095
1202
  `;
1096
1203
  // WU-1307: Lane inference configuration template (hierarchical Parent→Sublane format)
1097
1204
  // WU-1364: Added Core and Feature as parent lanes for intuitive naming
1205
+ // WU-1382: Added managed file header to prevent manual edits
1098
1206
  // This format is required by lane-inference.ts and lane-checker.ts
1099
- const LANE_INFERENCE_TEMPLATE = `# Lane Inference Configuration
1207
+ const LANE_INFERENCE_TEMPLATE = `# ============================================================================
1208
+ # LUMENFLOW MANAGED FILE - DO NOT EDIT MANUALLY
1209
+ # ============================================================================
1100
1210
  # Generated by: lumenflow init
1211
+ # Regenerate with: pnpm exec lumenflow init --force
1212
+ #
1213
+ # This file is managed by LumenFlow tooling. Manual edits may be overwritten.
1214
+ # To customize lanes, use: pnpm lane:suggest --output .lumenflow.lane-inference.yaml
1215
+ # ============================================================================
1216
+
1217
+ # Lane Inference Configuration
1101
1218
  #
1102
1219
  # Hierarchical format: Parent -> Sublane -> { code_paths, keywords }
1103
1220
  # This format is required by lane-inference.ts for proper sub-lane suggestion.
@@ -1363,6 +1480,7 @@ LumenFlow uses Work Units (WUs) to track all changes:
1363
1480
  - [quick-ref-commands.md](quick-ref-commands.md) - Complete command reference
1364
1481
  - [agent-safety-card.md](agent-safety-card.md) - Safety guidelines
1365
1482
  - [wu-create-checklist.md](wu-create-checklist.md) - WU creation guide
1483
+ - [wu-sizing-guide.md](wu-sizing-guide.md) - WU complexity and context management
1366
1484
  `;
1367
1485
  const WU_CREATE_CHECKLIST_TEMPLATE = `# WU Creation Checklist
1368
1486
 
@@ -1751,6 +1869,96 @@ This detects:
1751
1869
  - Overlapping code paths between lanes
1752
1870
  - Code files not covered by any lane
1753
1871
  `;
1872
+ // WU-1385: WU sizing guide template for agent onboarding
1873
+ const WU_SIZING_GUIDE_TEMPLATE = `# Work Unit Sizing & Strategy Guide
1874
+
1875
+ **Last updated:** {{DATE}}
1876
+
1877
+ **Purpose:** Decision framework for agents to determine execution strategy based on task complexity.
1878
+
1879
+ **Status:** Active — Thresholds are **mandatory limits**, not guidelines.
1880
+
1881
+ ---
1882
+
1883
+ ## Complexity Assessment Matrix
1884
+
1885
+ Before claiming a WU, estimate its "weight" using these heuristics.
1886
+
1887
+ | Complexity | Files | Tool Calls | Context Budget | Strategy |
1888
+ | :------------ | :---- | :--------- | :------------- | :------------------------------------------- |
1889
+ | **Simple** | <20 | <50 | <30% | **Single Session** (Tier 2 Context) |
1890
+ | **Medium** | 20-50 | 50-100 | 30-50% | **Checkpoint-Resume** (Standard Handoff) |
1891
+ | **Complex** | 50+ | 100+ | >50% | **Orchestrator-Worker** OR **Decomposition** |
1892
+ | **Oversized** | 100+ | 200+ | — | **MUST Split** (See Patterns below) |
1893
+
1894
+ **These thresholds are mandatory.** Exceeding them leads to context exhaustion and rule loss. Agents operate in context windows and tool calls, not clock time.
1895
+
1896
+ ---
1897
+
1898
+ ## Context Safety Triggers
1899
+
1900
+ If you hit ANY of these triggers during a session, you MUST checkpoint and spawn fresh:
1901
+
1902
+ - **Token Limit:** Context usage hits **50% (Warning)** or **80% (Critical)**.
1903
+ - **Tool Volume:** **50+ tool calls** in current session.
1904
+ - **File Volume:** **20+ files** modified in \`git status\`.
1905
+ - **Session Staleness:** Repeated redundant queries or forgotten context.
1906
+
1907
+ ---
1908
+
1909
+ ## Spawn Fresh, Don't Continue
1910
+
1911
+ **When approaching context limits, spawn a fresh agent instead of continuing after compaction.**
1912
+
1913
+ Context compaction causes agents to lose critical rules. The disciplined approach:
1914
+
1915
+ 1. Checkpoint your progress: \`pnpm mem:checkpoint --wu WU-XXX\`
1916
+ 2. Commit and push work
1917
+ 3. Generate fresh agent prompt: \`pnpm wu:spawn --id WU-XXX\`
1918
+ 4. EXIT current session (do NOT continue after compaction)
1919
+
1920
+ ---
1921
+
1922
+ ## Splitting Patterns
1923
+
1924
+ When a WU is Oversized or Complex, split it using approved patterns:
1925
+
1926
+ - **Tracer Bullet**: WU-1 proves skeleton works, WU-2 implements real logic
1927
+ - **Layer Split**: WU-1 for ports/application, WU-2 for infrastructure
1928
+ - **UI/Logic Split**: WU-1 for backend, WU-2 for frontend
1929
+ - **Feature Flag**: WU-1 behind flag, WU-2 removes flag
1930
+
1931
+ ---
1932
+
1933
+ ## Quick Reference
1934
+
1935
+ | Scenario | Strategy | Action |
1936
+ | :---------------------------------- | :------------------ | :------------------------------------------- |
1937
+ | Bug fix, single file, <20 calls | Simple | Claim, fix, commit, \`wu:done\` |
1938
+ | Feature 50-100 calls, clear phases | Checkpoint-Resume | Phase 1 → checkpoint → Phase 2 → done |
1939
+ | Multi-domain, must land atomically | Orchestrator-Worker | Main agent coordinates, spawns sub-agents |
1940
+ | Large refactor 100+ calls | Feature Flag Split | WU-A: New behind flag → WU-B: Remove flag |
1941
+
1942
+ ---
1943
+
1944
+ ## Documentation-Only Exception
1945
+
1946
+ Documentation WUs (\`type: documentation\`) have relaxed file count thresholds:
1947
+
1948
+ | Complexity | Files (docs) | Tool Calls | Strategy |
1949
+ | :--------- | :----------- | :--------- | :---------------- |
1950
+ | **Simple** | <40 | <50 | Single Session |
1951
+ | **Medium** | 40-80 | 50-100 | Checkpoint-Resume |
1952
+
1953
+ **Applies when ALL true:**
1954
+ - WU \`type: documentation\`
1955
+ - Only modifies: \`docs/**\`, \`*.md\`
1956
+ - Does NOT touch code paths
1957
+
1958
+ ---
1959
+
1960
+ For complete sizing guidance, see the canonical [wu-sizing-guide.md](https://lumenflow.dev/reference/wu-sizing-guide/) documentation.
1961
+ `;
1754
1962
  // WU-1083: Claude skills templates
1755
1963
  const WU_LIFECYCLE_SKILL_TEMPLATE = `---
1756
1964
  name: wu-lifecycle
@@ -2127,8 +2335,17 @@ export async function scaffoldProject(targetDir, options) {
2127
2335
  };
2128
2336
  // Create .lumenflow.config.yaml (WU-1067: includes gate preset if specified)
2129
2337
  // WU-1364: Includes git config overrides (e.g., requireRemote: false for local-only)
2338
+ // WU-1383: Includes enforcement hooks for Claude client
2130
2339
  // Note: Config files don't use merge mode (always skip or force)
2131
- await createFile(path.join(targetDir, CONFIG_FILE_NAME), generateLumenflowConfigYaml(options.gatePreset, gitConfigOverride), options.force ? 'force' : 'skip', result, targetDir);
2340
+ const configPath = path.join(targetDir, CONFIG_FILE_NAME);
2341
+ // WU-1383: Warn if config already exists to discourage manual editing
2342
+ if (fs.existsSync(configPath) && !options.force) {
2343
+ result.warnings = result.warnings ?? [];
2344
+ result.warnings.push(`${CONFIG_FILE_NAME} already exists. ` +
2345
+ 'To modify configuration, use CLI commands (e.g., pnpm lumenflow:init --force) ' +
2346
+ 'instead of manual editing.');
2347
+ }
2348
+ await createFile(configPath, generateLumenflowConfigYaml(options.gatePreset, gitConfigOverride, client), options.force ? 'force' : 'skip', result, targetDir);
2132
2349
  // WU-1171: Create AGENTS.md (universal entry point for all agents)
2133
2350
  try {
2134
2351
  const agentsTemplate = loadTemplate('core/AGENTS.md.template');
@@ -2382,6 +2599,8 @@ async function scaffoldAgentOnboardingDocs(targetDir, options, result, tokens) {
2382
2599
  await createFile(path.join(onboardingDir, 'troubleshooting-wu-done.md'), processTemplate(TROUBLESHOOTING_WU_DONE_TEMPLATE, tokens), options.force, result, targetDir);
2383
2600
  await createFile(path.join(onboardingDir, 'agent-safety-card.md'), processTemplate(AGENT_SAFETY_CARD_TEMPLATE, tokens), options.force, result, targetDir);
2384
2601
  await createFile(path.join(onboardingDir, 'wu-create-checklist.md'), processTemplate(WU_CREATE_CHECKLIST_TEMPLATE, tokens), options.force, result, targetDir);
2602
+ // WU-1385: Add wu-sizing-guide.md to onboarding docs
2603
+ await createFile(path.join(onboardingDir, 'wu-sizing-guide.md'), processTemplate(WU_SIZING_GUIDE_TEMPLATE, tokens), options.force, result, targetDir);
2385
2604
  }
2386
2605
  /**
2387
2606
  * WU-1083: Scaffold Claude skills
@@ -2429,7 +2648,34 @@ async function scaffoldClientFiles(targetDir, options, result, tokens, client) {
2429
2648
  await createFile(path.join(targetDir, 'CLAUDE.md'), processTemplate(CLAUDE_MD_TEMPLATE, tokens), fileMode, result, targetDir);
2430
2649
  await createDirectory(path.join(targetDir, CLAUDE_AGENTS_DIR), result, targetDir);
2431
2650
  await createFile(path.join(targetDir, CLAUDE_AGENTS_DIR, '.gitkeep'), '', options.force ? 'force' : 'skip', result, targetDir);
2432
- await createFile(path.join(targetDir, CLAUDE_DIR, 'settings.json'), CLAUDE_SETTINGS_TEMPLATE, options.force ? 'force' : 'skip', result, targetDir);
2651
+ // WU-1394: Load settings.json from template (includes PreCompact/SessionStart hooks)
2652
+ let settingsContent;
2653
+ try {
2654
+ settingsContent = loadTemplate(CLAUDE_HOOKS.TEMPLATES.SETTINGS);
2655
+ }
2656
+ catch {
2657
+ settingsContent = CLAUDE_SETTINGS_TEMPLATE;
2658
+ }
2659
+ await createFile(path.join(targetDir, CLAUDE_DIR, 'settings.json'), settingsContent, options.force ? 'force' : 'skip', result, targetDir);
2660
+ // WU-1394: Scaffold recovery hook scripts with executable permissions
2661
+ const hooksDir = path.join(targetDir, CLAUDE_DIR, 'hooks');
2662
+ await createDirectory(hooksDir, result, targetDir);
2663
+ // Load and write pre-compact-checkpoint.sh
2664
+ try {
2665
+ const preCompactScript = loadTemplate(CLAUDE_HOOKS.TEMPLATES.PRE_COMPACT);
2666
+ await createExecutableScript(path.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.PRE_COMPACT_CHECKPOINT), preCompactScript, options.force ? 'force' : 'skip', result, targetDir);
2667
+ }
2668
+ catch {
2669
+ // Template not found - hook won't be scaffolded
2670
+ }
2671
+ // Load and write session-start-recovery.sh
2672
+ try {
2673
+ const sessionStartScript = loadTemplate(CLAUDE_HOOKS.TEMPLATES.SESSION_START);
2674
+ await createExecutableScript(path.join(hooksDir, CLAUDE_HOOKS.SCRIPTS.SESSION_START_RECOVERY), sessionStartScript, options.force ? 'force' : 'skip', result, targetDir);
2675
+ }
2676
+ catch {
2677
+ // Template not found - hook won't be scaffolded
2678
+ }
2433
2679
  // WU-1083: Scaffold Claude skills
2434
2680
  await scaffoldClaudeSkills(targetDir, options, result, tokens);
2435
2681
  // WU-1083: Scaffold agent onboarding docs for Claude vendor (even without --full)
@@ -2555,12 +2801,45 @@ function writeNewFile(filePath, content, result, relativePath) {
2555
2801
  fs.writeFileSync(filePath, content);
2556
2802
  result.created.push(relativePath);
2557
2803
  }
2804
+ /**
2805
+ * WU-1394: Create an executable script file with proper permissions
2806
+ * Similar to createFile but sets 0o755 mode for shell scripts
2807
+ */
2808
+ async function createExecutableScript(filePath, content, mode, result, targetDir) {
2809
+ const relativePath = getRelativePath(targetDir, filePath);
2810
+ const resolvedMode = resolveBooleanToFileMode(mode);
2811
+ result.merged = result.merged ?? [];
2812
+ result.warnings = result.warnings ?? [];
2813
+ const fileExists = fs.existsSync(filePath);
2814
+ if (fileExists && resolvedMode === 'skip') {
2815
+ result.skipped.push(relativePath);
2816
+ return;
2817
+ }
2818
+ // Write file with executable permissions
2819
+ const parentDir = path.dirname(filePath);
2820
+ if (!fs.existsSync(parentDir)) {
2821
+ fs.mkdirSync(parentDir, { recursive: true });
2822
+ }
2823
+ fs.writeFileSync(filePath, content, { mode: 0o755 });
2824
+ result.created.push(relativePath);
2825
+ }
2558
2826
  /**
2559
2827
  * CLI entry point
2560
2828
  * WU-1085: Updated to use parseInitOptions for proper --help support
2561
2829
  * WU-1171: Added --merge and --client support
2830
+ * WU-1378: Added subcommand routing for 'commands' subcommand
2562
2831
  */
2563
2832
  export async function main() {
2833
+ // WU-1378: Check for subcommands before parsing init options
2834
+ const subcommand = process.argv[2];
2835
+ if (subcommand === 'commands') {
2836
+ // Route to commands subcommand
2837
+ const { main: commandsMain } = await import('./commands.js');
2838
+ // Remove 'commands' from argv so the subcommand parser sees clean args
2839
+ process.argv.splice(2, 1);
2840
+ await commandsMain();
2841
+ return;
2842
+ }
2564
2843
  const opts = parseInitOptions();
2565
2844
  const targetDir = process.cwd();
2566
2845
  console.log('[lumenflow init] Scaffolding LumenFlow project...');
@@ -2603,6 +2882,18 @@ export async function main() {
2603
2882
  console.log('\nWarnings:');
2604
2883
  result.warnings.forEach((w) => console.log(` ⚠ ${w}`));
2605
2884
  }
2885
+ // WU-1386: Run doctor auto-check (non-blocking)
2886
+ // This provides feedback on workflow health without failing init
2887
+ try {
2888
+ const doctorResult = await runDoctorForInit(targetDir);
2889
+ if (doctorResult.output) {
2890
+ console.log('');
2891
+ console.log(doctorResult.output);
2892
+ }
2893
+ }
2894
+ catch {
2895
+ // Doctor check is non-blocking - if it fails, continue with init
2896
+ }
2606
2897
  // WU-1359: Show complete lifecycle with auto-ID (no --id flag required)
2607
2898
  // WU-1364: Added initiative-first guidance for product visions
2608
2899
  console.log('\n[lumenflow init] Done! Next steps:');
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Memory Recover CLI (WU-1390)
4
+ *
5
+ * Generate post-compaction recovery context for agents that have lost
6
+ * their LumenFlow instructions due to context compaction.
7
+ *
8
+ * Usage:
9
+ * pnpm mem:recover --wu WU-XXXX [options]
10
+ *
11
+ * Options:
12
+ * --max-size <bytes> Maximum output size in bytes (default: 2048)
13
+ * --format <json|human> Output format (default: human)
14
+ * --quiet Suppress header/footer output
15
+ *
16
+ * The recovery context includes:
17
+ * - Last checkpoint for the WU
18
+ * - Compact constraints (7 rules)
19
+ * - Essential CLI commands
20
+ * - Guidance to spawn fresh agent
21
+ *
22
+ * @see {@link packages/@lumenflow/memory/src/mem-recover-core.ts} - Core logic
23
+ * @see {@link packages/@lumenflow/memory/__tests__/mem-recover-core.test.ts} - Tests
24
+ */
25
+ import fs from 'node:fs/promises';
26
+ import path from 'node:path';
27
+ import { generateRecoveryContext } from '@lumenflow/memory/dist/mem-recover-core.js';
28
+ import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
29
+ import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/dist/wu-constants.js';
30
+ /**
31
+ * Log prefix for mem:recover output
32
+ */
33
+ const LOG_PREFIX = '[mem:recover]';
34
+ /**
35
+ * Tool name for audit logging
36
+ */
37
+ const TOOL_NAME = 'mem:recover';
38
+ /**
39
+ * Valid output formats
40
+ */
41
+ const VALID_FORMATS = ['json', 'human'];
42
+ /**
43
+ * CLI argument options specific to mem:recover
44
+ */
45
+ const CLI_OPTIONS = {
46
+ maxSize: {
47
+ name: 'maxSize',
48
+ flags: '-m, --max-size <bytes>',
49
+ description: 'Maximum output size in bytes (default: 2048)',
50
+ },
51
+ format: {
52
+ name: 'format',
53
+ flags: '-f, --format <format>',
54
+ description: 'Output format: json or human (default: human)',
55
+ },
56
+ baseDir: {
57
+ name: 'baseDir',
58
+ flags: '-d, --base-dir <path>',
59
+ description: 'Base directory (defaults to current directory)',
60
+ },
61
+ quiet: {
62
+ name: 'quiet',
63
+ flags: '-q, --quiet',
64
+ description: 'Suppress header/footer output, only show recovery context',
65
+ },
66
+ };
67
+ /**
68
+ * Write audit log entry for tool execution
69
+ */
70
+ async function writeAuditLog(baseDir, entry) {
71
+ try {
72
+ const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
73
+ const logDir = path.dirname(logPath);
74
+ await fs.mkdir(logDir, { recursive: true });
75
+ const line = `${JSON.stringify(entry)}\n`;
76
+ await fs.appendFile(logPath, line, 'utf-8');
77
+ }
78
+ catch {
79
+ // Audit logging is non-fatal - silently ignore errors
80
+ }
81
+ }
82
+ /**
83
+ * Validate and parse a positive integer argument
84
+ */
85
+ function parsePositiveInt(value, optionName) {
86
+ if (!value) {
87
+ return undefined;
88
+ }
89
+ const parsed = parseInt(value, 10);
90
+ if (isNaN(parsed) || parsed <= 0) {
91
+ throw new Error(`Invalid ${optionName} value: "${value}". Must be a positive integer.`);
92
+ }
93
+ return parsed;
94
+ }
95
+ /**
96
+ * Validate format argument
97
+ */
98
+ function validateFormat(format) {
99
+ if (!format) {
100
+ return 'human';
101
+ }
102
+ if (!VALID_FORMATS.includes(format)) {
103
+ throw new Error(`Invalid --format value: "${format}". Valid formats: ${VALID_FORMATS.join(', ')}`);
104
+ }
105
+ return format;
106
+ }
107
+ /**
108
+ * Print output in human-readable format
109
+ */
110
+ function printHumanFormat(result, wuId, quiet) {
111
+ if (!quiet) {
112
+ console.log(`${LOG_PREFIX} Recovery context for ${wuId}:`);
113
+ console.log('');
114
+ }
115
+ console.log(result.context);
116
+ if (!quiet) {
117
+ console.log('');
118
+ console.log(`${LOG_PREFIX} ${result.size} bytes${result.truncated ? ' (truncated)' : ''}`);
119
+ }
120
+ }
121
+ /**
122
+ * Print output in JSON format
123
+ */
124
+ function printJsonFormat(result, wuId) {
125
+ const output = {
126
+ wuId,
127
+ context: result.context,
128
+ size: result.size,
129
+ truncated: result.truncated,
130
+ };
131
+ console.log(JSON.stringify(output, null, 2));
132
+ }
133
+ /**
134
+ * Main CLI entry point
135
+ */
136
+ async function main() {
137
+ const args = createWUParser({
138
+ name: 'mem-recover',
139
+ description: 'Generate post-compaction recovery context for agents',
140
+ options: [
141
+ WU_OPTIONS.wu,
142
+ CLI_OPTIONS.maxSize,
143
+ CLI_OPTIONS.format,
144
+ CLI_OPTIONS.baseDir,
145
+ CLI_OPTIONS.quiet,
146
+ ],
147
+ required: ['wu'],
148
+ });
149
+ const baseDir = args.baseDir || process.cwd();
150
+ const startedAt = new Date().toISOString();
151
+ const startTime = Date.now();
152
+ let maxSize;
153
+ let format;
154
+ // Validate arguments
155
+ try {
156
+ maxSize = parsePositiveInt(args.maxSize, '--max-size');
157
+ format = validateFormat(args.format);
158
+ }
159
+ catch (err) {
160
+ const error = err;
161
+ console.error(`${LOG_PREFIX} Error: ${error.message}`);
162
+ process.exit(EXIT_CODES.ERROR);
163
+ }
164
+ let result;
165
+ let error = null;
166
+ try {
167
+ result = await generateRecoveryContext({
168
+ wuId: args.wu,
169
+ baseDir,
170
+ maxSize,
171
+ });
172
+ }
173
+ catch (err) {
174
+ const e = err;
175
+ error = e.message;
176
+ result = {
177
+ success: false,
178
+ context: '',
179
+ size: 0,
180
+ truncated: false,
181
+ };
182
+ }
183
+ const durationMs = Date.now() - startTime;
184
+ // Write audit log entry
185
+ await writeAuditLog(baseDir, {
186
+ tool: TOOL_NAME,
187
+ status: error ? 'failed' : 'success',
188
+ startedAt,
189
+ completedAt: new Date().toISOString(),
190
+ durationMs,
191
+ input: {
192
+ baseDir,
193
+ wuId: args.wu,
194
+ maxSize,
195
+ format,
196
+ quiet: args.quiet,
197
+ },
198
+ output: result.success
199
+ ? {
200
+ contextSize: result.size,
201
+ truncated: result.truncated,
202
+ }
203
+ : null,
204
+ error: error ? { message: error } : null,
205
+ });
206
+ if (error) {
207
+ console.error(`${LOG_PREFIX} Error: ${error}`);
208
+ process.exit(EXIT_CODES.ERROR);
209
+ }
210
+ // Print output based on format
211
+ if (format === 'json') {
212
+ printJsonFormat(result, args.wu);
213
+ }
214
+ else {
215
+ printHumanFormat(result, args.wu, !!args.quiet);
216
+ }
217
+ }
218
+ main().catch((e) => {
219
+ console.error(`${LOG_PREFIX} ${e.message}`);
220
+ process.exit(EXIT_CODES.ERROR);
221
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/cli",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "Command-line interface for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -99,6 +99,7 @@
99
99
  "lumenflow-init": "./dist/init.js",
100
100
  "lumenflow": "./dist/init.js",
101
101
  "lumenflow-doctor": "./dist/doctor.js",
102
+ "lumenflow-commands": "./dist/commands.js",
102
103
  "lumenflow-release": "./dist/release.js",
103
104
  "lumenflow-docs-sync": "./dist/docs-sync.js",
104
105
  "lumenflow-sync-templates": "./dist/sync-templates.js",
@@ -150,11 +151,11 @@
150
151
  "pretty-ms": "^9.2.0",
151
152
  "simple-git": "^3.30.0",
152
153
  "yaml": "^2.8.2",
153
- "@lumenflow/core": "2.8.0",
154
- "@lumenflow/memory": "2.8.0",
155
- "@lumenflow/metrics": "2.8.0",
156
- "@lumenflow/agent": "2.8.0",
157
- "@lumenflow/initiatives": "2.8.0"
154
+ "@lumenflow/core": "2.10.0",
155
+ "@lumenflow/metrics": "2.10.0",
156
+ "@lumenflow/initiatives": "2.10.0",
157
+ "@lumenflow/agent": "2.10.0",
158
+ "@lumenflow/memory": "2.10.0"
158
159
  },
159
160
  "devDependencies": {
160
161
  "@vitest/coverage-v8": "^4.0.17",