@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.
- package/README.md +121 -105
- package/dist/__tests__/agent-spawn-coordination.test.js +451 -0
- package/dist/__tests__/commands/integrate.test.js +165 -0
- package/dist/__tests__/commands.test.js +75 -0
- package/dist/__tests__/doctor.test.js +510 -0
- package/dist/__tests__/gates-config.test.js +0 -1
- package/dist/__tests__/hooks/enforcement.test.js +279 -0
- package/dist/__tests__/init-greenfield.test.js +247 -0
- package/dist/__tests__/init-quick-ref.test.js +0 -1
- package/dist/__tests__/init-template-portability.test.js +0 -1
- package/dist/__tests__/init.test.js +249 -0
- package/dist/__tests__/initiative-e2e.test.js +442 -0
- package/dist/__tests__/initiative-plan-replacement.test.js +0 -1
- package/dist/__tests__/memory-integration.test.js +333 -0
- package/dist/__tests__/release.test.js +1 -1
- package/dist/__tests__/safe-git.test.js +0 -1
- package/dist/__tests__/state-doctor.test.js +54 -0
- package/dist/__tests__/sync-templates.test.js +255 -0
- package/dist/__tests__/wu-create-required-fields.test.js +121 -0
- package/dist/__tests__/wu-done-auto-cleanup.test.js +135 -0
- package/dist/__tests__/wu-lifecycle-integration.test.js +388 -0
- package/dist/backlog-prune.js +0 -1
- package/dist/cli-entry-point.js +0 -1
- package/dist/commands/integrate.js +229 -0
- package/dist/commands.js +171 -0
- package/dist/docs-sync.js +46 -0
- package/dist/doctor.js +479 -10
- package/dist/gates.js +0 -7
- package/dist/hooks/enforcement-checks.js +209 -0
- package/dist/hooks/enforcement-generator.js +365 -0
- package/dist/hooks/enforcement-sync.js +243 -0
- package/dist/hooks/index.js +7 -0
- package/dist/init.js +502 -17
- package/dist/initiative-add-wu.js +0 -2
- package/dist/initiative-create.js +0 -3
- package/dist/initiative-edit.js +0 -5
- package/dist/initiative-plan.js +0 -1
- package/dist/initiative-remove-wu.js +0 -2
- package/dist/lane-health.js +0 -2
- package/dist/lane-suggest.js +0 -1
- package/dist/mem-checkpoint.js +0 -2
- package/dist/mem-cleanup.js +0 -2
- package/dist/mem-context.js +0 -3
- package/dist/mem-create.js +0 -2
- package/dist/mem-delete.js +0 -3
- package/dist/mem-inbox.js +0 -2
- package/dist/mem-index.js +0 -1
- package/dist/mem-init.js +0 -2
- package/dist/mem-profile.js +0 -1
- package/dist/mem-promote.js +0 -1
- package/dist/mem-ready.js +0 -2
- package/dist/mem-signal.js +0 -2
- package/dist/mem-start.js +0 -2
- package/dist/mem-summarize.js +0 -2
- package/dist/metrics-cli.js +1 -1
- package/dist/metrics-snapshot.js +1 -1
- package/dist/onboarding-smoke-test.js +0 -5
- package/dist/orchestrate-init-status.js +0 -1
- package/dist/orchestrate-initiative.js +0 -1
- package/dist/orchestrate-monitor.js +0 -1
- package/dist/plan-create.js +0 -2
- package/dist/plan-edit.js +0 -2
- package/dist/plan-link.js +0 -2
- package/dist/plan-promote.js +0 -2
- package/dist/signal-cleanup.js +0 -4
- package/dist/state-bootstrap.js +0 -1
- package/dist/state-cleanup.js +0 -4
- package/dist/state-doctor-fix.js +5 -8
- package/dist/state-doctor.js +0 -11
- package/dist/sync-templates.js +188 -34
- package/dist/wu-block.js +100 -48
- package/dist/wu-claim.js +1 -22
- package/dist/wu-cleanup.js +0 -1
- package/dist/wu-create.js +0 -2
- package/dist/wu-done-auto-cleanup.js +139 -0
- package/dist/wu-done.js +11 -4
- package/dist/wu-edit.js +0 -12
- package/dist/wu-preflight.js +0 -1
- package/dist/wu-prep.js +0 -1
- package/dist/wu-proto.js +0 -1
- package/dist/wu-spawn.js +0 -3
- package/dist/wu-unblock.js +0 -2
- package/dist/wu-validate.js +0 -1
- 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
|
+
}
|
package/dist/commands.js
ADDED
|
@@ -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)
|