@lumenflow/cli 2.18.3 → 2.19.0
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/README.md +42 -41
- package/dist/delegation-list.js +140 -0
- package/dist/delegation-list.js.map +1 -0
- package/dist/doctor.js +35 -99
- package/dist/doctor.js.map +1 -1
- package/dist/gates-plan-resolvers.js +150 -0
- package/dist/gates-plan-resolvers.js.map +1 -0
- package/dist/gates-runners.js +533 -0
- package/dist/gates-runners.js.map +1 -0
- package/dist/gates-types.js +3 -0
- package/dist/gates-types.js.map +1 -1
- package/dist/gates-utils.js +316 -0
- package/dist/gates-utils.js.map +1 -0
- package/dist/gates.js +44 -1016
- package/dist/gates.js.map +1 -1
- package/dist/hooks/enforcement-generator.js +16 -880
- package/dist/hooks/enforcement-generator.js.map +1 -1
- package/dist/hooks/enforcement-sync.js +1 -4
- package/dist/hooks/enforcement-sync.js.map +1 -1
- package/dist/hooks/generators/auto-checkpoint.js +123 -0
- package/dist/hooks/generators/auto-checkpoint.js.map +1 -0
- package/dist/hooks/generators/enforce-worktree.js +188 -0
- package/dist/hooks/generators/enforce-worktree.js.map +1 -0
- package/dist/hooks/generators/index.js +16 -0
- package/dist/hooks/generators/index.js.map +1 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js +134 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js.map +1 -0
- package/dist/hooks/generators/require-wu.js +115 -0
- package/dist/hooks/generators/require-wu.js.map +1 -0
- package/dist/hooks/generators/session-start-recovery.js +101 -0
- package/dist/hooks/generators/session-start-recovery.js.map +1 -0
- package/dist/hooks/generators/signal-utils.js +52 -0
- package/dist/hooks/generators/signal-utils.js.map +1 -0
- package/dist/hooks/generators/warn-incomplete.js +65 -0
- package/dist/hooks/generators/warn-incomplete.js.map +1 -0
- package/dist/init-detection.js +228 -0
- package/dist/init-detection.js.map +1 -0
- package/dist/init-scaffolding.js +146 -0
- package/dist/init-scaffolding.js.map +1 -0
- package/dist/init-templates.js +1928 -0
- package/dist/init-templates.js.map +1 -0
- package/dist/init.js +136 -2425
- package/dist/init.js.map +1 -1
- package/dist/initiative-edit.js +42 -11
- package/dist/initiative-edit.js.map +1 -1
- package/dist/initiative-remove-wu.js +0 -0
- package/dist/initiative-status.js +29 -2
- package/dist/initiative-status.js.map +1 -1
- package/dist/mem-context.js +22 -9
- package/dist/mem-context.js.map +1 -1
- package/dist/orchestrate-init-status.js +32 -1
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/orchestrate-monitor.js +38 -38
- package/dist/orchestrate-monitor.js.map +1 -1
- package/dist/public-manifest.js +12 -5
- package/dist/public-manifest.js.map +1 -1
- package/dist/shared-validators.js +1 -0
- package/dist/shared-validators.js.map +1 -1
- package/dist/spawn-list.js +0 -0
- package/dist/wu-claim-branch.js +121 -0
- package/dist/wu-claim-branch.js.map +1 -0
- package/dist/wu-claim-output.js +83 -0
- package/dist/wu-claim-output.js.map +1 -0
- package/dist/wu-claim-resume-handler.js +85 -0
- package/dist/wu-claim-resume-handler.js.map +1 -0
- package/dist/wu-claim-state.js +572 -0
- package/dist/wu-claim-state.js.map +1 -0
- package/dist/wu-claim-validation.js +439 -0
- package/dist/wu-claim-validation.js.map +1 -0
- package/dist/wu-claim-worktree.js +221 -0
- package/dist/wu-claim-worktree.js.map +1 -0
- package/dist/wu-claim.js +54 -1402
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-create-content.js +254 -0
- package/dist/wu-create-content.js.map +1 -0
- package/dist/wu-create-readiness.js +57 -0
- package/dist/wu-create-readiness.js.map +1 -0
- package/dist/wu-create-validation.js +149 -0
- package/dist/wu-create-validation.js.map +1 -0
- package/dist/wu-create.js +39 -441
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-done.js +144 -249
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-edit-operations.js +432 -0
- package/dist/wu-edit-operations.js.map +1 -0
- package/dist/wu-edit-validators.js +280 -0
- package/dist/wu-edit-validators.js.map +1 -0
- package/dist/wu-edit.js +27 -713
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-prep.js +32 -2
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-repair.js +1 -1
- package/dist/wu-repair.js.map +1 -1
- package/dist/wu-spawn-prompt-builders.js +1123 -0
- package/dist/wu-spawn-prompt-builders.js.map +1 -0
- package/dist/wu-spawn-strategy-resolver.js +314 -0
- package/dist/wu-spawn-strategy-resolver.js.map +1 -0
- package/dist/wu-spawn.js +9 -1398
- package/dist/wu-spawn.js.map +1 -1
- package/package.json +10 -7
- package/templates/core/LUMENFLOW.md.template +29 -99
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +29 -4
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +8 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wu-create-validation.js","sourceRoot":"","sources":["../src/wu-create-validation.ts"],"names":[],"mappings":"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"}
|
package/dist/wu-create.js
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* WU Create
|
|
3
|
+
* WU Create Orchestrator (WU-1262, WU-1439, WU-1651)
|
|
4
4
|
*
|
|
5
5
|
* Race-safe WU creation using shared micro-worktree isolation.
|
|
6
6
|
*
|
|
7
|
+
* WU-1651: Decomposed into focused modules:
|
|
8
|
+
* - wu-create-validation.ts: Spec validation and strict mode checks
|
|
9
|
+
* - wu-create-content.ts: YAML content building, backlog updates, plan templates
|
|
10
|
+
* - wu-create-readiness.ts: Post-create readiness summary display
|
|
11
|
+
* - wu-create-cloud.ts: Cloud mode context builder
|
|
12
|
+
*
|
|
13
|
+
* This file remains the orchestrator, coordinating CLI parsing, validation,
|
|
14
|
+
* content generation, and transaction execution.
|
|
15
|
+
*
|
|
7
16
|
* Canonical sequence:
|
|
8
17
|
* 1) Validate inputs (id, lane, title)
|
|
9
18
|
* 2) Ensure on main branch
|
|
@@ -15,61 +24,37 @@
|
|
|
15
24
|
* e) Push to origin/main
|
|
16
25
|
* f) Cleanup temp branch and micro-worktree
|
|
17
26
|
*
|
|
18
|
-
* Benefits:
|
|
19
|
-
* - Main checkout never switches branches (no impact on other agents)
|
|
20
|
-
* - Race conditions handled via rebase+retry (up to 3 attempts)
|
|
21
|
-
* - Cleanup guaranteed even on failure
|
|
22
|
-
*
|
|
23
27
|
* Usage:
|
|
24
28
|
* pnpm wu:create --id WU-706 --lane Intelligence --title "Fix XYZ issue"
|
|
25
|
-
*
|
|
26
|
-
* Context: WU-705 (fix agent coordination failures), WU-1262 (micro-worktree isolation),
|
|
27
|
-
* WU-1439 (refactor to shared helper)
|
|
28
29
|
*/
|
|
29
30
|
import { getGitForCwd } from '@lumenflow/core/git-adapter';
|
|
30
31
|
import { die } from '@lumenflow/core/error-handler';
|
|
31
|
-
import { existsSync
|
|
32
|
+
import { existsSync } from 'node:fs';
|
|
32
33
|
import { join } from 'node:path';
|
|
33
|
-
// WU-1352: Use centralized YAML functions from wu-yaml.ts
|
|
34
|
-
import { stringifyYAML } from '@lumenflow/core/wu-yaml';
|
|
35
|
-
// WU-1428: Use date-utils for consistent YYYY-MM-DD format (library-first)
|
|
36
34
|
import { todayISO } from '@lumenflow/core/date-utils';
|
|
37
35
|
import { validateLaneFormat, extractParent } from '@lumenflow/core/lane-checker';
|
|
38
|
-
// WU-2330: Import lane inference for sub-lane suggestions
|
|
39
36
|
import { inferSubLane } from '@lumenflow/core/lane-inference';
|
|
40
|
-
import { parseBacklogFrontmatter } from '@lumenflow/core/backlog-parser';
|
|
41
37
|
import { createWUParser, WU_CREATE_OPTIONS, WU_OPTIONS } from '@lumenflow/core/arg-parser';
|
|
42
38
|
import { WU_PATHS } from '@lumenflow/core/wu-paths';
|
|
43
39
|
import { getConfig } from '@lumenflow/core/config';
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
import { hasSpecRefs, validateSpecRefs } from '@lumenflow/core/wu-create-validators';
|
|
47
|
-
import { COMMIT_FORMATS, FILE_SYSTEM, READINESS_UI, REMOTES, STRING_LITERALS, WU_TYPES, } from '@lumenflow/core/wu-constants';
|
|
48
|
-
// WU-1593: Use centralized validateWUIDFormat (DRY)
|
|
40
|
+
import { validateSpecRefs, hasSpecRefs } from '@lumenflow/core/wu-create-validators';
|
|
41
|
+
import { COMMIT_FORMATS, REMOTES, STRING_LITERALS, WU_TYPES } from '@lumenflow/core/wu-constants';
|
|
49
42
|
import { ensureOnMain, validateWUIDFormat } from '@lumenflow/core/wu-helpers';
|
|
50
|
-
// WU-1439: Use shared micro-worktree helper
|
|
51
43
|
import { withMicroWorktree } from '@lumenflow/core/micro-worktree';
|
|
52
|
-
// WU-1246: Auto-generate WU IDs when --id not provided
|
|
53
44
|
import { generateWuIdWithRetry } from '@lumenflow/core/wu-id-generator';
|
|
54
|
-
// WU-1620: Import spec completeness validator for readiness summary
|
|
55
|
-
import { validateSpecCompleteness } from '@lumenflow/core/wu-done-validators';
|
|
56
|
-
// WU-1620: Import readWU to read back created YAML for validation
|
|
57
|
-
import { readWU } from '@lumenflow/core/wu-yaml';
|
|
58
|
-
// WU-2253: Import WU spec linter for acceptance/code_paths validation
|
|
59
45
|
import { lintWUSpec, formatLintErrors } from '@lumenflow/core/wu-lint';
|
|
60
|
-
// WU-1329: Import path existence validators for strict mode
|
|
61
|
-
import { validateCodePathsExistence, validateTestPathsExistence, } from '@lumenflow/core/wu-preflight-validators';
|
|
62
|
-
// WU-1025: Import placeholder validator for inline content validation
|
|
63
|
-
import { validateNoPlaceholders, buildPlaceholderErrorMessage } from '@lumenflow/core/wu-validator';
|
|
64
|
-
import { isCodeFile } from '@lumenflow/core/manual-test-validator';
|
|
65
46
|
import { WU_CREATE_DEFAULTS } from '@lumenflow/core/wu-create-defaults';
|
|
66
|
-
import { isDocsOrProcessType
|
|
67
|
-
// WU-1211: Import initiative validation for phase check
|
|
47
|
+
import { isDocsOrProcessType } from '@lumenflow/core/wu-type-helpers';
|
|
68
48
|
import { checkInitiativePhases, findInitiative } from '@lumenflow/initiatives';
|
|
69
|
-
// WU-1590: Cloud create context builder for --cloud path
|
|
70
49
|
import { buildCloudCreateContext } from './wu-create-cloud.js';
|
|
71
|
-
// WU-1495: Cloud auto-detection from config-driven env signals
|
|
72
50
|
import { detectCloudMode, resolveEffectiveCloudActivation, CLOUD_ACTIVATION_SOURCE, } from '@lumenflow/core/cloud-detect';
|
|
51
|
+
// WU-1651: Import from extracted modules
|
|
52
|
+
import { validateCreateSpec, containsCodeFiles, hasAnyItems, } from './wu-create-validation.js';
|
|
53
|
+
import { buildWUContent, truncateTitle, mergeSpecRefs, createPlanTemplate, createWUYamlInWorktree, updateBacklogInWorktree, getPlanProtocolRef, } from './wu-create-content.js';
|
|
54
|
+
import { displayReadinessSummary } from './wu-create-readiness.js';
|
|
55
|
+
// Re-export public API for backward compatibility (tests import from wu-create.js)
|
|
56
|
+
export { validateCreateSpec } from './wu-create-validation.js';
|
|
57
|
+
export { buildWUContent } from './wu-create-content.js';
|
|
73
58
|
/** Log prefix for console output */
|
|
74
59
|
const LOG_PREFIX = '[wu:create]';
|
|
75
60
|
/** Micro-worktree operation name */
|
|
@@ -78,22 +63,8 @@ const OPERATION_NAME = 'wu-create';
|
|
|
78
63
|
const DEFAULT_PRIORITY = 'P2';
|
|
79
64
|
/** Default WU type */
|
|
80
65
|
const DEFAULT_TYPE = WU_TYPES.FEATURE;
|
|
81
|
-
/** Maximum title length before truncation */
|
|
82
|
-
const MAX_TITLE_LENGTH = 60;
|
|
83
|
-
/** Truncation suffix */
|
|
84
|
-
const TRUNCATION_SUFFIX = '...';
|
|
85
|
-
/** Truncated title length (MAX_TITLE_LENGTH - TRUNCATION_SUFFIX.length) */
|
|
86
|
-
const TRUNCATED_TITLE_LENGTH = MAX_TITLE_LENGTH - TRUNCATION_SUFFIX.length;
|
|
87
66
|
/** Minimum confidence threshold to show lane suggestion warning (WU-2438: lowered from 50 to 30) */
|
|
88
67
|
const MIN_CONFIDENCE_FOR_WARNING = 30;
|
|
89
|
-
function containsCodeFiles(codePaths) {
|
|
90
|
-
if (!codePaths || codePaths.length === 0)
|
|
91
|
-
return false;
|
|
92
|
-
return codePaths.some((p) => isCodeFile(p));
|
|
93
|
-
}
|
|
94
|
-
function hasAnyItems(value) {
|
|
95
|
-
return Array.isArray(value) && value.length > 0;
|
|
96
|
-
}
|
|
97
68
|
/**
|
|
98
69
|
* Resolve branch-aware cloud activation for wu:create.
|
|
99
70
|
*
|
|
@@ -185,379 +156,6 @@ function checkWUExists(id) {
|
|
|
185
156
|
` 3. Delete existing WU: pnpm wu:delete --id ${id} (if obsolete)`);
|
|
186
157
|
}
|
|
187
158
|
}
|
|
188
|
-
/**
|
|
189
|
-
* Truncate title for commit message if needed
|
|
190
|
-
* @param {string} title - Original title
|
|
191
|
-
* @returns {string} Truncated title
|
|
192
|
-
*/
|
|
193
|
-
function truncateTitle(title) {
|
|
194
|
-
return title.length > MAX_TITLE_LENGTH
|
|
195
|
-
? `${title.substring(0, TRUNCATED_TITLE_LENGTH)}${TRUNCATION_SUFFIX}`
|
|
196
|
-
: title;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* WU-1620: Display readiness summary after create/edit
|
|
200
|
-
*
|
|
201
|
-
* Shows whether WU is ready for wu:claim based on spec completeness.
|
|
202
|
-
* Non-blocking - just informational to help agents understand what's missing.
|
|
203
|
-
*
|
|
204
|
-
* @param {string} id - WU ID
|
|
205
|
-
*/
|
|
206
|
-
function displayReadinessSummary(id) {
|
|
207
|
-
try {
|
|
208
|
-
const wuPath = WU_PATHS.WU(id);
|
|
209
|
-
const wuDoc = readWU(wuPath, id);
|
|
210
|
-
const { valid, errors } = validateSpecCompleteness(wuDoc, id);
|
|
211
|
-
const { BOX, BOX_WIDTH, MESSAGES, ERROR_MAX_LENGTH, ERROR_TRUNCATE_LENGTH, TRUNCATION_SUFFIX, PADDING, } = READINESS_UI;
|
|
212
|
-
console.log(`\n${BOX.TOP_LEFT}${BOX.HORIZONTAL.repeat(BOX_WIDTH)}${BOX.TOP_RIGHT}`);
|
|
213
|
-
if (valid) {
|
|
214
|
-
console.log(`${BOX.VERTICAL} ${MESSAGES.READY_YES}${''.padEnd(PADDING.READY_YES)}${BOX.VERTICAL}`);
|
|
215
|
-
console.log(`${BOX.VERTICAL}${''.padEnd(BOX_WIDTH)}${BOX.VERTICAL}`);
|
|
216
|
-
const claimCmd = `Run: pnpm wu:claim --id ${id}`;
|
|
217
|
-
console.log(`${BOX.VERTICAL} ${claimCmd}${''.padEnd(BOX_WIDTH - claimCmd.length - 1)}${BOX.VERTICAL}`);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
console.log(`${BOX.VERTICAL} ${MESSAGES.READY_NO}${''.padEnd(PADDING.READY_NO)}${BOX.VERTICAL}`);
|
|
221
|
-
console.log(`${BOX.VERTICAL}${''.padEnd(BOX_WIDTH)}${BOX.VERTICAL}`);
|
|
222
|
-
console.log(`${BOX.VERTICAL} ${MESSAGES.MISSING_HEADER}${''.padEnd(PADDING.MISSING_HEADER)}${BOX.VERTICAL}`);
|
|
223
|
-
for (const error of errors) {
|
|
224
|
-
// Truncate long error messages to fit box
|
|
225
|
-
const truncated = error.length > ERROR_MAX_LENGTH
|
|
226
|
-
? `${error.substring(0, ERROR_TRUNCATE_LENGTH)}${TRUNCATION_SUFFIX}`
|
|
227
|
-
: error;
|
|
228
|
-
console.log(`${BOX.VERTICAL} ${MESSAGES.BULLET} ${truncated}${''.padEnd(Math.max(0, PADDING.ERROR_BULLET - truncated.length))}${BOX.VERTICAL}`);
|
|
229
|
-
}
|
|
230
|
-
console.log(`${BOX.VERTICAL}${''.padEnd(BOX_WIDTH)}${BOX.VERTICAL}`);
|
|
231
|
-
const editCmd = `Run: pnpm wu:edit --id ${id} --help`;
|
|
232
|
-
console.log(`${BOX.VERTICAL} ${editCmd}${''.padEnd(BOX_WIDTH - editCmd.length - 1)}${BOX.VERTICAL}`);
|
|
233
|
-
}
|
|
234
|
-
console.log(`${BOX.BOTTOM_LEFT}${BOX.HORIZONTAL.repeat(BOX_WIDTH)}${BOX.BOTTOM_RIGHT}`);
|
|
235
|
-
}
|
|
236
|
-
catch (err) {
|
|
237
|
-
// Non-blocking - if validation fails, just warn
|
|
238
|
-
console.warn(`${LOG_PREFIX} ⚠️ Could not validate readiness: ${err.message}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function mergeSpecRefs(specRefs, extraRef) {
|
|
242
|
-
const refs = specRefs ? [...specRefs] : [];
|
|
243
|
-
if (extraRef && !refs.includes(extraRef)) {
|
|
244
|
-
refs.push(extraRef);
|
|
245
|
-
}
|
|
246
|
-
return refs;
|
|
247
|
-
}
|
|
248
|
-
function createPlanTemplate(wuId, title) {
|
|
249
|
-
const plansDir = getPlansDir();
|
|
250
|
-
mkdirSync(plansDir, { recursive: true });
|
|
251
|
-
const planPath = getPlanPath(wuId);
|
|
252
|
-
if (existsSync(planPath)) {
|
|
253
|
-
die(`Plan already exists: ${planPath}\n\n` +
|
|
254
|
-
`Options:\n` +
|
|
255
|
-
` 1. Open the existing plan and continue editing\n` +
|
|
256
|
-
` 2. Delete or rename the existing plan before retrying\n` +
|
|
257
|
-
` 3. Run wu:create without --plan`);
|
|
258
|
-
}
|
|
259
|
-
const today = todayISO();
|
|
260
|
-
const content = `# ${wuId} Plan — ${title}\n\n` +
|
|
261
|
-
`Created: ${today}\n\n` +
|
|
262
|
-
`## Goal\n\n` +
|
|
263
|
-
`## Scope\n\n` +
|
|
264
|
-
`## Approach\n\n` +
|
|
265
|
-
`## Risks\n\n` +
|
|
266
|
-
`## Open Questions\n`;
|
|
267
|
-
writeFileSync(planPath, content, { encoding: FILE_SYSTEM.UTF8 });
|
|
268
|
-
console.log(`${LOG_PREFIX} ✅ Created plan template: ${planPath}`);
|
|
269
|
-
return planPath;
|
|
270
|
-
}
|
|
271
|
-
export function buildWUContent({ id, lane, title, priority, type, created, opts, }) {
|
|
272
|
-
const { description, acceptance, notes, codePaths, testPathsManual, testPathsUnit, testPathsE2e, initiative, phase, blockedBy, blocks, labels, assignedTo, exposure, userJourney, uiPairingWus, specRefs, } = opts;
|
|
273
|
-
// Arrays come directly from Commander.js repeatable options - no parsing needed
|
|
274
|
-
const code_paths = codePaths ?? [];
|
|
275
|
-
const tests = {
|
|
276
|
-
manual: testPathsManual ?? [],
|
|
277
|
-
unit: testPathsUnit ?? [],
|
|
278
|
-
e2e: testPathsE2e ?? [],
|
|
279
|
-
};
|
|
280
|
-
// WU-1443: Auto-insert minimal manual test stub for plan-first specs when no tests are provided,
|
|
281
|
-
// as long as code_paths does not include actual code files (automated tests still required for code).
|
|
282
|
-
if (!isDocsOrProcessType(type) && !hasAnyTests(tests) && !containsCodeFiles(code_paths)) {
|
|
283
|
-
tests.manual = [WU_CREATE_DEFAULTS.AUTO_MANUAL_TEST_PLACEHOLDER];
|
|
284
|
-
}
|
|
285
|
-
return {
|
|
286
|
-
id,
|
|
287
|
-
title,
|
|
288
|
-
lane,
|
|
289
|
-
type,
|
|
290
|
-
status: 'ready',
|
|
291
|
-
priority,
|
|
292
|
-
created,
|
|
293
|
-
description,
|
|
294
|
-
acceptance,
|
|
295
|
-
code_paths,
|
|
296
|
-
tests,
|
|
297
|
-
artifacts: [WU_PATHS.STAMP(id)],
|
|
298
|
-
dependencies: [],
|
|
299
|
-
risks: [],
|
|
300
|
-
// WU-1443: Default notes to non-empty placeholder to avoid strict completeness failures.
|
|
301
|
-
notes: typeof notes === 'string' && notes.trim().length > 0
|
|
302
|
-
? notes
|
|
303
|
-
: WU_CREATE_DEFAULTS.AUTO_NOTES_PLACEHOLDER,
|
|
304
|
-
requires_review: false,
|
|
305
|
-
...(initiative && { initiative }),
|
|
306
|
-
...(phase && { phase: parseInt(phase, 10) }),
|
|
307
|
-
...(blockedBy?.length && { blocked_by: blockedBy }),
|
|
308
|
-
...(blocks?.length && { blocks }),
|
|
309
|
-
...(labels?.length && { labels }),
|
|
310
|
-
...(assignedTo && { assigned_to: assignedTo }),
|
|
311
|
-
...(exposure && { exposure }),
|
|
312
|
-
...(userJourney && { user_journey: userJourney }),
|
|
313
|
-
...(uiPairingWus?.length && { ui_pairing_wus: uiPairingWus }),
|
|
314
|
-
...(specRefs?.length && { spec_refs: specRefs }),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Validate WU spec for creation
|
|
319
|
-
*
|
|
320
|
-
* WU-1329: Strict mode (default) validates that code_paths and test_paths exist on disk.
|
|
321
|
-
* Use opts.strict = false to bypass path existence checks.
|
|
322
|
-
*
|
|
323
|
-
* @param params - Validation parameters
|
|
324
|
-
* @returns {{ valid: boolean, errors: string[] }}
|
|
325
|
-
*/
|
|
326
|
-
export function validateCreateSpec({ id, lane, title, priority, type, opts, }) {
|
|
327
|
-
const errors = [];
|
|
328
|
-
const effectiveType = type || DEFAULT_TYPE;
|
|
329
|
-
// WU-1329: Strict mode is the default
|
|
330
|
-
const strict = opts.strict !== false;
|
|
331
|
-
// WU-1329: Log when strict validation is bypassed
|
|
332
|
-
if (!strict) {
|
|
333
|
-
console.warn(`${LOG_PREFIX} WARNING: strict validation bypassed (--no-strict). Path existence checks skipped.`);
|
|
334
|
-
}
|
|
335
|
-
if (!opts.description) {
|
|
336
|
-
errors.push('--description is required');
|
|
337
|
-
}
|
|
338
|
-
if (!opts.acceptance || opts.acceptance.length === 0) {
|
|
339
|
-
errors.push('--acceptance is required (repeatable)');
|
|
340
|
-
}
|
|
341
|
-
if (!opts.exposure) {
|
|
342
|
-
errors.push('--exposure is required');
|
|
343
|
-
}
|
|
344
|
-
const hasTestPaths = hasAnyItems(opts.testPathsManual) ||
|
|
345
|
-
hasAnyItems(opts.testPathsUnit) ||
|
|
346
|
-
hasAnyItems(opts.testPathsE2e);
|
|
347
|
-
const hasManualTestPaths = hasManualTests({ manual: opts.testPathsManual });
|
|
348
|
-
if (!isDocsOrProcessType(effectiveType)) {
|
|
349
|
-
const codePaths = opts.codePaths ?? [];
|
|
350
|
-
if (codePaths.length === 0) {
|
|
351
|
-
errors.push('--code-paths is required for non-documentation WUs');
|
|
352
|
-
}
|
|
353
|
-
// WU-1443: Plan-first WUs may not know tests yet.
|
|
354
|
-
// Allow auto-manual stub ONLY when code_paths does not include code files.
|
|
355
|
-
const canAutoAddManualTests = !hasTestPaths && codePaths.length > 0 && !containsCodeFiles(codePaths);
|
|
356
|
-
if (!hasTestPaths && !canAutoAddManualTests) {
|
|
357
|
-
errors.push('At least one test path flag is required (--test-paths-manual, --test-paths-unit, or --test-paths-e2e)');
|
|
358
|
-
}
|
|
359
|
-
if (!hasManualTestPaths && !canAutoAddManualTests) {
|
|
360
|
-
errors.push('--test-paths-manual is required for non-documentation WUs');
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
if (effectiveType === WU_TYPES.FEATURE && !hasSpecRefs(opts.specRefs)) {
|
|
364
|
-
errors.push('--spec-refs is required for type: feature WUs\n' +
|
|
365
|
-
' Tip: Create a plan first with: pnpm plan:create --id <WU-ID> --title "..."\n' +
|
|
366
|
-
' Then use --plan flag or --spec-refs lumenflow://plans/<WU-ID>-plan.md');
|
|
367
|
-
}
|
|
368
|
-
// WU-1530: Single-pass validation — collect all errors before returning.
|
|
369
|
-
// Always build WU content and run all validation stages, even when early fields are missing.
|
|
370
|
-
// buildWUContent handles undefined gracefully; Zod catches missing required fields.
|
|
371
|
-
// Stage 2b: Placeholder check (only meaningful if fields exist)
|
|
372
|
-
if (opts.description && opts.acceptance && opts.acceptance.length > 0) {
|
|
373
|
-
const placeholderResult = validateNoPlaceholders({
|
|
374
|
-
description: opts.description,
|
|
375
|
-
acceptance: opts.acceptance,
|
|
376
|
-
});
|
|
377
|
-
if (!placeholderResult.valid) {
|
|
378
|
-
errors.push(buildPlaceholderErrorMessage('wu:create', placeholderResult));
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
// Stage 2c-2d: Schema + completeness — always run to catch enum/format errors
|
|
382
|
-
// even when required fields are missing (Zod reports both)
|
|
383
|
-
const today = todayISO();
|
|
384
|
-
const wuContent = buildWUContent({
|
|
385
|
-
id,
|
|
386
|
-
lane,
|
|
387
|
-
title,
|
|
388
|
-
priority,
|
|
389
|
-
type: effectiveType,
|
|
390
|
-
created: today,
|
|
391
|
-
opts,
|
|
392
|
-
});
|
|
393
|
-
const schemaResult = validateWU(wuContent);
|
|
394
|
-
if (!schemaResult.success) {
|
|
395
|
-
// Deduplicate: skip schema errors already covered by field-level checks above
|
|
396
|
-
const fieldErrorFields = new Set(['description', 'acceptance', 'code_paths', 'tests']);
|
|
397
|
-
const schemaErrors = schemaResult.error.issues
|
|
398
|
-
.filter((issue) => !fieldErrorFields.has(issue.path[0]) || errors.length === 0)
|
|
399
|
-
.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
|
|
400
|
-
errors.push(...schemaErrors);
|
|
401
|
-
}
|
|
402
|
-
// Only run completeness if schema passed (it depends on well-formed data)
|
|
403
|
-
if (schemaResult.success) {
|
|
404
|
-
const completeness = validateSpecCompleteness(wuContent, id);
|
|
405
|
-
if (!completeness.valid) {
|
|
406
|
-
errors.push(...completeness.errors);
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
// Stage 2e: Strict mode validates path existence
|
|
410
|
-
if (strict) {
|
|
411
|
-
const rootDir = process.cwd();
|
|
412
|
-
if (opts.codePaths && opts.codePaths.length > 0) {
|
|
413
|
-
const codePathsResult = validateCodePathsExistence(opts.codePaths, rootDir);
|
|
414
|
-
if (!codePathsResult.valid) {
|
|
415
|
-
errors.push(...codePathsResult.errors);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
const testsObj = {
|
|
419
|
-
unit: opts.testPathsUnit || [],
|
|
420
|
-
e2e: opts.testPathsE2e || [],
|
|
421
|
-
};
|
|
422
|
-
const testPathsResult = validateTestPathsExistence(testsObj, rootDir);
|
|
423
|
-
if (!testPathsResult.valid) {
|
|
424
|
-
errors.push(...testPathsResult.errors);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
if (errors.length > 0) {
|
|
428
|
-
return { valid: false, errors };
|
|
429
|
-
}
|
|
430
|
-
return { valid: true, errors: [] };
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Create WU YAML file in micro-worktree
|
|
434
|
-
*
|
|
435
|
-
* @param {string} worktreePath - Path to micro-worktree
|
|
436
|
-
* @param {string} id - WU ID
|
|
437
|
-
* @param {string} lane - WU lane
|
|
438
|
-
* @param {string} title - WU title
|
|
439
|
-
* @param {string} priority - WU priority
|
|
440
|
-
* @param {string} type - WU type
|
|
441
|
-
* @param {Object} opts - Additional options
|
|
442
|
-
* @returns {string} Relative path to created YAML file
|
|
443
|
-
*/
|
|
444
|
-
function createWUYamlInWorktree(worktreePath, id, lane, title, priority, type, opts = {}) {
|
|
445
|
-
const wuRelativePath = WU_PATHS.WU(id);
|
|
446
|
-
const wuAbsolutePath = join(worktreePath, wuRelativePath);
|
|
447
|
-
const wuDir = join(worktreePath, WU_PATHS.WU_DIR());
|
|
448
|
-
mkdirSync(wuDir, { recursive: true });
|
|
449
|
-
// WU-1428: Use todayISO() for consistent YYYY-MM-DD format (library-first)
|
|
450
|
-
const today = todayISO();
|
|
451
|
-
const wuContent = buildWUContent({
|
|
452
|
-
id,
|
|
453
|
-
lane,
|
|
454
|
-
title,
|
|
455
|
-
priority,
|
|
456
|
-
type,
|
|
457
|
-
created: today,
|
|
458
|
-
opts,
|
|
459
|
-
});
|
|
460
|
-
// WU-1539: Validate WU structure before writing (fail-fast, no placeholders)
|
|
461
|
-
// WU-1750: Zod transforms normalize embedded newlines in arrays and strings
|
|
462
|
-
const validationResult = validateWU(wuContent);
|
|
463
|
-
if (!validationResult.success) {
|
|
464
|
-
const errors = validationResult.error.issues
|
|
465
|
-
.map((issue) => ` • ${issue.path.join('.')}: ${issue.message}`)
|
|
466
|
-
.join(STRING_LITERALS.NEWLINE);
|
|
467
|
-
die(`${LOG_PREFIX} ❌ WU YAML validation failed:\n\n${errors}\n\n` +
|
|
468
|
-
`Fix the issues above and retry.`);
|
|
469
|
-
}
|
|
470
|
-
const completenessResult = validateSpecCompleteness(wuContent, id);
|
|
471
|
-
if (!completenessResult.valid) {
|
|
472
|
-
const errorList = completenessResult.errors
|
|
473
|
-
.map((error) => ` • ${error}`)
|
|
474
|
-
.join(STRING_LITERALS.NEWLINE);
|
|
475
|
-
die(`${LOG_PREFIX} ❌ WU SPEC INCOMPLETE:\n\n${errorList}\n\n` +
|
|
476
|
-
`Provide the missing fields and retry.`);
|
|
477
|
-
}
|
|
478
|
-
// WU-2253: Validate acceptance/code_paths consistency and invariants compliance
|
|
479
|
-
// This blocks WU creation if acceptance references paths not in code_paths
|
|
480
|
-
// or if code_paths conflicts with tools/invariants.yml
|
|
481
|
-
const invariantsPath = join(process.cwd(), 'tools/invariants.yml');
|
|
482
|
-
const lintResult = lintWUSpec(wuContent, { invariantsPath });
|
|
483
|
-
if (!lintResult.valid) {
|
|
484
|
-
const formatted = formatLintErrors(lintResult.errors);
|
|
485
|
-
die(`${LOG_PREFIX} ❌ WU SPEC LINT FAILED:\n\n${formatted}\n` +
|
|
486
|
-
`Fix the issues above before creating this WU.`);
|
|
487
|
-
}
|
|
488
|
-
// WU-1352: Use centralized stringify (lineWidth: -1 = no wrapping for WU creation)
|
|
489
|
-
// WU-1750: CRITICAL - Use validationResult.data (transformed) NOT wuContent (raw input)
|
|
490
|
-
// This ensures embedded newlines are normalized before YAML output
|
|
491
|
-
const yamlContent = stringifyYAML(validationResult.data, { lineWidth: -1 });
|
|
492
|
-
writeFileSync(wuAbsolutePath, yamlContent, { encoding: FILE_SYSTEM.UTF8 });
|
|
493
|
-
console.log(`${LOG_PREFIX} ✅ Created ${id}.yaml in micro-worktree`);
|
|
494
|
-
return wuRelativePath;
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Update backlog.md in micro-worktree
|
|
498
|
-
*
|
|
499
|
-
* @param {string} worktreePath - Path to micro-worktree
|
|
500
|
-
* @param {string} id - WU ID
|
|
501
|
-
* @param {string} lane - WU lane
|
|
502
|
-
* @param {string} title - WU title
|
|
503
|
-
* @returns {string} Relative path to backlog.md
|
|
504
|
-
*/
|
|
505
|
-
function updateBacklogInWorktree(worktreePath, id, lane, title) {
|
|
506
|
-
const backlogRelativePath = WU_PATHS.BACKLOG();
|
|
507
|
-
const backlogAbsolutePath = join(worktreePath, backlogRelativePath);
|
|
508
|
-
if (!existsSync(backlogAbsolutePath)) {
|
|
509
|
-
// WU-1311: Use config-based backlog path in error message
|
|
510
|
-
die(`Backlog not found in micro-worktree: ${backlogAbsolutePath}\n\n` +
|
|
511
|
-
`Options:\n` +
|
|
512
|
-
` 1. Ensure backlog.md exists at ${getConfig().directories.backlogPath}\n` +
|
|
513
|
-
` 2. Run from repository root directory`);
|
|
514
|
-
}
|
|
515
|
-
const { frontmatter, markdown } = parseBacklogFrontmatter(backlogAbsolutePath);
|
|
516
|
-
if (!frontmatter) {
|
|
517
|
-
die('Backlog frontmatter missing in micro-worktree.\n\n' +
|
|
518
|
-
'The backlog.md file requires YAML frontmatter to define section headings.\n\n' +
|
|
519
|
-
'Options:\n' +
|
|
520
|
-
' 1. Check backlog.md has valid YAML frontmatter between --- markers\n' +
|
|
521
|
-
' 2. Ensure sections.ready.heading is defined in frontmatter');
|
|
522
|
-
}
|
|
523
|
-
if (!frontmatter.sections?.ready?.heading) {
|
|
524
|
-
die('Invalid backlog frontmatter: Missing sections.ready.heading\n\n' +
|
|
525
|
-
'Options:\n' +
|
|
526
|
-
' 1. Add sections.ready.heading to backlog.md frontmatter\n' +
|
|
527
|
-
' 2. Check frontmatter YAML structure');
|
|
528
|
-
}
|
|
529
|
-
const readyHeading = frontmatter.sections.ready.heading;
|
|
530
|
-
const insertionStrategy = frontmatter.sections.ready.insertion || 'after_heading_blank_line';
|
|
531
|
-
const lines = markdown.split(STRING_LITERALS.NEWLINE);
|
|
532
|
-
const headingIndex = lines.findIndex((line) => line === readyHeading);
|
|
533
|
-
if (headingIndex === -1) {
|
|
534
|
-
die(`Could not find Ready section heading: '${readyHeading}'\n\n` +
|
|
535
|
-
`Options:\n` +
|
|
536
|
-
` 1. Add the heading '${readyHeading}' to backlog.md\n` +
|
|
537
|
-
` 2. Update sections.ready.heading in backlog.md frontmatter`);
|
|
538
|
-
}
|
|
539
|
-
let insertionIndex;
|
|
540
|
-
if (insertionStrategy === 'after_heading_blank_line') {
|
|
541
|
-
const LINES_AFTER_HEADING = 2;
|
|
542
|
-
insertionIndex = headingIndex + LINES_AFTER_HEADING;
|
|
543
|
-
}
|
|
544
|
-
else {
|
|
545
|
-
die(`Unknown insertion strategy: ${insertionStrategy}\n\n` +
|
|
546
|
-
`Options:\n` +
|
|
547
|
-
` 1. Use 'after_heading_blank_line' in backlog.md frontmatter\n` +
|
|
548
|
-
` 2. Check sections.ready.insertion value`);
|
|
549
|
-
}
|
|
550
|
-
const newEntry = `- [${id} — ${title}](wu/${id}.yaml) — ${lane}`;
|
|
551
|
-
lines.splice(insertionIndex, 0, newEntry);
|
|
552
|
-
const updatedMarkdown = lines.join(STRING_LITERALS.NEWLINE);
|
|
553
|
-
// WU-1352: Use centralized stringify for frontmatter
|
|
554
|
-
const updatedBacklog = `---\n${stringifyYAML(frontmatter, { lineWidth: -1 })}---\n${updatedMarkdown}`;
|
|
555
|
-
writeFileSync(backlogAbsolutePath, updatedBacklog, {
|
|
556
|
-
encoding: FILE_SYSTEM.UTF8,
|
|
557
|
-
});
|
|
558
|
-
console.log(`${LOG_PREFIX} ✅ Updated backlog.md in micro-worktree`);
|
|
559
|
-
return backlogRelativePath;
|
|
560
|
-
}
|
|
561
159
|
/**
|
|
562
160
|
* Get default assigned_to value from git config user.email (WU-1368)
|
|
563
161
|
* @returns {Promise<string>} User email or empty string if not configured
|
|
@@ -568,7 +166,7 @@ async function getDefaultAssignedTo() {
|
|
|
568
166
|
return email || '';
|
|
569
167
|
}
|
|
570
168
|
catch {
|
|
571
|
-
console.warn(`${LOG_PREFIX}
|
|
169
|
+
console.warn(`${LOG_PREFIX} git config user.email not set - assigned_to will be empty`);
|
|
572
170
|
return '';
|
|
573
171
|
}
|
|
574
172
|
}
|
|
@@ -690,17 +288,17 @@ async function main() {
|
|
|
690
288
|
// WU-1368: Get assigned_to from flag or git config user.email
|
|
691
289
|
const assignedTo = args.assignedTo || (await getDefaultAssignedTo());
|
|
692
290
|
if (!assignedTo) {
|
|
693
|
-
console.warn(`${LOG_PREFIX}
|
|
291
|
+
console.warn(`${LOG_PREFIX} No assigned_to set - WU will need manual assignment`);
|
|
694
292
|
}
|
|
695
293
|
const planSpecRef = args.plan ? getPlanProtocolRef(wuId) : undefined;
|
|
696
|
-
const
|
|
294
|
+
const mergedRefs = mergeSpecRefs(args.specRefs, planSpecRef);
|
|
697
295
|
// WU-1443: Apply resilient defaults so a plan-first WU doesn't immediately fail strict validation.
|
|
698
296
|
const effectiveType = args.type || DEFAULT_TYPE;
|
|
699
297
|
const resolvedNotes = typeof args.notes === 'string' && args.notes.trim().length > 0
|
|
700
298
|
? args.notes
|
|
701
299
|
: WU_CREATE_DEFAULTS.AUTO_NOTES_PLACEHOLDER;
|
|
702
300
|
if (resolvedNotes === WU_CREATE_DEFAULTS.AUTO_NOTES_PLACEHOLDER) {
|
|
703
|
-
console.warn(`${LOG_PREFIX}
|
|
301
|
+
console.warn(`${LOG_PREFIX} No --notes provided; using placeholder notes (edit before done).`);
|
|
704
302
|
}
|
|
705
303
|
const hasProvidedTests = hasAnyItems(args.testPathsManual) ||
|
|
706
304
|
hasAnyItems(args.testPathsUnit) ||
|
|
@@ -710,7 +308,7 @@ async function main() {
|
|
|
710
308
|
? [WU_CREATE_DEFAULTS.AUTO_MANUAL_TEST_PLACEHOLDER]
|
|
711
309
|
: args.testPathsManual;
|
|
712
310
|
if (canAutoAddManualTests) {
|
|
713
|
-
console.warn(`${LOG_PREFIX}
|
|
311
|
+
console.warn(`${LOG_PREFIX} No test paths provided; inserting a minimal manual test stub (add automated tests before code changes).`);
|
|
714
312
|
}
|
|
715
313
|
const createSpecValidation = validateCreateSpec({
|
|
716
314
|
id: wuId,
|
|
@@ -729,7 +327,7 @@ async function main() {
|
|
|
729
327
|
exposure: args.exposure,
|
|
730
328
|
userJourney: args.userJourney,
|
|
731
329
|
uiPairingWus: args.uiPairingWus,
|
|
732
|
-
specRefs:
|
|
330
|
+
specRefs: mergedRefs,
|
|
733
331
|
initiative: args.initiative,
|
|
734
332
|
phase: args.phase,
|
|
735
333
|
blockedBy: args.blockedBy,
|
|
@@ -742,11 +340,11 @@ async function main() {
|
|
|
742
340
|
});
|
|
743
341
|
if (!createSpecValidation.valid) {
|
|
744
342
|
const errorList = createSpecValidation.errors
|
|
745
|
-
.map((error) => `
|
|
343
|
+
.map((error) => ` - ${error}`)
|
|
746
344
|
.join(STRING_LITERALS.NEWLINE);
|
|
747
|
-
die(`${LOG_PREFIX}
|
|
345
|
+
die(`${LOG_PREFIX} Spec validation failed:\n\n${errorList}`);
|
|
748
346
|
}
|
|
749
|
-
console.log(`${LOG_PREFIX}
|
|
347
|
+
console.log(`${LOG_PREFIX} Spec validation passed`);
|
|
750
348
|
// WU-1530: Run spec lint BEFORE micro-worktree creation.
|
|
751
349
|
// Previously this ran inside createWUYamlInWorktree after worktree setup,
|
|
752
350
|
// meaning lint errors only appeared after a ~10s worktree creation.
|
|
@@ -768,7 +366,7 @@ async function main() {
|
|
|
768
366
|
exposure: args.exposure,
|
|
769
367
|
userJourney: args.userJourney,
|
|
770
368
|
uiPairingWus: args.uiPairingWus,
|
|
771
|
-
specRefs:
|
|
369
|
+
specRefs: mergedRefs,
|
|
772
370
|
initiative: args.initiative,
|
|
773
371
|
phase: args.phase,
|
|
774
372
|
blockedBy: args.blockedBy,
|
|
@@ -781,20 +379,20 @@ async function main() {
|
|
|
781
379
|
const preflightLint = lintWUSpec(preflightWU, { invariantsPath });
|
|
782
380
|
if (!preflightLint.valid) {
|
|
783
381
|
const formatted = formatLintErrors(preflightLint.errors);
|
|
784
|
-
die(`${LOG_PREFIX}
|
|
382
|
+
die(`${LOG_PREFIX} WU SPEC LINT FAILED:\n\n${formatted}\n` +
|
|
785
383
|
`Fix the issues above before creating this WU.`);
|
|
786
384
|
}
|
|
787
|
-
const specRefsList =
|
|
385
|
+
const specRefsList = mergedRefs;
|
|
788
386
|
const specRefsValidation = validateSpecRefs(specRefsList);
|
|
789
387
|
if (!specRefsValidation.valid) {
|
|
790
388
|
const errorList = specRefsValidation.errors
|
|
791
|
-
.map((error) => `
|
|
389
|
+
.map((error) => ` - ${error}`)
|
|
792
390
|
.join(STRING_LITERALS.NEWLINE);
|
|
793
|
-
die(`${LOG_PREFIX}
|
|
391
|
+
die(`${LOG_PREFIX} Spec reference validation failed:\n\n${errorList}`);
|
|
794
392
|
}
|
|
795
393
|
if (specRefsValidation.warnings.length > 0) {
|
|
796
394
|
for (const warning of specRefsValidation.warnings) {
|
|
797
|
-
console.warn(`${LOG_PREFIX}
|
|
395
|
+
console.warn(`${LOG_PREFIX} ${warning}`);
|
|
798
396
|
}
|
|
799
397
|
}
|
|
800
398
|
if (args.initiative) {
|
|
@@ -807,7 +405,7 @@ async function main() {
|
|
|
807
405
|
specRefs: specRefsList,
|
|
808
406
|
});
|
|
809
407
|
for (const warning of warnings) {
|
|
810
|
-
console.warn(`${LOG_PREFIX}
|
|
408
|
+
console.warn(`${LOG_PREFIX} ${warning}`);
|
|
811
409
|
}
|
|
812
410
|
}
|
|
813
411
|
}
|
|
@@ -841,7 +439,7 @@ async function main() {
|
|
|
841
439
|
userJourney: args.userJourney,
|
|
842
440
|
uiPairingWus: args.uiPairingWus,
|
|
843
441
|
// WU-2320: Spec references
|
|
844
|
-
specRefs:
|
|
442
|
+
specRefs: mergedRefs,
|
|
845
443
|
};
|
|
846
444
|
if (cloudCtx.skipMicroWorktree) {
|
|
847
445
|
// WU-1590: Cloud path - write and commit directly on current branch
|
|
@@ -893,7 +491,7 @@ async function main() {
|
|
|
893
491
|
}
|
|
894
492
|
}
|
|
895
493
|
}
|
|
896
|
-
console.log(`\n${LOG_PREFIX}
|
|
494
|
+
console.log(`\n${LOG_PREFIX} Transaction complete!`);
|
|
897
495
|
console.log(`\nWU ${wuId} created successfully:`);
|
|
898
496
|
console.log(` File: ${WU_PATHS.WU(wuId)}`);
|
|
899
497
|
console.log(` Lane: ${args.lane}`);
|