@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.
Files changed (49) hide show
  1. package/dist/agent-patterns-registry.js +4 -2
  2. package/dist/arg-parser.js +5 -0
  3. package/dist/constants/linter-constants.d.ts +1 -1
  4. package/dist/constants/linter-constants.js +1 -1
  5. package/dist/docs-path-validator.js +3 -3
  6. package/dist/domain/context.schemas.d.ts +3 -3
  7. package/dist/domain/orchestration.constants.d.ts +1 -1
  8. package/dist/domain/orchestration.constants.js +1 -1
  9. package/dist/domain/orchestration.types.d.ts +2 -2
  10. package/dist/domain/validation.schemas.d.ts +4 -4
  11. package/dist/hardcoded-strings.js +1 -1
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.js +5 -3
  14. package/dist/invariants/check-automated-tests.js +3 -1
  15. package/dist/lumenflow-config-schema.d.ts +5 -5
  16. package/dist/lumenflow-config-schema.js +5 -5
  17. package/dist/lumenflow-config.d.ts +1 -1
  18. package/dist/lumenflow-config.js +7 -7
  19. package/dist/micro-worktree.d.ts +2 -2
  20. package/dist/micro-worktree.js +112 -77
  21. package/dist/orchestration-rules.js +1 -1
  22. package/dist/prompt-linter.js +5 -5
  23. package/dist/spawn-escalation.d.ts +2 -2
  24. package/dist/spawn-escalation.js +7 -3
  25. package/dist/spawn-monitor.js +3 -1
  26. package/dist/state-doctor-core.d.ts +29 -1
  27. package/dist/state-doctor-core.js +142 -2
  28. package/dist/telemetry.js +3 -3
  29. package/dist/template-loader.js +6 -3
  30. package/dist/test-baseline.d.ts +2 -2
  31. package/dist/test-baseline.js +3 -2
  32. package/dist/wu-constants.d.ts +69 -57
  33. package/dist/wu-constants.js +70 -8
  34. package/dist/wu-create-validators.d.ts +7 -0
  35. package/dist/wu-create-validators.js +12 -2
  36. package/dist/wu-done-concurrent-merge.js +2 -2
  37. package/dist/wu-done-metadata.js +2 -2
  38. package/dist/wu-done-validation.js +2 -2
  39. package/dist/wu-done-worktree.js +6 -5
  40. package/dist/wu-events-cleanup.js +2 -5
  41. package/dist/wu-list.d.ts +92 -0
  42. package/dist/wu-list.js +177 -0
  43. package/dist/wu-paths.js +4 -4
  44. package/dist/wu-spawn-context.js +3 -2
  45. package/dist/wu-spawn-skills.js +8 -5
  46. package/dist/wu-spawn.js +4 -5
  47. package/package.json +2 -2
  48. package/dist/beacon-migration.d.ts +0 -56
  49. package/dist/beacon-migration.js +0 -101
@@ -127,7 +127,7 @@ const AGENT_TRIGGER_DESCRIPTIONS = {
127
127
  // No mandatory agent triggers for LumenFlow framework development.
128
128
  // Example for application-specific triggers:
129
129
  // 'security-auditor': 'supabase/migrations/**, auth/**, rls/**',
130
- // 'beacon-guardian': 'prompts/**, llm/**',
130
+ // 'llm-reviewer': 'prompts/**, llm/**',
131
131
  };
132
132
  /**
133
133
  * WU-1542: Build a formatted error message for mandatory agent enforcement failures.
@@ -17,14 +17,14 @@ import { resolve, dirname } from 'path';
17
17
  import { fileURLToPath } from 'url';
18
18
  import { glob } from 'glob';
19
19
  import yaml from 'yaml';
20
- import { EXIT_CODES, STRING_LITERALS } from './wu-constants.js';
20
+ import { EXIT_CODES, STRING_LITERALS, LUMENFLOW_PATHS } from './wu-constants.js';
21
21
  const __filename = fileURLToPath(import.meta.url);
22
22
  const __dirname = dirname(__filename);
23
23
  const ROOT_DIR = resolve(__dirname, '../..');
24
24
  // Default config path
25
25
  const DEFAULT_CONFIG_PATH = resolve(ROOT_DIR, 'config/prompts/linter.yml');
26
- // Telemetry cache path (for storing previous metrics)
27
- const METRICS_CACHE_PATH = resolve(ROOT_DIR, '.lumenflow/telemetry/prompt-metrics.json');
26
+ // Telemetry cache path (for storing previous metrics) - WU-1430: Use centralized constant
27
+ const METRICS_CACHE_PATH = resolve(ROOT_DIR, LUMENFLOW_PATHS.PROMPT_METRICS);
28
28
  /**
29
29
  * Load config from YAML file with fallback to defaults
30
30
  * @param {string} configPath - Path to config file (optional)
@@ -139,8 +139,8 @@ async function log(level, event, data, output = {}) {
139
139
  event,
140
140
  ...data,
141
141
  };
142
- // For CLI, write to .lumenflow/telemetry/prompt-lint.ndjson
143
- const ndjsonPath = resolve(ROOT_DIR, '.lumenflow/telemetry/prompt-lint.ndjson');
142
+ // For CLI, write to telemetry prompt lint file - WU-1430: Use centralized constant
143
+ const ndjsonPath = resolve(ROOT_DIR, LUMENFLOW_PATHS.PROMPT_LINT);
144
144
  const line = `${JSON.stringify(entry)}${STRING_LITERALS.NEWLINE}`;
145
145
  try {
146
146
  const dir = dirname(ndjsonPath);
@@ -52,7 +52,7 @@ export declare const SuggestedAction: Readonly<{
52
52
  *
53
53
  * @param {string} spawnId - ID of the stuck spawn
54
54
  * @param {Object} options - Options
55
- * @param {string} options.baseDir - Base directory for .beacon/
55
+ * @param {string} options.baseDir - Base directory for .lumenflow/
56
56
  * @param {boolean} [options.dryRun=false] - If true, returns signal without sending
57
57
  * @returns {Promise<EscalationResult>} Escalation result with signal details
58
58
  *
@@ -66,7 +66,7 @@ export declare const SuggestedAction: Readonly<{
66
66
  * console.log(`Signal sent: ${result.signalId}, action: ${result.signal.suggested_action}`);
67
67
  */
68
68
  export interface EscalateStuckSpawnOptions {
69
- /** Base directory for .beacon/ */
69
+ /** Base directory for .lumenflow/ */
70
70
  baseDir?: string;
71
71
  /** If true, return spec only without sending signal */
72
72
  dryRun?: boolean;
@@ -23,6 +23,7 @@ import path from 'node:path';
23
23
  import { SpawnRegistryStore } from './spawn-registry-store.js';
24
24
  import { SpawnStatus } from './spawn-registry-schema.js';
25
25
  import { RECOVERY_DIR_NAME } from './spawn-recovery.js';
26
+ import { LUMENFLOW_PATHS } from './wu-constants.js';
26
27
  let createSignal = null;
27
28
  try {
28
29
  const mod = await import('@lumenflow/memory/signal');
@@ -91,7 +92,8 @@ export const SuggestedAction = Object.freeze({
91
92
  * @returns {Promise<number>} Number of previous escalation attempts
92
93
  */
93
94
  async function countEscalationAttempts(baseDir, spawnId) {
94
- const recoveryDir = path.join(baseDir, '.beacon', RECOVERY_DIR_NAME);
95
+ // WU-1421: Use LUMENFLOW_PATHS.BASE for consistency
96
+ const recoveryDir = path.join(baseDir, LUMENFLOW_PATHS.BASE, RECOVERY_DIR_NAME);
95
97
  try {
96
98
  const files = await fs.readdir(recoveryDir);
97
99
  const spawnFiles = files.filter((f) => f.startsWith(`${spawnId}-`) && f.endsWith('.json'));
@@ -138,7 +140,8 @@ function determineEscalationLevel(attempts) {
138
140
  * @returns {Promise<AuditLogEntry|null>} Audit log entry or null if not found
139
141
  */
140
142
  async function findEscalationAuditLog(baseDir, spawnId) {
141
- const recoveryDir = path.join(baseDir, '.beacon', RECOVERY_DIR_NAME);
143
+ // WU-1421: Use LUMENFLOW_PATHS.BASE for consistency
144
+ const recoveryDir = path.join(baseDir, LUMENFLOW_PATHS.BASE, RECOVERY_DIR_NAME);
142
145
  try {
143
146
  const files = await fs.readdir(recoveryDir);
144
147
  // Filter files for this spawn ID, sorted by name (timestamp-based)
@@ -188,7 +191,8 @@ function buildSpawnFailureSignal(spawn, auditLog, attempts) {
188
191
  }
189
192
  export async function escalateStuckSpawn(spawnId, options = {}) {
190
193
  const { baseDir = process.cwd(), dryRun = false } = options;
191
- const registryDir = path.join(baseDir, '.beacon', 'state');
194
+ // WU-1421: Use LUMENFLOW_PATHS.STATE_DIR for consistency
195
+ const registryDir = path.join(baseDir, LUMENFLOW_PATHS.STATE_DIR);
192
196
  // Load spawn registry
193
197
  const store = new SpawnRegistryStore(registryDir);
194
198
  try {
@@ -26,6 +26,7 @@ import { SpawnStatus } from './spawn-registry-schema.js';
26
26
  import { isZombieLock, readLockMetadata } from './lane-lock.js';
27
27
  import { recoverStuckSpawn, RecoveryAction } from './spawn-recovery.js';
28
28
  import { escalateStuckSpawn, SPAWN_FAILURE_SIGNAL_TYPE, SuggestedAction, } from './spawn-escalation.js';
29
+ import { LUMENFLOW_PATHS } from './wu-constants.js';
29
30
  let loadSignals = null;
30
31
  let markSignalsAsRead = null;
31
32
  try {
@@ -163,7 +164,8 @@ export function detectStuckSpawns(spawns, thresholdMinutes = DEFAULT_THRESHOLD_M
163
164
  }
164
165
  export async function checkZombieLocks(options = {}) {
165
166
  const { baseDir = process.cwd() } = options;
166
- const locksDir = path.join(baseDir, '.beacon', 'locks');
167
+ // WU-1421: Use LUMENFLOW_PATHS.LOCKS_DIR (same as lane-lock.ts) for consistency
168
+ const locksDir = path.join(baseDir, LUMENFLOW_PATHS.LOCKS_DIR);
167
169
  const zombies = [];
168
170
  try {
169
171
  // Check if locks directory exists
@@ -1,10 +1,11 @@
1
1
  /**
2
- * State Doctor Core (WU-1209)
2
+ * State Doctor Core (WU-1209, WU-1420)
3
3
  *
4
4
  * Integrity checker for LumenFlow state that detects:
5
5
  * - Orphaned WUs (done status but no stamp)
6
6
  * - Dangling signals (reference non-existent WUs)
7
7
  * - Broken memory relationships (events for missing WU specs)
8
+ * - Status mismatches between WU YAML and state store (WU-1420)
8
9
  *
9
10
  * Inspired by Beads bd doctor command.
10
11
  *
@@ -27,6 +28,8 @@ export declare const ISSUE_TYPES: {
27
28
  readonly DANGLING_SIGNAL: "dangling_signal";
28
29
  /** Event references a WU that doesn't exist */
29
30
  readonly BROKEN_EVENT: "broken_event";
31
+ /** WU YAML status differs from state store derived status (WU-1420) */
32
+ readonly STATUS_MISMATCH: "status_mismatch";
30
33
  };
31
34
  /**
32
35
  * Issue severity levels
@@ -72,6 +75,25 @@ export interface MockEvent {
72
75
  wuId: string;
73
76
  type: string;
74
77
  timestamp?: string;
78
+ lane?: string;
79
+ title?: string;
80
+ reason?: string;
81
+ }
82
+ /**
83
+ * Event to emit for fixing status mismatches (WU-1420)
84
+ */
85
+ export interface EmitEventPayload {
86
+ wuId: string;
87
+ type: 'release' | 'complete';
88
+ reason?: string;
89
+ timestamp?: string;
90
+ }
91
+ /**
92
+ * Status mismatch details for fixing (WU-1420)
93
+ */
94
+ export interface StatusMismatchDetails {
95
+ yamlStatus: string;
96
+ derivedStatus: string;
75
97
  }
76
98
  /**
77
99
  * Dependencies for state doctor (injectable for testing)
@@ -91,6 +113,8 @@ export interface StateDoctorDeps {
91
113
  removeEvent?: (wuId: string) => Promise<void>;
92
114
  /** Create a stamp for a WU (for --fix) */
93
115
  createStamp?: (wuId: string, title: string) => Promise<void>;
116
+ /** Emit an event to fix status mismatch (WU-1420) */
117
+ emitEvent?: (event: EmitEventPayload) => Promise<void>;
94
118
  }
95
119
  /**
96
120
  * A detected issue in the state
@@ -110,6 +134,8 @@ export interface DiagnosisIssue {
110
134
  suggestion: string;
111
135
  /** Whether this issue can be auto-fixed */
112
136
  canAutoFix: boolean;
137
+ /** Status mismatch details for fixing (WU-1420) */
138
+ statusMismatch?: StatusMismatchDetails;
113
139
  }
114
140
  /**
115
141
  * A fix error that occurred during auto-repair
@@ -134,6 +160,8 @@ export interface DiagnosisSummary {
134
160
  danglingSignals: number;
135
161
  /** Number of broken events */
136
162
  brokenEvents: number;
163
+ /** Number of status mismatches (WU-1420) */
164
+ statusMismatches: number;
137
165
  /** Total number of issues */
138
166
  totalIssues: number;
139
167
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
- * State Doctor Core (WU-1209)
2
+ * State Doctor Core (WU-1209, WU-1420)
3
3
  *
4
4
  * Integrity checker for LumenFlow state that detects:
5
5
  * - Orphaned WUs (done status but no stamp)
6
6
  * - Dangling signals (reference non-existent WUs)
7
7
  * - Broken memory relationships (events for missing WU specs)
8
+ * - Status mismatches between WU YAML and state store (WU-1420)
8
9
  *
9
10
  * Inspired by Beads bd doctor command.
10
11
  *
@@ -27,6 +28,8 @@ export const ISSUE_TYPES = {
27
28
  DANGLING_SIGNAL: 'dangling_signal',
28
29
  /** Event references a WU that doesn't exist */
29
30
  BROKEN_EVENT: 'broken_event',
31
+ /** WU YAML status differs from state store derived status (WU-1420) */
32
+ STATUS_MISMATCH: 'status_mismatch',
30
33
  };
31
34
  /**
32
35
  * Issue severity levels
@@ -39,6 +42,86 @@ export const ISSUE_SEVERITY = {
39
42
  /** Informational findings */
40
43
  INFO: 'info',
41
44
  };
45
+ /**
46
+ * Derive WU status from events (WU-1420)
47
+ *
48
+ * Replays events to determine the current derived status:
49
+ * - claim/create -> in_progress
50
+ * - release -> ready
51
+ * - complete -> done
52
+ * - block -> blocked
53
+ * - unblock -> in_progress
54
+ *
55
+ * @param events - All events for this WU
56
+ * @returns Derived status or undefined if no events
57
+ */
58
+ function deriveStatusFromEvents(events) {
59
+ if (events.length === 0) {
60
+ return undefined;
61
+ }
62
+ let status = undefined;
63
+ for (const event of events) {
64
+ switch (event.type) {
65
+ case 'claim':
66
+ case 'create':
67
+ status = 'in_progress';
68
+ break;
69
+ case 'release':
70
+ status = 'ready';
71
+ break;
72
+ case 'complete':
73
+ status = 'done';
74
+ break;
75
+ case 'block':
76
+ status = 'blocked';
77
+ break;
78
+ case 'unblock':
79
+ status = 'in_progress';
80
+ break;
81
+ // checkpoint doesn't change status
82
+ }
83
+ }
84
+ return status;
85
+ }
86
+ /**
87
+ * Detect status mismatches between WU YAML and state store (WU-1420)
88
+ *
89
+ * Compares the status field in WU YAML against the derived status from events.
90
+ * Only reports issues for WUs that have events in the state store.
91
+ */
92
+ function detectStatusMismatches(wus, events) {
93
+ const issues = [];
94
+ // Group events by WU ID
95
+ const eventsByWu = new Map();
96
+ for (const event of events) {
97
+ const existing = eventsByWu.get(event.wuId) || [];
98
+ existing.push(event);
99
+ eventsByWu.set(event.wuId, existing);
100
+ }
101
+ for (const wu of wus) {
102
+ const wuEvents = eventsByWu.get(wu.id);
103
+ if (!wuEvents || wuEvents.length === 0) {
104
+ // No events for this WU - nothing to compare
105
+ continue;
106
+ }
107
+ const derivedStatus = deriveStatusFromEvents(wuEvents);
108
+ if (derivedStatus && derivedStatus !== wu.status) {
109
+ issues.push({
110
+ type: ISSUE_TYPES.STATUS_MISMATCH,
111
+ severity: ISSUE_SEVERITY.WARNING,
112
+ wuId: wu.id,
113
+ description: `WU ${wu.id} YAML status is '${wu.status}' but state store says '${derivedStatus}'`,
114
+ suggestion: `Emit corrective event using: pnpm state:doctor --fix`,
115
+ canAutoFix: true,
116
+ statusMismatch: {
117
+ yamlStatus: wu.status,
118
+ derivedStatus,
119
+ },
120
+ });
121
+ }
122
+ }
123
+ return issues;
124
+ }
42
125
  /**
43
126
  * Detect orphaned WUs (done status but no stamp)
44
127
  */
@@ -114,6 +197,7 @@ function calculateSummary(issues) {
114
197
  let orphanedWUs = 0;
115
198
  let danglingSignals = 0;
116
199
  let brokenEvents = 0;
200
+ let statusMismatches = 0;
117
201
  for (const issue of issues) {
118
202
  switch (issue.type) {
119
203
  case ISSUE_TYPES.ORPHANED_WU:
@@ -125,15 +209,42 @@ function calculateSummary(issues) {
125
209
  case ISSUE_TYPES.BROKEN_EVENT:
126
210
  brokenEvents++;
127
211
  break;
212
+ case ISSUE_TYPES.STATUS_MISMATCH:
213
+ statusMismatches++;
214
+ break;
128
215
  }
129
216
  }
130
217
  return {
131
218
  orphanedWUs,
132
219
  danglingSignals,
133
220
  brokenEvents,
221
+ statusMismatches,
134
222
  totalIssues: issues.length,
135
223
  };
136
224
  }
225
+ /**
226
+ * Determine the corrective event type for a status mismatch (WU-1420)
227
+ *
228
+ * When YAML says X but state says Y, emit event to transition state to X:
229
+ * - YAML=ready, state=in_progress -> emit release
230
+ * - YAML=done, state=in_progress -> emit complete
231
+ * - YAML=in_progress, state=ready -> cannot fix (would need claim with lane/title)
232
+ * - YAML=in_progress, state=done -> cannot fix (cannot un-complete)
233
+ */
234
+ function getCorrectiveEventType(yamlStatus, derivedStatus) {
235
+ // Transition from in_progress to ready: emit release
236
+ if (yamlStatus === 'ready' && derivedStatus === 'in_progress') {
237
+ return 'release';
238
+ }
239
+ // Transition from in_progress to done: emit complete
240
+ if (yamlStatus === 'done' && derivedStatus === 'in_progress') {
241
+ return 'complete';
242
+ }
243
+ // Other transitions are not auto-fixable by emitting events
244
+ // - ready -> in_progress would need claim (requires lane/title context)
245
+ // - done -> in_progress would need to revert complete (not supported)
246
+ return null;
247
+ }
137
248
  /**
138
249
  * Attempt to fix an issue
139
250
  */
@@ -159,6 +270,29 @@ async function fixIssue(issue, deps, wus) {
159
270
  return { fixed: true };
160
271
  }
161
272
  return { fixed: false, error: 'No createStamp function provided' };
273
+ case ISSUE_TYPES.STATUS_MISMATCH: {
274
+ if (!deps.emitEvent) {
275
+ return { fixed: false, error: 'No emitEvent function provided' };
276
+ }
277
+ if (!issue.wuId || !issue.statusMismatch) {
278
+ return { fixed: false, error: 'Missing WU ID or status mismatch details' };
279
+ }
280
+ const { yamlStatus, derivedStatus } = issue.statusMismatch;
281
+ const eventType = getCorrectiveEventType(yamlStatus, derivedStatus);
282
+ if (!eventType) {
283
+ return {
284
+ fixed: false,
285
+ error: `Cannot auto-fix: transition from '${derivedStatus}' to '${yamlStatus}' requires manual intervention`,
286
+ };
287
+ }
288
+ await deps.emitEvent({
289
+ wuId: issue.wuId,
290
+ type: eventType,
291
+ reason: `state:doctor --fix: reconciling state store with YAML status '${yamlStatus}'`,
292
+ timestamp: new Date().toISOString(),
293
+ });
294
+ return { fixed: true };
295
+ }
162
296
  default:
163
297
  return { fixed: false, error: `Unknown issue type: ${issue.type}` };
164
298
  }
@@ -229,7 +363,13 @@ export async function diagnoseState(_baseDir, deps, options = {}) {
229
363
  const orphanedWUissues = detectOrphanedWUs(wus, stampIds);
230
364
  const danglingSignalIssues = detectDanglingSignals(signals, wuIds);
231
365
  const brokenEventIssues = detectBrokenEvents(events, wuIds);
232
- const issues = [...orphanedWUissues, ...danglingSignalIssues, ...brokenEventIssues];
366
+ const statusMismatchIssues = detectStatusMismatches(wus, events);
367
+ const issues = [
368
+ ...orphanedWUissues,
369
+ ...danglingSignalIssues,
370
+ ...brokenEventIssues,
371
+ ...statusMismatchIssues,
372
+ ];
233
373
  const summary = calculateSummary(issues);
234
374
  // Initialize result
235
375
  const result = {
package/dist/telemetry.js CHANGED
@@ -8,11 +8,11 @@
8
8
  import { appendFileSync, mkdirSync, existsSync } from 'node:fs';
9
9
  import { execSync } from 'node:child_process';
10
10
  import path from 'node:path';
11
- import { BEACON_PATHS, FILE_EXTENSIONS, STDIO, STRING_LITERALS, } from './wu-constants.js';
12
- const TELEMETRY_DIR = BEACON_PATHS.TELEMETRY;
11
+ import { LUMENFLOW_PATHS, FILE_EXTENSIONS, STDIO, STRING_LITERALS, } from './wu-constants.js';
12
+ const TELEMETRY_DIR = LUMENFLOW_PATHS.TELEMETRY;
13
13
  const GATES_LOG = `${TELEMETRY_DIR}/gates${FILE_EXTENSIONS.NDJSON}`;
14
14
  const LLM_CLASSIFICATION_LOG = `${TELEMETRY_DIR}/llm-classification${FILE_EXTENSIONS.NDJSON}`;
15
- const FLOW_LOG = BEACON_PATHS.FLOW_LOG;
15
+ const FLOW_LOG = LUMENFLOW_PATHS.FLOW_LOG;
16
16
  /**
17
17
  * Ensure telemetry directory exists
18
18
  */
@@ -19,8 +19,10 @@ import { join } from 'node:path';
19
19
  import matter from 'gray-matter';
20
20
  import yaml from 'yaml';
21
21
  import { createError, ErrorCodes } from './error-handler.js';
22
- const MANIFEST_PATH = '.lumenflow/templates/manifest.yaml';
23
- const TEMPLATES_DIR = '.lumenflow/templates/spawn-prompt';
22
+ import { LUMENFLOW_PATHS } from './wu-constants.js';
23
+ /** WU-1430: Use centralized constants for template paths */
24
+ const MANIFEST_PATH = LUMENFLOW_PATHS.TEMPLATE_MANIFEST;
25
+ const TEMPLATES_DIR = LUMENFLOW_PATHS.SPAWN_PROMPT_DIR;
24
26
  /**
25
27
  * Validate a template entry from the manifest.
26
28
  * @throws If entry is missing required fields
@@ -172,7 +174,8 @@ export function loadTemplate(templatePath) {
172
174
  export function loadTemplatesWithOverrides(baseDir, clientName) {
173
175
  const templates = new Map();
174
176
  const baseTemplatesDir = join(baseDir, TEMPLATES_DIR);
175
- const clientTemplatesDir = join(baseDir, `.lumenflow/templates.${clientName}/spawn-prompt`);
177
+ // WU-1430: Construct client templates path from constants
178
+ const clientTemplatesDir = join(baseDir, `${LUMENFLOW_PATHS.BASE}/templates.${clientName}/spawn-prompt`);
176
179
  // Load base templates first
177
180
  if (existsSync(baseTemplatesDir)) {
178
181
  loadTemplatesFromDir(baseTemplatesDir, templates);
@@ -13,8 +13,8 @@
13
13
  * @see https://lumenflow.dev/reference/test-ratchet/
14
14
  */
15
15
  import { z } from 'zod';
16
- /** Default path for the test baseline file */
17
- export declare const DEFAULT_BASELINE_PATH = ".lumenflow/test-baseline.json";
16
+ /** Default path for the test baseline file (WU-1430: Use centralized constant) */
17
+ export declare const DEFAULT_BASELINE_PATH: string;
18
18
  /** Environment variable to override baseline path */
19
19
  export declare const BASELINE_PATH_ENV = "LUMENFLOW_TEST_BASELINE";
20
20
  /** Current schema version */
@@ -14,11 +14,12 @@
14
14
  */
15
15
  import { z } from 'zod';
16
16
  import { parseISO, isValid } from 'date-fns';
17
+ import { LUMENFLOW_PATHS } from './wu-constants.js';
17
18
  // ============================================================================
18
19
  // Constants
19
20
  // ============================================================================
20
- /** Default path for the test baseline file */
21
- export const DEFAULT_BASELINE_PATH = '.lumenflow/test-baseline.json';
21
+ /** Default path for the test baseline file (WU-1430: Use centralized constant) */
22
+ export const DEFAULT_BASELINE_PATH = LUMENFLOW_PATHS.TEST_BASELINE;
22
23
  /** Environment variable to override baseline path */
23
24
  export const BASELINE_PATH_ENV = 'LUMENFLOW_TEST_BASELINE';
24
25
  /** Current schema version */
@@ -1021,6 +1021,7 @@ export declare const DIRECTORIES: {
1021
1021
  WORKTREES: string;
1022
1022
  AI: string;
1023
1023
  CLAUDE: string;
1024
+ CLAUDE_HOOKS: string;
1024
1025
  DOCS: string;
1025
1026
  PACKAGES: string;
1026
1027
  TOOLS: string;
@@ -1030,6 +1031,43 @@ export declare const DIRECTORIES: {
1030
1031
  BACKLOG_PATH: string;
1031
1032
  STATUS_PATH: string;
1032
1033
  };
1034
+ /**
1035
+ * Claude Code hook script constants (WU-1394)
1036
+ *
1037
+ * Centralized constants for Claude Code enforcement and recovery hooks.
1038
+ * Used by enforcement-generator.ts, enforcement-sync.ts, and init.ts.
1039
+ *
1040
+ * @see packages/@lumenflow/cli/src/hooks/enforcement-generator.ts
1041
+ * @see packages/@lumenflow/cli/src/hooks/enforcement-sync.ts
1042
+ */
1043
+ export declare const CLAUDE_HOOKS: {
1044
+ /** Hook script filenames */
1045
+ readonly SCRIPTS: {
1046
+ readonly ENFORCE_WORKTREE: "enforce-worktree.sh";
1047
+ readonly REQUIRE_WU: "require-wu.sh";
1048
+ readonly WARN_INCOMPLETE: "warn-incomplete.sh";
1049
+ readonly PRE_COMPACT_CHECKPOINT: "pre-compact-checkpoint.sh";
1050
+ readonly SESSION_START_RECOVERY: "session-start-recovery.sh";
1051
+ };
1052
+ /** Hook command path prefix (uses Claude Code's $CLAUDE_PROJECT_DIR variable) */
1053
+ readonly PATH_PREFIX: "$CLAUDE_PROJECT_DIR/.claude/hooks";
1054
+ /** Hook matchers for settings.json */
1055
+ readonly MATCHERS: {
1056
+ readonly ALL: ".*";
1057
+ readonly WRITE_EDIT: "Write|Edit";
1058
+ readonly COMPACT: "compact";
1059
+ readonly RESUME: "resume";
1060
+ readonly CLEAR: "clear";
1061
+ };
1062
+ /** Template paths (relative to templates directory) */
1063
+ readonly TEMPLATES: {
1064
+ readonly SETTINGS: "vendors/claude/.claude/settings.json.template";
1065
+ readonly PRE_COMPACT: "vendors/claude/.claude/hooks/pre-compact-checkpoint.sh";
1066
+ readonly SESSION_START: "vendors/claude/.claude/hooks/session-start-recovery.sh";
1067
+ };
1068
+ };
1069
+ /** Build full hook command path from script name */
1070
+ export declare const getHookCommand: (scriptName: string) => string;
1033
1071
  /**
1034
1072
  * ESLint cache strategy values
1035
1073
  */
@@ -1125,7 +1163,7 @@ export declare const GIT_COMMAND_STRINGS: {
1125
1163
  export declare const PATH_PATTERNS: {
1126
1164
  /** Matches WU YAML paths in both legacy and current locations */
1127
1165
  WU_YAML: RegExp;
1128
- /** Matches stamp file paths (supports both .beacon and .lumenflow for migration) */
1166
+ /** Matches stamp file paths */
1129
1167
  STAMP: RegExp;
1130
1168
  };
1131
1169
  /**
@@ -1240,8 +1278,6 @@ export declare const LOCK_DIR_NAME = ".lumenflow-locks";
1240
1278
  *
1241
1279
  * Centralized paths for .lumenflow directory structure to eliminate hardcoded strings.
1242
1280
  * Used by telemetry, agent-session, agent-incidents, memory, and commands-logger modules.
1243
- *
1244
- * @since 1.4.0 Renamed from BEACON_PATHS (WU-1075)
1245
1281
  */
1246
1282
  export declare const LUMENFLOW_PATHS: {
1247
1283
  /** Base directory for all LumenFlow runtime data */
@@ -1250,6 +1286,8 @@ export declare const LUMENFLOW_PATHS: {
1250
1286
  STATE_DIR: string;
1251
1287
  /** Stamp directory (WU completion markers) */
1252
1288
  STAMPS_DIR: string;
1289
+ /** Archive directory for old WU events (WU-1430) */
1290
+ ARCHIVE_DIR: string;
1253
1291
  /** Merge lock file (runtime coordination, WU-1747) */
1254
1292
  MERGE_LOCK: string;
1255
1293
  /** Base telemetry directory */
@@ -1280,60 +1318,30 @@ export declare const LUMENFLOW_PATHS: {
1280
1318
  LOCKS_DIR: string;
1281
1319
  /** Force bypass audit log */
1282
1320
  FORCE_BYPASSES: string;
1283
- /**
1284
- * WU-1174: Runtime lock directory for merge/cleanup locks
1285
- *
1286
- * These locks are transient and should NOT be created in the main checkout
1287
- * because wu:done runs from main. Using os.tmpdir() ensures:
1288
- * 1. Locks don't pollute the git working tree
1289
- * 2. Locks work across processes on the same machine
1290
- * 3. No "Working tree is not clean" errors if process crashes
1291
- *
1292
- * Note: Lane locks still use LOCKS_DIR (.lumenflow/locks) because they need
1293
- * to persist across sessions and be visible to other agents.
1294
- */
1295
- LOCK_DIR: string;
1296
- };
1297
- /**
1298
- * @deprecated Use LUMENFLOW_PATHS instead. Will be removed in v2.0.
1299
- */
1300
- export declare const BEACON_PATHS: {
1301
- /** Base directory for all LumenFlow runtime data */
1302
- BASE: string;
1303
- /** WU state store directory */
1304
- STATE_DIR: string;
1305
- /** Stamp directory (WU completion markers) */
1306
- STAMPS_DIR: string;
1307
- /** Merge lock file (runtime coordination, WU-1747) */
1308
- MERGE_LOCK: string;
1309
- /** Base telemetry directory */
1310
- TELEMETRY: string;
1311
- /** Flow log file (WU flow events) */
1312
- FLOW_LOG: string;
1313
- /** Agent sessions directory */
1314
- SESSIONS: string;
1315
- /** Agent incidents directory */
1316
- INCIDENTS: string;
1317
- /** Git commands log file */
1318
- COMMANDS_LOG: string;
1319
- /** Memory layer directory */
1320
- MEMORY_DIR: string;
1321
- /** Memory layer JSONL file */
1322
- MEMORY_JSONL: string;
1323
- /** Audit log for tool calls */
1324
- AUDIT_LOG: string;
1325
- /** Feedback drafts directory */
1326
- FEEDBACK_DRAFTS: string;
1327
- /** Feedback index file */
1328
- FEEDBACK_INDEX: string;
1329
- /** Current session file */
1330
- SESSION_CURRENT: string;
1331
- /** WU events log */
1332
- WU_EVENTS: string;
1333
- /** Lock files directory (lane locks - persisted) */
1334
- LOCKS_DIR: string;
1335
- /** Force bypass audit log */
1336
- FORCE_BYPASSES: string;
1321
+ /** Test baseline file for ratchet pattern (WU-1430) */
1322
+ TEST_BASELINE: string;
1323
+ /** Templates directory (WU-1430) */
1324
+ TEMPLATES_DIR: string;
1325
+ /** Spawn prompt templates (WU-1430) */
1326
+ SPAWN_PROMPT_DIR: string;
1327
+ /** Template manifest file (WU-1430) */
1328
+ TEMPLATE_MANIFEST: string;
1329
+ /** Skills directory for agent skills (WU-1430) */
1330
+ SKILLS_DIR: string;
1331
+ /** Agents directory for agent definitions (WU-1430) */
1332
+ AGENTS_DIR: string;
1333
+ /** Methodology log for spawn telemetry (WU-1430) */
1334
+ METHODOLOGY_LOG: string;
1335
+ /** Prompt metrics cache (WU-1430) */
1336
+ PROMPT_METRICS: string;
1337
+ /** Prompt lint results (WU-1430) */
1338
+ PROMPT_LINT: string;
1339
+ /** Recovery markers directory (WU-1430) */
1340
+ RECOVERY_DIR: string;
1341
+ /** Checkpoints directory (WU-1430) */
1342
+ CHECKPOINTS_DIR: string;
1343
+ /** Cache directory under user home (WU-1430) */
1344
+ HOME_CACHE: string;
1337
1345
  /**
1338
1346
  * WU-1174: Runtime lock directory for merge/cleanup locks
1339
1347
  *
@@ -1405,6 +1413,10 @@ export declare const PATH_LITERALS: {
1405
1413
  PLAN_FILE_SUFFIX: string;
1406
1414
  /** Trailing slash regex pattern */
1407
1415
  TRAILING_SLASH_REGEX: RegExp;
1416
+ /** .lumenflow path prefix for internal path detection (WU-1430) */
1417
+ LUMENFLOW_PREFIX: string;
1418
+ /** Current directory prefix for repo-internal paths (WU-1430) */
1419
+ CURRENT_DIR_PREFIX: string;
1408
1420
  };
1409
1421
  /**
1410
1422
  * Slice lengths for path operations