@lumenflow/cli 2.6.0 → 2.8.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 (88) hide show
  1. package/README.md +120 -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__/gates-config.test.js +0 -1
  5. package/dist/__tests__/hooks/enforcement.test.js +279 -0
  6. package/dist/__tests__/init-greenfield.test.js +247 -0
  7. package/dist/__tests__/init-quick-ref.test.js +0 -1
  8. package/dist/__tests__/init-template-portability.test.js +0 -1
  9. package/dist/__tests__/init.test.js +27 -0
  10. package/dist/__tests__/initiative-e2e.test.js +442 -0
  11. package/dist/__tests__/initiative-plan-replacement.test.js +0 -1
  12. package/dist/__tests__/memory-integration.test.js +333 -0
  13. package/dist/__tests__/release.test.js +1 -1
  14. package/dist/__tests__/safe-git.test.js +0 -1
  15. package/dist/__tests__/state-doctor.test.js +54 -0
  16. package/dist/__tests__/sync-templates.test.js +255 -0
  17. package/dist/__tests__/wu-create-required-fields.test.js +121 -0
  18. package/dist/__tests__/wu-done-auto-cleanup.test.js +135 -0
  19. package/dist/__tests__/wu-lifecycle-integration.test.js +388 -0
  20. package/dist/__tests__/wu-proto.test.js +97 -0
  21. package/dist/backlog-prune.js +0 -1
  22. package/dist/cli-entry-point.js +0 -1
  23. package/dist/commands/integrate.js +229 -0
  24. package/dist/docs-sync.js +46 -0
  25. package/dist/doctor.js +0 -2
  26. package/dist/gates.js +0 -7
  27. package/dist/hooks/enforcement-checks.js +209 -0
  28. package/dist/hooks/enforcement-generator.js +365 -0
  29. package/dist/hooks/enforcement-sync.js +243 -0
  30. package/dist/hooks/index.js +7 -0
  31. package/dist/init.js +266 -11
  32. package/dist/initiative-add-wu.js +0 -2
  33. package/dist/initiative-create.js +0 -3
  34. package/dist/initiative-edit.js +0 -5
  35. package/dist/initiative-plan.js +0 -1
  36. package/dist/initiative-remove-wu.js +0 -2
  37. package/dist/lane-health.js +0 -2
  38. package/dist/lane-suggest.js +0 -1
  39. package/dist/mem-checkpoint.js +0 -2
  40. package/dist/mem-cleanup.js +0 -2
  41. package/dist/mem-context.js +0 -3
  42. package/dist/mem-create.js +0 -2
  43. package/dist/mem-delete.js +0 -3
  44. package/dist/mem-inbox.js +0 -2
  45. package/dist/mem-index.js +0 -1
  46. package/dist/mem-init.js +0 -2
  47. package/dist/mem-profile.js +0 -1
  48. package/dist/mem-promote.js +0 -1
  49. package/dist/mem-ready.js +0 -2
  50. package/dist/mem-signal.js +0 -2
  51. package/dist/mem-start.js +0 -2
  52. package/dist/mem-summarize.js +0 -2
  53. package/dist/metrics-cli.js +1 -1
  54. package/dist/metrics-snapshot.js +1 -1
  55. package/dist/onboarding-smoke-test.js +0 -5
  56. package/dist/orchestrate-init-status.js +0 -1
  57. package/dist/orchestrate-initiative.js +0 -1
  58. package/dist/orchestrate-monitor.js +0 -1
  59. package/dist/plan-create.js +0 -2
  60. package/dist/plan-edit.js +0 -2
  61. package/dist/plan-link.js +0 -2
  62. package/dist/plan-promote.js +0 -2
  63. package/dist/signal-cleanup.js +0 -4
  64. package/dist/state-bootstrap.js +0 -1
  65. package/dist/state-cleanup.js +0 -4
  66. package/dist/state-doctor-fix.js +5 -8
  67. package/dist/state-doctor.js +0 -11
  68. package/dist/sync-templates.js +188 -34
  69. package/dist/wu-block.js +100 -48
  70. package/dist/wu-claim.js +1 -22
  71. package/dist/wu-cleanup.js +0 -1
  72. package/dist/wu-create.js +0 -2
  73. package/dist/wu-done-auto-cleanup.js +139 -0
  74. package/dist/wu-done.js +11 -4
  75. package/dist/wu-edit.js +0 -12
  76. package/dist/wu-preflight.js +0 -1
  77. package/dist/wu-prep.js +0 -1
  78. package/dist/wu-proto.js +329 -0
  79. package/dist/wu-spawn.js +0 -3
  80. package/dist/wu-unblock.js +0 -2
  81. package/dist/wu-validate.js +0 -1
  82. package/package.json +9 -7
  83. package/templates/core/.husky/pre-commit.template +93 -0
  84. package/templates/core/ai/onboarding/quick-ref-commands.md.template +27 -0
  85. package/templates/core/ai/onboarding/rapid-prototyping.md +143 -0
  86. package/templates/core/ai/onboarding/starting-prompt.md.template +3 -3
  87. package/templates/vendors/claude/.claude/CLAUDE.md.template +25 -0
  88. package/templates/vendors/claude/.claude/hooks/enforce-worktree.sh +135 -0
package/dist/init.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * WU-1028: Vendor-agnostic core + vendor overlays
6
6
  * WU-1085: Added createWUParser for proper --help support
7
7
  * WU-1171: Added --merge mode, --client flag, AGENTS.md, updated vendor paths
8
+ * WU-1362: Added branch guard to check branch before writing tracked files
8
9
  */
9
10
  import * as fs from 'node:fs';
10
11
  import * as path from 'node:path';
@@ -16,6 +17,8 @@ import { getDefaultConfig, createWUParser, WU_OPTIONS } from '@lumenflow/core';
16
17
  import { GATE_PRESETS } from '@lumenflow/core/dist/gates-config.js';
17
18
  // WU-1171: Import merge block utilities
18
19
  import { updateMergeBlock } from './merge-block.js';
20
+ // WU-1362: Import worktree guard utilities for branch checking
21
+ import { isMainBranch, isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
19
22
  /**
20
23
  * WU-1085: CLI option definitions for init command
21
24
  * WU-1171: Added --merge and --client options
@@ -131,6 +134,44 @@ const LUMENFLOW_DIR = '.lumenflow';
131
134
  const LUMENFLOW_AGENTS_DIR = `${LUMENFLOW_DIR}/agents`;
132
135
  const CLAUDE_DIR = '.claude';
133
136
  const CLAUDE_AGENTS_DIR = path.join(CLAUDE_DIR, 'agents');
137
+ /**
138
+ * WU-1362: Check branch guard before writing tracked files
139
+ *
140
+ * Warns (but does not block) if:
141
+ * - On main branch AND
142
+ * - Not in a worktree directory AND
143
+ * - Git repository exists (has .git)
144
+ *
145
+ * This prevents accidental main branch pollution during init operations.
146
+ * Uses warning instead of error to allow initial project setup.
147
+ *
148
+ * @param targetDir - Directory where files will be written
149
+ * @param result - ScaffoldResult to add warnings to
150
+ */
151
+ async function checkBranchGuard(targetDir, result) {
152
+ result.warnings = result.warnings ?? [];
153
+ // Only check if target is a git repository
154
+ const gitDir = path.join(targetDir, '.git');
155
+ if (!fs.existsSync(gitDir)) {
156
+ // Not a git repo - allow scaffold (initial setup)
157
+ return;
158
+ }
159
+ // Check if we're in a worktree (always allow)
160
+ if (isInWorktree({ cwd: targetDir })) {
161
+ return;
162
+ }
163
+ // Check if on main branch
164
+ try {
165
+ const onMain = await isMainBranch();
166
+ if (onMain) {
167
+ result.warnings.push('Running init on main branch in main checkout. ' +
168
+ 'Consider using a worktree for changes to tracked files.');
169
+ }
170
+ }
171
+ catch {
172
+ // Git error (e.g., not initialized) - silently allow
173
+ }
174
+ }
134
175
  /**
135
176
  * WU-1177: Detect IDE environment from environment variables
136
177
  * Auto-detects which AI coding assistant is running
@@ -279,8 +320,9 @@ const DEFAULT_LANE_DEFINITIONS = [
279
320
  * Generate YAML configuration with header comment
280
321
  * WU-1067: Supports --preset option for config-driven gates
281
322
  * WU-1307: Includes default lane definitions for onboarding
323
+ * WU-1364: Supports git config overrides (requireRemote)
282
324
  */
283
- function generateLumenflowConfigYaml(gatePreset) {
325
+ function generateLumenflowConfigYaml(gatePreset, gitConfigOverride) {
284
326
  const header = `# LumenFlow Configuration\n# Generated by: lumenflow init\n# Customize paths based on your project structure\n\n`;
285
327
  const config = getDefaultConfig();
286
328
  config.directories.agentsDir = LUMENFLOW_AGENTS_DIR;
@@ -296,6 +338,12 @@ function generateLumenflowConfigYaml(gatePreset) {
296
338
  config.lanes = {
297
339
  definitions: DEFAULT_LANE_DEFINITIONS,
298
340
  };
341
+ // WU-1364: Add git config overrides (e.g., requireRemote: false for local-only)
342
+ if (gitConfigOverride) {
343
+ config.git = {
344
+ requireRemote: gitConfigOverride.requireRemote,
345
+ };
346
+ }
299
347
  return header + yaml.stringify(config);
300
348
  }
301
349
  /**
@@ -314,7 +362,6 @@ function normalizeFrameworkName(framework) {
314
362
  .replace(/[^a-z0-9_-]+/g, '-')
315
363
  // Remove leading dashes and trailing dashes separately (explicit precedence)
316
364
  .replace(/^-+/, '')
317
- // eslint-disable-next-line sonarjs/slow-regex -- Simple pattern, no catastrophic backtracking risk
318
365
  .replace(/-+$/, '');
319
366
  if (!slug) {
320
367
  throw new Error(`Invalid framework name: "${framework}"`);
@@ -400,7 +447,8 @@ This file provides universal guidance for all AI agents. Additional vendor-speci
400
447
  `;
401
448
  // Template for LUMENFLOW.md (main entry point)
402
449
  // WU-1309: Use <project-root> placeholder for portability
403
- const LUMENFLOW_MD_TEMPLATE = `# LumenFlow Workflow Guide\n\n**Last updated:** {{DATE}}\n\nLumenFlow is a vendor-agnostic workflow framework for AI-native software development.\n\n---\n\n## Critical Rule: ALWAYS Run wu:done\n\n**After completing work on a WU, you MUST run \`pnpm wu:done --id WU-XXXX\` from the main checkout.**\n\nThis is the single most forgotten step. Do NOT:\n- Write "To Complete: pnpm wu:done" and stop\n- Ask if you should run wu:done\n- Forget to run wu:done\n\n**DO**: Run \`pnpm wu:done --id WU-XXXX\` immediately after gates pass.\n\n---\n\n## Quick Start\n\n\`\`\`bash\n# 1. Create a WU\npnpm wu:create --id WU-XXXX --lane <Lane> --title "Title"\n\n# 2. Edit WU spec with acceptance criteria, then claim:\npnpm wu:claim --id WU-XXXX --lane <Lane>\ncd worktrees/<lane>-wu-xxxx\n\n# 3. Implement in worktree\n\n# 4. Run gates\npnpm gates --docs-only # for docs changes\npnpm gates # for code changes\n\n# 5. Complete (from main checkout)\ncd <project-root>\npnpm wu:done --id WU-XXXX\n\`\`\`\n\n---\n\n## Core Principles\n\n1. **TDD**: Failing test -> implementation -> passing test (>=90% coverage on new code)\n2. **Library-First**: Search existing libraries before custom code\n3. **DRY/SOLID/KISS/YAGNI**: No magic numbers, no hardcoded strings\n4. **Worktree Discipline**: After \`wu:claim\`, work ONLY in the worktree\n5. **Gates Before Done**: All gates must pass before \`wu:done\`\n6. **Do Not Bypass Hooks**: No \`--no-verify\`, fix issues properly\n7. **Always wu:done**: Complete every WU by running \`pnpm wu:done\`\n\n---\n\n## Documentation Structure\n\n### Core (Vendor-Agnostic)\n\n- **LUMENFLOW.md** - This file, main entry point\n- **.lumenflow/constraints.md** - Non-negotiable workflow constraints\n- **.lumenflow/agents/** - Agent instructions (vendor-agnostic)\n- **.lumenflow.config.yaml** - Workflow configuration\n\n### Optional Overlays\n\n- **CLAUDE.md + .claude/agents/** - Claude Code overlay (auto if Claude Code detected)\n- **{{DOCS_TASKS_PATH}}** - Task boards and WU storage (\`lumenflow init --full\`)\n- **{{DOCS_ONBOARDING_PATH}}** - Agent onboarding docs\n- **.lumenflow.framework.yaml** - Framework hint file (created with \`--framework\`)\n\n---\n\n## Worktree Discipline (IMMUTABLE LAW)\n\nAfter claiming a WU, you MUST work in its worktree:\n\n\`\`\`bash\n# 1. Claim creates worktree\npnpm wu:claim --id WU-XXX --lane <lane>\n\n# 2. IMMEDIATELY cd to worktree\ncd worktrees/<lane>-wu-xxx\n\n# 3. ALL work happens here\n\n# 4. Return to main ONLY to complete\ncd <project-root>\npnpm wu:done --id WU-XXX\n\`\`\`\n\n---\n\n## Definition of Done\n\n- Acceptance criteria satisfied\n- Gates green (\`pnpm gates\` or \`pnpm gates --docs-only\`)\n- WU YAML status = \`done\`\n- \`wu:done\` has been run\n\n---\n\n## Commands Reference\n\n| Command | Description |\n| ----------------- | ----------------------------------- |\n| \`pnpm wu:create\` | Create new WU spec |\n| \`pnpm wu:claim\` | Claim WU and create worktree |\n| \`pnpm wu:done\` | Complete WU (merge, stamp, cleanup) |\n| \`pnpm gates\` | Run quality gates |\n\n---\n\n## Constraints\n\nSee [.lumenflow/constraints.md](.lumenflow/constraints.md) for the 6 non-negotiable rules.\n\n---\n\n## Agent Onboarding\n\n- Start with **CLAUDE.md** if present (Claude Code overlay).\n- Add vendor-agnostic guidance in **.lumenflow/agents/**.\n- Check the onboarding docs in **{{DOCS_ONBOARDING_PATH}}** for detailed guidance.\n`;
450
+ // WU-1364: Added initiative workflow section
451
+ const LUMENFLOW_MD_TEMPLATE = `# LumenFlow Workflow Guide\n\n**Last updated:** {{DATE}}\n\nLumenFlow is a vendor-agnostic workflow framework for AI-native software development.\n\n---\n\n## Critical Rule: ALWAYS Run wu:done\n\n**After completing work on a WU, you MUST run \`pnpm wu:done --id WU-XXXX\` from the main checkout.**\n\nThis is the single most forgotten step. Do NOT:\n- Write "To Complete: pnpm wu:done" and stop\n- Ask if you should run wu:done\n- Forget to run wu:done\n\n**DO**: Run \`pnpm wu:done --id WU-XXXX\` immediately after gates pass.\n\n---\n\n## When to Use Initiatives\n\nUse **Initiatives** for multi-phase work spanning multiple WUs:\n\n- **Product visions**: "Build a task management app"\n- **Larger features**: Work requiring multiple WUs across lanes\n- **Complex projects**: Anything that needs phased delivery\n\n\`\`\`bash\n# Create an initiative for multi-phase work\npnpm initiative:create --id INIT-001 --title "Feature Name" \\\\\n --description "..." --phase "Phase 1: MVP" --phase "Phase 2: Polish"\n\n# Add WUs to the initiative\npnpm initiative:add-wu --initiative INIT-001 --wu WU-XXX --phase 1\n\n# Track progress\npnpm initiative:status --id INIT-001\n\`\`\`\n\n**Skip initiatives** for: single-file bug fixes, small docs updates, isolated refactoring.\n\n---\n\n## Quick Start\n\n\`\`\`bash\n# 1. Create a WU\npnpm wu:create --id WU-XXXX --lane <Lane> --title "Title"\n\n# 2. Edit WU spec with acceptance criteria, then claim:\npnpm wu:claim --id WU-XXXX --lane <Lane>\ncd worktrees/<lane>-wu-xxxx\n\n# 3. Implement in worktree\n\n# 4. Run gates\npnpm gates --docs-only # for docs changes\npnpm gates # for code changes\n\n# 5. Complete (from main checkout)\ncd <project-root>\npnpm wu:done --id WU-XXXX\n\`\`\`\n\n---\n\n## Core Principles\n\n1. **TDD**: Failing test -> implementation -> passing test (>=90% coverage on new code)\n2. **Library-First**: Search existing libraries before custom code\n3. **DRY/SOLID/KISS/YAGNI**: No magic numbers, no hardcoded strings\n4. **Worktree Discipline**: After \`wu:claim\`, work ONLY in the worktree\n5. **Gates Before Done**: All gates must pass before \`wu:done\`\n6. **Do Not Bypass Hooks**: No \`--no-verify\`, fix issues properly\n7. **Always wu:done**: Complete every WU by running \`pnpm wu:done\`\n\n---\n\n## Documentation Structure\n\n### Core (Vendor-Agnostic)\n\n- **LUMENFLOW.md** - This file, main entry point\n- **.lumenflow/constraints.md** - Non-negotiable workflow constraints\n- **.lumenflow/agents/** - Agent instructions (vendor-agnostic)\n- **.lumenflow.config.yaml** - Workflow configuration\n\n### Optional Overlays\n\n- **CLAUDE.md + .claude/agents/** - Claude Code overlay (auto if Claude Code detected)\n- **{{DOCS_TASKS_PATH}}** - Task boards and WU storage (\`lumenflow init --full\`)\n- **{{DOCS_ONBOARDING_PATH}}** - Agent onboarding docs\n- **.lumenflow.framework.yaml** - Framework hint file (created with \`--framework\`)\n\n---\n\n## Worktree Discipline (IMMUTABLE LAW)\n\nAfter claiming a WU, you MUST work in its worktree:\n\n\`\`\`bash\n# 1. Claim creates worktree\npnpm wu:claim --id WU-XXX --lane <lane>\n\n# 2. IMMEDIATELY cd to worktree\ncd worktrees/<lane>-wu-xxx\n\n# 3. ALL work happens here\n\n# 4. Return to main ONLY to complete\ncd <project-root>\npnpm wu:done --id WU-XXX\n\`\`\`\n\n---\n\n## Definition of Done\n\n- Acceptance criteria satisfied\n- Gates green (\`pnpm gates\` or \`pnpm gates --docs-only\`)\n- WU YAML status = \`done\`\n- \`wu:done\` has been run\n\n---\n\n## Commands Reference\n\n| Command | Description |\n| ----------------- | ----------------------------------- |\n| \`pnpm wu:create\` | Create new WU spec |\n| \`pnpm wu:claim\` | Claim WU and create worktree |\n| \`pnpm wu:done\` | Complete WU (merge, stamp, cleanup) |\n| \`pnpm gates\` | Run quality gates |\n| \`pnpm initiative:create\` | Create multi-phase initiative |\n| \`pnpm initiative:status\` | View initiative progress |\n\n---\n\n## Constraints\n\nSee [.lumenflow/constraints.md](.lumenflow/constraints.md) for the 6 non-negotiable rules.\n\n---\n\n## Agent Onboarding\n\n- Start with **CLAUDE.md** if present (Claude Code overlay).\n- Add vendor-agnostic guidance in **.lumenflow/agents/**.\n- Check the onboarding docs in **{{DOCS_ONBOARDING_PATH}}** for detailed guidance.\n`;
404
452
  // Template for .lumenflow/constraints.md
405
453
  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`;
406
454
  // Template for root CLAUDE.md
@@ -1046,17 +1094,74 @@ Choose the safer path:
1046
1094
  - Ask rather than assume
1047
1095
  `;
1048
1096
  // WU-1307: Lane inference configuration template (hierarchical Parent→Sublane format)
1097
+ // WU-1364: Added Core and Feature as parent lanes for intuitive naming
1049
1098
  // This format is required by lane-inference.ts and lane-checker.ts
1050
1099
  const LANE_INFERENCE_TEMPLATE = `# Lane Inference Configuration
1051
1100
  # Generated by: lumenflow init
1052
1101
  #
1053
1102
  # Hierarchical format: Parent -> Sublane -> { code_paths, keywords }
1054
1103
  # This format is required by lane-inference.ts for proper sub-lane suggestion.
1104
+ #
1105
+ # Common parent lanes: Core, Feature, Framework, Experience, Operations, Content
1106
+
1107
+ # Core Lane: Platform foundations, shared libraries, base infrastructure
1108
+ Core:
1109
+ Platform:
1110
+ description: 'Core platform: shared utilities, base infrastructure, common libraries'
1111
+ code_paths:
1112
+ - 'packages/**/core/**'
1113
+ - 'src/core/**'
1114
+ - 'src/lib/**'
1115
+ - 'lib/**'
1116
+ keywords:
1117
+ - 'platform'
1118
+ - 'core'
1119
+ - 'infrastructure'
1120
+ - 'foundation'
1121
+
1122
+ Library:
1123
+ description: 'Shared libraries and utilities'
1124
+ code_paths:
1125
+ - 'packages/**/lib/**'
1126
+ - 'src/utils/**'
1127
+ - 'src/helpers/**'
1128
+ keywords:
1129
+ - 'library'
1130
+ - 'utility'
1131
+ - 'helper'
1132
+ - 'shared'
1055
1133
 
1056
- # Framework Lane: Core packages and libraries
1134
+ # Feature Lane: Product features and user-facing functionality
1135
+ Feature:
1136
+ Backend:
1137
+ description: 'Backend features: APIs, services, business logic'
1138
+ code_paths:
1139
+ - 'src/api/**'
1140
+ - 'src/services/**'
1141
+ - 'packages/**/api/**'
1142
+ keywords:
1143
+ - 'api'
1144
+ - 'service'
1145
+ - 'backend'
1146
+ - 'business logic'
1147
+
1148
+ Frontend:
1149
+ description: 'Frontend features: UI, components, pages'
1150
+ code_paths:
1151
+ - 'src/components/**'
1152
+ - 'src/pages/**'
1153
+ - 'src/app/**'
1154
+ - 'apps/web/**'
1155
+ keywords:
1156
+ - 'frontend'
1157
+ - 'ui'
1158
+ - 'component'
1159
+ - 'page'
1160
+
1161
+ # Framework Lane: Framework-specific code and tooling
1057
1162
  Framework:
1058
1163
  Core:
1059
- description: 'Core library: business logic, domain models, utilities'
1164
+ description: 'Core framework: business logic, domain models, utilities'
1060
1165
  code_paths:
1061
1166
  - 'packages/**/core/**'
1062
1167
  - 'src/core/**'
@@ -1151,6 +1256,7 @@ Content:
1151
1256
  {{FRAMEWORK_LANES}}
1152
1257
  `;
1153
1258
  // WU-1300: Starting prompt template for agent onboarding
1259
+ // WU-1364: Added "When Starting From Product Vision" section for initiative-first workflow
1154
1260
  const STARTING_PROMPT_TEMPLATE = `# Starting Prompt for LumenFlow Agents
1155
1261
 
1156
1262
  **Last updated:** {{DATE}}
@@ -1159,6 +1265,48 @@ This document provides the initial context for AI agents working on this project
1159
1265
 
1160
1266
  ---
1161
1267
 
1268
+ ## When Starting From Product Vision
1269
+
1270
+ If you are starting a new project or feature from a product vision (e.g., "Build a task management app"), **do NOT create standalone WUs immediately**. Instead, follow the initiative-first workflow:
1271
+
1272
+ ### 4-Step Initiative Workflow
1273
+
1274
+ 1. **Create an Initiative**: Capture the vision as an initiative
1275
+ \`\`\`bash
1276
+ pnpm initiative:create --id INIT-001 --title "Task Management App" \\
1277
+ --description "Build a task management application with..." \\
1278
+ --phase "Phase 1: Core MVP" --phase "Phase 2: Collaboration"
1279
+ \`\`\`
1280
+
1281
+ 2. **Define Phases**: Break the vision into logical phases (MVP, iteration, polish)
1282
+
1283
+ 3. **Create WUs under the Initiative**: Each WU belongs to a phase
1284
+ \`\`\`bash
1285
+ pnpm wu:create --lane "Core: Platform" --title "Add task model" \\
1286
+ --description "..." --acceptance "..." --code-paths "..." \\
1287
+ && pnpm initiative:add-wu --initiative INIT-001 --wu WU-XXX --phase 1
1288
+ \`\`\`
1289
+
1290
+ 4. **Track Progress**: Use \`pnpm initiative:status --id INIT-001\` to see overall progress
1291
+
1292
+ ### Why Initiatives Matter
1293
+
1294
+ - **Avoid orphan WUs**: Without initiative structure, agents create disconnected WUs that lack coherent scope
1295
+ - **Better coordination**: Phases enable parallel work across lanes
1296
+ - **Clear completion criteria**: The initiative tracks when all phases are done
1297
+ - **Visibility**: Stakeholders can see multi-phase progress
1298
+
1299
+ ### When to Skip Initiatives
1300
+
1301
+ Only skip initiatives for:
1302
+ - Single-file bug fixes
1303
+ - Small documentation updates
1304
+ - Isolated refactoring tasks
1305
+
1306
+ If work spans multiple WUs or multiple days, create an initiative first.
1307
+
1308
+ ---
1309
+
1162
1310
  ## Step 1: Read Core Documentation
1163
1311
 
1164
1312
  Before starting any work, read these documents in order:
@@ -1197,6 +1345,8 @@ LumenFlow uses Work Units (WUs) to track all changes:
1197
1345
  | \`pnpm gates\` | Run quality gates |
1198
1346
  | \`pnpm wu:done --id WU-XXX\` | Complete WU |
1199
1347
  | \`pnpm wu:status --id WU-XXX\` | Check WU status |
1348
+ | \`pnpm initiative:create ...\` | Create a new initiative |
1349
+ | \`pnpm initiative:status --id INIT-XXX\` | Check initiative progress |
1200
1350
 
1201
1351
  ---
1202
1352
 
@@ -1835,6 +1985,85 @@ function getFileMode(options) {
1835
1985
  }
1836
1986
  return 'skip';
1837
1987
  }
1988
+ /**
1989
+ * WU-1364: Check if directory is a git repository
1990
+ */
1991
+ function isGitRepo(targetDir) {
1992
+ try {
1993
+ execFileSync('git', ['rev-parse', '--git-dir'], {
1994
+ cwd: targetDir,
1995
+ stdio: 'pipe',
1996
+ });
1997
+ return true;
1998
+ }
1999
+ catch {
2000
+ return false;
2001
+ }
2002
+ }
2003
+ /**
2004
+ * WU-1364: Check if git repo has any commits
2005
+ */
2006
+ function hasGitCommits(targetDir) {
2007
+ try {
2008
+ execFileSync('git', ['rev-parse', 'HEAD'], {
2009
+ cwd: targetDir,
2010
+ stdio: 'pipe',
2011
+ });
2012
+ return true;
2013
+ }
2014
+ catch {
2015
+ return false;
2016
+ }
2017
+ }
2018
+ /**
2019
+ * WU-1364: Check if git repo has an origin remote
2020
+ */
2021
+ function hasOriginRemote(targetDir) {
2022
+ try {
2023
+ const result = execFileSync('git', ['remote', 'get-url', 'origin'], {
2024
+ cwd: targetDir,
2025
+ encoding: 'utf-8',
2026
+ stdio: 'pipe',
2027
+ });
2028
+ return result.trim().length > 0;
2029
+ }
2030
+ catch {
2031
+ return false;
2032
+ }
2033
+ }
2034
+ /**
2035
+ * WU-1364: Create initial commit if git repo has no commits
2036
+ */
2037
+ function createInitialCommitIfNeeded(targetDir) {
2038
+ if (!isGitRepo(targetDir) || hasGitCommits(targetDir)) {
2039
+ return false;
2040
+ }
2041
+ try {
2042
+ // Stage all files
2043
+ execFileSync('git', ['add', '.'], { cwd: targetDir, stdio: 'pipe' });
2044
+ // Create initial commit
2045
+ execFileSync('git', ['commit', '-m', 'chore: initialize LumenFlow project'], {
2046
+ cwd: targetDir,
2047
+ stdio: 'pipe',
2048
+ });
2049
+ return true;
2050
+ }
2051
+ catch {
2052
+ return false;
2053
+ }
2054
+ }
2055
+ function detectGitStateConfig(targetDir) {
2056
+ // If not a git repo, default to local-only mode for safety
2057
+ if (!isGitRepo(targetDir)) {
2058
+ return { requireRemote: false };
2059
+ }
2060
+ // If git repo but no origin remote, set requireRemote: false
2061
+ if (!hasOriginRemote(targetDir)) {
2062
+ return { requireRemote: false };
2063
+ }
2064
+ // Has origin remote - use default (requireRemote: true)
2065
+ return null;
2066
+ }
1838
2067
  /**
1839
2068
  * WU-1171: Get templates directory path
1840
2069
  */
@@ -1862,6 +2091,7 @@ function loadTemplate(templatePath) {
1862
2091
  /**
1863
2092
  * Scaffold a new LumenFlow project
1864
2093
  * WU-1171: Added AGENTS.md, --merge mode, updated vendor/client handling
2094
+ * WU-1362: Added branch guard to prevent main branch pollution
1865
2095
  */
1866
2096
  export async function scaffoldProject(targetDir, options) {
1867
2097
  const result = {
@@ -1870,9 +2100,12 @@ export async function scaffoldProject(targetDir, options) {
1870
2100
  merged: [],
1871
2101
  warnings: [],
1872
2102
  };
2103
+ // WU-1362: Check branch before writing tracked files
2104
+ // Only block if we're on main branch AND not in a worktree
2105
+ // This allows scaffold to run in worktrees and during initial setup
2106
+ await checkBranchGuard(targetDir, result);
1873
2107
  const defaultClient = options.defaultClient ?? detectDefaultClient();
1874
2108
  // WU-1171: Use resolveClientType with both client and vendor (vendor is deprecated but kept for backwards compat)
1875
- // eslint-disable-next-line sonarjs/deprecation -- Intentional backwards compatibility
1876
2109
  const client = resolveClientType(options.client, options.vendor, defaultClient);
1877
2110
  const fileMode = getFileMode(options);
1878
2111
  // Ensure target directory exists
@@ -1882,6 +2115,8 @@ export async function scaffoldProject(targetDir, options) {
1882
2115
  // WU-1309: Detect or use specified docs structure
1883
2116
  const docsStructure = options.docsStructure ?? detectDocsStructure(targetDir);
1884
2117
  const docsPaths = getDocsPath(docsStructure);
2118
+ // WU-1364: Detect git state for config generation
2119
+ const gitConfigOverride = detectGitStateConfig(targetDir);
1885
2120
  const tokenDefaults = {
1886
2121
  DATE: getCurrentDate(),
1887
2122
  PROJECT_ROOT: '<project-root>', // WU-1309: Use portable placeholder
@@ -1891,8 +2126,9 @@ export async function scaffoldProject(targetDir, options) {
1891
2126
  DOCS_ONBOARDING_PATH: docsPaths.onboarding,
1892
2127
  };
1893
2128
  // Create .lumenflow.config.yaml (WU-1067: includes gate preset if specified)
2129
+ // WU-1364: Includes git config overrides (e.g., requireRemote: false for local-only)
1894
2130
  // Note: Config files don't use merge mode (always skip or force)
1895
- await createFile(path.join(targetDir, CONFIG_FILE_NAME), generateLumenflowConfigYaml(options.gatePreset), options.force ? 'force' : 'skip', result, targetDir);
2131
+ await createFile(path.join(targetDir, CONFIG_FILE_NAME), generateLumenflowConfigYaml(options.gatePreset, gitConfigOverride), options.force ? 'force' : 'skip', result, targetDir);
1896
2132
  // WU-1171: Create AGENTS.md (universal entry point for all agents)
1897
2133
  try {
1898
2134
  const agentsTemplate = loadTemplate('core/AGENTS.md.template');
@@ -1925,6 +2161,12 @@ export async function scaffoldProject(targetDir, options) {
1925
2161
  if (options.full) {
1926
2162
  await injectPackageJsonScripts(targetDir, options, result);
1927
2163
  }
2164
+ // WU-1364: Create initial commit if git repo has no commits
2165
+ // This must be done after all files are created
2166
+ const createdInitialCommit = createInitialCommitIfNeeded(targetDir);
2167
+ if (createdInitialCommit) {
2168
+ result.created.push('Initial git commit');
2169
+ }
1928
2170
  return result;
1929
2171
  }
1930
2172
  /**
@@ -2011,6 +2253,7 @@ const LUMENFLOW_SCRIPTS = {
2011
2253
  'wu:claim': 'wu-claim',
2012
2254
  'wu:done': 'wu-done',
2013
2255
  'wu:create': 'wu-create',
2256
+ 'wu:proto': 'wu-proto', // WU-1359: Prototype WU with relaxed validation
2014
2257
  'wu:status': 'wu-status',
2015
2258
  'wu:block': 'wu-block',
2016
2259
  'wu:unblock': 'wu-unblock',
@@ -2318,7 +2561,6 @@ function writeNewFile(filePath, content, result, relativePath) {
2318
2561
  * WU-1171: Added --merge and --client support
2319
2562
  */
2320
2563
  export async function main() {
2321
- /* eslint-disable no-console -- CLI tool requires console output for user feedback */
2322
2564
  const opts = parseInitOptions();
2323
2565
  const targetDir = process.cwd();
2324
2566
  console.log('[lumenflow init] Scaffolding LumenFlow project...');
@@ -2361,17 +2603,30 @@ export async function main() {
2361
2603
  console.log('\nWarnings:');
2362
2604
  result.warnings.forEach((w) => console.log(` ⚠ ${w}`));
2363
2605
  }
2606
+ // WU-1359: Show complete lifecycle with auto-ID (no --id flag required)
2607
+ // WU-1364: Added initiative-first guidance for product visions
2364
2608
  console.log('\n[lumenflow init] Done! Next steps:');
2365
2609
  console.log(' 1. Review AGENTS.md and LUMENFLOW.md for workflow documentation');
2366
2610
  console.log(` 2. Edit ${CONFIG_FILE_NAME} to match your project structure`);
2367
- console.log(' 3. Run: pnpm wu:create --id WU-0001 --lane <lane> --title "First WU"');
2368
- /* eslint-enable no-console */
2611
+ console.log('');
2612
+ console.log(' For a product vision (multi-phase work):');
2613
+ console.log(' pnpm initiative:create --id INIT-001 --title "Project Name" \\');
2614
+ console.log(' --phase "Phase 1: MVP" --phase "Phase 2: Polish"');
2615
+ console.log('');
2616
+ console.log(' For a single WU:');
2617
+ console.log(' pnpm wu:create --lane <lane> --title "First WU" \\');
2618
+ console.log(' --description "Context: ... Problem: ... Solution: ..." \\');
2619
+ console.log(' --acceptance "Criterion 1" --code-paths "src/..." --exposure backend-only');
2620
+ console.log('');
2621
+ console.log(' # Or for rapid prototyping (minimal validation):');
2622
+ console.log(' pnpm wu:proto --lane <lane> --title "Quick experiment"');
2623
+ console.log('');
2624
+ console.log(' Full lifecycle: wu:create -> wu:claim -> wu:prep -> wu:done');
2369
2625
  }
2370
2626
  // WU-1297: Use import.meta.main instead of exporting main() without calling it
2371
2627
  // This ensures main() runs when the script is executed as a CLI entry point
2372
2628
  if (import.meta.main) {
2373
2629
  main().catch((err) => {
2374
- // eslint-disable-next-line no-console -- CLI error output
2375
2630
  console.error('[lumenflow init] Error:', err instanceof Error ? err.message : String(err));
2376
2631
  process.exit(1);
2377
2632
  });
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable security/detect-non-literal-fs-filename */
3
- /* eslint-disable no-console -- CLI tool requires console output */
4
2
  /**
5
3
  * Initiative Add WU Command (WU-1389)
6
4
  *
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable security/detect-non-literal-fs-filename */
3
- /* eslint-disable no-console -- CLI tool requires console output */
4
- /* eslint-disable @typescript-eslint/explicit-function-return-type -- CLI tool uses dynamic YAML types */
5
2
  /**
6
3
  * Initiative Create Helper (WU-1247, WU-1439)
7
4
  *
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI tool requires console output */
3
- /* eslint-disable @typescript-eslint/explicit-function-return-type -- CLI tool uses dynamic YAML types */
4
2
  /**
5
3
  * Initiative Edit Helper
6
4
  *
@@ -225,12 +223,10 @@ function validateCreatedDate(date) {
225
223
  */
226
224
  function loadInitiative(id) {
227
225
  const initPath = INIT_PATHS.INITIATIVE(id);
228
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool validates init files
229
226
  if (!existsSync(initPath)) {
230
227
  die(`Initiative ${id} not found at ${initPath}\n\n` +
231
228
  `Ensure the Initiative exists and you're in the repo root.`);
232
229
  }
233
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool validates init files
234
230
  const content = readFileSync(initPath, { encoding: FILE_SYSTEM.ENCODING });
235
231
  return parseYAML(content);
236
232
  }
@@ -428,7 +424,6 @@ async function main() {
428
424
  // Write updated Initiative to micro-worktree
429
425
  const initPath = join(worktreePath, INIT_PATHS.INITIATIVE(id));
430
426
  const yamlContent = stringifyYAML(updatedInit);
431
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes init files
432
427
  writeFileSync(initPath, yamlContent, { encoding: FILE_SYSTEM.ENCODING });
433
428
  console.log(`${PREFIX} Updated ${id}.yaml in micro-worktree`);
434
429
  return {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable security/detect-non-literal-fs-filename */
3
2
  /**
4
3
  * Initiative Plan Command (WU-1105, renamed in WU-1193)
5
4
  *
@@ -1,6 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable security/detect-non-literal-fs-filename */
3
- /* eslint-disable no-console -- CLI tool requires console output */
4
2
  /**
5
3
  * Initiative Remove WU Command (WU-1328)
6
4
  *
@@ -301,9 +301,7 @@ export function formatLaneHealthReport(report) {
301
301
  // CLI Entry Point
302
302
  // ============================================================================
303
303
  /** Logger for CLI output */
304
- // eslint-disable-next-line no-console
305
304
  const log = console.log.bind(console);
306
- // eslint-disable-next-line no-console
307
305
  const warn = console.warn.bind(console);
308
306
  /**
309
307
  * Run lane health check
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console */
3
2
  /**
4
3
  * lane:suggest CLI Command (WU-1189, WU-1190)
5
4
  *
@@ -76,10 +76,8 @@ async function writeAuditLog(baseDir, entry) {
76
76
  try {
77
77
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
78
78
  const logDir = path.dirname(logPath);
79
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
80
79
  await fs.mkdir(logDir, { recursive: true });
81
80
  const line = `${JSON.stringify(entry)}\n`;
82
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
83
81
  await fs.appendFile(logPath, line, 'utf-8');
84
82
  }
85
83
  catch {
@@ -88,10 +88,8 @@ async function writeAuditLog(baseDir, entry) {
88
88
  try {
89
89
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
90
90
  const logDir = path.dirname(logPath);
91
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
92
91
  await fs.mkdir(logDir, { recursive: true });
93
92
  const line = `${JSON.stringify(entry)}\n`;
94
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
95
93
  await fs.appendFile(logPath, line, 'utf-8');
96
94
  }
97
95
  catch {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI tool requires console output */
3
2
  /**
4
3
  * Memory Context CLI (WU-1234, WU-1292)
5
4
  *
@@ -96,10 +95,8 @@ async function writeAuditLog(baseDir, entry) {
96
95
  try {
97
96
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
98
97
  const logDir = path.dirname(logPath);
99
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
100
98
  await fs.mkdir(logDir, { recursive: true });
101
99
  const line = `${JSON.stringify(entry)}\n`;
102
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
103
100
  await fs.appendFile(logPath, line, 'utf-8');
104
101
  }
105
102
  catch {
@@ -83,10 +83,8 @@ async function writeAuditLog(baseDir, entry) {
83
83
  try {
84
84
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
85
85
  const logDir = path.dirname(logPath);
86
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
87
86
  await fs.mkdir(logDir, { recursive: true });
88
87
  const line = `${JSON.stringify(entry)}\n`;
89
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
90
88
  await fs.appendFile(logPath, line, 'utf-8');
91
89
  }
92
90
  catch {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI command uses console for status output */
3
2
  /**
4
3
  * Memory Delete CLI (WU-1284)
5
4
  *
@@ -81,10 +80,8 @@ async function writeAuditLog(baseDir, entry) {
81
80
  try {
82
81
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
83
82
  const logDir = path.dirname(logPath);
84
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
85
83
  await fs.mkdir(logDir, { recursive: true });
86
84
  const line = `${JSON.stringify(entry)}\n`;
87
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
88
85
  await fs.appendFile(logPath, line, 'utf-8');
89
86
  }
90
87
  catch {
package/dist/mem-inbox.js CHANGED
@@ -77,10 +77,8 @@ async function writeAuditLog(baseDir, entry) {
77
77
  try {
78
78
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
79
79
  const logDir = path.dirname(logPath);
80
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
81
80
  await fs.mkdir(logDir, { recursive: true });
82
81
  const line = `${JSON.stringify(entry)}\n`;
83
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
84
82
  await fs.appendFile(logPath, line, 'utf-8');
85
83
  }
86
84
  catch {
package/dist/mem-index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI tool requires console output */
3
2
  /**
4
3
  * Memory Index CLI (WU-1235)
5
4
  *
package/dist/mem-init.js CHANGED
@@ -36,11 +36,9 @@ async function writeAuditLog(baseDir, entry) {
36
36
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
37
37
  const logDir = path.dirname(logPath);
38
38
  // Ensure telemetry directory exists
39
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
40
39
  await fs.mkdir(logDir, { recursive: true });
41
40
  // Append NDJSON entry
42
41
  const line = `${JSON.stringify(entry)}\n`;
43
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
44
42
  await fs.appendFile(logPath, line, 'utf-8');
45
43
  }
46
44
  catch {
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI tool requires console output */
3
2
  /**
4
3
  * Memory Profile CLI (WU-1237)
5
4
  *
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable no-console -- CLI tool requires console output */
3
2
  /**
4
3
  * Memory Promote CLI (WU-1237)
5
4
  *
package/dist/mem-ready.js CHANGED
@@ -62,10 +62,8 @@ async function writeAuditLog(baseDir, entry) {
62
62
  try {
63
63
  const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
64
64
  const logDir = path.dirname(logPath);
65
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
66
65
  await fs.mkdir(logDir, { recursive: true });
67
66
  const line = `${JSON.stringify(entry)}\n`;
68
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
69
67
  await fs.appendFile(logPath, line, 'utf-8');
70
68
  }
71
69
  catch {