@lumenflow/cli 2.1.0 → 2.1.2

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.
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @file guard-main-branch.test.ts
3
+ * @description Tests for guard-main-branch worktree context detection (WU-1130)
4
+ */
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
+ import { guardMainBranch } from '../guard-main-branch.js';
7
+ // Mock the core module
8
+ vi.mock('@lumenflow/core', () => ({
9
+ createGitForPath: vi.fn(),
10
+ getGitForCwd: vi.fn(),
11
+ isAgentBranch: vi.fn().mockResolvedValue(false),
12
+ getConfig: vi.fn().mockReturnValue({
13
+ git: {
14
+ mainBranch: 'main',
15
+ laneBranchPrefix: 'lane/',
16
+ },
17
+ }),
18
+ }));
19
+ // Mock the worktree-guard module
20
+ vi.mock('@lumenflow/core/dist/core/worktree-guard.js', () => ({
21
+ isInWorktree: vi.fn(),
22
+ }));
23
+ import { getGitForCwd, createGitForPath } from '@lumenflow/core';
24
+ import { isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
25
+ describe('guard-main-branch (WU-1130)', () => {
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+ afterEach(() => {
30
+ vi.restoreAllMocks();
31
+ });
32
+ describe('lane branch worktree detection', () => {
33
+ it('should allow operations when on lane branch AND in worktree', async () => {
34
+ // Setup: On lane branch, in worktree
35
+ const mockGit = {
36
+ getCurrentBranch: vi.fn().mockResolvedValue('lane/framework-cli/wu-1130'),
37
+ };
38
+ vi.mocked(getGitForCwd).mockReturnValue(mockGit);
39
+ vi.mocked(isInWorktree).mockReturnValue(true);
40
+ const result = await guardMainBranch({});
41
+ expect(result.success).toBe(true);
42
+ expect(result.isProtected).toBe(false);
43
+ expect(result.currentBranch).toBe('lane/framework-cli/wu-1130');
44
+ });
45
+ it('should block operations when on lane branch but NOT in worktree', async () => {
46
+ // Setup: On lane branch, but not in worktree (e.g., checked out directly)
47
+ const mockGit = {
48
+ getCurrentBranch: vi.fn().mockResolvedValue('lane/framework-cli/wu-1130'),
49
+ };
50
+ vi.mocked(getGitForCwd).mockReturnValue(mockGit);
51
+ vi.mocked(isInWorktree).mockReturnValue(false);
52
+ const result = await guardMainBranch({});
53
+ expect(result.success).toBe(true);
54
+ expect(result.isProtected).toBe(true);
55
+ expect(result.reason).toContain('requires worktree');
56
+ });
57
+ it('should use baseDir for worktree detection when provided', async () => {
58
+ const mockGit = {
59
+ getCurrentBranch: vi.fn().mockResolvedValue('lane/ops-tooling/wu-2725'),
60
+ };
61
+ vi.mocked(createGitForPath).mockReturnValue(mockGit);
62
+ vi.mocked(isInWorktree).mockReturnValue(true);
63
+ const result = await guardMainBranch({ baseDir: '/path/to/worktrees/ops-tooling-wu-2725' });
64
+ expect(isInWorktree).toHaveBeenCalledWith({ cwd: '/path/to/worktrees/ops-tooling-wu-2725' });
65
+ expect(result.isProtected).toBe(false);
66
+ });
67
+ });
68
+ describe('main branch protection', () => {
69
+ it('should block operations on main branch', async () => {
70
+ const mockGit = {
71
+ getCurrentBranch: vi.fn().mockResolvedValue('main'),
72
+ };
73
+ vi.mocked(getGitForCwd).mockReturnValue(mockGit);
74
+ const result = await guardMainBranch({});
75
+ expect(result.isProtected).toBe(true);
76
+ expect(result.reason).toContain("'main' is protected");
77
+ });
78
+ });
79
+ });
@@ -3,18 +3,50 @@
3
3
  * Tests for lumenflow-upgrade CLI command
4
4
  *
5
5
  * WU-1112: INIT-003 Phase 6 - Migrate remaining Tier 1 tools
6
+ * WU-1127: Add micro-worktree isolation pattern
6
7
  *
7
8
  * lumenflow-upgrade updates all @lumenflow/* packages to latest versions.
8
9
  * Key requirements:
9
- * - Uses worktree pattern (runs pnpm install in worktree, not main)
10
+ * - Uses micro-worktree pattern (atomic changes to main without requiring user worktree)
10
11
  * - Checks all 7 @lumenflow/* packages
12
+ * - Supports --dry-run and --latest flags
11
13
  */
12
- import { describe, it, expect, vi, beforeEach } from 'vitest';
13
- // Import functions under test
14
- import { parseUpgradeArgs, LUMENFLOW_PACKAGES, buildUpgradeCommands, } from '../lumenflow-upgrade.js';
14
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
15
+ import { execSync } from 'node:child_process';
16
+ // Mock modules with inline factories (no external references)
17
+ vi.mock('@lumenflow/core/dist/micro-worktree.js', () => ({
18
+ withMicroWorktree: vi.fn(),
19
+ }));
20
+ vi.mock('@lumenflow/core/dist/git-adapter.js', () => ({
21
+ getGitForCwd: vi.fn(),
22
+ }));
23
+ vi.mock('node:child_process', async (importOriginal) => {
24
+ const actual = await importOriginal();
25
+ return {
26
+ ...actual,
27
+ execSync: vi.fn(),
28
+ };
29
+ });
30
+ // Import mocked modules to access mock functions
31
+ import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
32
+ import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
33
+ // Import functions under test after mocks are set up
34
+ import { parseUpgradeArgs, LUMENFLOW_PACKAGES, buildUpgradeCommands, executeUpgradeInMicroWorktree, validateMainCheckout, } from '../lumenflow-upgrade.js';
35
+ // Cast mocks for TypeScript
36
+ const mockWithMicroWorktree = withMicroWorktree;
37
+ const mockGetGitForCwd = getGitForCwd;
38
+ const mockExecSync = execSync;
15
39
  describe('lumenflow-upgrade', () => {
16
40
  beforeEach(() => {
17
41
  vi.clearAllMocks();
42
+ // Default mock behavior for git adapter
43
+ mockGetGitForCwd.mockReturnValue({
44
+ raw: vi.fn().mockResolvedValue('main'),
45
+ getStatus: vi.fn().mockResolvedValue(''),
46
+ });
47
+ });
48
+ afterEach(() => {
49
+ vi.restoreAllMocks();
18
50
  });
19
51
  describe('LUMENFLOW_PACKAGES constant', () => {
20
52
  it('should include all 7 @lumenflow/* packages', () => {
@@ -94,14 +126,135 @@ describe('lumenflow-upgrade', () => {
94
126
  expect(packageCount).toBe(7);
95
127
  });
96
128
  });
97
- describe('worktree pattern enforcement', () => {
98
- it('should include note about worktree usage in commands', () => {
99
- const args = { version: '1.5.0' };
100
- const commands = buildUpgradeCommands(args);
101
- // The command should be designed to run in worktree
102
- // Actual execution is tested in integration tests
103
- expect(commands.addCommand).toBeDefined();
104
- expect(commands.addCommand.length).toBeGreaterThan(0);
129
+ // WU-1127: Tests for micro-worktree isolation pattern
130
+ describe('validateMainCheckout', () => {
131
+ let originalCwd;
132
+ beforeEach(() => {
133
+ originalCwd = process.cwd;
134
+ });
135
+ afterEach(() => {
136
+ process.cwd = originalCwd;
137
+ });
138
+ it('should return valid when on main branch and not in worktree', async () => {
139
+ // Mock process.cwd to be on main checkout (not worktree)
140
+ process.cwd = vi.fn().mockReturnValue('/path/to/repo');
141
+ mockGetGitForCwd.mockReturnValue({
142
+ raw: vi.fn().mockResolvedValue('main'),
143
+ getStatus: vi.fn().mockResolvedValue(''),
144
+ });
145
+ const result = await validateMainCheckout();
146
+ expect(result.valid).toBe(true);
147
+ });
148
+ it('should return invalid when not on main branch', async () => {
149
+ // Mock process.cwd to be on main checkout (not worktree)
150
+ process.cwd = vi.fn().mockReturnValue('/path/to/repo');
151
+ mockGetGitForCwd.mockReturnValue({
152
+ raw: vi.fn().mockResolvedValue('lane/framework-cli/wu-123'),
153
+ getStatus: vi.fn().mockResolvedValue(''),
154
+ });
155
+ const result = await validateMainCheckout();
156
+ expect(result.valid).toBe(false);
157
+ expect(result.error).toContain('must be run from main checkout');
158
+ });
159
+ it('should return invalid when in a worktree directory', async () => {
160
+ // Mock process.cwd() to be in a worktree
161
+ process.cwd = vi
162
+ .fn()
163
+ .mockReturnValue('/path/to/repo/worktrees/some-wu');
164
+ mockGetGitForCwd.mockReturnValue({
165
+ raw: vi.fn().mockResolvedValue('main'),
166
+ getStatus: vi.fn().mockResolvedValue(''),
167
+ });
168
+ const result = await validateMainCheckout();
169
+ expect(result.valid).toBe(false);
170
+ expect(result.error).toContain('worktree');
171
+ });
172
+ });
173
+ describe('executeUpgradeInMicroWorktree', () => {
174
+ it('should call withMicroWorktree with correct operation name', async () => {
175
+ mockWithMicroWorktree.mockResolvedValue({});
176
+ mockExecSync.mockReturnValue('');
177
+ const args = { version: '2.1.0' };
178
+ await executeUpgradeInMicroWorktree(args);
179
+ expect(mockWithMicroWorktree).toHaveBeenCalledTimes(1);
180
+ expect(mockWithMicroWorktree).toHaveBeenCalledWith(expect.objectContaining({
181
+ operation: 'lumenflow-upgrade',
182
+ }));
183
+ });
184
+ it('should use a unique ID for micro-worktree based on timestamp', async () => {
185
+ mockWithMicroWorktree.mockResolvedValue({});
186
+ mockExecSync.mockReturnValue('');
187
+ const args = { latest: true };
188
+ await executeUpgradeInMicroWorktree(args);
189
+ // ID should be a timestamp-like string
190
+ expect(mockWithMicroWorktree).toHaveBeenCalledWith(expect.objectContaining({
191
+ id: expect.stringMatching(/^upgrade-\d+$/),
192
+ }));
193
+ });
194
+ it('should execute pnpm add in the micro-worktree', async () => {
195
+ mockWithMicroWorktree.mockImplementation(async (options) => {
196
+ // Simulate calling the execute function with a worktree path
197
+ return options.execute({ worktreePath: '/tmp/test-worktree' });
198
+ });
199
+ const args = { version: '2.1.0' };
200
+ await executeUpgradeInMicroWorktree(args);
201
+ // Verify pnpm add was executed with correct cwd
202
+ expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining('pnpm add'), expect.objectContaining({
203
+ cwd: '/tmp/test-worktree',
204
+ }));
205
+ });
206
+ it('should include all 7 packages in the pnpm add command', async () => {
207
+ mockWithMicroWorktree.mockImplementation(async (options) => {
208
+ return options.execute({ worktreePath: '/tmp/test-worktree' });
209
+ });
210
+ const args = { version: '2.1.0' };
211
+ await executeUpgradeInMicroWorktree(args);
212
+ // Get the command that was executed
213
+ const execCall = mockExecSync.mock.calls[0][0];
214
+ expect(typeof execCall).toBe('string');
215
+ // Verify all 7 packages are included
216
+ for (const pkg of LUMENFLOW_PACKAGES) {
217
+ expect(execCall).toContain(`${pkg}@2.1.0`);
218
+ }
219
+ });
220
+ it('should return appropriate commit message and files', async () => {
221
+ let executeResult;
222
+ mockWithMicroWorktree.mockImplementation(async (options) => {
223
+ executeResult = await options.execute({ worktreePath: '/tmp/test-worktree' });
224
+ return executeResult;
225
+ });
226
+ const args = { version: '2.1.0' };
227
+ await executeUpgradeInMicroWorktree(args);
228
+ expect(executeResult).toBeDefined();
229
+ expect(executeResult.commitMessage).toContain('upgrade @lumenflow packages');
230
+ expect(executeResult.files).toContain('package.json');
231
+ expect(executeResult.files).toContain('pnpm-lock.yaml');
232
+ });
233
+ it('should use --latest version specifier when latest flag is set', async () => {
234
+ mockWithMicroWorktree.mockImplementation(async (options) => {
235
+ return options.execute({ worktreePath: '/tmp/test-worktree' });
236
+ });
237
+ const args = { latest: true };
238
+ await executeUpgradeInMicroWorktree(args);
239
+ const execCall = mockExecSync.mock.calls[0][0];
240
+ expect(execCall).toContain('@lumenflow/core@latest');
241
+ });
242
+ });
243
+ describe('dry-run mode', () => {
244
+ it('should not call withMicroWorktree when dryRun is true', async () => {
245
+ const args = { version: '2.1.0', dryRun: true };
246
+ // In dry-run mode, executeUpgradeInMicroWorktree should not be called
247
+ // This is handled by the main() function checking dryRun before calling execute
248
+ // We just verify the function exists and can be called
249
+ expect(typeof executeUpgradeInMicroWorktree).toBe('function');
250
+ });
251
+ });
252
+ describe('legacy worktree validation removal', () => {
253
+ it('should not require user to be in a worktree', () => {
254
+ // The old implementation required users to be inside a worktree
255
+ // The new implementation uses micro-worktree and runs from main checkout
256
+ // This test verifies the old validateWorktreeContext is no longer used
257
+ expect(typeof validateMainCheckout).toBe('function');
105
258
  });
106
259
  });
107
260
  });
@@ -13,6 +13,7 @@
13
13
  * WU-1109: INIT-003 Phase 4b - Migrate git operations
14
14
  */
15
15
  import { createGitForPath, getGitForCwd, isAgentBranch, getConfig } from '@lumenflow/core';
16
+ import { isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
16
17
  /**
17
18
  * Parse command line arguments for guard-main-branch
18
19
  */
@@ -90,6 +91,16 @@ export async function guardMainBranch(args) {
90
91
  }
91
92
  // Check if on a lane branch (requires worktree discipline)
92
93
  if (isLaneBranch(currentBranch)) {
94
+ // If we're actually in a worktree, allow the operation (WU-1130)
95
+ const cwd = args.baseDir ?? process.cwd();
96
+ if (isInWorktree({ cwd })) {
97
+ return {
98
+ success: true,
99
+ isProtected: false,
100
+ currentBranch,
101
+ };
102
+ }
103
+ // On lane branch but not in worktree - block
93
104
  return {
94
105
  success: true,
95
106
  isProtected: true,
@@ -3,13 +3,16 @@
3
3
  * LumenFlow Upgrade CLI Command
4
4
  *
5
5
  * Updates all @lumenflow/* packages to a specified version or latest.
6
- * Uses worktree pattern to ensure pnpm install runs in worktree, not main.
6
+ * Uses micro-worktree pattern for atomic changes to main without requiring
7
+ * users to be in a worktree.
7
8
  *
8
9
  * WU-1112: INIT-003 Phase 6 - Migrate remaining Tier 1 tools
10
+ * WU-1127: Use micro-worktree isolation pattern (fixes user blocking issue)
9
11
  *
10
- * Key requirements (from WU acceptance criteria):
11
- * - Uses worktree pattern (install runs in worktree, not main)
12
- * - Checks all 7 @lumenflow/* packages (not just 4)
12
+ * Key requirements:
13
+ * - Uses micro-worktree pattern (atomic changes, no user worktree needed)
14
+ * - Runs from main checkout (not inside a worktree)
15
+ * - Checks all 7 @lumenflow/* packages
13
16
  *
14
17
  * Usage:
15
18
  * pnpm lumenflow:upgrade --version 1.5.0
@@ -17,11 +20,14 @@
17
20
  * pnpm lumenflow:upgrade --latest --dry-run
18
21
  */
19
22
  import { execSync } from 'node:child_process';
20
- import { STDIO_MODES, EXIT_CODES, PKG_MANAGER, } from '@lumenflow/core/dist/wu-constants.js';
23
+ import { STDIO_MODES, EXIT_CODES, PKG_MANAGER, DEFAULTS, BRANCHES, } from '@lumenflow/core/dist/wu-constants.js';
24
+ import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
25
+ import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
21
26
  import { runCLI } from './cli-entry-point.js';
22
- import { validateWorktreeContext } from './deps-add.js';
23
27
  /** Log prefix for console output */
24
28
  const LOG_PREFIX = '[lumenflow:upgrade]';
29
+ /** Operation name for micro-worktree */
30
+ const OPERATION_NAME = 'lumenflow-upgrade';
25
31
  /**
26
32
  * All @lumenflow/* packages that should be upgraded together
27
33
  *
@@ -85,6 +91,98 @@ export function buildUpgradeCommands(args) {
85
91
  versionSpec,
86
92
  };
87
93
  }
94
+ /**
95
+ * WU-1127: Validate that the command is run from main checkout
96
+ *
97
+ * The micro-worktree pattern requires the command to be run from the main
98
+ * checkout (not inside a worktree). This is the inverse of the old behavior
99
+ * which required users to be IN a worktree.
100
+ *
101
+ * @returns Validation result with error and fix command if invalid
102
+ */
103
+ export async function validateMainCheckout() {
104
+ const cwd = process.cwd();
105
+ const worktreesDir = `/${DEFAULTS.WORKTREES_DIR}/`;
106
+ // Check if we're inside a worktree directory
107
+ if (cwd.includes(worktreesDir)) {
108
+ return {
109
+ valid: false,
110
+ error: `Cannot run lumenflow:upgrade from inside a worktree.\n\n` +
111
+ `This command must be run from main checkout because it uses\n` +
112
+ `micro-worktree isolation to atomically update package.json and lockfile.`,
113
+ fixCommand: `cd to main checkout and re-run:\n cd <main-checkout>\n pnpm lumenflow:upgrade --latest`,
114
+ };
115
+ }
116
+ // Check if we're on main branch
117
+ try {
118
+ const git = getGitForCwd();
119
+ const currentBranch = await git.raw(['rev-parse', '--abbrev-ref', 'HEAD']);
120
+ const branchName = currentBranch.trim();
121
+ if (branchName !== BRANCHES.MAIN) {
122
+ return {
123
+ valid: false,
124
+ error: `lumenflow:upgrade must be run from main checkout (on main branch).\n\n` +
125
+ `Current branch: ${branchName}\n` +
126
+ `Expected branch: main`,
127
+ fixCommand: `Switch to main branch:\n git checkout main\n pnpm lumenflow:upgrade --latest`,
128
+ };
129
+ }
130
+ }
131
+ catch (error) {
132
+ // If git fails, assume we're not in a valid git repo
133
+ return {
134
+ valid: false,
135
+ error: `Failed to detect git branch. Ensure you're in a git repository.`,
136
+ };
137
+ }
138
+ return { valid: true };
139
+ }
140
+ /**
141
+ * WU-1127: Execute the upgrade in a micro-worktree
142
+ *
143
+ * Uses the shared micro-worktree pattern (like wu:create, wu:edit) to:
144
+ * 1. Create a temporary worktree without switching main checkout
145
+ * 2. Run pnpm add in the temporary worktree
146
+ * 3. Commit the changes
147
+ * 4. FF-only merge to main
148
+ * 5. Push to origin
149
+ * 6. Cleanup
150
+ *
151
+ * @param args - Parsed upgrade arguments
152
+ * @returns Promise resolving when upgrade is complete
153
+ */
154
+ export async function executeUpgradeInMicroWorktree(args) {
155
+ const { addCommand, versionSpec } = buildUpgradeCommands(args);
156
+ // Generate unique ID for this upgrade operation using timestamp
157
+ const upgradeId = `upgrade-${Date.now()}`;
158
+ console.log(`${LOG_PREFIX} Using micro-worktree isolation (WU-1127)`);
159
+ console.log(`${LOG_PREFIX} Upgrading @lumenflow/* packages to ${versionSpec}`);
160
+ console.log(`${LOG_PREFIX} Packages: ${LUMENFLOW_PACKAGES.length} packages`);
161
+ await withMicroWorktree({
162
+ operation: OPERATION_NAME,
163
+ id: upgradeId,
164
+ logPrefix: LOG_PREFIX,
165
+ execute: async ({ worktreePath }) => {
166
+ console.log(`${LOG_PREFIX} Running: ${addCommand}`);
167
+ // Execute pnpm add in the micro-worktree
168
+ execSync(addCommand, {
169
+ stdio: STDIO_MODES.INHERIT,
170
+ cwd: worktreePath,
171
+ });
172
+ console.log(`${LOG_PREFIX} Package installation complete`);
173
+ // Return files to stage and commit message
174
+ return {
175
+ commitMessage: `chore: upgrade @lumenflow packages to ${versionSpec}`,
176
+ files: ['package.json', 'pnpm-lock.yaml'],
177
+ };
178
+ },
179
+ });
180
+ console.log(`\n${LOG_PREFIX} Upgrade complete!`);
181
+ console.log(`${LOG_PREFIX} Upgraded to ${versionSpec}`);
182
+ console.log(`\n${LOG_PREFIX} Next steps:`);
183
+ console.log(` 1. Run 'pnpm build' to rebuild with new versions`);
184
+ console.log(` 2. Run 'pnpm gates' to verify everything works`);
185
+ }
88
186
  /**
89
187
  * Print help message for lumenflow-upgrade
90
188
  */
@@ -94,7 +192,7 @@ function printHelp() {
94
192
  Usage: lumenflow-upgrade [options]
95
193
 
96
194
  Upgrade all @lumenflow/* packages to a specified version.
97
- Must be run from inside a worktree to enforce worktree discipline.
195
+ Uses micro-worktree isolation to atomically update packages from main checkout.
98
196
 
99
197
  Options:
100
198
  -v, --version <ver> Upgrade to specific version (e.g., 1.5.0)
@@ -110,13 +208,13 @@ Examples:
110
208
  lumenflow:upgrade --latest # Upgrade to latest
111
209
  lumenflow:upgrade --latest --dry-run # Preview upgrade commands
112
210
 
113
- Worktree Discipline:
114
- This command only works inside a worktree to prevent lockfile
115
- conflicts on main checkout. Claim a WU first:
211
+ Micro-Worktree Pattern (WU-1127):
212
+ This command uses micro-worktree isolation to atomically update
213
+ package.json and pnpm-lock.yaml without requiring you to claim a WU.
116
214
 
117
- pnpm wu:claim --id WU-XXXX --lane "Your Lane"
118
- cd worktrees/<lane>-wu-<id>/
119
- lumenflow:upgrade --latest
215
+ Run from your main checkout (NOT from inside a worktree):
216
+ cd /path/to/main
217
+ pnpm lumenflow:upgrade --latest
120
218
  `);
121
219
  }
122
220
  /**
@@ -135,40 +233,32 @@ async function main() {
135
233
  printHelp();
136
234
  process.exit(EXIT_CODES.ERROR);
137
235
  }
138
- // Validate worktree context (WU-1112 requirement: must run in worktree)
139
- const validation = validateWorktreeContext(process.cwd());
236
+ // WU-1127: Validate we're on main checkout (not in a worktree)
237
+ const validation = await validateMainCheckout();
140
238
  if (!validation.valid) {
141
239
  console.error(`${LOG_PREFIX} ${validation.error}`);
142
- console.error(`\nTo fix:\n${validation.fixCommand}`);
240
+ if (validation.fixCommand) {
241
+ console.error(`\nTo fix:\n${validation.fixCommand}`);
242
+ }
143
243
  process.exit(EXIT_CODES.ERROR);
144
244
  }
145
- // Build upgrade commands
245
+ // Build upgrade commands for dry-run display
146
246
  const { addCommand, versionSpec } = buildUpgradeCommands(args);
147
- console.log(`${LOG_PREFIX} Upgrading @lumenflow/* packages to ${versionSpec}`);
148
- console.log(`${LOG_PREFIX} Packages: ${LUMENFLOW_PACKAGES.length} packages`);
149
247
  if (args.dryRun) {
150
- console.log(`\n${LOG_PREFIX} DRY RUN - Commands that would be executed:`);
248
+ console.log(`${LOG_PREFIX} DRY RUN - Commands that would be executed:`);
151
249
  console.log(` ${addCommand}`);
250
+ console.log(`\n${LOG_PREFIX} Packages: ${LUMENFLOW_PACKAGES.length}`);
251
+ console.log(`${LOG_PREFIX} Version: ${versionSpec}`);
152
252
  console.log(`\n${LOG_PREFIX} No changes made.`);
153
253
  process.exit(EXIT_CODES.SUCCESS);
154
254
  }
155
- // Execute upgrade
156
- console.log(`${LOG_PREFIX} Running: ${addCommand}`);
255
+ // Execute upgrade using micro-worktree
157
256
  try {
158
- execSync(addCommand, {
159
- stdio: STDIO_MODES.INHERIT,
160
- cwd: process.cwd(),
161
- });
162
- console.log(`\n${LOG_PREFIX} ✅ Upgrade complete!`);
163
- console.log(`${LOG_PREFIX} Upgraded to ${versionSpec}`);
164
- console.log(`\n${LOG_PREFIX} Next steps:`);
165
- console.log(` 1. Run 'pnpm build' to rebuild with new versions`);
166
- console.log(` 2. Run 'pnpm gates' to verify everything works`);
167
- console.log(` 3. Commit the changes`);
257
+ await executeUpgradeInMicroWorktree(args);
168
258
  }
169
259
  catch (error) {
170
- console.error(`\n${LOG_PREFIX} Upgrade failed`);
171
- console.error(`${LOG_PREFIX} Check the error above and try again.`);
260
+ console.error(`\n${LOG_PREFIX} Upgrade failed`);
261
+ console.error(`${LOG_PREFIX} ${error instanceof Error ? error.message : String(error)}`);
172
262
  process.exit(EXIT_CODES.ERROR);
173
263
  }
174
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/cli",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "Command-line interface for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -132,11 +132,11 @@
132
132
  "pretty-ms": "^9.2.0",
133
133
  "simple-git": "^3.30.0",
134
134
  "yaml": "^2.8.2",
135
- "@lumenflow/core": "2.1.0",
136
- "@lumenflow/metrics": "2.1.0",
137
- "@lumenflow/memory": "2.1.0",
138
- "@lumenflow/initiatives": "2.1.0",
139
- "@lumenflow/agent": "2.1.0"
135
+ "@lumenflow/core": "2.1.2",
136
+ "@lumenflow/metrics": "2.1.2",
137
+ "@lumenflow/memory": "2.1.2",
138
+ "@lumenflow/initiatives": "2.1.2",
139
+ "@lumenflow/agent": "2.1.2"
140
140
  },
141
141
  "devDependencies": {
142
142
  "@vitest/coverage-v8": "^4.0.17",