@lumenflow/core 2.9.0 → 2.11.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/dist/agent-patterns-registry.js +4 -2
- package/dist/arg-parser.js +5 -0
- package/dist/constants/linter-constants.d.ts +1 -1
- package/dist/constants/linter-constants.js +1 -1
- package/dist/docs-path-validator.js +3 -3
- package/dist/domain/context.schemas.d.ts +3 -3
- package/dist/domain/orchestration.constants.d.ts +1 -1
- package/dist/domain/orchestration.constants.js +1 -1
- package/dist/domain/orchestration.types.d.ts +2 -2
- package/dist/domain/validation.schemas.d.ts +4 -4
- package/dist/hardcoded-strings.js +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5 -3
- package/dist/invariants/check-automated-tests.js +3 -1
- package/dist/lumenflow-config-schema.d.ts +5 -5
- package/dist/lumenflow-config-schema.js +5 -5
- package/dist/lumenflow-config.d.ts +1 -1
- package/dist/lumenflow-config.js +7 -7
- package/dist/micro-worktree.d.ts +2 -2
- package/dist/micro-worktree.js +112 -77
- package/dist/orchestration-rules.js +1 -1
- package/dist/prompt-linter.js +5 -5
- package/dist/spawn-escalation.d.ts +2 -2
- package/dist/spawn-escalation.js +7 -3
- package/dist/spawn-monitor.js +3 -1
- package/dist/state-doctor-core.d.ts +29 -1
- package/dist/state-doctor-core.js +142 -2
- package/dist/telemetry.js +3 -3
- package/dist/template-loader.js +6 -3
- package/dist/test-baseline.d.ts +2 -2
- package/dist/test-baseline.js +3 -2
- package/dist/wu-constants.d.ts +69 -57
- package/dist/wu-constants.js +70 -8
- package/dist/wu-create-validators.d.ts +7 -0
- package/dist/wu-create-validators.js +12 -2
- package/dist/wu-done-concurrent-merge.js +2 -2
- package/dist/wu-done-metadata.js +2 -2
- package/dist/wu-done-validation.js +2 -2
- package/dist/wu-done-worktree.js +6 -5
- package/dist/wu-events-cleanup.js +2 -5
- package/dist/wu-list.d.ts +92 -0
- package/dist/wu-list.js +177 -0
- package/dist/wu-paths.js +4 -4
- package/dist/wu-spawn-context.js +3 -2
- package/dist/wu-spawn-skills.js +8 -5
- package/dist/wu-spawn.js +4 -5
- package/package.json +2 -2
- package/dist/beacon-migration.d.ts +0 -56
- package/dist/beacon-migration.js +0 -101
package/dist/wu-constants.js
CHANGED
|
@@ -1066,6 +1066,7 @@ export const DIRECTORIES = {
|
|
|
1066
1066
|
WORKTREES: 'worktrees/',
|
|
1067
1067
|
AI: 'ai/',
|
|
1068
1068
|
CLAUDE: '.claude/',
|
|
1069
|
+
CLAUDE_HOOKS: '.claude/hooks/',
|
|
1069
1070
|
DOCS: 'docs/',
|
|
1070
1071
|
PACKAGES: 'packages/',
|
|
1071
1072
|
TOOLS: 'tools/',
|
|
@@ -1076,6 +1077,43 @@ export const DIRECTORIES = {
|
|
|
1076
1077
|
BACKLOG_PATH: 'docs/04-operations/tasks/backlog.md',
|
|
1077
1078
|
STATUS_PATH: 'docs/04-operations/tasks/status.md',
|
|
1078
1079
|
};
|
|
1080
|
+
/**
|
|
1081
|
+
* Claude Code hook script constants (WU-1394)
|
|
1082
|
+
*
|
|
1083
|
+
* Centralized constants for Claude Code enforcement and recovery hooks.
|
|
1084
|
+
* Used by enforcement-generator.ts, enforcement-sync.ts, and init.ts.
|
|
1085
|
+
*
|
|
1086
|
+
* @see packages/@lumenflow/cli/src/hooks/enforcement-generator.ts
|
|
1087
|
+
* @see packages/@lumenflow/cli/src/hooks/enforcement-sync.ts
|
|
1088
|
+
*/
|
|
1089
|
+
export const CLAUDE_HOOKS = {
|
|
1090
|
+
/** Hook script filenames */
|
|
1091
|
+
SCRIPTS: {
|
|
1092
|
+
ENFORCE_WORKTREE: 'enforce-worktree.sh',
|
|
1093
|
+
REQUIRE_WU: 'require-wu.sh',
|
|
1094
|
+
WARN_INCOMPLETE: 'warn-incomplete.sh',
|
|
1095
|
+
PRE_COMPACT_CHECKPOINT: 'pre-compact-checkpoint.sh',
|
|
1096
|
+
SESSION_START_RECOVERY: 'session-start-recovery.sh',
|
|
1097
|
+
},
|
|
1098
|
+
/** Hook command path prefix (uses Claude Code's $CLAUDE_PROJECT_DIR variable) */
|
|
1099
|
+
PATH_PREFIX: '$CLAUDE_PROJECT_DIR/.claude/hooks',
|
|
1100
|
+
/** Hook matchers for settings.json */
|
|
1101
|
+
MATCHERS: {
|
|
1102
|
+
ALL: '.*',
|
|
1103
|
+
WRITE_EDIT: 'Write|Edit',
|
|
1104
|
+
COMPACT: 'compact',
|
|
1105
|
+
RESUME: 'resume',
|
|
1106
|
+
CLEAR: 'clear',
|
|
1107
|
+
},
|
|
1108
|
+
/** Template paths (relative to templates directory) */
|
|
1109
|
+
TEMPLATES: {
|
|
1110
|
+
SETTINGS: 'vendors/claude/.claude/settings.json.template',
|
|
1111
|
+
PRE_COMPACT: 'vendors/claude/.claude/hooks/pre-compact-checkpoint.sh',
|
|
1112
|
+
SESSION_START: 'vendors/claude/.claude/hooks/session-start-recovery.sh',
|
|
1113
|
+
},
|
|
1114
|
+
};
|
|
1115
|
+
/** Build full hook command path from script name */
|
|
1116
|
+
export const getHookCommand = (scriptName) => `${CLAUDE_HOOKS.PATH_PREFIX}/${scriptName}`;
|
|
1079
1117
|
/**
|
|
1080
1118
|
* ESLint cache strategy values
|
|
1081
1119
|
*/
|
|
@@ -1171,8 +1209,8 @@ export const GIT_COMMAND_STRINGS = {
|
|
|
1171
1209
|
export const PATH_PATTERNS = {
|
|
1172
1210
|
/** Matches WU YAML paths in both legacy and current locations */
|
|
1173
1211
|
WU_YAML: /(?:memory-bank|docs\/04-operations)\/tasks\/wu\/(WU-\d+)\.ya?ml$/i,
|
|
1174
|
-
/** Matches stamp file paths
|
|
1175
|
-
STAMP: /\.
|
|
1212
|
+
/** Matches stamp file paths */
|
|
1213
|
+
STAMP: /\.lumenflow\/stamps\/(WU-\d+)\.done$/i,
|
|
1176
1214
|
};
|
|
1177
1215
|
/**
|
|
1178
1216
|
* Common shell commands
|
|
@@ -1286,8 +1324,6 @@ export const LOCK_DIR_NAME = '.lumenflow-locks';
|
|
|
1286
1324
|
*
|
|
1287
1325
|
* Centralized paths for .lumenflow directory structure to eliminate hardcoded strings.
|
|
1288
1326
|
* Used by telemetry, agent-session, agent-incidents, memory, and commands-logger modules.
|
|
1289
|
-
*
|
|
1290
|
-
* @since 1.4.0 Renamed from BEACON_PATHS (WU-1075)
|
|
1291
1327
|
*/
|
|
1292
1328
|
export const LUMENFLOW_PATHS = {
|
|
1293
1329
|
/** Base directory for all LumenFlow runtime data */
|
|
@@ -1296,6 +1332,8 @@ export const LUMENFLOW_PATHS = {
|
|
|
1296
1332
|
STATE_DIR: '.lumenflow/state',
|
|
1297
1333
|
/** Stamp directory (WU completion markers) */
|
|
1298
1334
|
STAMPS_DIR: '.lumenflow/stamps',
|
|
1335
|
+
/** Archive directory for old WU events (WU-1430) */
|
|
1336
|
+
ARCHIVE_DIR: '.lumenflow/archive',
|
|
1299
1337
|
/** Merge lock file (runtime coordination, WU-1747) */
|
|
1300
1338
|
MERGE_LOCK: '.lumenflow/merge.lock',
|
|
1301
1339
|
/** Base telemetry directory */
|
|
@@ -1326,6 +1364,30 @@ export const LUMENFLOW_PATHS = {
|
|
|
1326
1364
|
LOCKS_DIR: '.lumenflow/locks',
|
|
1327
1365
|
/** Force bypass audit log */
|
|
1328
1366
|
FORCE_BYPASSES: '.lumenflow/force-bypasses.log',
|
|
1367
|
+
/** Test baseline file for ratchet pattern (WU-1430) */
|
|
1368
|
+
TEST_BASELINE: '.lumenflow/test-baseline.json',
|
|
1369
|
+
/** Templates directory (WU-1430) */
|
|
1370
|
+
TEMPLATES_DIR: '.lumenflow/templates',
|
|
1371
|
+
/** Spawn prompt templates (WU-1430) */
|
|
1372
|
+
SPAWN_PROMPT_DIR: '.lumenflow/templates/spawn-prompt',
|
|
1373
|
+
/** Template manifest file (WU-1430) */
|
|
1374
|
+
TEMPLATE_MANIFEST: '.lumenflow/templates/manifest.yaml',
|
|
1375
|
+
/** Skills directory for agent skills (WU-1430) */
|
|
1376
|
+
SKILLS_DIR: '.lumenflow/skills',
|
|
1377
|
+
/** Agents directory for agent definitions (WU-1430) */
|
|
1378
|
+
AGENTS_DIR: '.lumenflow/agents',
|
|
1379
|
+
/** Methodology log for spawn telemetry (WU-1430) */
|
|
1380
|
+
METHODOLOGY_LOG: '.lumenflow/telemetry/methodology.ndjson',
|
|
1381
|
+
/** Prompt metrics cache (WU-1430) */
|
|
1382
|
+
PROMPT_METRICS: '.lumenflow/telemetry/prompt-metrics.json',
|
|
1383
|
+
/** Prompt lint results (WU-1430) */
|
|
1384
|
+
PROMPT_LINT: '.lumenflow/telemetry/prompt-lint.ndjson',
|
|
1385
|
+
/** Recovery markers directory (WU-1430) */
|
|
1386
|
+
RECOVERY_DIR: '.lumenflow/recovery',
|
|
1387
|
+
/** Checkpoints directory (WU-1430) */
|
|
1388
|
+
CHECKPOINTS_DIR: '.lumenflow/checkpoints',
|
|
1389
|
+
/** Cache directory under user home (WU-1430) */
|
|
1390
|
+
HOME_CACHE: 'cache',
|
|
1329
1391
|
/**
|
|
1330
1392
|
* WU-1174: Runtime lock directory for merge/cleanup locks
|
|
1331
1393
|
*
|
|
@@ -1340,10 +1402,6 @@ export const LUMENFLOW_PATHS = {
|
|
|
1340
1402
|
*/
|
|
1341
1403
|
LOCK_DIR: path.join(tmpdir(), LOCK_DIR_NAME),
|
|
1342
1404
|
};
|
|
1343
|
-
/**
|
|
1344
|
-
* @deprecated Use LUMENFLOW_PATHS instead. Will be removed in v2.0.
|
|
1345
|
-
*/
|
|
1346
|
-
export const BEACON_PATHS = LUMENFLOW_PATHS;
|
|
1347
1405
|
/**
|
|
1348
1406
|
* File extensions
|
|
1349
1407
|
*
|
|
@@ -1401,6 +1459,10 @@ export const PATH_LITERALS = {
|
|
|
1401
1459
|
PLAN_FILE_SUFFIX: '-plan.md',
|
|
1402
1460
|
/** Trailing slash regex pattern */
|
|
1403
1461
|
TRAILING_SLASH_REGEX: /\/+$/,
|
|
1462
|
+
/** .lumenflow path prefix for internal path detection (WU-1430) */
|
|
1463
|
+
LUMENFLOW_PREFIX: '.lumenflow/',
|
|
1464
|
+
/** Current directory prefix for repo-internal paths (WU-1430) */
|
|
1465
|
+
CURRENT_DIR_PREFIX: './',
|
|
1404
1466
|
};
|
|
1405
1467
|
/**
|
|
1406
1468
|
* Slice lengths for path operations
|
|
@@ -86,6 +86,13 @@ export declare function validateSpecRefs(specRefs: string[]): {
|
|
|
86
86
|
* @returns {boolean} True if any spec_ref is an external path
|
|
87
87
|
*/
|
|
88
88
|
export declare function hasExternalSpecRefs(specRefs: string[]): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* WU-1429: Check if spec_refs is non-empty
|
|
91
|
+
*
|
|
92
|
+
* @param {string[]|undefined} specRefs - Array of spec reference paths
|
|
93
|
+
* @returns {boolean} True if spec_refs contains at least one entry
|
|
94
|
+
*/
|
|
95
|
+
export declare function hasSpecRefs(specRefs: string[] | undefined): boolean;
|
|
89
96
|
/**
|
|
90
97
|
* WU-1062: Normalize all spec_refs paths
|
|
91
98
|
*
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
* No external library exists for LumenFlow lane inference validation.
|
|
16
16
|
*/
|
|
17
17
|
import { isExternalPath, normalizeSpecRef } from './lumenflow-home.js';
|
|
18
|
+
import { PATH_LITERALS } from './wu-constants.js';
|
|
18
19
|
/** Confidence threshold for showing suggestion (percentage) */
|
|
19
20
|
const CONFIDENCE_THRESHOLD_LOW = 30;
|
|
20
|
-
/** Prefixes that indicate repo-internal paths (WU-1069) */
|
|
21
|
-
const REPO_INTERNAL_PREFIXES = [
|
|
21
|
+
/** Prefixes that indicate repo-internal paths (WU-1069, WU-1430: Use centralized constants) */
|
|
22
|
+
const REPO_INTERNAL_PREFIXES = [PATH_LITERALS.CURRENT_DIR_PREFIX, PATH_LITERALS.LUMENFLOW_PREFIX];
|
|
22
23
|
/**
|
|
23
24
|
* WU-1069: Check if a path is a repo-internal path that should be rejected
|
|
24
25
|
*
|
|
@@ -186,6 +187,15 @@ export function hasExternalSpecRefs(specRefs) {
|
|
|
186
187
|
}
|
|
187
188
|
return specRefs.some((ref) => isExternalPath(ref));
|
|
188
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* WU-1429: Check if spec_refs is non-empty
|
|
192
|
+
*
|
|
193
|
+
* @param {string[]|undefined} specRefs - Array of spec reference paths
|
|
194
|
+
* @returns {boolean} True if spec_refs contains at least one entry
|
|
195
|
+
*/
|
|
196
|
+
export function hasSpecRefs(specRefs) {
|
|
197
|
+
return Array.isArray(specRefs) && specRefs.length > 0;
|
|
198
|
+
}
|
|
189
199
|
/**
|
|
190
200
|
* WU-1062: Normalize all spec_refs paths
|
|
191
201
|
*
|
|
@@ -18,7 +18,7 @@ import { validateWUEvent } from './wu-state-schema.js';
|
|
|
18
18
|
import { generateBacklog, generateStatus } from './backlog-generator.js';
|
|
19
19
|
import { getStateStoreDirFromBacklog } from './wu-paths.js';
|
|
20
20
|
import { getGitForCwd } from './git-adapter.js';
|
|
21
|
-
import { REMOTES, BRANCHES,
|
|
21
|
+
import { REMOTES, BRANCHES, LUMENFLOW_PATHS } from './wu-constants.js';
|
|
22
22
|
/**
|
|
23
23
|
* Creates a unique key for an event to detect duplicates.
|
|
24
24
|
* Events are considered identical if they have the same type, wuId, and timestamp.
|
|
@@ -46,7 +46,7 @@ export async function fetchMainEventsContent() {
|
|
|
46
46
|
console.warn('[wu-done] Warning: Could not fetch latest main, using cached version');
|
|
47
47
|
}
|
|
48
48
|
// Try to read wu-events.jsonl from origin/main
|
|
49
|
-
const eventsPath = `${
|
|
49
|
+
const eventsPath = `${LUMENFLOW_PATHS.STATE_DIR}/${WU_EVENTS_FILE_NAME}`;
|
|
50
50
|
const content = await git.raw(['show', `${REMOTES.ORIGIN}/${BRANCHES.MAIN}:${eventsPath}`]);
|
|
51
51
|
return content;
|
|
52
52
|
}
|
package/dist/wu-done-metadata.js
CHANGED
|
@@ -11,7 +11,7 @@ import { moveWUToDoneBacklog } from './wu-backlog-updater.js';
|
|
|
11
11
|
import { createStamp } from './stamp-utils.js';
|
|
12
12
|
import { WU_EVENTS_FILE_NAME } from './wu-state-store.js';
|
|
13
13
|
import { computeWUYAMLContent, computeStatusContentFromMergedState, computeBacklogContent, computeWUEventsContentAfterComplete, computeStampContent, } from './wu-transaction-collectors.js';
|
|
14
|
-
import { DEFAULTS, LOG_PREFIX, EMOJI, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS,
|
|
14
|
+
import { DEFAULTS, LOG_PREFIX, EMOJI, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, LUMENFLOW_PATHS, } from './wu-constants.js';
|
|
15
15
|
import { applyExposureDefaults } from './wu-done-validation.js';
|
|
16
16
|
import { createFileNotFoundError, createValidationError } from './wu-done-errors.js';
|
|
17
17
|
import { writeWU } from './wu-yaml.js';
|
|
@@ -170,7 +170,7 @@ export async function stageAndFormatMetadata({ id, wuPath, statusPath, backlogPa
|
|
|
170
170
|
// The singleton git adapter captures cwd at import time, which is wrong after process.chdir()
|
|
171
171
|
const gitCwd = getGitForCwd();
|
|
172
172
|
// Stage files
|
|
173
|
-
const wuEventsPath = path.join(
|
|
173
|
+
const wuEventsPath = path.join(LUMENFLOW_PATHS.STATE_DIR, WU_EVENTS_FILE_NAME);
|
|
174
174
|
const filesToStage = [wuPath, statusPath, backlogPath, stampsDir];
|
|
175
175
|
if (existsSync(wuEventsPath)) {
|
|
176
176
|
filesToStage.push(wuEventsPath);
|
|
@@ -261,7 +261,7 @@ const DOCS_ONLY_ALLOWED_PATTERNS = [
|
|
|
261
261
|
/^memory-bank\//i,
|
|
262
262
|
/^docs\//i,
|
|
263
263
|
/\.md$/i,
|
|
264
|
-
/^\.
|
|
264
|
+
/^\.lumenflow\/stamps\//i,
|
|
265
265
|
/^\.claude\//i,
|
|
266
266
|
/^ai\//i,
|
|
267
267
|
/^README\.md$/i,
|
|
@@ -333,7 +333,7 @@ Allowed paths for documentation WUs:
|
|
|
333
333
|
- ai/
|
|
334
334
|
- .claude/
|
|
335
335
|
- memory-bank/
|
|
336
|
-
- .lumenflow/stamps/
|
|
336
|
+
- .lumenflow/stamps/
|
|
337
337
|
- *.md files
|
|
338
338
|
|
|
339
339
|
After fixing, retry: pnpm wu:done --id ${id}
|
package/dist/wu-done-worktree.js
CHANGED
|
@@ -24,7 +24,7 @@ import { generateCommitMessage, collectMetadataToTransaction, stageAndFormatMeta
|
|
|
24
24
|
import { getGitForCwd } from './git-adapter.js';
|
|
25
25
|
import { readWU, writeWU } from './wu-yaml.js';
|
|
26
26
|
import { WU_PATHS } from './wu-paths.js';
|
|
27
|
-
import { BRANCHES, REMOTES, THRESHOLDS, LOG_PREFIX, EMOJI, COMMIT_FORMATS, BOX, STRING_LITERALS, WU_STATUS, GIT_COMMANDS, GIT_FLAGS, } from './wu-constants.js';
|
|
27
|
+
import { BRANCHES, REMOTES, THRESHOLDS, LOG_PREFIX, EMOJI, COMMIT_FORMATS, BOX, STRING_LITERALS, WU_STATUS, GIT_COMMANDS, GIT_FLAGS, LUMENFLOW_PATHS, } from './wu-constants.js';
|
|
28
28
|
import { RECOVERY, REBASE, PREFLIGHT, MERGE } from './wu-done-messages.js';
|
|
29
29
|
import { getDriftLevel, DRIFT_LEVELS } from './branch-drift.js';
|
|
30
30
|
import { createError, ErrorCodes } from './error-handler.js';
|
|
@@ -242,7 +242,7 @@ export async function executeWorktreeCompletion(context) {
|
|
|
242
242
|
// WU-2310: Capture file state before transaction commit
|
|
243
243
|
// This allows rollback if git commit fails AFTER files are written
|
|
244
244
|
// Note: We use the relative paths since we're already chdir'd into the worktree
|
|
245
|
-
const workingEventsPath = path.join(
|
|
245
|
+
const workingEventsPath = path.join(LUMENFLOW_PATHS.STATE_DIR, WU_EVENTS_FILE_NAME);
|
|
246
246
|
const pathsToSnapshot = [
|
|
247
247
|
workingWUPath,
|
|
248
248
|
workingStatusPath,
|
|
@@ -580,13 +580,14 @@ export async function checkBranchDrift(branch) {
|
|
|
580
580
|
* that would break if paths are rearranged.
|
|
581
581
|
*/
|
|
582
582
|
const APPEND_ONLY_FILES = [
|
|
583
|
-
// State store events file (append-only by design)
|
|
584
|
-
path.join(
|
|
583
|
+
// State store events file (append-only by design) - WU-1430: Use centralized constant
|
|
584
|
+
path.join(LUMENFLOW_PATHS.STATE_DIR, WU_EVENTS_FILE_NAME),
|
|
585
585
|
// Status and backlog are generated from state store but may conflict during rebase
|
|
586
586
|
WU_PATHS.STATUS(),
|
|
587
587
|
WU_PATHS.BACKLOG(),
|
|
588
588
|
];
|
|
589
|
-
|
|
589
|
+
// WU-1430: Use centralized constant
|
|
590
|
+
const WU_EVENTS_PATH = path.join(LUMENFLOW_PATHS.STATE_DIR, WU_EVENTS_FILE_NAME);
|
|
590
591
|
function normalizeEventForKey(event) {
|
|
591
592
|
const normalized = {};
|
|
592
593
|
for (const key of Object.keys(event).sort()) {
|
|
@@ -29,12 +29,9 @@ import { WUStateStore, WU_EVENTS_FILE_NAME } from './wu-state-store.js';
|
|
|
29
29
|
*/
|
|
30
30
|
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Import centralized path constants (WU-1430)
|
|
33
33
|
*/
|
|
34
|
-
|
|
35
|
-
STATE_DIR: '.lumenflow/state',
|
|
36
|
-
ARCHIVE_DIR: '.lumenflow/archive',
|
|
37
|
-
};
|
|
34
|
+
import { LUMENFLOW_PATHS } from './wu-constants.js';
|
|
38
35
|
/**
|
|
39
36
|
* Default event archival configuration
|
|
40
37
|
*/
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU List Helper (WU-1411)
|
|
3
|
+
*
|
|
4
|
+
* Provides a consistent list of WUs by merging WUStateStore with YAML metadata.
|
|
5
|
+
* MCP server and other tools can use this instead of duplicating listing logic.
|
|
6
|
+
*
|
|
7
|
+
* Key behaviors:
|
|
8
|
+
* - Status from state store takes precedence over YAML status (more current)
|
|
9
|
+
* - Falls back to YAML status when WU is not in state store
|
|
10
|
+
* - Supports filtering by status and lane
|
|
11
|
+
* - Gracefully handles errors (missing files, invalid YAML)
|
|
12
|
+
*
|
|
13
|
+
* @module wu-list
|
|
14
|
+
* @see {@link ./wu-state-store.ts} - State store for runtime status
|
|
15
|
+
* @see {@link ./wu-yaml.ts} - YAML operations
|
|
16
|
+
* @see {@link ./wu-paths.ts} - Path utilities
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* WU list entry returned by listWUs.
|
|
20
|
+
* Contains essential fields for display and filtering.
|
|
21
|
+
*/
|
|
22
|
+
export interface WUListEntry {
|
|
23
|
+
/** WU identifier (e.g., 'WU-100') */
|
|
24
|
+
id: string;
|
|
25
|
+
/** Short title describing the work */
|
|
26
|
+
title: string;
|
|
27
|
+
/** Lane assignment (e.g., 'Framework: Core') */
|
|
28
|
+
lane: string;
|
|
29
|
+
/** Current status (from state store or YAML) */
|
|
30
|
+
status: string;
|
|
31
|
+
/** Work type (feature, bug, documentation, etc.) */
|
|
32
|
+
type: string;
|
|
33
|
+
/** Priority level (P0-P3) */
|
|
34
|
+
priority: string;
|
|
35
|
+
/** Parent initiative reference (optional) */
|
|
36
|
+
initiative?: string;
|
|
37
|
+
/** Phase number within parent initiative (optional) */
|
|
38
|
+
phase?: number;
|
|
39
|
+
/** Creation date (YYYY-MM-DD) */
|
|
40
|
+
created?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Options for listWUs function.
|
|
44
|
+
*/
|
|
45
|
+
export interface ListWUsOptions {
|
|
46
|
+
/** Project root directory (default: cwd) */
|
|
47
|
+
projectRoot?: string;
|
|
48
|
+
/** Filter by status (e.g., 'in_progress', 'blocked', 'done') */
|
|
49
|
+
status?: string;
|
|
50
|
+
/** Filter by lane (e.g., 'Framework: Core') */
|
|
51
|
+
lane?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Direct path to WU directory (overrides config-based path).
|
|
54
|
+
* Useful for testing with virtual filesystems.
|
|
55
|
+
*/
|
|
56
|
+
wuDir?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Direct path to state directory (overrides config-based path).
|
|
59
|
+
* Useful for testing with virtual filesystems.
|
|
60
|
+
*/
|
|
61
|
+
stateDir?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Lists WUs by merging state store status with YAML metadata.
|
|
65
|
+
*
|
|
66
|
+
* The state store contains the most current status (claim, block, complete events),
|
|
67
|
+
* while YAML files contain the full metadata (title, lane, type, etc.).
|
|
68
|
+
*
|
|
69
|
+
* Status precedence:
|
|
70
|
+
* 1. State store status (if WU has events)
|
|
71
|
+
* 2. YAML status (fallback if not in state store)
|
|
72
|
+
*
|
|
73
|
+
* @param options - Listing options
|
|
74
|
+
* @returns Array of WU list entries
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // List all WUs
|
|
78
|
+
* const allWUs = await listWUs();
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Filter by status
|
|
82
|
+
* const inProgress = await listWUs({ status: 'in_progress' });
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* // Filter by lane
|
|
86
|
+
* const coreWUs = await listWUs({ lane: 'Framework: Core' });
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Filter by both
|
|
90
|
+
* const blockedCore = await listWUs({ status: 'blocked', lane: 'Framework: Core' });
|
|
91
|
+
*/
|
|
92
|
+
export declare function listWUs(options?: ListWUsOptions): Promise<WUListEntry[]>;
|
package/dist/wu-list.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU List Helper (WU-1411)
|
|
3
|
+
*
|
|
4
|
+
* Provides a consistent list of WUs by merging WUStateStore with YAML metadata.
|
|
5
|
+
* MCP server and other tools can use this instead of duplicating listing logic.
|
|
6
|
+
*
|
|
7
|
+
* Key behaviors:
|
|
8
|
+
* - Status from state store takes precedence over YAML status (more current)
|
|
9
|
+
* - Falls back to YAML status when WU is not in state store
|
|
10
|
+
* - Supports filtering by status and lane
|
|
11
|
+
* - Gracefully handles errors (missing files, invalid YAML)
|
|
12
|
+
*
|
|
13
|
+
* @module wu-list
|
|
14
|
+
* @see {@link ./wu-state-store.ts} - State store for runtime status
|
|
15
|
+
* @see {@link ./wu-yaml.ts} - YAML operations
|
|
16
|
+
* @see {@link ./wu-paths.ts} - Path utilities
|
|
17
|
+
*/
|
|
18
|
+
import { readdir } from 'node:fs/promises';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { parse as parseYaml } from 'yaml';
|
|
21
|
+
import { readFile } from 'node:fs/promises';
|
|
22
|
+
import { WUStateStore } from './wu-state-store.js';
|
|
23
|
+
import { getConfig } from './lumenflow-config.js';
|
|
24
|
+
/**
|
|
25
|
+
* Lists WUs by merging state store status with YAML metadata.
|
|
26
|
+
*
|
|
27
|
+
* The state store contains the most current status (claim, block, complete events),
|
|
28
|
+
* while YAML files contain the full metadata (title, lane, type, etc.).
|
|
29
|
+
*
|
|
30
|
+
* Status precedence:
|
|
31
|
+
* 1. State store status (if WU has events)
|
|
32
|
+
* 2. YAML status (fallback if not in state store)
|
|
33
|
+
*
|
|
34
|
+
* @param options - Listing options
|
|
35
|
+
* @returns Array of WU list entries
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // List all WUs
|
|
39
|
+
* const allWUs = await listWUs();
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // Filter by status
|
|
43
|
+
* const inProgress = await listWUs({ status: 'in_progress' });
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Filter by lane
|
|
47
|
+
* const coreWUs = await listWUs({ lane: 'Framework: Core' });
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Filter by both
|
|
51
|
+
* const blockedCore = await listWUs({ status: 'blocked', lane: 'Framework: Core' });
|
|
52
|
+
*/
|
|
53
|
+
export async function listWUs(options = {}) {
|
|
54
|
+
const { projectRoot = process.cwd(), status: filterStatus, lane: filterLane, wuDir: wuDirOverride, stateDir: stateDirOverride, } = options;
|
|
55
|
+
// Determine paths: use overrides if provided, otherwise use config
|
|
56
|
+
let wuDir;
|
|
57
|
+
let stateDir;
|
|
58
|
+
if (wuDirOverride && stateDirOverride) {
|
|
59
|
+
// Direct paths provided (e.g., for testing)
|
|
60
|
+
wuDir = wuDirOverride;
|
|
61
|
+
stateDir = stateDirOverride;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// Get configuration for paths
|
|
65
|
+
const config = getConfig({ projectRoot });
|
|
66
|
+
wuDir = wuDirOverride ?? join(projectRoot, config.directories.wuDir);
|
|
67
|
+
stateDir = stateDirOverride ?? join(projectRoot, config.state.stateDir);
|
|
68
|
+
}
|
|
69
|
+
// Load state store for runtime statuses
|
|
70
|
+
const stateMap = await loadStateStore(stateDir);
|
|
71
|
+
// Read all WU YAML files
|
|
72
|
+
const wuEntries = await readWUYamlFiles(wuDir);
|
|
73
|
+
// Merge state store status with YAML data
|
|
74
|
+
const entries = [];
|
|
75
|
+
for (const yamlData of wuEntries) {
|
|
76
|
+
// Skip invalid entries
|
|
77
|
+
if (!yamlData.id || typeof yamlData.id !== 'string') {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const wuId = yamlData.id;
|
|
81
|
+
// Get status: state store takes precedence
|
|
82
|
+
const stateEntry = stateMap.get(wuId);
|
|
83
|
+
const yamlStatus = typeof yamlData.status === 'string' ? yamlData.status : 'ready';
|
|
84
|
+
const status = stateEntry?.status ?? yamlStatus;
|
|
85
|
+
// Get lane: prefer state store (more current), fall back to YAML
|
|
86
|
+
const yamlLane = typeof yamlData.lane === 'string' ? yamlData.lane : '';
|
|
87
|
+
const lane = stateEntry?.lane ?? yamlLane;
|
|
88
|
+
// Extract string fields with type guards
|
|
89
|
+
const yamlTitle = typeof yamlData.title === 'string' ? yamlData.title : '';
|
|
90
|
+
const yamlType = typeof yamlData.type === 'string' ? yamlData.type : 'feature';
|
|
91
|
+
const yamlPriority = typeof yamlData.priority === 'string' ? yamlData.priority : 'P2';
|
|
92
|
+
// Build entry
|
|
93
|
+
const entry = {
|
|
94
|
+
id: wuId,
|
|
95
|
+
title: stateEntry?.title ?? yamlTitle,
|
|
96
|
+
lane,
|
|
97
|
+
status,
|
|
98
|
+
type: yamlType,
|
|
99
|
+
priority: yamlPriority,
|
|
100
|
+
};
|
|
101
|
+
// Add optional fields if present with type guards
|
|
102
|
+
if (typeof yamlData.initiative === 'string') {
|
|
103
|
+
entry.initiative = yamlData.initiative;
|
|
104
|
+
}
|
|
105
|
+
if (typeof yamlData.phase === 'number') {
|
|
106
|
+
entry.phase = yamlData.phase;
|
|
107
|
+
}
|
|
108
|
+
if (typeof yamlData.created === 'string') {
|
|
109
|
+
entry.created = yamlData.created;
|
|
110
|
+
}
|
|
111
|
+
// Apply filters
|
|
112
|
+
if (filterStatus && status !== filterStatus) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (filterLane && lane !== filterLane) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
entries.push(entry);
|
|
119
|
+
}
|
|
120
|
+
return entries;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Loads state store and returns a map of WU ID to state entry.
|
|
124
|
+
* Returns empty map on errors (graceful degradation).
|
|
125
|
+
*/
|
|
126
|
+
async function loadStateStore(stateDir) {
|
|
127
|
+
const map = new Map();
|
|
128
|
+
try {
|
|
129
|
+
const store = new WUStateStore(stateDir);
|
|
130
|
+
await store.load();
|
|
131
|
+
// Collect all known statuses
|
|
132
|
+
const statuses = ['in_progress', 'blocked', 'done', 'ready'];
|
|
133
|
+
for (const status of statuses) {
|
|
134
|
+
const wuIds = store.getByStatus(status);
|
|
135
|
+
for (const wuId of wuIds) {
|
|
136
|
+
const state = store.getWUState(wuId);
|
|
137
|
+
if (state) {
|
|
138
|
+
map.set(wuId, state);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Graceful degradation: return empty map on errors
|
|
145
|
+
}
|
|
146
|
+
return map;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Reads all WU YAML files from the WU directory.
|
|
150
|
+
* Skips invalid files (graceful degradation).
|
|
151
|
+
*/
|
|
152
|
+
async function readWUYamlFiles(wuDir) {
|
|
153
|
+
const entries = [];
|
|
154
|
+
try {
|
|
155
|
+
const files = await readdir(wuDir);
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
// Only process WU-*.yaml files
|
|
158
|
+
if (!file.startsWith('WU-') || !file.endsWith('.yaml')) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const content = await readFile(join(wuDir, file), 'utf-8');
|
|
163
|
+
const parsed = parseYaml(content);
|
|
164
|
+
if (parsed && typeof parsed === 'object') {
|
|
165
|
+
entries.push(parsed);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Skip invalid YAML files
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Directory doesn't exist or can't be read
|
|
175
|
+
}
|
|
176
|
+
return entries;
|
|
177
|
+
}
|
package/dist/wu-paths.js
CHANGED
|
@@ -45,7 +45,7 @@ export function resolveRepoRoot(absolutePath, depth) {
|
|
|
45
45
|
export function getStateStoreDirFromBacklog(backlogPath) {
|
|
46
46
|
const config = getConfig();
|
|
47
47
|
const repoRoot = resolveRepoRoot(backlogPath, PATH_DEPTHS.BACKLOG);
|
|
48
|
-
return path.join(repoRoot, config.
|
|
48
|
+
return path.join(repoRoot, config.state.stateDir);
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* Create WU paths object with configurable base paths
|
|
@@ -82,18 +82,18 @@ export function createWuPaths(options = {}) {
|
|
|
82
82
|
* Get path to stamps directory
|
|
83
83
|
* @returns Path to stamps directory
|
|
84
84
|
*/
|
|
85
|
-
STAMPS_DIR: () => config.
|
|
85
|
+
STAMPS_DIR: () => config.state.stampsDir,
|
|
86
86
|
/**
|
|
87
87
|
* Get path to WU done stamp file
|
|
88
88
|
* @param id - WU ID (e.g., 'WU-123')
|
|
89
89
|
* @returns Path to stamp file
|
|
90
90
|
*/
|
|
91
|
-
STAMP: (id) => path.join(config.
|
|
91
|
+
STAMP: (id) => path.join(config.state.stampsDir, `${id}.done`),
|
|
92
92
|
/**
|
|
93
93
|
* Get path to state directory
|
|
94
94
|
* @returns Path to state directory
|
|
95
95
|
*/
|
|
96
|
-
STATE_DIR: () => config.
|
|
96
|
+
STATE_DIR: () => config.state.stateDir,
|
|
97
97
|
/**
|
|
98
98
|
* Get path to initiatives directory
|
|
99
99
|
* @returns Path to initiatives directory
|
package/dist/wu-spawn-context.js
CHANGED
|
@@ -22,15 +22,16 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { existsSync, statSync } from 'node:fs';
|
|
24
24
|
import path from 'node:path';
|
|
25
|
+
import { LUMENFLOW_PATHS } from './wu-constants.js';
|
|
25
26
|
/**
|
|
26
27
|
* Default maximum context size in bytes (4KB)
|
|
27
28
|
*/
|
|
28
29
|
const DEFAULT_MAX_SIZE = 4096;
|
|
29
30
|
/**
|
|
30
|
-
* Memory layer paths
|
|
31
|
+
* Memory layer paths (WU-1430: Use centralized constants)
|
|
31
32
|
*/
|
|
32
33
|
const MEMORY_PATHS = {
|
|
33
|
-
MEMORY_DIR:
|
|
34
|
+
MEMORY_DIR: LUMENFLOW_PATHS.MEMORY_DIR,
|
|
34
35
|
MEMORY_FILE: 'memory.jsonl',
|
|
35
36
|
};
|
|
36
37
|
/**
|
package/dist/wu-spawn-skills.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { LUMENFLOW_PATHS } from './wu-constants.js';
|
|
3
|
+
/** WU-1430: Compose known skills directories from centralized constants */
|
|
2
4
|
const KNOWN_SKILLS_DIRS = [
|
|
3
|
-
|
|
5
|
+
LUMENFLOW_PATHS.SKILLS_DIR,
|
|
4
6
|
'.claude/skills',
|
|
5
7
|
'.codex/skills',
|
|
6
8
|
'.gemini/skills',
|
|
7
9
|
];
|
|
10
|
+
/** WU-1430: Compose known agents directories from centralized constants */
|
|
8
11
|
const KNOWN_AGENTS_DIRS = [
|
|
9
|
-
|
|
12
|
+
LUMENFLOW_PATHS.AGENTS_DIR,
|
|
10
13
|
'.claude/agents',
|
|
11
14
|
'.codex/agents',
|
|
12
15
|
'.gemini/agents',
|
|
@@ -30,7 +33,7 @@ const CONTEXT_HINTS = {
|
|
|
30
33
|
tddWorkflow: '- `tdd-workflow` — TDD is mandatory for feature/enhancement WUs',
|
|
31
34
|
bugClassification: '- `bug-classification` — Bug severity assessment',
|
|
32
35
|
lumenflowGates: '- `lumenflow-gates` — Tooling often affects gates',
|
|
33
|
-
|
|
36
|
+
llmCompliance: '- `llm-compliance` — Intelligence lane requires LLM validation',
|
|
34
37
|
promptManagement: '- `prompt-management` — For prompt template work',
|
|
35
38
|
frontendDesign: '- `frontend-design` — For UI component work',
|
|
36
39
|
};
|
|
@@ -38,7 +41,7 @@ const ADDITIONAL_SKILLS_TABLE = `| Skill | Use When |
|
|
|
38
41
|
|-------|----------|
|
|
39
42
|
| lumenflow-gates | Gates fail, debugging format/lint/typecheck errors |
|
|
40
43
|
| bug-classification | Bug discovered mid-WU, need priority classification |
|
|
41
|
-
|
|
|
44
|
+
| llm-compliance | Code touches LLM, prompts, classification |
|
|
42
45
|
| prompt-management | Working with prompt templates, golden datasets |
|
|
43
46
|
| frontend-design | Building UI components, pages |
|
|
44
47
|
| initiative-management | Multi-phase projects, INIT-XXX coordination |
|
|
@@ -159,7 +162,7 @@ export function generateSkillsSelectionSection(doc, config, clientName) {
|
|
|
159
162
|
contextHints.push(CONTEXT_HINTS.lumenflowGates);
|
|
160
163
|
}
|
|
161
164
|
if (laneParent === 'Intelligence') {
|
|
162
|
-
contextHints.push(CONTEXT_HINTS.
|
|
165
|
+
contextHints.push(CONTEXT_HINTS.llmCompliance);
|
|
163
166
|
contextHints.push(CONTEXT_HINTS.promptManagement);
|
|
164
167
|
}
|
|
165
168
|
if (laneParent === 'Experience') {
|