@lumenflow/cli 1.3.4 → 1.3.6

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 (122) hide show
  1. package/dist/__tests__/release.test.js +283 -0
  2. package/dist/agent-issues-query.js +3 -2
  3. package/dist/agent-log-issue.js +2 -2
  4. package/dist/init.js +1 -1
  5. package/dist/mem-checkpoint.js +3 -7
  6. package/dist/mem-cleanup.js +2 -6
  7. package/dist/mem-create.js +3 -7
  8. package/dist/mem-inbox.js +3 -7
  9. package/dist/mem-init.js +5 -9
  10. package/dist/mem-ready.js +3 -7
  11. package/dist/mem-signal.js +3 -7
  12. package/dist/mem-start.js +3 -7
  13. package/dist/mem-summarize.js +2 -6
  14. package/dist/mem-triage.js +2 -6
  15. package/dist/metrics-snapshot.js +2 -2
  16. package/dist/orchestrate-init-status.js +3 -4
  17. package/dist/orchestrate-monitor.js +4 -5
  18. package/dist/release.js +416 -0
  19. package/dist/spawn-list.js +3 -3
  20. package/dist/wu-block.js +2 -2
  21. package/dist/wu-claim.js +9 -4
  22. package/dist/wu-create.js +1 -1
  23. package/dist/wu-done.js +17 -17
  24. package/dist/wu-release.js +142 -0
  25. package/dist/wu-repair.js +3 -3
  26. package/dist/wu-spawn.js +6 -6
  27. package/dist/wu-unblock.js +2 -2
  28. package/package.json +9 -7
  29. package/dist/gates.d.ts +0 -41
  30. package/dist/gates.d.ts.map +0 -1
  31. package/dist/gates.js.map +0 -1
  32. package/dist/initiative-add-wu.d.ts +0 -22
  33. package/dist/initiative-add-wu.d.ts.map +0 -1
  34. package/dist/initiative-add-wu.js.map +0 -1
  35. package/dist/initiative-create.d.ts +0 -28
  36. package/dist/initiative-create.d.ts.map +0 -1
  37. package/dist/initiative-create.js.map +0 -1
  38. package/dist/initiative-edit.d.ts +0 -34
  39. package/dist/initiative-edit.d.ts.map +0 -1
  40. package/dist/initiative-edit.js.map +0 -1
  41. package/dist/initiative-list.d.ts +0 -12
  42. package/dist/initiative-list.d.ts.map +0 -1
  43. package/dist/initiative-list.js.map +0 -1
  44. package/dist/initiative-status.d.ts +0 -11
  45. package/dist/initiative-status.d.ts.map +0 -1
  46. package/dist/initiative-status.js.map +0 -1
  47. package/dist/mem-checkpoint.d.ts +0 -16
  48. package/dist/mem-checkpoint.d.ts.map +0 -1
  49. package/dist/mem-checkpoint.js.map +0 -1
  50. package/dist/mem-cleanup.d.ts +0 -29
  51. package/dist/mem-cleanup.d.ts.map +0 -1
  52. package/dist/mem-cleanup.js.map +0 -1
  53. package/dist/mem-create.d.ts +0 -17
  54. package/dist/mem-create.d.ts.map +0 -1
  55. package/dist/mem-create.js.map +0 -1
  56. package/dist/mem-inbox.d.ts +0 -35
  57. package/dist/mem-inbox.d.ts.map +0 -1
  58. package/dist/mem-inbox.js.map +0 -1
  59. package/dist/mem-init.d.ts +0 -15
  60. package/dist/mem-init.d.ts.map +0 -1
  61. package/dist/mem-init.js.map +0 -1
  62. package/dist/mem-ready.d.ts +0 -16
  63. package/dist/mem-ready.d.ts.map +0 -1
  64. package/dist/mem-ready.js.map +0 -1
  65. package/dist/mem-signal.d.ts +0 -16
  66. package/dist/mem-signal.d.ts.map +0 -1
  67. package/dist/mem-signal.js.map +0 -1
  68. package/dist/mem-start.d.ts +0 -16
  69. package/dist/mem-start.d.ts.map +0 -1
  70. package/dist/mem-start.js.map +0 -1
  71. package/dist/mem-summarize.d.ts +0 -22
  72. package/dist/mem-summarize.d.ts.map +0 -1
  73. package/dist/mem-summarize.js.map +0 -1
  74. package/dist/mem-triage.d.ts +0 -22
  75. package/dist/mem-triage.d.ts.map +0 -1
  76. package/dist/mem-triage.js.map +0 -1
  77. package/dist/spawn-list.d.ts +0 -16
  78. package/dist/spawn-list.d.ts.map +0 -1
  79. package/dist/spawn-list.js.map +0 -1
  80. package/dist/wu-block.d.ts +0 -16
  81. package/dist/wu-block.d.ts.map +0 -1
  82. package/dist/wu-block.js.map +0 -1
  83. package/dist/wu-claim.d.ts +0 -32
  84. package/dist/wu-claim.d.ts.map +0 -1
  85. package/dist/wu-claim.js.map +0 -1
  86. package/dist/wu-cleanup.d.ts +0 -17
  87. package/dist/wu-cleanup.d.ts.map +0 -1
  88. package/dist/wu-cleanup.js.map +0 -1
  89. package/dist/wu-create.d.ts +0 -38
  90. package/dist/wu-create.d.ts.map +0 -1
  91. package/dist/wu-create.js.map +0 -1
  92. package/dist/wu-deps.d.ts +0 -13
  93. package/dist/wu-deps.d.ts.map +0 -1
  94. package/dist/wu-deps.js.map +0 -1
  95. package/dist/wu-done.d.ts +0 -153
  96. package/dist/wu-done.d.ts.map +0 -1
  97. package/dist/wu-done.js.map +0 -1
  98. package/dist/wu-edit.d.ts +0 -29
  99. package/dist/wu-edit.d.ts.map +0 -1
  100. package/dist/wu-edit.js.map +0 -1
  101. package/dist/wu-infer-lane.d.ts +0 -17
  102. package/dist/wu-infer-lane.d.ts.map +0 -1
  103. package/dist/wu-infer-lane.js.map +0 -1
  104. package/dist/wu-preflight.d.ts +0 -47
  105. package/dist/wu-preflight.d.ts.map +0 -1
  106. package/dist/wu-preflight.js.map +0 -1
  107. package/dist/wu-prune.d.ts +0 -16
  108. package/dist/wu-prune.d.ts.map +0 -1
  109. package/dist/wu-prune.js.map +0 -1
  110. package/dist/wu-repair.d.ts +0 -60
  111. package/dist/wu-repair.d.ts.map +0 -1
  112. package/dist/wu-repair.js.map +0 -1
  113. package/dist/wu-spawn-completion.d.ts +0 -10
  114. package/dist/wu-spawn.d.ts +0 -168
  115. package/dist/wu-spawn.d.ts.map +0 -1
  116. package/dist/wu-spawn.js.map +0 -1
  117. package/dist/wu-unblock.d.ts +0 -16
  118. package/dist/wu-unblock.d.ts.map +0 -1
  119. package/dist/wu-unblock.js.map +0 -1
  120. package/dist/wu-validate.d.ts +0 -16
  121. package/dist/wu-validate.d.ts.map +0 -1
  122. package/dist/wu-validate.js.map +0 -1
@@ -23,7 +23,7 @@ import fs from 'node:fs/promises';
23
23
  import path from 'node:path';
24
24
  import { summarizeWu } from '@lumenflow/memory/dist/mem-summarize-core.js';
25
25
  import { createWUParser } from '@lumenflow/core/dist/arg-parser.js';
26
- import { EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
26
+ import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/dist/wu-constants.js';
27
27
  /**
28
28
  * Log prefix for mem:summarize output
29
29
  */
@@ -32,10 +32,6 @@ const LOG_PREFIX = '[mem:summarize]';
32
32
  * Tool name for audit logging
33
33
  */
34
34
  const TOOL_NAME = 'mem:summarize';
35
- /**
36
- * Audit log file path
37
- */
38
- const AUDIT_LOG_PATH = '.beacon/telemetry/tools.ndjson';
39
35
  /**
40
36
  * CLI argument options specific to mem:summarize
41
37
  */
@@ -74,7 +70,7 @@ const CLI_OPTIONS = {
74
70
  */
75
71
  async function writeAuditLog(baseDir, entry) {
76
72
  try {
77
- const logPath = path.join(baseDir, AUDIT_LOG_PATH);
73
+ const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
78
74
  const logDir = path.dirname(logPath);
79
75
  // eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
80
76
  await fs.mkdir(logDir, { recursive: true });
@@ -23,7 +23,7 @@ import fs from 'node:fs/promises';
23
23
  import path from 'node:path';
24
24
  import { listOpenDiscoveries, promoteDiscovery, archiveDiscovery, } from '@lumenflow/memory/dist/mem-triage-core.js';
25
25
  import { createWUParser } from '@lumenflow/core/dist/arg-parser.js';
26
- import { EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
26
+ import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/dist/wu-constants.js';
27
27
  /**
28
28
  * Log prefix for mem:triage output
29
29
  */
@@ -32,10 +32,6 @@ const LOG_PREFIX = '[mem:triage]';
32
32
  * Tool name for audit logging
33
33
  */
34
34
  const TOOL_NAME = 'mem:triage';
35
- /**
36
- * Audit log file path
37
- */
38
- const AUDIT_LOG_PATH = '.beacon/telemetry/tools.ndjson';
39
35
  /**
40
36
  * CLI argument options specific to mem:triage
41
37
  */
@@ -109,7 +105,7 @@ const CLI_OPTIONS = {
109
105
  */
110
106
  async function writeAuditLog(baseDir, entry) {
111
107
  try {
112
- const logPath = path.join(baseDir, AUDIT_LOG_PATH);
108
+ const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
113
109
  const logDir = path.dirname(logPath);
114
110
  await fs.mkdir(logDir, { recursive: true });
115
111
  const line = `${JSON.stringify(entry)}\n`;
@@ -27,11 +27,11 @@ import { die } from '@lumenflow/core/dist/error-handler.js';
27
27
  /** Log prefix for console output */
28
28
  const LOG_PREFIX = '[metrics:snapshot]';
29
29
  /** Default snapshot output path */
30
- const DEFAULT_OUTPUT = '.beacon/snapshots/metrics-latest.json';
30
+ const DEFAULT_OUTPUT = '.lumenflow/snapshots/metrics-latest.json';
31
31
  /** WU directory relative to repo root */
32
32
  const WU_DIR = 'docs/04-operations/tasks/wu';
33
33
  /** Skip-gates audit file path */
34
- const SKIP_GATES_PATH = '.beacon/skip-gates-audit.ndjson';
34
+ const SKIP_GATES_PATH = '.lumenflow/skip-gates-audit.ndjson';
35
35
  /** Snapshot type options */
36
36
  const SNAPSHOT_TYPES = ['all', 'dora', 'lanes', 'flow'];
37
37
  /**
@@ -11,16 +11,15 @@
11
11
  import { Command } from 'commander';
12
12
  import { existsSync, readdirSync } from 'node:fs';
13
13
  import { loadInitiativeWUs, calculateProgress, formatProgress } from '@lumenflow/initiatives';
14
- import { EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
14
+ import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/dist/wu-constants.js';
15
15
  import chalk from 'chalk';
16
16
  const LOG_PREFIX = '[orchestrate:init-status]';
17
- const STAMPS_DIR = '.beacon/stamps';
18
17
  function getCompletedWUs(wuIds) {
19
18
  const completed = new Set();
20
- if (!existsSync(STAMPS_DIR)) {
19
+ if (!existsSync(LUMENFLOW_PATHS.STAMPS_DIR)) {
21
20
  return completed;
22
21
  }
23
- const files = readdirSync(STAMPS_DIR);
22
+ const files = readdirSync(LUMENFLOW_PATHS.STAMPS_DIR);
24
23
  for (const wuId of wuIds) {
25
24
  if (files.includes(`${wuId}.done`)) {
26
25
  completed.add(wuId);
@@ -11,11 +11,10 @@
11
11
  import { Command } from 'commander';
12
12
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
13
13
  import { join } from 'node:path';
14
- import { EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
14
+ import { EXIT_CODES, LUMENFLOW_PATHS } from '@lumenflow/core/dist/wu-constants.js';
15
15
  import chalk from 'chalk';
16
16
  import ms from 'ms';
17
17
  const LOG_PREFIX = '[orchestrate:monitor]';
18
- const MEMORY_DIR = '.beacon/memory';
19
18
  function parseTimeString(timeStr) {
20
19
  const msValue = ms(timeStr);
21
20
  if (typeof msValue === 'number') {
@@ -29,12 +28,12 @@ function parseTimeString(timeStr) {
29
28
  }
30
29
  function loadRecentSignals(since) {
31
30
  const signals = [];
32
- if (!existsSync(MEMORY_DIR)) {
31
+ if (!existsSync(LUMENFLOW_PATHS.MEMORY_DIR)) {
33
32
  return signals;
34
33
  }
35
- const files = readdirSync(MEMORY_DIR).filter((f) => f.endsWith('.ndjson'));
34
+ const files = readdirSync(LUMENFLOW_PATHS.MEMORY_DIR).filter((f) => f.endsWith('.ndjson'));
36
35
  for (const file of files) {
37
- const filePath = join(MEMORY_DIR, file);
36
+ const filePath = join(LUMENFLOW_PATHS.MEMORY_DIR, file);
38
37
  const content = readFileSync(filePath, 'utf-8');
39
38
  const lines = content.trim().split('\n').filter(Boolean);
40
39
  for (const line of lines) {
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Release Command
4
+ *
5
+ * Orchestrates npm release for all @lumenflow/* packages using micro-worktree isolation.
6
+ *
7
+ * Features:
8
+ * - Validates semver version format
9
+ * - Bumps all @lumenflow/* package versions atomically
10
+ * - Uses micro-worktree isolation for version commit (no main branch pollution)
11
+ * - Builds all packages via turbo
12
+ * - Publishes to npm with proper auth (requires NPM_TOKEN)
13
+ * - Creates git tag vX.Y.Z
14
+ *
15
+ * Usage:
16
+ * pnpm release --version 1.3.0
17
+ * pnpm release --version 1.3.0 --dry-run # Preview without making changes
18
+ * pnpm release --version 1.3.0 --skip-publish # Bump and tag only (no npm publish)
19
+ *
20
+ * WU-1074: Add release command for npm publishing
21
+ */
22
+ import { Command } from 'commander';
23
+ import { existsSync, readFileSync, readdirSync, statSync, unlinkSync, } from 'node:fs';
24
+ import { readFile, writeFile } from 'node:fs/promises';
25
+ import { join } from 'node:path';
26
+ import { homedir } from 'node:os';
27
+ import { execSync } from 'node:child_process';
28
+ import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
29
+ import { die } from '@lumenflow/core/dist/error-handler.js';
30
+ import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
31
+ import { ensureOnMain } from '@lumenflow/core/dist/wu-helpers.js';
32
+ import { REMOTES, FILE_SYSTEM, PKG_MANAGER, } from '@lumenflow/core/dist/wu-constants.js';
33
+ import { runCLI } from './cli-entry-point.js';
34
+ /** Log prefix for console output */
35
+ const LOG_PREFIX = '[release]';
36
+ /** Micro-worktree operation name */
37
+ const OPERATION_NAME = 'release';
38
+ /** Directory containing @lumenflow packages */
39
+ const LUMENFLOW_PACKAGES_DIR = 'packages/@lumenflow';
40
+ /** Semver regex pattern (strict) */
41
+ const SEMVER_REGEX = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
42
+ /** JSON indent size for package.json files */
43
+ const JSON_INDENT = 2;
44
+ /** Default npm registry */
45
+ const NPM_REGISTRY = 'https://registry.npmjs.org';
46
+ /** Environment variable for npm authentication token */
47
+ const NPM_TOKEN_ENV = 'NPM_TOKEN';
48
+ /** Environment variable for alternative npm auth */
49
+ const NODE_AUTH_TOKEN_ENV = 'NODE_AUTH_TOKEN';
50
+ /** Pattern to detect npm auth token in .npmrc files */
51
+ const NPMRC_AUTH_TOKEN_PATTERN = /_authToken=/;
52
+ /** Changeset pre.json filename */
53
+ const CHANGESET_PRE_JSON = 'pre.json';
54
+ /** Changeset directory name */
55
+ const CHANGESET_DIR = '.changeset';
56
+ /** Environment variable to force bypass hooks */
57
+ const LUMENFLOW_FORCE_ENV = 'LUMENFLOW_FORCE';
58
+ /** Environment variable to provide reason for force bypass */
59
+ const LUMENFLOW_FORCE_REASON_ENV = 'LUMENFLOW_FORCE_REASON';
60
+ /**
61
+ * Validate that a string is a valid semver version
62
+ *
63
+ * @param version - Version string to validate
64
+ * @returns true if valid semver, false otherwise
65
+ */
66
+ export function validateSemver(version) {
67
+ if (!version || typeof version !== 'string') {
68
+ return false;
69
+ }
70
+ return SEMVER_REGEX.test(version);
71
+ }
72
+ /**
73
+ * Find all public @lumenflow/* package.json paths
74
+ *
75
+ * @param baseDir - Base directory to search from (defaults to cwd)
76
+ * @returns Array of absolute paths to package.json files
77
+ */
78
+ export function findPackageJsonPaths(baseDir = process.cwd()) {
79
+ const packagesDir = join(baseDir, LUMENFLOW_PACKAGES_DIR);
80
+ const paths = [];
81
+ if (!existsSync(packagesDir)) {
82
+ return paths;
83
+ }
84
+ const entries = readdirSync(packagesDir);
85
+ for (const entry of entries) {
86
+ const entryPath = join(packagesDir, entry);
87
+ const packageJsonPath = join(entryPath, 'package.json');
88
+ if (statSync(entryPath).isDirectory() && existsSync(packageJsonPath)) {
89
+ // Read package.json to check if it's private
90
+ const content = JSON.parse(readFileSync(packageJsonPath, { encoding: FILE_SYSTEM.UTF8 }));
91
+ // Only include public packages (not marked private)
92
+ if (!content.private) {
93
+ paths.push(packageJsonPath);
94
+ }
95
+ }
96
+ }
97
+ return paths;
98
+ }
99
+ /**
100
+ * Update version in specified package.json files
101
+ *
102
+ * @param paths - Array of package.json paths
103
+ * @param version - New version string
104
+ */
105
+ export async function updatePackageVersions(paths, version) {
106
+ for (const packagePath of paths) {
107
+ const content = await readFile(packagePath, {
108
+ encoding: FILE_SYSTEM.ENCODING,
109
+ });
110
+ const pkg = JSON.parse(content);
111
+ // Update version
112
+ pkg.version = version;
113
+ // Write back with preserved formatting (2-space indent)
114
+ const updated = JSON.stringify(pkg, null, JSON_INDENT) + '\n';
115
+ await writeFile(packagePath, updated, { encoding: FILE_SYSTEM.ENCODING });
116
+ }
117
+ }
118
+ /**
119
+ * Build commit message for version bump
120
+ *
121
+ * @param version - New version string
122
+ * @returns Commit message
123
+ */
124
+ export function buildCommitMessage(version) {
125
+ return `chore: bump all packages to v${version}`;
126
+ }
127
+ /**
128
+ * Build git tag name from version
129
+ *
130
+ * @param version - Version string
131
+ * @returns Git tag name with 'v' prefix
132
+ */
133
+ export function buildTagName(version) {
134
+ return `v${version}`;
135
+ }
136
+ /**
137
+ * Get relative path from worktree root
138
+ *
139
+ * @param absolutePath - Absolute file path
140
+ * @param worktreePath - Worktree root path
141
+ * @returns Relative path
142
+ */
143
+ function getRelativePath(absolutePath, worktreePath) {
144
+ return absolutePath.replace(worktreePath + '/', '');
145
+ }
146
+ /**
147
+ * Execute a shell command and handle errors
148
+ *
149
+ * @param cmd - Command to execute
150
+ * @param options - Options for execution
151
+ */
152
+ function runCommand(cmd, options = {}) {
153
+ const { cwd = process.cwd(), dryRun = false, label } = options;
154
+ const prefix = label ? `[${label}] ` : '';
155
+ if (dryRun) {
156
+ console.log(`${LOG_PREFIX} ${prefix}Would run: ${cmd}`);
157
+ return;
158
+ }
159
+ console.log(`${LOG_PREFIX} ${prefix}Running: ${cmd}`);
160
+ try {
161
+ execSync(cmd, {
162
+ cwd,
163
+ stdio: 'inherit',
164
+ encoding: FILE_SYSTEM.ENCODING,
165
+ });
166
+ }
167
+ catch (error) {
168
+ throw new Error(`Command failed: ${cmd}`);
169
+ }
170
+ }
171
+ /**
172
+ * Check if npm authentication is available
173
+ *
174
+ * Checks for auth in this order:
175
+ * 1. NPM_TOKEN environment variable
176
+ * 2. NODE_AUTH_TOKEN environment variable
177
+ * 3. Auth token in specified .npmrc file (or ~/.npmrc by default)
178
+ *
179
+ * @param npmrcPath - Optional path to .npmrc file (defaults to ~/.npmrc)
180
+ * @returns true if any auth method is found
181
+ */
182
+ export function hasNpmAuth(npmrcPath) {
183
+ // Check environment variables first
184
+ if (process.env[NPM_TOKEN_ENV] || process.env[NODE_AUTH_TOKEN_ENV]) {
185
+ return true;
186
+ }
187
+ // Check .npmrc file
188
+ const npmrcFile = npmrcPath ?? join(homedir(), '.npmrc');
189
+ if (existsSync(npmrcFile)) {
190
+ try {
191
+ const content = readFileSync(npmrcFile, { encoding: FILE_SYSTEM.UTF8 });
192
+ // Look for authToken lines (e.g., //registry.npmjs.org/:_authToken=...)
193
+ return NPMRC_AUTH_TOKEN_PATTERN.test(content);
194
+ }
195
+ catch {
196
+ // If we can't read the file, assume no auth
197
+ return false;
198
+ }
199
+ }
200
+ return false;
201
+ }
202
+ /**
203
+ * Check if the project is in changeset pre-release mode
204
+ *
205
+ * Changeset pre mode is indicated by the presence of .changeset/pre.json
206
+ *
207
+ * @param baseDir - Base directory to check (defaults to cwd)
208
+ * @returns true if in pre-release mode
209
+ */
210
+ export function isInChangesetPreMode(baseDir = process.cwd()) {
211
+ const preJsonPath = join(baseDir, CHANGESET_DIR, CHANGESET_PRE_JSON);
212
+ return existsSync(preJsonPath);
213
+ }
214
+ /**
215
+ * Exit changeset pre-release mode by removing .changeset/pre.json
216
+ *
217
+ * This is safe to call even if not in pre mode (no-op if file doesn't exist)
218
+ *
219
+ * @param baseDir - Base directory to operate in (defaults to cwd)
220
+ */
221
+ export function exitChangesetPreMode(baseDir = process.cwd()) {
222
+ const preJsonPath = join(baseDir, CHANGESET_DIR, CHANGESET_PRE_JSON);
223
+ if (existsSync(preJsonPath)) {
224
+ unlinkSync(preJsonPath);
225
+ }
226
+ }
227
+ /**
228
+ * Push a git tag to origin, bypassing pre-push hooks via LUMENFLOW_FORCE
229
+ *
230
+ * This is necessary because the release script runs in a micro-worktree context
231
+ * and pre-push hooks may block tag pushes. The force is logged and requires
232
+ * a reason for audit purposes.
233
+ *
234
+ * @param git - SimpleGit instance
235
+ * @param tagName - Name of the tag to push
236
+ * @param reason - Reason for bypassing hooks (for audit log)
237
+ */
238
+ export async function pushTagWithForce(git, tagName, reason = 'release: tag push from micro-worktree') {
239
+ // Set environment variables to bypass hooks
240
+ const originalForce = process.env[LUMENFLOW_FORCE_ENV];
241
+ const originalReason = process.env[LUMENFLOW_FORCE_REASON_ENV];
242
+ try {
243
+ process.env[LUMENFLOW_FORCE_ENV] = '1';
244
+ process.env[LUMENFLOW_FORCE_REASON_ENV] = reason;
245
+ await git.push(REMOTES.ORIGIN, tagName);
246
+ }
247
+ finally {
248
+ // Restore original environment
249
+ if (originalForce === undefined) {
250
+ delete process.env[LUMENFLOW_FORCE_ENV];
251
+ }
252
+ else {
253
+ process.env[LUMENFLOW_FORCE_ENV] = originalForce;
254
+ }
255
+ if (originalReason === undefined) {
256
+ delete process.env[LUMENFLOW_FORCE_REASON_ENV];
257
+ }
258
+ else {
259
+ process.env[LUMENFLOW_FORCE_REASON_ENV] = originalReason;
260
+ }
261
+ }
262
+ }
263
+ /**
264
+ * Main release function
265
+ */
266
+ async function main() {
267
+ const program = new Command()
268
+ .name('release')
269
+ .description('Release @lumenflow/* packages to npm with version bump, tag, and publish')
270
+ .requiredOption('-v, --version <version>', 'Semver version to release (e.g., 1.3.0)')
271
+ .option('--dry-run', 'Preview changes without making them', false)
272
+ .option('--skip-publish', 'Skip npm publish (only bump and tag)', false)
273
+ .option('--skip-build', 'Skip build step (use existing dist)', false)
274
+ .helpOption('-h, --help', 'Display help for command');
275
+ program.parse();
276
+ const opts = program.opts();
277
+ const { version, dryRun, skipPublish, skipBuild } = opts;
278
+ console.log(`${LOG_PREFIX} Starting release process for v${version}`);
279
+ if (dryRun) {
280
+ console.log(`${LOG_PREFIX} DRY RUN MODE - no changes will be made`);
281
+ }
282
+ // Validate version format
283
+ if (!validateSemver(version)) {
284
+ die(`Invalid semver version: ${version}\n\n` +
285
+ `Expected format: MAJOR.MINOR.PATCH (e.g., 1.3.0)\n` +
286
+ `Optional pre-release suffix: 1.3.0-alpha, 1.3.0-beta.1`);
287
+ }
288
+ // Ensure we're on main branch
289
+ const git = getGitForCwd();
290
+ await ensureOnMain(git);
291
+ // Check for uncommitted changes
292
+ const isClean = await git.isClean();
293
+ if (!isClean) {
294
+ die(`Working directory has uncommitted changes.\n\n` +
295
+ `Commit or stash changes before releasing:\n` +
296
+ ` git status\n` +
297
+ ` git stash # or git commit`);
298
+ }
299
+ // Find all @lumenflow/* packages to update
300
+ const packagePaths = findPackageJsonPaths();
301
+ if (packagePaths.length === 0) {
302
+ die(`No @lumenflow/* packages found in ${LUMENFLOW_PACKAGES_DIR}`);
303
+ }
304
+ console.log(`${LOG_PREFIX} Found ${packagePaths.length} packages to update:`);
305
+ for (const p of packagePaths) {
306
+ console.log(` - ${p.replace(process.cwd() + '/', '')}`);
307
+ }
308
+ // Check npm authentication for publish
309
+ if (!skipPublish && !dryRun && !hasNpmAuth()) {
310
+ die(`npm authentication not found.\n\n` +
311
+ `Set one of these environment variables:\n` +
312
+ ` export NPM_TOKEN=<your-npm-token>\n` +
313
+ ` export NODE_AUTH_TOKEN=<your-npm-token>\n\n` +
314
+ `Get a token at: https://www.npmjs.com/settings/tokens\n` +
315
+ `Or use --skip-publish to only bump versions and create tag.`);
316
+ }
317
+ // Execute version bump in micro-worktree
318
+ if (dryRun) {
319
+ console.log(`${LOG_PREFIX} Would bump versions to ${version} using micro-worktree isolation`);
320
+ console.log(`${LOG_PREFIX} Would commit: ${buildCommitMessage(version)}`);
321
+ }
322
+ else {
323
+ console.log(`${LOG_PREFIX} Bumping versions using micro-worktree isolation...`);
324
+ await withMicroWorktree({
325
+ operation: OPERATION_NAME,
326
+ id: `v${version}`,
327
+ logPrefix: LOG_PREFIX,
328
+ execute: async ({ worktreePath }) => {
329
+ // Check and exit changeset pre mode if active
330
+ if (isInChangesetPreMode(worktreePath)) {
331
+ console.log(`${LOG_PREFIX} Detected changeset pre-release mode, exiting...`);
332
+ exitChangesetPreMode(worktreePath);
333
+ console.log(`${LOG_PREFIX} ✅ Exited changeset pre mode`);
334
+ }
335
+ // Find package paths within the worktree
336
+ const worktreePackagePaths = findPackageJsonPaths(worktreePath);
337
+ // Update versions
338
+ console.log(`${LOG_PREFIX} Updating ${worktreePackagePaths.length} package versions...`);
339
+ await updatePackageVersions(worktreePackagePaths, version);
340
+ // Get relative paths for commit
341
+ const relativePaths = worktreePackagePaths.map((p) => getRelativePath(p, worktreePath));
342
+ // If we exited pre mode, include the deleted pre.json in files to commit
343
+ // (the deletion will be staged automatically by git add -A behavior)
344
+ const changesetPrePath = join(CHANGESET_DIR, CHANGESET_PRE_JSON);
345
+ const filesToCommit = [...relativePaths];
346
+ // Note: Deletion of pre.json is handled by git detecting the missing file
347
+ console.log(`${LOG_PREFIX} ✅ Versions updated to ${version}`);
348
+ return {
349
+ commitMessage: buildCommitMessage(version),
350
+ files: filesToCommit,
351
+ };
352
+ },
353
+ });
354
+ console.log(`${LOG_PREFIX} ✅ Version bump committed and pushed`);
355
+ }
356
+ // Build packages
357
+ if (!skipBuild) {
358
+ runCommand(`${PKG_MANAGER} build`, { dryRun, label: 'build' });
359
+ console.log(`${LOG_PREFIX} ✅ Build complete`);
360
+ }
361
+ else {
362
+ console.log(`${LOG_PREFIX} Skipping build (--skip-build)`);
363
+ }
364
+ // Create git tag
365
+ const tagName = buildTagName(version);
366
+ if (dryRun) {
367
+ console.log(`${LOG_PREFIX} Would create tag: ${tagName}`);
368
+ console.log(`${LOG_PREFIX} Would push tag to ${REMOTES.ORIGIN}`);
369
+ }
370
+ else {
371
+ console.log(`${LOG_PREFIX} Creating tag ${tagName}...`);
372
+ await git.raw(['tag', '-a', tagName, '-m', `Release ${tagName}`]);
373
+ console.log(`${LOG_PREFIX} ✅ Tag created: ${tagName}`);
374
+ console.log(`${LOG_PREFIX} Pushing tag to ${REMOTES.ORIGIN}...`);
375
+ await pushTagWithForce(git, tagName, 'release: pushing version tag');
376
+ console.log(`${LOG_PREFIX} ✅ Tag pushed`);
377
+ }
378
+ // Publish to npm
379
+ if (!skipPublish) {
380
+ if (dryRun) {
381
+ console.log(`${LOG_PREFIX} Would publish packages to npm`);
382
+ }
383
+ else {
384
+ console.log(`${LOG_PREFIX} Publishing packages to npm...`);
385
+ runCommand(`${PKG_MANAGER} -r publish --access public --no-git-checks`, { label: 'publish' });
386
+ console.log(`${LOG_PREFIX} ✅ Packages published to npm`);
387
+ }
388
+ }
389
+ else {
390
+ console.log(`${LOG_PREFIX} Skipping npm publish (--skip-publish)`);
391
+ }
392
+ // Summary
393
+ console.log(`\n${LOG_PREFIX} 🎉 Release complete!`);
394
+ console.log(`${LOG_PREFIX} Version: ${version}`);
395
+ console.log(`${LOG_PREFIX} Tag: ${tagName}`);
396
+ if (!skipPublish && !dryRun) {
397
+ console.log(`${LOG_PREFIX} npm: https://www.npmjs.com/org/lumenflow`);
398
+ }
399
+ console.log(`\n${LOG_PREFIX} Next steps:`);
400
+ if (dryRun) {
401
+ console.log(` - Run without --dry-run to execute the release`);
402
+ }
403
+ else {
404
+ console.log(` - Create GitHub release: gh release create ${tagName} --title "Release ${tagName}"`);
405
+ if (skipPublish) {
406
+ console.log(` - Publish to npm: ${PKG_MANAGER} -r publish --access public --no-git-checks`);
407
+ }
408
+ console.log(` - Verify packages: npm view @lumenflow/cli version`);
409
+ }
410
+ }
411
+ // Export for testing
412
+ export { main };
413
+ // Guard main() for testability
414
+ if (import.meta.main) {
415
+ runCLI(main);
416
+ }
@@ -15,7 +15,7 @@
15
15
  */
16
16
  import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
17
17
  import { die } from '@lumenflow/core/dist/error-handler.js';
18
- import { PATTERNS } from '@lumenflow/core/dist/wu-constants.js';
18
+ import { PATTERNS, LUMENFLOW_PATHS, DIRECTORIES } from '@lumenflow/core/dist/wu-constants.js';
19
19
  /** Local EMOJI constants for spawn-list output */
20
20
  const EMOJI = {
21
21
  WARNING: '⚠️',
@@ -40,8 +40,8 @@ import { SpawnStatus } from '@lumenflow/core/dist/spawn-registry-schema.js';
40
40
  const LOG_PREFIX = '[spawn:list]';
41
41
  /** Default paths for spawn registry and WU files */
42
42
  const DEFAULT_PATHS = Object.freeze({
43
- REGISTRY_DIR: '.beacon/state',
44
- WU_DIR: 'docs/04-operations/tasks/wu',
43
+ REGISTRY_DIR: LUMENFLOW_PATHS.STATE_DIR,
44
+ WU_DIR: DIRECTORIES.WU_DIR,
45
45
  });
46
46
  /** Initiative ID pattern */
47
47
  const INIT_PATTERN = /^INIT-\d+$/;
package/dist/wu-block.js CHANGED
@@ -190,7 +190,7 @@ async function main() {
190
190
  // Update backlog.md in micro-worktree (WU-1574: regenerate from state store)
191
191
  await regenerateBacklogFromState(microBacklogPath);
192
192
  // Append block event to WUStateStore (WU-1573)
193
- const stateDir = path.join(worktreePath, '.beacon', 'state');
193
+ const stateDir = path.join(worktreePath, '.lumenflow', 'state');
194
194
  const store = new WUStateStore(stateDir);
195
195
  await store.load();
196
196
  await store.block(id, args.reason || 'No reason provided');
@@ -200,7 +200,7 @@ async function main() {
200
200
  WU_PATHS.WU(id),
201
201
  WU_PATHS.STATUS(),
202
202
  WU_PATHS.BACKLOG(),
203
- '.beacon/state/wu-events.jsonl',
203
+ '.lumenflow/state/wu-events.jsonl',
204
204
  ],
205
205
  };
206
206
  },
package/dist/wu-claim.js CHANGED
@@ -351,7 +351,7 @@ async function appendClaimEventOnly(stateDir, id, title, lane) {
351
351
  export function getWorktreeCommitFiles(wuId) {
352
352
  return [
353
353
  `docs/04-operations/tasks/wu/${wuId}.yaml`,
354
- '.beacon/state/wu-events.jsonl', // WU-1740: Event store is source of truth
354
+ '.lumenflow/state/wu-events.jsonl', // WU-1740: Event store is source of truth
355
355
  // WU-1746: Explicitly NOT including:
356
356
  // - docs/04-operations/tasks/backlog.md
357
357
  // - docs/04-operations/tasks/status.md
@@ -407,7 +407,12 @@ async function applyCanonicalClaimUpdate(ctx, sessionId) {
407
407
  let updatedTitle = '';
408
408
  const filesToCommit = args.noAuto && stagedChanges.length > 0
409
409
  ? stagedChanges.map((change) => change.filePath).filter(Boolean)
410
- : [WU_PATHS.WU(id), WU_PATHS.STATUS(), WU_PATHS.BACKLOG(), '.beacon/state/wu-events.jsonl'];
410
+ : [
411
+ WU_PATHS.WU(id),
412
+ WU_PATHS.STATUS(),
413
+ WU_PATHS.BACKLOG(),
414
+ '.lumenflow/state/wu-events.jsonl',
415
+ ];
411
416
  console.log(`${PREFIX} Updating canonical claim state (push-only)...`);
412
417
  await withMicroWorktree({
413
418
  operation: MICRO_WORKTREE_OPERATIONS.WU_CLAIM,
@@ -560,8 +565,8 @@ async function handleOrphanCheck(lane, id) {
560
565
  files: [
561
566
  WU_PATHS.BACKLOG(),
562
567
  WU_PATHS.STATUS(),
563
- `.beacon/stamps/${orphanId}.done`,
564
- '.beacon/state/wu-events.jsonl',
568
+ `.lumenflow/stamps/${orphanId}.done`,
569
+ '.lumenflow/state/wu-events.jsonl',
565
570
  ],
566
571
  };
567
572
  },
package/dist/wu-create.js CHANGED
@@ -236,7 +236,7 @@ function buildWUContent({ id, lane, title, priority, type, created, opts, }) {
236
236
  acceptance,
237
237
  code_paths,
238
238
  tests,
239
- artifacts: [`.beacon/stamps/${id}.done`],
239
+ artifacts: [`.lumenflow/stamps/${id}.done`],
240
240
  dependencies: [],
241
241
  risks: [],
242
242
  notes: '',