@lumenflow/cli 2.3.1 → 2.3.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.
@@ -135,6 +135,67 @@ describe('release command integration', () => {
135
135
  expect(typeof module.updatePackageVersions).toBe('function');
136
136
  });
137
137
  });
138
+ /**
139
+ * WU-1296: Tests for release flow trunk protection compatibility
140
+ *
141
+ * Verifies:
142
+ * - RELEASE_WU_TOOL constant is exported for pre-push hook bypass
143
+ * - withReleaseEnv helper sets LUMENFLOW_WU_TOOL=release during execution
144
+ * - Environment is properly restored after execution (including on error)
145
+ */
146
+ describe('WU-1296: release flow trunk protection compatibility', () => {
147
+ it('should export RELEASE_WU_TOOL constant for pre-push hook bypass', async () => {
148
+ const { RELEASE_WU_TOOL } = await import('../release.js');
149
+ expect(RELEASE_WU_TOOL).toBe('release');
150
+ });
151
+ it('should export withReleaseEnv helper for setting LUMENFLOW_WU_TOOL', async () => {
152
+ const { withReleaseEnv } = await import('../release.js');
153
+ expect(typeof withReleaseEnv).toBe('function');
154
+ });
155
+ it('withReleaseEnv should set and restore LUMENFLOW_WU_TOOL', async () => {
156
+ const { withReleaseEnv } = await import('../release.js');
157
+ // Save original value
158
+ const originalValue = process.env.LUMENFLOW_WU_TOOL;
159
+ let capturedValue;
160
+ await withReleaseEnv(async () => {
161
+ capturedValue = process.env.LUMENFLOW_WU_TOOL;
162
+ });
163
+ expect(capturedValue).toBe('release');
164
+ expect(process.env.LUMENFLOW_WU_TOOL).toBe(originalValue);
165
+ });
166
+ it('withReleaseEnv should restore LUMENFLOW_WU_TOOL even on error', async () => {
167
+ const { withReleaseEnv } = await import('../release.js');
168
+ // Save original value
169
+ const originalValue = process.env.LUMENFLOW_WU_TOOL;
170
+ try {
171
+ await withReleaseEnv(async () => {
172
+ throw new Error('Test error');
173
+ });
174
+ }
175
+ catch {
176
+ // Expected to throw
177
+ }
178
+ expect(process.env.LUMENFLOW_WU_TOOL).toBe(originalValue);
179
+ });
180
+ it('withReleaseEnv should preserve existing LUMENFLOW_WU_TOOL value', async () => {
181
+ const { withReleaseEnv } = await import('../release.js');
182
+ // Set a specific value before running
183
+ const testValue = 'wu-done';
184
+ process.env.LUMENFLOW_WU_TOOL = testValue;
185
+ try {
186
+ let capturedValue;
187
+ await withReleaseEnv(async () => {
188
+ capturedValue = process.env.LUMENFLOW_WU_TOOL;
189
+ });
190
+ expect(capturedValue).toBe('release');
191
+ expect(process.env.LUMENFLOW_WU_TOOL).toBe(testValue);
192
+ }
193
+ finally {
194
+ // Cleanup
195
+ delete process.env.LUMENFLOW_WU_TOOL;
196
+ }
197
+ });
198
+ });
138
199
  /**
139
200
  * WU-1077: Tests for release script bug fixes
140
201
  *
package/dist/init.js CHANGED
@@ -1597,3 +1597,12 @@ export async function main() {
1597
1597
  console.log(' 3. Run: pnpm wu:create --id WU-0001 --lane <lane> --title "First WU"');
1598
1598
  /* eslint-enable no-console */
1599
1599
  }
1600
+ // WU-1297: Use import.meta.main instead of exporting main() without calling it
1601
+ // This ensures main() runs when the script is executed as a CLI entry point
1602
+ if (import.meta.main) {
1603
+ main().catch((err) => {
1604
+ // eslint-disable-next-line no-console -- CLI error output
1605
+ console.error('[lumenflow init] Error:', err instanceof Error ? err.message : String(err));
1606
+ process.exit(1);
1607
+ });
1608
+ }
package/dist/release.js CHANGED
@@ -60,6 +60,42 @@ const CHANGESET_DIR = '.changeset';
60
60
  const LUMENFLOW_FORCE_ENV = 'LUMENFLOW_FORCE';
61
61
  /** Environment variable to provide reason for force bypass */
62
62
  const LUMENFLOW_FORCE_REASON_ENV = 'LUMENFLOW_FORCE_REASON';
63
+ /**
64
+ * Environment variable for WU tool identification (WU-1296)
65
+ * Pre-push hook checks this to allow approved tool operations
66
+ */
67
+ const LUMENFLOW_WU_TOOL_ENV = 'LUMENFLOW_WU_TOOL';
68
+ /**
69
+ * Release tool identifier for pre-push hook bypass (WU-1296)
70
+ * Added to ALLOWED_WU_TOOLS in pre-push.mjs
71
+ */
72
+ export const RELEASE_WU_TOOL = 'release';
73
+ /**
74
+ * Execute a function with LUMENFLOW_WU_TOOL set to 'release' (WU-1296)
75
+ *
76
+ * This allows the release command to push to main via micro-worktree
77
+ * without requiring LUMENFLOW_FORCE bypass. The pre-push hook checks
78
+ * LUMENFLOW_WU_TOOL and allows approved tools like 'release'.
79
+ *
80
+ * @param fn - Async function to execute with release env set
81
+ * @returns Result of the function
82
+ */
83
+ export async function withReleaseEnv(fn) {
84
+ const originalValue = process.env[LUMENFLOW_WU_TOOL_ENV];
85
+ try {
86
+ process.env[LUMENFLOW_WU_TOOL_ENV] = RELEASE_WU_TOOL;
87
+ return await fn();
88
+ }
89
+ finally {
90
+ // Restore original value (or delete if it wasn't set)
91
+ if (originalValue === undefined) {
92
+ delete process.env[LUMENFLOW_WU_TOOL_ENV];
93
+ }
94
+ else {
95
+ process.env[LUMENFLOW_WU_TOOL_ENV] = originalValue;
96
+ }
97
+ }
98
+ }
63
99
  /**
64
100
  * Validate that a string is a valid semver version
65
101
  *
@@ -327,35 +363,39 @@ async function main() {
327
363
  }
328
364
  else {
329
365
  console.log(`${LOG_PREFIX} Bumping versions using micro-worktree isolation...`);
330
- await withMicroWorktree({
331
- operation: OPERATION_NAME,
332
- id: `v${version}`,
333
- logPrefix: LOG_PREFIX,
334
- execute: async ({ worktreePath }) => {
335
- // Check and exit changeset pre mode if active
336
- if (isInChangesetPreMode(worktreePath)) {
337
- console.log(`${LOG_PREFIX} Detected changeset pre-release mode, exiting...`);
338
- exitChangesetPreMode(worktreePath);
339
- console.log(`${LOG_PREFIX} ✅ Exited changeset pre mode`);
340
- }
341
- // Find package paths within the worktree
342
- const worktreePackagePaths = findPackageJsonPaths(worktreePath);
343
- // Update versions
344
- console.log(`${LOG_PREFIX} Updating ${worktreePackagePaths.length} package versions...`);
345
- await updatePackageVersions(worktreePackagePaths, version);
346
- // Get relative paths for commit
347
- const relativePaths = worktreePackagePaths.map((p) => getRelativePath(p, worktreePath));
348
- // If we exited pre mode, include the deleted pre.json in files to commit
349
- // (the deletion will be staged automatically by git add -A behavior)
350
- const changesetPrePath = join(CHANGESET_DIR, CHANGESET_PRE_JSON);
351
- const filesToCommit = [...relativePaths];
352
- // Note: Deletion of pre.json is handled by git detecting the missing file
353
- console.log(`${LOG_PREFIX} Versions updated to ${version}`);
354
- return {
355
- commitMessage: buildCommitMessage(version),
356
- files: filesToCommit,
357
- };
358
- },
366
+ // WU-1296: Use withReleaseEnv to set LUMENFLOW_WU_TOOL=release
367
+ // This allows the micro-worktree push to main without LUMENFLOW_FORCE
368
+ await withReleaseEnv(async () => {
369
+ await withMicroWorktree({
370
+ operation: OPERATION_NAME,
371
+ id: `v${version}`,
372
+ logPrefix: LOG_PREFIX,
373
+ execute: async ({ worktreePath }) => {
374
+ // Check and exit changeset pre mode if active
375
+ if (isInChangesetPreMode(worktreePath)) {
376
+ console.log(`${LOG_PREFIX} Detected changeset pre-release mode, exiting...`);
377
+ exitChangesetPreMode(worktreePath);
378
+ console.log(`${LOG_PREFIX} Exited changeset pre mode`);
379
+ }
380
+ // Find package paths within the worktree
381
+ const worktreePackagePaths = findPackageJsonPaths(worktreePath);
382
+ // Update versions
383
+ console.log(`${LOG_PREFIX} Updating ${worktreePackagePaths.length} package versions...`);
384
+ await updatePackageVersions(worktreePackagePaths, version);
385
+ // Get relative paths for commit
386
+ const relativePaths = worktreePackagePaths.map((p) => getRelativePath(p, worktreePath));
387
+ // If we exited pre mode, include the deleted pre.json in files to commit
388
+ // (the deletion will be staged automatically by git add -A behavior)
389
+ const changesetPrePath = join(CHANGESET_DIR, CHANGESET_PRE_JSON);
390
+ const filesToCommit = [...relativePaths];
391
+ // Note: Deletion of pre.json is handled by git detecting the missing file
392
+ console.log(`${LOG_PREFIX} ✅ Versions updated to ${version}`);
393
+ return {
394
+ commitMessage: buildCommitMessage(version),
395
+ files: filesToCommit,
396
+ };
397
+ },
398
+ });
359
399
  });
360
400
  console.log(`${LOG_PREFIX} ✅ Version bump committed and pushed`);
361
401
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/cli",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "Command-line interface for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -142,11 +142,11 @@
142
142
  "pretty-ms": "^9.2.0",
143
143
  "simple-git": "^3.30.0",
144
144
  "yaml": "^2.8.2",
145
- "@lumenflow/core": "2.3.1",
146
- "@lumenflow/metrics": "2.3.1",
147
- "@lumenflow/memory": "2.3.1",
148
- "@lumenflow/agent": "2.3.1",
149
- "@lumenflow/initiatives": "2.3.1"
145
+ "@lumenflow/core": "2.3.2",
146
+ "@lumenflow/memory": "2.3.2",
147
+ "@lumenflow/initiatives": "2.3.2",
148
+ "@lumenflow/agent": "2.3.2",
149
+ "@lumenflow/metrics": "2.3.2"
150
150
  },
151
151
  "devDependencies": {
152
152
  "@vitest/coverage-v8": "^4.0.17",