@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.
- package/dist/__tests__/release.test.js +283 -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 +416 -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-release.js +142 -0
- package/dist/wu-repair.js +3 -3
- package/dist/wu-spawn.js +6 -6
- package/dist/wu-unblock.js +2 -2
- package/package.json +9 -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/mem-summarize.js
CHANGED
|
@@ -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,
|
|
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 });
|
package/dist/mem-triage.js
CHANGED
|
@@ -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,
|
|
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`;
|
package/dist/metrics-snapshot.js
CHANGED
|
@@ -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 = '.
|
|
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 = '.
|
|
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) {
|
package/dist/release.js
ADDED
|
@@ -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
|
+
}
|
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: '',
|