@lumenflow/cli 1.3.4 → 1.3.5
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/dist/__tests__/release.test.js +137 -0
- package/dist/agent-issues-query.js +3 -2
- package/dist/agent-log-issue.js +2 -2
- package/dist/init.js +1 -1
- package/dist/mem-checkpoint.js +3 -7
- package/dist/mem-cleanup.js +2 -6
- package/dist/mem-create.js +3 -7
- package/dist/mem-inbox.js +3 -7
- package/dist/mem-init.js +5 -9
- package/dist/mem-ready.js +3 -7
- package/dist/mem-signal.js +3 -7
- package/dist/mem-start.js +3 -7
- package/dist/mem-summarize.js +2 -6
- package/dist/mem-triage.js +2 -6
- package/dist/metrics-snapshot.js +2 -2
- package/dist/orchestrate-init-status.js +3 -4
- package/dist/orchestrate-monitor.js +4 -5
- package/dist/release.js +310 -0
- package/dist/spawn-list.js +3 -3
- package/dist/wu-block.js +2 -2
- package/dist/wu-claim.js +9 -4
- package/dist/wu-create.js +1 -1
- package/dist/wu-done.js +17 -17
- package/dist/wu-repair.js +3 -3
- package/dist/wu-spawn.js +6 -6
- package/dist/wu-unblock.js +2 -2
- package/package.json +8 -7
- package/dist/gates.d.ts +0 -41
- package/dist/gates.d.ts.map +0 -1
- package/dist/gates.js.map +0 -1
- package/dist/initiative-add-wu.d.ts +0 -22
- package/dist/initiative-add-wu.d.ts.map +0 -1
- package/dist/initiative-add-wu.js.map +0 -1
- package/dist/initiative-create.d.ts +0 -28
- package/dist/initiative-create.d.ts.map +0 -1
- package/dist/initiative-create.js.map +0 -1
- package/dist/initiative-edit.d.ts +0 -34
- package/dist/initiative-edit.d.ts.map +0 -1
- package/dist/initiative-edit.js.map +0 -1
- package/dist/initiative-list.d.ts +0 -12
- package/dist/initiative-list.d.ts.map +0 -1
- package/dist/initiative-list.js.map +0 -1
- package/dist/initiative-status.d.ts +0 -11
- package/dist/initiative-status.d.ts.map +0 -1
- package/dist/initiative-status.js.map +0 -1
- package/dist/mem-checkpoint.d.ts +0 -16
- package/dist/mem-checkpoint.d.ts.map +0 -1
- package/dist/mem-checkpoint.js.map +0 -1
- package/dist/mem-cleanup.d.ts +0 -29
- package/dist/mem-cleanup.d.ts.map +0 -1
- package/dist/mem-cleanup.js.map +0 -1
- package/dist/mem-create.d.ts +0 -17
- package/dist/mem-create.d.ts.map +0 -1
- package/dist/mem-create.js.map +0 -1
- package/dist/mem-inbox.d.ts +0 -35
- package/dist/mem-inbox.d.ts.map +0 -1
- package/dist/mem-inbox.js.map +0 -1
- package/dist/mem-init.d.ts +0 -15
- package/dist/mem-init.d.ts.map +0 -1
- package/dist/mem-init.js.map +0 -1
- package/dist/mem-ready.d.ts +0 -16
- package/dist/mem-ready.d.ts.map +0 -1
- package/dist/mem-ready.js.map +0 -1
- package/dist/mem-signal.d.ts +0 -16
- package/dist/mem-signal.d.ts.map +0 -1
- package/dist/mem-signal.js.map +0 -1
- package/dist/mem-start.d.ts +0 -16
- package/dist/mem-start.d.ts.map +0 -1
- package/dist/mem-start.js.map +0 -1
- package/dist/mem-summarize.d.ts +0 -22
- package/dist/mem-summarize.d.ts.map +0 -1
- package/dist/mem-summarize.js.map +0 -1
- package/dist/mem-triage.d.ts +0 -22
- package/dist/mem-triage.d.ts.map +0 -1
- package/dist/mem-triage.js.map +0 -1
- package/dist/spawn-list.d.ts +0 -16
- package/dist/spawn-list.d.ts.map +0 -1
- package/dist/spawn-list.js.map +0 -1
- package/dist/wu-block.d.ts +0 -16
- package/dist/wu-block.d.ts.map +0 -1
- package/dist/wu-block.js.map +0 -1
- package/dist/wu-claim.d.ts +0 -32
- package/dist/wu-claim.d.ts.map +0 -1
- package/dist/wu-claim.js.map +0 -1
- package/dist/wu-cleanup.d.ts +0 -17
- package/dist/wu-cleanup.d.ts.map +0 -1
- package/dist/wu-cleanup.js.map +0 -1
- package/dist/wu-create.d.ts +0 -38
- package/dist/wu-create.d.ts.map +0 -1
- package/dist/wu-create.js.map +0 -1
- package/dist/wu-deps.d.ts +0 -13
- package/dist/wu-deps.d.ts.map +0 -1
- package/dist/wu-deps.js.map +0 -1
- package/dist/wu-done.d.ts +0 -153
- package/dist/wu-done.d.ts.map +0 -1
- package/dist/wu-done.js.map +0 -1
- package/dist/wu-edit.d.ts +0 -29
- package/dist/wu-edit.d.ts.map +0 -1
- package/dist/wu-edit.js.map +0 -1
- package/dist/wu-infer-lane.d.ts +0 -17
- package/dist/wu-infer-lane.d.ts.map +0 -1
- package/dist/wu-infer-lane.js.map +0 -1
- package/dist/wu-preflight.d.ts +0 -47
- package/dist/wu-preflight.d.ts.map +0 -1
- package/dist/wu-preflight.js.map +0 -1
- package/dist/wu-prune.d.ts +0 -16
- package/dist/wu-prune.d.ts.map +0 -1
- package/dist/wu-prune.js.map +0 -1
- package/dist/wu-repair.d.ts +0 -60
- package/dist/wu-repair.d.ts.map +0 -1
- package/dist/wu-repair.js.map +0 -1
- package/dist/wu-spawn-completion.d.ts +0 -10
- package/dist/wu-spawn.d.ts +0 -168
- package/dist/wu-spawn.d.ts.map +0 -1
- package/dist/wu-spawn.js.map +0 -1
- package/dist/wu-unblock.d.ts +0 -16
- package/dist/wu-unblock.d.ts.map +0 -1
- package/dist/wu-unblock.js.map +0 -1
- package/dist/wu-validate.d.ts +0 -16
- package/dist/wu-validate.d.ts.map +0 -1
- package/dist/wu-validate.js.map +0 -1
package/dist/release.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
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 } from 'node:fs';
|
|
24
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { execSync } from 'node:child_process';
|
|
27
|
+
import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
|
|
28
|
+
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
29
|
+
import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
|
|
30
|
+
import { ensureOnMain } from '@lumenflow/core/dist/wu-helpers.js';
|
|
31
|
+
import { REMOTES, FILE_SYSTEM, PKG_MANAGER, } from '@lumenflow/core/dist/wu-constants.js';
|
|
32
|
+
import { runCLI } from './cli-entry-point.js';
|
|
33
|
+
/** Log prefix for console output */
|
|
34
|
+
const LOG_PREFIX = '[release]';
|
|
35
|
+
/** Micro-worktree operation name */
|
|
36
|
+
const OPERATION_NAME = 'release';
|
|
37
|
+
/** Directory containing @lumenflow packages */
|
|
38
|
+
const LUMENFLOW_PACKAGES_DIR = 'packages/@lumenflow';
|
|
39
|
+
/** Semver regex pattern (strict) */
|
|
40
|
+
const SEMVER_REGEX = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/;
|
|
41
|
+
/** JSON indent size for package.json files */
|
|
42
|
+
const JSON_INDENT = 2;
|
|
43
|
+
/** Default npm registry */
|
|
44
|
+
const NPM_REGISTRY = 'https://registry.npmjs.org';
|
|
45
|
+
/** Environment variable for npm authentication token */
|
|
46
|
+
const NPM_TOKEN_ENV = 'NPM_TOKEN';
|
|
47
|
+
/** Environment variable for alternative npm auth */
|
|
48
|
+
const NODE_AUTH_TOKEN_ENV = 'NODE_AUTH_TOKEN';
|
|
49
|
+
/**
|
|
50
|
+
* Validate that a string is a valid semver version
|
|
51
|
+
*
|
|
52
|
+
* @param version - Version string to validate
|
|
53
|
+
* @returns true if valid semver, false otherwise
|
|
54
|
+
*/
|
|
55
|
+
export function validateSemver(version) {
|
|
56
|
+
if (!version || typeof version !== 'string') {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return SEMVER_REGEX.test(version);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Find all public @lumenflow/* package.json paths
|
|
63
|
+
*
|
|
64
|
+
* @param baseDir - Base directory to search from (defaults to cwd)
|
|
65
|
+
* @returns Array of absolute paths to package.json files
|
|
66
|
+
*/
|
|
67
|
+
export function findPackageJsonPaths(baseDir = process.cwd()) {
|
|
68
|
+
const packagesDir = join(baseDir, LUMENFLOW_PACKAGES_DIR);
|
|
69
|
+
const paths = [];
|
|
70
|
+
if (!existsSync(packagesDir)) {
|
|
71
|
+
return paths;
|
|
72
|
+
}
|
|
73
|
+
const entries = readdirSync(packagesDir);
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const entryPath = join(packagesDir, entry);
|
|
76
|
+
const packageJsonPath = join(entryPath, 'package.json');
|
|
77
|
+
if (statSync(entryPath).isDirectory() && existsSync(packageJsonPath)) {
|
|
78
|
+
// Read package.json to check if it's private
|
|
79
|
+
const content = JSON.parse(readFileSync(packageJsonPath, { encoding: FILE_SYSTEM.UTF8 }));
|
|
80
|
+
// Only include public packages (not marked private)
|
|
81
|
+
if (!content.private) {
|
|
82
|
+
paths.push(packageJsonPath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return paths;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Update version in specified package.json files
|
|
90
|
+
*
|
|
91
|
+
* @param paths - Array of package.json paths
|
|
92
|
+
* @param version - New version string
|
|
93
|
+
*/
|
|
94
|
+
export async function updatePackageVersions(paths, version) {
|
|
95
|
+
for (const packagePath of paths) {
|
|
96
|
+
const content = await readFile(packagePath, {
|
|
97
|
+
encoding: FILE_SYSTEM.ENCODING,
|
|
98
|
+
});
|
|
99
|
+
const pkg = JSON.parse(content);
|
|
100
|
+
// Update version
|
|
101
|
+
pkg.version = version;
|
|
102
|
+
// Write back with preserved formatting (2-space indent)
|
|
103
|
+
const updated = JSON.stringify(pkg, null, JSON_INDENT) + '\n';
|
|
104
|
+
await writeFile(packagePath, updated, { encoding: FILE_SYSTEM.ENCODING });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Build commit message for version bump
|
|
109
|
+
*
|
|
110
|
+
* @param version - New version string
|
|
111
|
+
* @returns Commit message
|
|
112
|
+
*/
|
|
113
|
+
export function buildCommitMessage(version) {
|
|
114
|
+
return `chore: bump all packages to v${version}`;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Build git tag name from version
|
|
118
|
+
*
|
|
119
|
+
* @param version - Version string
|
|
120
|
+
* @returns Git tag name with 'v' prefix
|
|
121
|
+
*/
|
|
122
|
+
export function buildTagName(version) {
|
|
123
|
+
return `v${version}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get relative path from worktree root
|
|
127
|
+
*
|
|
128
|
+
* @param absolutePath - Absolute file path
|
|
129
|
+
* @param worktreePath - Worktree root path
|
|
130
|
+
* @returns Relative path
|
|
131
|
+
*/
|
|
132
|
+
function getRelativePath(absolutePath, worktreePath) {
|
|
133
|
+
return absolutePath.replace(worktreePath + '/', '');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Execute a shell command and handle errors
|
|
137
|
+
*
|
|
138
|
+
* @param cmd - Command to execute
|
|
139
|
+
* @param options - Options for execution
|
|
140
|
+
*/
|
|
141
|
+
function runCommand(cmd, options = {}) {
|
|
142
|
+
const { cwd = process.cwd(), dryRun = false, label } = options;
|
|
143
|
+
const prefix = label ? `[${label}] ` : '';
|
|
144
|
+
if (dryRun) {
|
|
145
|
+
console.log(`${LOG_PREFIX} ${prefix}Would run: ${cmd}`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
console.log(`${LOG_PREFIX} ${prefix}Running: ${cmd}`);
|
|
149
|
+
try {
|
|
150
|
+
execSync(cmd, {
|
|
151
|
+
cwd,
|
|
152
|
+
stdio: 'inherit',
|
|
153
|
+
encoding: FILE_SYSTEM.ENCODING,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
throw new Error(`Command failed: ${cmd}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if npm authentication is available
|
|
162
|
+
*
|
|
163
|
+
* @returns true if NPM_TOKEN or NODE_AUTH_TOKEN is set
|
|
164
|
+
*/
|
|
165
|
+
function hasNpmAuth() {
|
|
166
|
+
return Boolean(process.env[NPM_TOKEN_ENV] || process.env[NODE_AUTH_TOKEN_ENV]);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Main release function
|
|
170
|
+
*/
|
|
171
|
+
async function main() {
|
|
172
|
+
const program = new Command()
|
|
173
|
+
.name('release')
|
|
174
|
+
.description('Release @lumenflow/* packages to npm with version bump, tag, and publish')
|
|
175
|
+
.requiredOption('-v, --version <version>', 'Semver version to release (e.g., 1.3.0)')
|
|
176
|
+
.option('--dry-run', 'Preview changes without making them', false)
|
|
177
|
+
.option('--skip-publish', 'Skip npm publish (only bump and tag)', false)
|
|
178
|
+
.option('--skip-build', 'Skip build step (use existing dist)', false)
|
|
179
|
+
.helpOption('-h, --help', 'Display help for command');
|
|
180
|
+
program.parse();
|
|
181
|
+
const opts = program.opts();
|
|
182
|
+
const { version, dryRun, skipPublish, skipBuild } = opts;
|
|
183
|
+
console.log(`${LOG_PREFIX} Starting release process for v${version}`);
|
|
184
|
+
if (dryRun) {
|
|
185
|
+
console.log(`${LOG_PREFIX} DRY RUN MODE - no changes will be made`);
|
|
186
|
+
}
|
|
187
|
+
// Validate version format
|
|
188
|
+
if (!validateSemver(version)) {
|
|
189
|
+
die(`Invalid semver version: ${version}\n\n` +
|
|
190
|
+
`Expected format: MAJOR.MINOR.PATCH (e.g., 1.3.0)\n` +
|
|
191
|
+
`Optional pre-release suffix: 1.3.0-alpha, 1.3.0-beta.1`);
|
|
192
|
+
}
|
|
193
|
+
// Ensure we're on main branch
|
|
194
|
+
const git = getGitForCwd();
|
|
195
|
+
await ensureOnMain(git);
|
|
196
|
+
// Check for uncommitted changes
|
|
197
|
+
const isClean = await git.isClean();
|
|
198
|
+
if (!isClean) {
|
|
199
|
+
die(`Working directory has uncommitted changes.\n\n` +
|
|
200
|
+
`Commit or stash changes before releasing:\n` +
|
|
201
|
+
` git status\n` +
|
|
202
|
+
` git stash # or git commit`);
|
|
203
|
+
}
|
|
204
|
+
// Find all @lumenflow/* packages to update
|
|
205
|
+
const packagePaths = findPackageJsonPaths();
|
|
206
|
+
if (packagePaths.length === 0) {
|
|
207
|
+
die(`No @lumenflow/* packages found in ${LUMENFLOW_PACKAGES_DIR}`);
|
|
208
|
+
}
|
|
209
|
+
console.log(`${LOG_PREFIX} Found ${packagePaths.length} packages to update:`);
|
|
210
|
+
for (const p of packagePaths) {
|
|
211
|
+
console.log(` - ${p.replace(process.cwd() + '/', '')}`);
|
|
212
|
+
}
|
|
213
|
+
// Check npm authentication for publish
|
|
214
|
+
if (!skipPublish && !dryRun && !hasNpmAuth()) {
|
|
215
|
+
die(`npm authentication not found.\n\n` +
|
|
216
|
+
`Set one of these environment variables:\n` +
|
|
217
|
+
` export NPM_TOKEN=<your-npm-token>\n` +
|
|
218
|
+
` export NODE_AUTH_TOKEN=<your-npm-token>\n\n` +
|
|
219
|
+
`Get a token at: https://www.npmjs.com/settings/tokens\n` +
|
|
220
|
+
`Or use --skip-publish to only bump versions and create tag.`);
|
|
221
|
+
}
|
|
222
|
+
// Execute version bump in micro-worktree
|
|
223
|
+
if (dryRun) {
|
|
224
|
+
console.log(`${LOG_PREFIX} Would bump versions to ${version} using micro-worktree isolation`);
|
|
225
|
+
console.log(`${LOG_PREFIX} Would commit: ${buildCommitMessage(version)}`);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
console.log(`${LOG_PREFIX} Bumping versions using micro-worktree isolation...`);
|
|
229
|
+
await withMicroWorktree({
|
|
230
|
+
operation: OPERATION_NAME,
|
|
231
|
+
id: `v${version}`,
|
|
232
|
+
logPrefix: LOG_PREFIX,
|
|
233
|
+
execute: async ({ worktreePath }) => {
|
|
234
|
+
// Find package paths within the worktree
|
|
235
|
+
const worktreePackagePaths = findPackageJsonPaths(worktreePath);
|
|
236
|
+
// Update versions
|
|
237
|
+
console.log(`${LOG_PREFIX} Updating ${worktreePackagePaths.length} package versions...`);
|
|
238
|
+
await updatePackageVersions(worktreePackagePaths, version);
|
|
239
|
+
// Get relative paths for commit
|
|
240
|
+
const relativePaths = worktreePackagePaths.map((p) => getRelativePath(p, worktreePath));
|
|
241
|
+
console.log(`${LOG_PREFIX} ✅ Versions updated to ${version}`);
|
|
242
|
+
return {
|
|
243
|
+
commitMessage: buildCommitMessage(version),
|
|
244
|
+
files: relativePaths,
|
|
245
|
+
};
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
console.log(`${LOG_PREFIX} ✅ Version bump committed and pushed`);
|
|
249
|
+
}
|
|
250
|
+
// Build packages
|
|
251
|
+
if (!skipBuild) {
|
|
252
|
+
runCommand(`${PKG_MANAGER} build`, { dryRun, label: 'build' });
|
|
253
|
+
console.log(`${LOG_PREFIX} ✅ Build complete`);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log(`${LOG_PREFIX} Skipping build (--skip-build)`);
|
|
257
|
+
}
|
|
258
|
+
// Create git tag
|
|
259
|
+
const tagName = buildTagName(version);
|
|
260
|
+
if (dryRun) {
|
|
261
|
+
console.log(`${LOG_PREFIX} Would create tag: ${tagName}`);
|
|
262
|
+
console.log(`${LOG_PREFIX} Would push tag to ${REMOTES.ORIGIN}`);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
console.log(`${LOG_PREFIX} Creating tag ${tagName}...`);
|
|
266
|
+
await git.raw(['tag', '-a', tagName, '-m', `Release ${tagName}`]);
|
|
267
|
+
console.log(`${LOG_PREFIX} ✅ Tag created: ${tagName}`);
|
|
268
|
+
console.log(`${LOG_PREFIX} Pushing tag to ${REMOTES.ORIGIN}...`);
|
|
269
|
+
await git.push(REMOTES.ORIGIN, tagName);
|
|
270
|
+
console.log(`${LOG_PREFIX} ✅ Tag pushed`);
|
|
271
|
+
}
|
|
272
|
+
// Publish to npm
|
|
273
|
+
if (!skipPublish) {
|
|
274
|
+
if (dryRun) {
|
|
275
|
+
console.log(`${LOG_PREFIX} Would publish packages to npm`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
console.log(`${LOG_PREFIX} Publishing packages to npm...`);
|
|
279
|
+
runCommand(`${PKG_MANAGER} -r publish --access public --no-git-checks`, { label: 'publish' });
|
|
280
|
+
console.log(`${LOG_PREFIX} ✅ Packages published to npm`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.log(`${LOG_PREFIX} Skipping npm publish (--skip-publish)`);
|
|
285
|
+
}
|
|
286
|
+
// Summary
|
|
287
|
+
console.log(`\n${LOG_PREFIX} 🎉 Release complete!`);
|
|
288
|
+
console.log(`${LOG_PREFIX} Version: ${version}`);
|
|
289
|
+
console.log(`${LOG_PREFIX} Tag: ${tagName}`);
|
|
290
|
+
if (!skipPublish && !dryRun) {
|
|
291
|
+
console.log(`${LOG_PREFIX} npm: https://www.npmjs.com/org/lumenflow`);
|
|
292
|
+
}
|
|
293
|
+
console.log(`\n${LOG_PREFIX} Next steps:`);
|
|
294
|
+
if (dryRun) {
|
|
295
|
+
console.log(` - Run without --dry-run to execute the release`);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
console.log(` - Create GitHub release: gh release create ${tagName} --title "Release ${tagName}"`);
|
|
299
|
+
if (skipPublish) {
|
|
300
|
+
console.log(` - Publish to npm: ${PKG_MANAGER} -r publish --access public --no-git-checks`);
|
|
301
|
+
}
|
|
302
|
+
console.log(` - Verify packages: npm view @lumenflow/cli version`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Export for testing
|
|
306
|
+
export { main };
|
|
307
|
+
// Guard main() for testability
|
|
308
|
+
if (import.meta.main) {
|
|
309
|
+
runCLI(main);
|
|
310
|
+
}
|
package/dist/spawn-list.js
CHANGED
|
@@ -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:
|
|
44
|
-
WU_DIR:
|
|
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, '.
|
|
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
|
-
'.
|
|
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
|
-
'.
|
|
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
|
-
: [
|
|
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
|
-
`.
|
|
564
|
-
'.
|
|
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: [`.
|
|
239
|
+
artifacts: [`.lumenflow/stamps/${id}.done`],
|
|
240
240
|
dependencies: [],
|
|
241
241
|
risks: [],
|
|
242
242
|
notes: '',
|
package/dist/wu-done.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 2) Pre-flight validation: run ALL pre-commit hooks before merge (prevents partial completion)
|
|
8
8
|
* 3) cd into worktree
|
|
9
9
|
* 4) Auto-update WU YAML/backlog/status to Done in worktree (unless --no-auto)
|
|
10
|
-
* 5) Create `.
|
|
10
|
+
* 5) Create `.lumenflow/stamps/WU-{id}.done` in worktree
|
|
11
11
|
* 6) Validate staged files against whitelist
|
|
12
12
|
* 7) Commit metadata changes in worktree (on lane branch)
|
|
13
13
|
* 8) cd back to main
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* 10) Push to `main`
|
|
16
16
|
* 11) Remove the associated worktree (unless --no-remove)
|
|
17
17
|
* 12) Optionally delete the lane branch (with --delete-branch)
|
|
18
|
-
* 13) Emit telemetry to .
|
|
18
|
+
* 13) Emit telemetry to .lumenflow/flow.log
|
|
19
19
|
*
|
|
20
20
|
* Canonical sequence (Branch-Only mode - LEGACY):
|
|
21
21
|
* 1) Run gates on lane branch (in main checkout)
|
|
@@ -94,7 +94,7 @@ const MEMORY_CHECKPOINT_NOTES = {
|
|
|
94
94
|
};
|
|
95
95
|
const MEMORY_SIGNAL_WINDOW_MS = 60 * 60 * 1000; // 1 hour for recent signals
|
|
96
96
|
// Path constant for wu-events.jsonl (used in multiple places)
|
|
97
|
-
const WU_EVENTS_PATH = '.
|
|
97
|
+
const WU_EVENTS_PATH = '.lumenflow/state/wu-events.jsonl';
|
|
98
98
|
/**
|
|
99
99
|
* WU-1804: Preflight validation for claim metadata before gates.
|
|
100
100
|
*
|
|
@@ -118,7 +118,7 @@ async function validateClaimMetadataBeforeGates(id, worktreePath, yamlStatus) {
|
|
|
118
118
|
}
|
|
119
119
|
// Check 2: State store must show WU as in_progress
|
|
120
120
|
const resolvedWorktreePath = path.resolve(worktreePath);
|
|
121
|
-
const stateDir = path.join(resolvedWorktreePath, '.
|
|
121
|
+
const stateDir = path.join(resolvedWorktreePath, '.lumenflow', 'state');
|
|
122
122
|
const eventsPath = path.join(resolvedWorktreePath, WU_EVENTS_PATH);
|
|
123
123
|
try {
|
|
124
124
|
const store = new WUStateStore(stateDir);
|
|
@@ -247,7 +247,7 @@ export function buildGatesCommand(options) {
|
|
|
247
247
|
}
|
|
248
248
|
async function assertWorktreeWUInProgressInStateStore(id, worktreePath) {
|
|
249
249
|
const resolvedWorktreePath = path.resolve(worktreePath);
|
|
250
|
-
const stateDir = path.join(resolvedWorktreePath, '.
|
|
250
|
+
const stateDir = path.join(resolvedWorktreePath, '.lumenflow', 'state');
|
|
251
251
|
const eventsPath = path.join(resolvedWorktreePath, WU_EVENTS_PATH);
|
|
252
252
|
const store = new WUStateStore(stateDir);
|
|
253
253
|
try {
|
|
@@ -367,12 +367,12 @@ async function checkInboxForRecentSignals(id, baseDir = process.cwd()) {
|
|
|
367
367
|
* - Any error during update
|
|
368
368
|
*
|
|
369
369
|
* @param {string} id - WU ID being completed
|
|
370
|
-
* @param {string} baseDir - Base directory containing .
|
|
370
|
+
* @param {string} baseDir - Base directory containing .lumenflow/state/
|
|
371
371
|
* @returns {Promise<void>}
|
|
372
372
|
*/
|
|
373
373
|
export async function updateSpawnRegistryOnCompletion(id, baseDir = process.cwd()) {
|
|
374
374
|
try {
|
|
375
|
-
const store = new SpawnRegistryStore(path.join(baseDir, '.
|
|
375
|
+
const store = new SpawnRegistryStore(path.join(baseDir, '.lumenflow', 'state'));
|
|
376
376
|
await store.load();
|
|
377
377
|
const spawnEntry = store.getByTarget(id);
|
|
378
378
|
// Graceful skip if no spawn entry found (legacy WU)
|
|
@@ -684,7 +684,7 @@ async function ensureMainUpToDate() {
|
|
|
684
684
|
/**
|
|
685
685
|
* Tripwire check: Scan commands log for violations (WU-630 detective layer)
|
|
686
686
|
*
|
|
687
|
-
* Scans .
|
|
687
|
+
* Scans .lumenflow/commands.log for destructive git commands executed during
|
|
688
688
|
* this agent session. If violations are found, aborts wu:done and displays
|
|
689
689
|
* remediation guidance.
|
|
690
690
|
*
|
|
@@ -768,7 +768,7 @@ async function ensureNoAutoStagedOrNoop(paths) {
|
|
|
768
768
|
return { noop: false };
|
|
769
769
|
}
|
|
770
770
|
export function emitTelemetry(event) {
|
|
771
|
-
const logPath = path.join('.
|
|
771
|
+
const logPath = path.join('.lumenflow', 'flow.log');
|
|
772
772
|
const logDir = path.dirname(logPath);
|
|
773
773
|
if (!existsSync(logDir))
|
|
774
774
|
mkdirSync(logDir, { recursive: true });
|
|
@@ -776,7 +776,7 @@ export function emitTelemetry(event) {
|
|
|
776
776
|
appendFileSync(logPath, `${line}\n`, { encoding: FILE_SYSTEM.UTF8 });
|
|
777
777
|
}
|
|
778
778
|
async function auditSkipGates(id, reason, fixWU, worktreePath) {
|
|
779
|
-
const auditPath = path.join('.
|
|
779
|
+
const auditPath = path.join('.lumenflow', 'skip-gates-audit.log');
|
|
780
780
|
const auditDir = path.dirname(auditPath);
|
|
781
781
|
if (!existsSync(auditDir))
|
|
782
782
|
mkdirSync(auditDir, { recursive: true });
|
|
@@ -801,7 +801,7 @@ async function auditSkipGates(id, reason, fixWU, worktreePath) {
|
|
|
801
801
|
* Audit trail for --skip-cos-gates (COS v1.3 §7)
|
|
802
802
|
*/
|
|
803
803
|
async function auditSkipCosGates(id, reason) {
|
|
804
|
-
const auditPath = path.join('.
|
|
804
|
+
const auditPath = path.join('.lumenflow', 'skip-cos-gates-audit.log');
|
|
805
805
|
const auditDir = path.dirname(auditPath);
|
|
806
806
|
if (!existsSync(auditDir))
|
|
807
807
|
mkdirSync(auditDir, { recursive: true });
|
|
@@ -953,8 +953,8 @@ async function validateStagedFiles(id, isDocsOnly = false) {
|
|
|
953
953
|
// Whitelist exact matches
|
|
954
954
|
if (whitelist.includes(file))
|
|
955
955
|
return false;
|
|
956
|
-
// Whitelist .
|
|
957
|
-
if (file.startsWith('.
|
|
956
|
+
// Whitelist .lumenflow/stamps/** pattern
|
|
957
|
+
if (file.startsWith('.lumenflow/stamps/'))
|
|
958
958
|
return false;
|
|
959
959
|
// WU-1072: Whitelist apps/docs/**/*.mdx for auto-generated docs from turbo docs:generate
|
|
960
960
|
if (file.startsWith('apps/docs/') && file.endsWith('.mdx'))
|
|
@@ -967,7 +967,7 @@ async function validateStagedFiles(id, isDocsOnly = false) {
|
|
|
967
967
|
console.warn(`${LOG_PREFIX.DONE} Warning: other WU YAMLs are staged; proceeding and committing only current WU files.`);
|
|
968
968
|
}
|
|
969
969
|
else {
|
|
970
|
-
die(`Unexpected files staged (only current WU YAML, status.md, backlog.md, .
|
|
970
|
+
die(`Unexpected files staged (only current WU YAML, status.md, backlog.md, .lumenflow/stamps/<id>.done allowed):\n ${unexpected.join(`${STRING_LITERALS.NEWLINE} `)}`);
|
|
971
971
|
}
|
|
972
972
|
}
|
|
973
973
|
}
|
|
@@ -1349,7 +1349,7 @@ async function checkOwnership(id, doc, worktreePath, overrideOwner = false, over
|
|
|
1349
1349
|
* @param {object} auditEntry - Audit entry to log
|
|
1350
1350
|
*/
|
|
1351
1351
|
function auditOwnershipOverride(auditEntry) {
|
|
1352
|
-
const auditPath = path.join('.
|
|
1352
|
+
const auditPath = path.join('.lumenflow', 'ownership-override-audit.log');
|
|
1353
1353
|
const auditDir = path.dirname(auditPath);
|
|
1354
1354
|
if (!existsSync(auditDir))
|
|
1355
1355
|
mkdirSync(auditDir, { recursive: true });
|
|
@@ -2166,13 +2166,13 @@ export function printDocValidationNudge(id, changedDocPaths) {
|
|
|
2166
2166
|
* WU-1763: Load discoveries for a WU from memory store.
|
|
2167
2167
|
* Non-blocking - returns empty array on errors.
|
|
2168
2168
|
*
|
|
2169
|
-
* @param {string} baseDir - Base directory containing .
|
|
2169
|
+
* @param {string} baseDir - Base directory containing .lumenflow/memory/
|
|
2170
2170
|
* @param {string} wuId - WU ID to load discoveries for
|
|
2171
2171
|
* @returns {Promise<{count: number, ids: string[]}>} Discovery count and IDs
|
|
2172
2172
|
*/
|
|
2173
2173
|
async function loadDiscoveriesForWU(baseDir, wuId) {
|
|
2174
2174
|
try {
|
|
2175
|
-
const memory = await loadMemory(path.join(baseDir, '.
|
|
2175
|
+
const memory = await loadMemory(path.join(baseDir, '.lumenflow/memory'));
|
|
2176
2176
|
const wuNodes = memory.byWu.get(wuId) || [];
|
|
2177
2177
|
const discoveries = wuNodes.filter((node) => node.type === 'discovery');
|
|
2178
2178
|
return {
|
package/dist/wu-repair.js
CHANGED
|
@@ -161,12 +161,12 @@ function validateModeRequirements(options) {
|
|
|
161
161
|
* Run state file repair mode (WU-2240)
|
|
162
162
|
*
|
|
163
163
|
* @param {object} options - CLI options
|
|
164
|
-
* @param {string} [options.path] - Path to state file (defaults to .
|
|
164
|
+
* @param {string} [options.path] - Path to state file (defaults to .lumenflow/state/wu-events.jsonl)
|
|
165
165
|
* @returns {Promise<{success: boolean, exitCode: number}>}
|
|
166
166
|
*/
|
|
167
167
|
async function runStateRepairMode(options) {
|
|
168
|
-
// Default path is .
|
|
169
|
-
const defaultPath = path.join(process.cwd(), '.
|
|
168
|
+
// Default path is .lumenflow/state/wu-events.jsonl relative to cwd
|
|
169
|
+
const defaultPath = path.join(process.cwd(), '.lumenflow', 'state', WU_EVENTS_FILE_NAME);
|
|
170
170
|
const filePath = options.path || defaultPath;
|
|
171
171
|
console.log(`${PREFIX} Repairing state file: ${filePath}`);
|
|
172
172
|
try {
|
package/dist/wu-spawn.js
CHANGED
|
@@ -635,7 +635,7 @@ git rev-parse --show-toplevel
|
|
|
635
635
|
|
|
636
636
|
### Stamp Creation
|
|
637
637
|
|
|
638
|
-
When creating \`.
|
|
638
|
+
When creating \`.lumenflow/\` stamps or other artifacts:
|
|
639
639
|
|
|
640
640
|
1. **ALWAYS** create stamps in the **worktree**, not main
|
|
641
641
|
2. Use \`git rev-parse --show-toplevel\` to get the correct base path
|
|
@@ -644,11 +644,11 @@ When creating \`.beacon/\` stamps or other artifacts:
|
|
|
644
644
|
\`\`\`bash
|
|
645
645
|
# CORRECT: Create stamp in worktree
|
|
646
646
|
WORKTREE_ROOT=$(git rev-parse --show-toplevel)
|
|
647
|
-
mkdir -p "$WORKTREE_ROOT/.
|
|
648
|
-
touch "$WORKTREE_ROOT/.
|
|
647
|
+
mkdir -p "$WORKTREE_ROOT/.lumenflow/agent-runs"
|
|
648
|
+
touch "$WORKTREE_ROOT/.lumenflow/agent-runs/beacon-guardian.stamp"
|
|
649
649
|
|
|
650
650
|
# WRONG: Hardcoded path to main
|
|
651
|
-
# touch /path/to/main/.
|
|
651
|
+
# touch /path/to/main/.lumenflow/agent-runs/beacon-guardian.stamp
|
|
652
652
|
\`\`\`
|
|
653
653
|
|
|
654
654
|
### Why This Matters
|
|
@@ -774,7 +774,7 @@ cd worktrees/${laneSlug}-${id.toLowerCase()}
|
|
|
774
774
|
Then implement following all standards above.
|
|
775
775
|
|
|
776
776
|
**CRITICAL:** Never use \`git worktree add\` directly. Always use \`pnpm wu:claim\` to ensure:
|
|
777
|
-
- Event tracking in .
|
|
777
|
+
- Event tracking in .lumenflow/state/wu-events.jsonl
|
|
778
778
|
- Lane lock acquisition (WIP=1 enforcement)
|
|
779
779
|
- Session tracking for context recovery`;
|
|
780
780
|
}
|
|
@@ -1233,7 +1233,7 @@ async function main() {
|
|
|
1233
1233
|
parentWuId: args.parentWu,
|
|
1234
1234
|
targetWuId: id,
|
|
1235
1235
|
lane: doc.lane || 'Unknown',
|
|
1236
|
-
baseDir: '.
|
|
1236
|
+
baseDir: '.lumenflow/state',
|
|
1237
1237
|
});
|
|
1238
1238
|
const registryMessage = formatSpawnRecordedMessage(registryResult.spawnId, registryResult.error);
|
|
1239
1239
|
console.log(`\n${registryMessage}`);
|
package/dist/wu-unblock.js
CHANGED
|
@@ -187,7 +187,7 @@ async function main() {
|
|
|
187
187
|
appendNote(microDoc, noteLine);
|
|
188
188
|
writeWU(microWUPath, microDoc);
|
|
189
189
|
// WU-1574: Update state store first, then regenerate backlog.md from state
|
|
190
|
-
const stateDir = path.join(worktreePath, '.
|
|
190
|
+
const stateDir = path.join(worktreePath, '.lumenflow', 'state');
|
|
191
191
|
const store = new WUStateStore(stateDir);
|
|
192
192
|
await store.load();
|
|
193
193
|
await store.unblock(id);
|
|
@@ -206,7 +206,7 @@ async function main() {
|
|
|
206
206
|
WU_PATHS.WU(id),
|
|
207
207
|
WU_PATHS.STATUS(),
|
|
208
208
|
WU_PATHS.BACKLOG(),
|
|
209
|
-
'.
|
|
209
|
+
'.lumenflow/state/wu-events.jsonl',
|
|
210
210
|
],
|
|
211
211
|
};
|
|
212
212
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Command-line interface for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
"gates": "./dist/gates.js",
|
|
69
69
|
"lumenflow-gates": "./dist/gates.js",
|
|
70
70
|
"lumenflow-init": "./dist/init.js",
|
|
71
|
-
"lumenflow": "./dist/init.js"
|
|
71
|
+
"lumenflow": "./dist/init.js",
|
|
72
|
+
"lumenflow-release": "./dist/release.js"
|
|
72
73
|
},
|
|
73
74
|
"files": [
|
|
74
75
|
"dist",
|
|
@@ -85,11 +86,11 @@
|
|
|
85
86
|
"pretty-ms": "^9.2.0",
|
|
86
87
|
"simple-git": "^3.30.0",
|
|
87
88
|
"yaml": "^2.8.2",
|
|
88
|
-
"@lumenflow/core": "1.3.
|
|
89
|
-
"@lumenflow/
|
|
90
|
-
"@lumenflow/
|
|
91
|
-
"@lumenflow/agent": "1.3.
|
|
92
|
-
"@lumenflow/
|
|
89
|
+
"@lumenflow/core": "1.3.5",
|
|
90
|
+
"@lumenflow/metrics": "1.3.5",
|
|
91
|
+
"@lumenflow/initiatives": "1.3.5",
|
|
92
|
+
"@lumenflow/agent": "1.3.5",
|
|
93
|
+
"@lumenflow/memory": "1.3.5"
|
|
93
94
|
},
|
|
94
95
|
"devDependencies": {
|
|
95
96
|
"@vitest/coverage-v8": "^4.0.17",
|