@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
@@ -0,0 +1,229 @@
1
+ /**
2
+ * @file integrate.ts
3
+ * Integrate LumenFlow with Claude Code (WU-1367)
4
+ *
5
+ * This command generates enforcement hooks and updates Claude Code
6
+ * configuration based on .lumenflow.config.yaml settings.
7
+ *
8
+ * Usage:
9
+ * pnpm lumenflow:integrate --client claude-code
10
+ */
11
+ // CLI tool - console output is intentional for user feedback
12
+ // fs operations use runtime-provided paths from LumenFlow configuration
13
+ // Object injection sink warnings are false positives for array indexing
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import * as yaml from 'yaml';
17
+ import { createWUParser, WU_OPTIONS } from '@lumenflow/core';
18
+ import { generateEnforcementHooks, generateEnforceWorktreeScript, generateRequireWuScript, generateWarnIncompleteScript, } from '../hooks/enforcement-generator.js';
19
+ /**
20
+ * CLI options for integrate command
21
+ */
22
+ const INTEGRATE_OPTIONS = {
23
+ client: {
24
+ name: 'client',
25
+ flags: '--client <type>',
26
+ description: 'Client type to integrate (claude-code)',
27
+ },
28
+ force: WU_OPTIONS.force,
29
+ };
30
+ /**
31
+ * Parse command line options
32
+ */
33
+ export function parseIntegrateOptions() {
34
+ const opts = createWUParser({
35
+ name: 'lumenflow-integrate',
36
+ description: 'Integrate LumenFlow enforcement with AI client tools',
37
+ options: Object.values(INTEGRATE_OPTIONS),
38
+ });
39
+ return {
40
+ client: opts.client ?? 'claude-code',
41
+ force: opts.force ?? false,
42
+ };
43
+ }
44
+ /**
45
+ * Read existing Claude settings.json
46
+ */
47
+ function readClaudeSettings(projectDir) {
48
+ const settingsPath = path.join(projectDir, '.claude', 'settings.json');
49
+ if (!fs.existsSync(settingsPath)) {
50
+ return {
51
+ $schema: 'https://json.schemastore.org/claude-code-settings.json',
52
+ permissions: {
53
+ allow: ['Bash', 'Read', 'Write', 'Edit', 'WebFetch', 'WebSearch', 'Skill'],
54
+ },
55
+ };
56
+ }
57
+ try {
58
+ const content = fs.readFileSync(settingsPath, 'utf-8');
59
+ return JSON.parse(content);
60
+ }
61
+ catch {
62
+ return {
63
+ $schema: 'https://json.schemastore.org/claude-code-settings.json',
64
+ };
65
+ }
66
+ }
67
+ /**
68
+ * Merge generated hooks into existing settings
69
+ */
70
+ // Complexity is acceptable for hook merging logic - alternative would over-abstract
71
+ // eslint-disable-next-line sonarjs/cognitive-complexity
72
+ function mergeHooksIntoSettings(existing, generated) {
73
+ const result = { ...existing };
74
+ if (!result.hooks) {
75
+ result.hooks = {};
76
+ }
77
+ // Merge PreToolUse hooks
78
+ if (generated.preToolUse) {
79
+ if (!result.hooks.PreToolUse) {
80
+ result.hooks.PreToolUse = [];
81
+ }
82
+ for (const newHook of generated.preToolUse) {
83
+ const existingIndex = result.hooks.PreToolUse.findIndex((h) => h.matcher === newHook.matcher);
84
+ if (existingIndex >= 0) {
85
+ const existing = result.hooks.PreToolUse[existingIndex];
86
+ for (const hook of newHook.hooks) {
87
+ const isDuplicate = existing.hooks.some((h) => h.command === hook.command);
88
+ if (!isDuplicate) {
89
+ existing.hooks.push(hook);
90
+ }
91
+ }
92
+ }
93
+ else {
94
+ result.hooks.PreToolUse.push(newHook);
95
+ }
96
+ }
97
+ }
98
+ // Merge Stop hooks
99
+ if (generated.stop) {
100
+ if (!result.hooks.Stop) {
101
+ result.hooks.Stop = [];
102
+ }
103
+ for (const newHook of generated.stop) {
104
+ const existingIndex = result.hooks.Stop.findIndex((h) => h.matcher === newHook.matcher);
105
+ if (existingIndex >= 0) {
106
+ const existing = result.hooks.Stop[existingIndex];
107
+ for (const hook of newHook.hooks) {
108
+ const isDuplicate = existing.hooks.some((h) => h.command === hook.command);
109
+ if (!isDuplicate) {
110
+ existing.hooks.push(hook);
111
+ }
112
+ }
113
+ }
114
+ else {
115
+ result.hooks.Stop.push(newHook);
116
+ }
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Integrate Claude Code with LumenFlow enforcement hooks.
123
+ *
124
+ * This function:
125
+ * 1. Creates .claude/hooks directory if needed
126
+ * 2. Generates enforcement hook scripts
127
+ * 3. Updates .claude/settings.json with hook configuration
128
+ *
129
+ * @param projectDir - Project directory
130
+ * @param config - Client configuration with enforcement settings
131
+ */
132
+ export async function integrateClaudeCode(projectDir, config) {
133
+ const enforcement = config.enforcement;
134
+ // Skip if enforcement not enabled
135
+ if (!enforcement?.hooks) {
136
+ console.log('[integrate] Enforcement hooks not enabled, skipping');
137
+ return;
138
+ }
139
+ const claudeDir = path.join(projectDir, '.claude');
140
+ const hooksDir = path.join(claudeDir, 'hooks');
141
+ // Create directories
142
+ if (!fs.existsSync(hooksDir)) {
143
+ fs.mkdirSync(hooksDir, { recursive: true });
144
+ console.log('[integrate] Created .claude/hooks directory');
145
+ }
146
+ // Generate hooks based on config
147
+ const generatedHooks = generateEnforcementHooks({
148
+ block_outside_worktree: enforcement.block_outside_worktree ?? false,
149
+ require_wu_for_edits: enforcement.require_wu_for_edits ?? false,
150
+ warn_on_stop_without_wu_done: enforcement.warn_on_stop_without_wu_done ?? false,
151
+ });
152
+ // Write hook scripts
153
+ if (enforcement.block_outside_worktree) {
154
+ const scriptPath = path.join(hooksDir, 'enforce-worktree.sh');
155
+ fs.writeFileSync(scriptPath, generateEnforceWorktreeScript(), { mode: 0o755 });
156
+ console.log('[integrate] Generated enforce-worktree.sh');
157
+ }
158
+ if (enforcement.require_wu_for_edits) {
159
+ const scriptPath = path.join(hooksDir, 'require-wu.sh');
160
+ fs.writeFileSync(scriptPath, generateRequireWuScript(), { mode: 0o755 });
161
+ console.log('[integrate] Generated require-wu.sh');
162
+ }
163
+ if (enforcement.warn_on_stop_without_wu_done) {
164
+ const scriptPath = path.join(hooksDir, 'warn-incomplete.sh');
165
+ fs.writeFileSync(scriptPath, generateWarnIncompleteScript(), { mode: 0o755 });
166
+ console.log('[integrate] Generated warn-incomplete.sh');
167
+ }
168
+ // Update settings.json
169
+ const existingSettings = readClaudeSettings(projectDir);
170
+ const updatedSettings = mergeHooksIntoSettings(existingSettings, generatedHooks);
171
+ const settingsPath = path.join(claudeDir, 'settings.json');
172
+ fs.writeFileSync(settingsPath, JSON.stringify(updatedSettings, null, 2) + '\n', 'utf-8');
173
+ console.log('[integrate] Updated .claude/settings.json');
174
+ }
175
+ /**
176
+ * Read enforcement config from .lumenflow.config.yaml
177
+ */
178
+ function readEnforcementConfig(projectDir) {
179
+ const configPath = path.join(projectDir, '.lumenflow.config.yaml');
180
+ if (!fs.existsSync(configPath)) {
181
+ return null;
182
+ }
183
+ try {
184
+ const content = fs.readFileSync(configPath, 'utf-8');
185
+ const config = yaml.parse(content);
186
+ return config?.agents?.clients?.['claude-code']?.enforcement ?? null;
187
+ }
188
+ catch {
189
+ return null;
190
+ }
191
+ }
192
+ /**
193
+ * Main entry point for integrate command
194
+ */
195
+ export async function main() {
196
+ const opts = parseIntegrateOptions();
197
+ if (opts.client !== 'claude-code') {
198
+ console.error(`[integrate] Unsupported client: ${opts.client}`);
199
+ console.error('[integrate] Currently only "claude-code" is supported');
200
+ process.exit(1);
201
+ }
202
+ const projectDir = process.cwd();
203
+ // Read enforcement config from .lumenflow.config.yaml
204
+ const enforcement = readEnforcementConfig(projectDir);
205
+ if (!enforcement) {
206
+ console.log('[integrate] No enforcement config found in .lumenflow.config.yaml');
207
+ console.log('[integrate] Add this to your config to enable enforcement hooks:');
208
+ console.log(`
209
+ agents:
210
+ clients:
211
+ claude-code:
212
+ enforcement:
213
+ hooks: true
214
+ block_outside_worktree: true
215
+ require_wu_for_edits: true
216
+ warn_on_stop_without_wu_done: true
217
+ `);
218
+ return;
219
+ }
220
+ await integrateClaudeCode(projectDir, { enforcement });
221
+ console.log('[integrate] Claude Code integration complete');
222
+ }
223
+ // Run if executed directly
224
+ if (import.meta.url === `file://${process.argv[1]}`) {
225
+ main().catch((err) => {
226
+ console.error('[integrate] Error:', err.message);
227
+ process.exit(1);
228
+ });
229
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * @file commands.ts
3
+ * LumenFlow CLI commands discovery feature (WU-1378)
4
+ *
5
+ * Provides a way to discover all available CLI commands grouped by category.
6
+ * This helps agents and users find CLI workflows without reading docs.
7
+ */
8
+ import { createWUParser } from '@lumenflow/core';
9
+ import { runCLI } from './cli-entry-point.js';
10
+ /**
11
+ * Command categories organized by function
12
+ * Based on quick-ref-commands.md structure
13
+ */
14
+ const COMMAND_CATEGORIES = [
15
+ {
16
+ name: 'WU Lifecycle',
17
+ commands: [
18
+ { name: 'wu:create', description: 'Create new WU spec' },
19
+ { name: 'wu:claim', description: 'Claim WU and create worktree' },
20
+ { name: 'wu:prep', description: 'Run gates in worktree, prep for wu:done' },
21
+ { name: 'wu:done', description: 'Complete WU (merge, stamp, cleanup) from main' },
22
+ { name: 'wu:edit', description: 'Edit WU spec fields' },
23
+ { name: 'wu:block', description: 'Block WU with reason' },
24
+ { name: 'wu:unblock', description: 'Unblock WU' },
25
+ { name: 'wu:release', description: 'Release orphaned WU (in_progress to ready)' },
26
+ { name: 'wu:status', description: 'Show WU status, location, valid commands' },
27
+ { name: 'wu:spawn', description: 'Generate sub-agent spawn prompt' },
28
+ { name: 'wu:validate', description: 'Validate WU spec' },
29
+ { name: 'wu:recover', description: 'Analyze and fix WU state inconsistencies' },
30
+ ],
31
+ },
32
+ {
33
+ name: 'Gates & Quality',
34
+ commands: [
35
+ { name: 'gates', description: 'Run all quality gates' },
36
+ { name: 'format', description: 'Format all files (Prettier)' },
37
+ { name: 'lint', description: 'Run ESLint' },
38
+ { name: 'typecheck', description: 'Run TypeScript type checking' },
39
+ { name: 'test', description: 'Run all tests (Vitest)' },
40
+ { name: 'lane:health', description: 'Check lane config health' },
41
+ ],
42
+ },
43
+ {
44
+ name: 'Memory & Sessions',
45
+ commands: [
46
+ { name: 'mem:init', description: 'Initialize memory for WU' },
47
+ { name: 'mem:checkpoint', description: 'Save progress checkpoint' },
48
+ { name: 'mem:signal', description: 'Broadcast coordination signal' },
49
+ { name: 'mem:inbox', description: 'Check coordination signals' },
50
+ { name: 'mem:create', description: 'Create memory node (bug discovery)' },
51
+ { name: 'mem:context', description: 'Get context for current lane/WU' },
52
+ ],
53
+ },
54
+ {
55
+ name: 'Initiatives',
56
+ commands: [
57
+ { name: 'initiative:create', description: 'Create new initiative' },
58
+ { name: 'initiative:edit', description: 'Edit initiative fields' },
59
+ { name: 'initiative:list', description: 'List all initiatives' },
60
+ { name: 'initiative:status', description: 'Show initiative status' },
61
+ { name: 'initiative:add-wu', description: 'Add WU to initiative' },
62
+ ],
63
+ },
64
+ {
65
+ name: 'Orchestration',
66
+ commands: [
67
+ { name: 'orchestrate:initiative', description: 'Orchestrate initiative execution' },
68
+ { name: 'orchestrate:init-status', description: 'Compact initiative progress view' },
69
+ { name: 'orchestrate:monitor', description: 'Monitor spawn/agent activity' },
70
+ { name: 'spawn:list', description: 'List active spawned agents' },
71
+ ],
72
+ },
73
+ {
74
+ name: 'Setup & Development',
75
+ commands: [
76
+ { name: 'setup', description: 'Install deps and build CLI (first time)' },
77
+ { name: 'lumenflow', description: 'Initialize LumenFlow in a project' },
78
+ { name: 'lumenflow:doctor', description: 'Diagnose LumenFlow configuration' },
79
+ { name: 'lumenflow:upgrade', description: 'Upgrade LumenFlow packages' },
80
+ { name: 'docs:sync', description: 'Sync agent docs (for upgrades)' },
81
+ ],
82
+ },
83
+ {
84
+ name: 'Metrics & Flow',
85
+ commands: [
86
+ { name: 'flow:report', description: 'Generate flow metrics report' },
87
+ { name: 'flow:bottlenecks', description: 'Identify flow bottlenecks' },
88
+ { name: 'metrics:snapshot', description: 'Capture metrics snapshot' },
89
+ ],
90
+ },
91
+ {
92
+ name: 'State Management',
93
+ commands: [
94
+ { name: 'state:doctor', description: 'Diagnose state store issues' },
95
+ { name: 'state:cleanup', description: 'Clean up stale state data' },
96
+ { name: 'state:bootstrap', description: 'Bootstrap state store' },
97
+ ],
98
+ },
99
+ ];
100
+ /**
101
+ * Get the complete commands registry
102
+ * @returns Array of command categories with their commands
103
+ */
104
+ export function getCommandsRegistry() {
105
+ return COMMAND_CATEGORIES;
106
+ }
107
+ /**
108
+ * Format commands output for terminal display
109
+ * @returns Formatted string with all commands grouped by category
110
+ */
111
+ export function formatCommandsOutput() {
112
+ const lines = [];
113
+ lines.push('LumenFlow CLI Commands');
114
+ lines.push('======================');
115
+ lines.push('');
116
+ for (const category of COMMAND_CATEGORIES) {
117
+ lines.push(`## ${category.name}`);
118
+ lines.push('');
119
+ // Find the longest command name for alignment
120
+ const maxNameLength = Math.max(...category.commands.map((cmd) => cmd.name.length));
121
+ for (const cmd of category.commands) {
122
+ const padding = ' '.repeat(maxNameLength - cmd.name.length + 2);
123
+ lines.push(` ${cmd.name}${padding}${cmd.description}`);
124
+ }
125
+ lines.push('');
126
+ }
127
+ lines.push('---');
128
+ lines.push('Tip: Run `pnpm <command> --help` for detailed options.');
129
+ lines.push('');
130
+ return lines.join('\n');
131
+ }
132
+ /**
133
+ * CLI option definitions for commands command
134
+ */
135
+ const COMMANDS_OPTIONS = {
136
+ json: {
137
+ name: 'json',
138
+ flags: '--json',
139
+ description: 'Output commands as JSON',
140
+ },
141
+ };
142
+ /**
143
+ * Parse commands command options using createWUParser
144
+ */
145
+ export function parseCommandsOptions() {
146
+ const opts = createWUParser({
147
+ name: 'lumenflow-commands',
148
+ description: 'List all available LumenFlow CLI commands',
149
+ options: Object.values(COMMANDS_OPTIONS),
150
+ });
151
+ return {
152
+ json: opts.json ?? false,
153
+ };
154
+ }
155
+ /**
156
+ * Main function for the commands CLI
157
+ */
158
+ export async function main() {
159
+ const opts = parseCommandsOptions();
160
+ if (opts.json) {
161
+ console.log(JSON.stringify(getCommandsRegistry(), null, 2));
162
+ }
163
+ else {
164
+ console.log(formatCommandsOutput());
165
+ }
166
+ }
167
+ // CLI entry point
168
+ // WU-1071: Use import.meta.main for proper CLI detection with pnpm symlinks
169
+ if (import.meta.main) {
170
+ runCLI(main);
171
+ }
package/dist/docs-sync.js CHANGED
@@ -3,11 +3,14 @@
3
3
  * LumenFlow docs:sync command for syncing agent docs to existing projects (WU-1083)
4
4
  * WU-1085: Added createWUParser for proper --help support
5
5
  * WU-1124: Refactored to read templates from bundled files (INIT-004 Phase 2)
6
+ * WU-1362: Added branch guard to check branch before writing tracked files
6
7
  */
7
8
  import * as fs from 'node:fs';
8
9
  import * as path from 'node:path';
9
10
  import { fileURLToPath } from 'node:url';
10
11
  import { createWUParser, WU_OPTIONS } from '@lumenflow/core';
12
+ // WU-1362: Import worktree guard utilities for branch checking
13
+ import { isMainBranch, isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
11
14
  /**
12
15
  * WU-1085: CLI option definitions for docs-sync command
13
16
  */
@@ -182,9 +185,45 @@ export async function syncSkills(targetDir, options) {
182
185
  }
183
186
  return result;
184
187
  }
188
+ /**
189
+ * WU-1362: Check branch guard before writing tracked files
190
+ *
191
+ * Warns (but does not block) if:
192
+ * - On main branch AND
193
+ * - Not in a worktree directory AND
194
+ * - Git repository exists (has .git)
195
+ *
196
+ * @param targetDir - Directory where files will be written
197
+ * @returns Array of warning messages
198
+ */
199
+ async function checkBranchGuard(targetDir) {
200
+ const warnings = [];
201
+ // Only check if target is a git repository
202
+ const gitDir = path.join(targetDir, '.git');
203
+ if (!fs.existsSync(gitDir)) {
204
+ return warnings;
205
+ }
206
+ // Check if we're in a worktree (always allow)
207
+ if (isInWorktree({ cwd: targetDir })) {
208
+ return warnings;
209
+ }
210
+ // Check if on main branch
211
+ try {
212
+ const onMain = await isMainBranch();
213
+ if (onMain) {
214
+ warnings.push('Running docs:sync on main branch in main checkout. ' +
215
+ 'Consider using a worktree for changes to tracked files.');
216
+ }
217
+ }
218
+ catch {
219
+ // Git error - silently allow
220
+ }
221
+ return warnings;
222
+ }
185
223
  /**
186
224
  * CLI entry point for docs:sync command
187
225
  * WU-1085: Updated to use parseDocsSyncOptions for proper --help support
226
+ * WU-1362: Added branch guard check
188
227
  */
189
228
  export async function main() {
190
229
  const opts = parseDocsSyncOptions();
@@ -192,10 +231,13 @@ export async function main() {
192
231
  console.log('[lumenflow docs:sync] Syncing agent documentation...');
193
232
  console.log(` Vendor: ${opts.vendor}`);
194
233
  console.log(` Force: ${opts.force}`);
234
+ // WU-1362: Check branch guard before writing files
235
+ const branchWarnings = await checkBranchGuard(targetDir);
195
236
  const docsResult = await syncAgentDocs(targetDir, { force: opts.force });
196
237
  const skillsResult = await syncSkills(targetDir, { force: opts.force, vendor: opts.vendor });
197
238
  const created = [...docsResult.created, ...skillsResult.created];
198
239
  const skipped = [...docsResult.skipped, ...skillsResult.skipped];
240
+ const warnings = [...branchWarnings];
199
241
  if (created.length > 0) {
200
242
  console.log('\nCreated:');
201
243
  created.forEach((f) => console.log(` + ${f}`));
@@ -204,6 +246,10 @@ export async function main() {
204
246
  console.log('\nSkipped (already exists, use --force to overwrite):');
205
247
  skipped.forEach((f) => console.log(` - ${f}`));
206
248
  }
249
+ if (warnings.length > 0) {
250
+ console.log('\nWarnings:');
251
+ warnings.forEach((w) => console.log(` ! ${w}`));
252
+ }
207
253
  console.log('\n[lumenflow docs:sync] Done!');
208
254
  }
209
255
  // CLI entry point (WU-1071 pattern: import.meta.main)