@lumenflow/core 1.5.0 → 2.0.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 (74) hide show
  1. package/README.md +325 -1
  2. package/dist/adapters/context-adapters.d.ts +90 -0
  3. package/dist/adapters/context-adapters.js +99 -0
  4. package/dist/adapters/index.d.ts +14 -0
  5. package/dist/adapters/index.js +18 -0
  6. package/dist/adapters/recovery-adapters.d.ts +40 -0
  7. package/dist/adapters/recovery-adapters.js +43 -0
  8. package/dist/adapters/validation-adapters.d.ts +52 -0
  9. package/dist/adapters/validation-adapters.js +59 -0
  10. package/dist/context/context-computer.d.ts +46 -0
  11. package/dist/context/context-computer.js +125 -0
  12. package/dist/context/git-state-reader.d.ts +51 -0
  13. package/dist/context/git-state-reader.js +61 -0
  14. package/dist/context/index.d.ts +17 -0
  15. package/dist/context/index.js +17 -0
  16. package/dist/context/location-resolver.d.ts +48 -0
  17. package/dist/context/location-resolver.js +175 -0
  18. package/dist/context/wu-state-reader.d.ts +37 -0
  19. package/dist/context/wu-state-reader.js +76 -0
  20. package/dist/context-di.d.ts +184 -0
  21. package/dist/context-di.js +178 -0
  22. package/dist/context-validation-integration.d.ts +77 -0
  23. package/dist/context-validation-integration.js +157 -0
  24. package/dist/domain/context.schemas.d.ts +147 -0
  25. package/dist/domain/context.schemas.js +126 -0
  26. package/dist/domain/index.d.ts +14 -0
  27. package/dist/domain/index.js +18 -0
  28. package/dist/domain/recovery.schemas.d.ts +115 -0
  29. package/dist/domain/recovery.schemas.js +83 -0
  30. package/dist/domain/validation.schemas.d.ts +146 -0
  31. package/dist/domain/validation.schemas.js +114 -0
  32. package/dist/index.d.ts +17 -0
  33. package/dist/index.js +43 -0
  34. package/dist/lumenflow-config-schema.d.ts +41 -0
  35. package/dist/lumenflow-config-schema.js +37 -0
  36. package/dist/ports/context.ports.d.ts +135 -0
  37. package/dist/ports/context.ports.js +21 -0
  38. package/dist/ports/core-tools.ports.d.ts +266 -0
  39. package/dist/ports/core-tools.ports.js +21 -0
  40. package/dist/ports/git-validator.ports.d.ts +314 -0
  41. package/dist/ports/git-validator.ports.js +12 -0
  42. package/dist/ports/index.d.ts +20 -0
  43. package/dist/ports/index.js +20 -0
  44. package/dist/ports/recovery.ports.d.ts +58 -0
  45. package/dist/ports/recovery.ports.js +17 -0
  46. package/dist/ports/validation.ports.d.ts +74 -0
  47. package/dist/ports/validation.ports.js +17 -0
  48. package/dist/ports/wu-helpers.ports.d.ts +224 -0
  49. package/dist/ports/wu-helpers.ports.js +12 -0
  50. package/dist/recovery/index.d.ts +11 -0
  51. package/dist/recovery/index.js +11 -0
  52. package/dist/recovery/recovery-analyzer.d.ts +66 -0
  53. package/dist/recovery/recovery-analyzer.js +129 -0
  54. package/dist/usecases/analyze-recovery.usecase.d.ts +42 -0
  55. package/dist/usecases/analyze-recovery.usecase.js +45 -0
  56. package/dist/usecases/compute-context.usecase.d.ts +62 -0
  57. package/dist/usecases/compute-context.usecase.js +101 -0
  58. package/dist/usecases/index.d.ts +14 -0
  59. package/dist/usecases/index.js +18 -0
  60. package/dist/usecases/validate-command.usecase.d.ts +55 -0
  61. package/dist/usecases/validate-command.usecase.js +154 -0
  62. package/dist/validation/command-registry.d.ts +38 -0
  63. package/dist/validation/command-registry.js +229 -0
  64. package/dist/validation/index.d.ts +15 -0
  65. package/dist/validation/index.js +15 -0
  66. package/dist/validation/types.d.ts +135 -0
  67. package/dist/validation/types.js +11 -0
  68. package/dist/validation/validate-command.d.ts +27 -0
  69. package/dist/validation/validate-command.js +160 -0
  70. package/dist/wu-constants.d.ts +136 -0
  71. package/dist/wu-constants.js +124 -0
  72. package/dist/wu-helpers.d.ts +5 -1
  73. package/dist/wu-helpers.js +12 -1
  74. package/package.json +4 -2
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Validation Adapters
3
+ *
4
+ * WU-1094: INIT-002 Phase 2 - Implement adapters and dependency injection
5
+ *
6
+ * Concrete adapter implementations for validation-related port interfaces.
7
+ * These adapters wrap the existing implementation functions to conform
8
+ * to the port interfaces, enabling dependency injection.
9
+ *
10
+ * Adapters:
11
+ * - CommandRegistryAdapter - Implements ICommandRegistry
12
+ *
13
+ * @module adapters/validation-adapters
14
+ */
15
+ import type { ICommandRegistry, CommandDefinition, WuContext } from '../ports/validation.ports.js';
16
+ /**
17
+ * CommandRegistryAdapter
18
+ *
19
+ * Implements ICommandRegistry by delegating to the command registry functions.
20
+ * Provides access to command definitions and context-based validation.
21
+ *
22
+ * @example
23
+ * // Use default adapter
24
+ * const adapter = new CommandRegistryAdapter();
25
+ * const def = adapter.getCommandDefinition('wu:done');
26
+ *
27
+ * @example
28
+ * // Use as port interface
29
+ * const registry: ICommandRegistry = new CommandRegistryAdapter();
30
+ */
31
+ export declare class CommandRegistryAdapter implements ICommandRegistry {
32
+ /**
33
+ * Get command definition by name.
34
+ *
35
+ * @param command - Command name (e.g., 'wu:create', 'wu:done')
36
+ * @returns CommandDefinition or null if not found
37
+ */
38
+ getCommandDefinition(command: string): CommandDefinition | null;
39
+ /**
40
+ * Get all commands valid for the current context.
41
+ *
42
+ * @param context - Current WU context
43
+ * @returns Array of valid CommandDefinitions
44
+ */
45
+ getValidCommandsForContext(context: WuContext): CommandDefinition[];
46
+ /**
47
+ * Get all registered command definitions.
48
+ *
49
+ * @returns Array of all CommandDefinitions
50
+ */
51
+ getAllCommands(): CommandDefinition[];
52
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Validation Adapters
3
+ *
4
+ * WU-1094: INIT-002 Phase 2 - Implement adapters and dependency injection
5
+ *
6
+ * Concrete adapter implementations for validation-related port interfaces.
7
+ * These adapters wrap the existing implementation functions to conform
8
+ * to the port interfaces, enabling dependency injection.
9
+ *
10
+ * Adapters:
11
+ * - CommandRegistryAdapter - Implements ICommandRegistry
12
+ *
13
+ * @module adapters/validation-adapters
14
+ */
15
+ // Import existing implementations
16
+ import { COMMAND_REGISTRY, getCommandDefinition, getValidCommandsForContext, } from '../validation/command-registry.js';
17
+ /**
18
+ * CommandRegistryAdapter
19
+ *
20
+ * Implements ICommandRegistry by delegating to the command registry functions.
21
+ * Provides access to command definitions and context-based validation.
22
+ *
23
+ * @example
24
+ * // Use default adapter
25
+ * const adapter = new CommandRegistryAdapter();
26
+ * const def = adapter.getCommandDefinition('wu:done');
27
+ *
28
+ * @example
29
+ * // Use as port interface
30
+ * const registry: ICommandRegistry = new CommandRegistryAdapter();
31
+ */
32
+ export class CommandRegistryAdapter {
33
+ /**
34
+ * Get command definition by name.
35
+ *
36
+ * @param command - Command name (e.g., 'wu:create', 'wu:done')
37
+ * @returns CommandDefinition or null if not found
38
+ */
39
+ getCommandDefinition(command) {
40
+ return getCommandDefinition(command);
41
+ }
42
+ /**
43
+ * Get all commands valid for the current context.
44
+ *
45
+ * @param context - Current WU context
46
+ * @returns Array of valid CommandDefinitions
47
+ */
48
+ getValidCommandsForContext(context) {
49
+ return getValidCommandsForContext(context);
50
+ }
51
+ /**
52
+ * Get all registered command definitions.
53
+ *
54
+ * @returns Array of all CommandDefinitions
55
+ */
56
+ getAllCommands() {
57
+ return Array.from(COMMAND_REGISTRY.values());
58
+ }
59
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Context Computer for WU Operations
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ * WU-1092: Adds worktreeGit field for checking worktree state from main
6
+ *
7
+ * Computes the unified WuContext by gathering location, git, and WU state.
8
+ * Performance budget: <100ms for complete context computation.
9
+ *
10
+ * @module
11
+ */
12
+ import type { WuContext } from '../validation/types.js';
13
+ /**
14
+ * Options for computing context.
15
+ */
16
+ export interface ComputeContextOptions {
17
+ /** Current working directory (defaults to process.cwd()) */
18
+ cwd?: string;
19
+ /** WU ID to look up (optional) */
20
+ wuId?: string;
21
+ /** Session ID if known */
22
+ sessionId?: string;
23
+ }
24
+ /**
25
+ * Result of context computation with timing.
26
+ */
27
+ export interface ComputeContextResult {
28
+ /** The computed context */
29
+ context: WuContext;
30
+ /** Time taken to compute in milliseconds */
31
+ computationMs: number;
32
+ /** Whether computation exceeded budget */
33
+ exceededBudget: boolean;
34
+ }
35
+ /**
36
+ * Compute complete WuContext.
37
+ *
38
+ * Gathers location, git state, and WU state in parallel where possible.
39
+ * Performance budget: <100ms for complete computation.
40
+ *
41
+ * WU-1092: Also reads worktreeGit when running from main with in_progress WU.
42
+ *
43
+ * @param options - Options for context computation
44
+ * @returns Promise<ComputeContextResult> - Computed context with timing
45
+ */
46
+ export declare function computeContext(options?: ComputeContextOptions): Promise<ComputeContextResult>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Context Computer for WU Operations
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ * WU-1092: Adds worktreeGit field for checking worktree state from main
6
+ *
7
+ * Computes the unified WuContext by gathering location, git, and WU state.
8
+ * Performance budget: <100ms for complete context computation.
9
+ *
10
+ * @module
11
+ */
12
+ import { join } from 'node:path';
13
+ import { CONTEXT_VALIDATION, WU_STATUS, getWorktreePath } from '../wu-constants.js';
14
+ import { resolveLocation } from './location-resolver.js';
15
+ import { readGitState } from './git-state-reader.js';
16
+ import { readWuState } from './wu-state-reader.js';
17
+ const { THRESHOLDS, LOCATION_TYPES } = CONTEXT_VALIDATION;
18
+ /**
19
+ * Convert WuStateResult to WuState interface used in WuContext.
20
+ */
21
+ function toWuState(result) {
22
+ return {
23
+ id: result.id,
24
+ status: result.status,
25
+ lane: result.lane,
26
+ title: result.title,
27
+ yamlPath: result.yamlPath,
28
+ isConsistent: result.isConsistent,
29
+ inconsistencyReason: result.inconsistencyReason,
30
+ };
31
+ }
32
+ /**
33
+ * Extract WU ID from context if available.
34
+ *
35
+ * Priority:
36
+ * 1. Explicit wuId option
37
+ * 2. WU ID from worktree path
38
+ */
39
+ function getWuIdToLookup(options, location) {
40
+ if (options.wuId) {
41
+ return options.wuId;
42
+ }
43
+ if (location.worktreeWuId) {
44
+ return location.worktreeWuId;
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Determine if we should read worktree git state (WU-1092).
50
+ *
51
+ * We need worktreeGit when:
52
+ * - Running from main checkout (not already in worktree)
53
+ * - WU is specified and found
54
+ * - WU is in_progress (implying worktree exists)
55
+ */
56
+ function shouldReadWorktreeGit(location, wuState) {
57
+ return (location.type === LOCATION_TYPES.MAIN &&
58
+ wuState !== null &&
59
+ wuState.status === WU_STATUS.IN_PROGRESS);
60
+ }
61
+ /**
62
+ * Get the absolute path to a WU's worktree.
63
+ *
64
+ * @param mainCheckout - Path to main checkout
65
+ * @param lane - WU lane name
66
+ * @param wuId - WU ID
67
+ * @returns Absolute path to worktree
68
+ */
69
+ function getWorktreeAbsolutePath(mainCheckout, lane, wuId) {
70
+ const relativePath = getWorktreePath(lane, wuId);
71
+ return join(mainCheckout, relativePath);
72
+ }
73
+ /**
74
+ * Compute complete WuContext.
75
+ *
76
+ * Gathers location, git state, and WU state in parallel where possible.
77
+ * Performance budget: <100ms for complete computation.
78
+ *
79
+ * WU-1092: Also reads worktreeGit when running from main with in_progress WU.
80
+ *
81
+ * @param options - Options for context computation
82
+ * @returns Promise<ComputeContextResult> - Computed context with timing
83
+ */
84
+ export async function computeContext(options = {}) {
85
+ const startTime = performance.now();
86
+ const cwd = options.cwd ?? process.cwd();
87
+ // Step 1: Resolve location first (needed to determine WU ID and main checkout)
88
+ const location = await resolveLocation(cwd);
89
+ // Step 2: Get WU ID to look up
90
+ const wuId = getWuIdToLookup(options, location);
91
+ // Step 3: Read git state and WU state in parallel
92
+ const [gitState, wuStateResult] = await Promise.all([
93
+ readGitState(cwd),
94
+ wuId ? readWuState(wuId, location.mainCheckout) : Promise.resolve(null),
95
+ ]);
96
+ // Step 4: Build WU state (null if no WU found)
97
+ const wuState = wuStateResult ? toWuState(wuStateResult) : null;
98
+ // Step 5: Build session state
99
+ const session = {
100
+ isActive: !!options.sessionId,
101
+ sessionId: options.sessionId ?? null,
102
+ };
103
+ // Step 6: Read worktree git state if applicable (WU-1092)
104
+ let worktreeGit;
105
+ if (shouldReadWorktreeGit(location, wuState)) {
106
+ const worktreePath = getWorktreeAbsolutePath(location.mainCheckout, wuState.lane, wuState.id);
107
+ worktreeGit = await readGitState(worktreePath);
108
+ }
109
+ // Step 7: Build complete context
110
+ const context = {
111
+ location,
112
+ git: gitState,
113
+ wu: wuState,
114
+ session,
115
+ ...(worktreeGit !== undefined && { worktreeGit }),
116
+ };
117
+ const endTime = performance.now();
118
+ const computationMs = endTime - startTime;
119
+ const exceededBudget = computationMs > THRESHOLDS.CONTEXT_COMPUTATION_MS;
120
+ return {
121
+ context,
122
+ computationMs,
123
+ exceededBudget,
124
+ };
125
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Git State Reader for WU Context
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Reads git state including:
7
+ * - Current branch
8
+ * - Dirty working tree (uncommitted changes)
9
+ * - Staged changes
10
+ * - Ahead/behind tracking branch
11
+ *
12
+ * Uses simple-git library (NOT execSync) per library-first mandate.
13
+ *
14
+ * @module
15
+ */
16
+ /**
17
+ * Git state information for WU operations.
18
+ *
19
+ * Captures the current git state relevant to command execution.
20
+ */
21
+ export interface GitState {
22
+ /** Current branch name (null if detached) */
23
+ branch: string | null;
24
+ /** Whether HEAD is detached */
25
+ isDetached: boolean;
26
+ /** Whether working tree has uncommitted changes */
27
+ isDirty: boolean;
28
+ /** Whether there are staged changes */
29
+ hasStaged: boolean;
30
+ /** Commits ahead of tracking branch */
31
+ ahead: number;
32
+ /** Commits behind tracking branch */
33
+ behind: number;
34
+ /** Tracking branch (e.g., 'origin/main') */
35
+ tracking: string | null;
36
+ /** List of modified files */
37
+ modifiedFiles: string[];
38
+ /** Whether an error occurred reading state */
39
+ hasError: boolean;
40
+ /** Error message if hasError is true */
41
+ errorMessage: string | null;
42
+ }
43
+ /**
44
+ * Read current git state using simple-git library.
45
+ *
46
+ * Uses git status to efficiently gather all state in one operation.
47
+ *
48
+ * @param cwd - Current working directory (defaults to process.cwd())
49
+ * @returns Promise<GitState> - Current git state
50
+ */
51
+ export declare function readGitState(cwd?: string): Promise<GitState>;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Git State Reader for WU Context
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Reads git state including:
7
+ * - Current branch
8
+ * - Dirty working tree (uncommitted changes)
9
+ * - Staged changes
10
+ * - Ahead/behind tracking branch
11
+ *
12
+ * Uses simple-git library (NOT execSync) per library-first mandate.
13
+ *
14
+ * @module
15
+ */
16
+ import { simpleGit } from 'simple-git';
17
+ /**
18
+ * Read current git state using simple-git library.
19
+ *
20
+ * Uses git status to efficiently gather all state in one operation.
21
+ *
22
+ * @param cwd - Current working directory (defaults to process.cwd())
23
+ * @returns Promise<GitState> - Current git state
24
+ */
25
+ export async function readGitState(cwd = process.cwd()) {
26
+ try {
27
+ const git = simpleGit(cwd);
28
+ const status = await git.status();
29
+ // Determine if working tree is dirty
30
+ const isDirty = !status.isClean();
31
+ // Check for staged changes - simple-git provides staged array
32
+ const hasStaged = status.staged.length > 0;
33
+ return {
34
+ branch: status.current,
35
+ isDetached: status.detached === true,
36
+ isDirty,
37
+ hasStaged,
38
+ ahead: status.ahead,
39
+ behind: status.behind,
40
+ tracking: status.tracking,
41
+ modifiedFiles: status.modified || [],
42
+ hasError: false,
43
+ errorMessage: null,
44
+ };
45
+ }
46
+ catch (error) {
47
+ const message = error instanceof Error ? error.message : String(error);
48
+ return {
49
+ branch: null,
50
+ isDetached: false,
51
+ isDirty: false,
52
+ hasStaged: false,
53
+ ahead: 0,
54
+ behind: 0,
55
+ tracking: null,
56
+ modifiedFiles: [],
57
+ hasError: true,
58
+ errorMessage: message,
59
+ };
60
+ }
61
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Context Module
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Exports:
7
+ * - Location resolution (main vs worktree detection)
8
+ * - Git state reading (branch, dirty, staged, ahead/behind)
9
+ * - WU state reading (YAML + state store)
10
+ * - Context computation (unified context model)
11
+ *
12
+ * @module
13
+ */
14
+ export * from './location-resolver.js';
15
+ export * from './git-state-reader.js';
16
+ export * from './wu-state-reader.js';
17
+ export * from './context-computer.js';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Context Module
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Exports:
7
+ * - Location resolution (main vs worktree detection)
8
+ * - Git state reading (branch, dirty, staged, ahead/behind)
9
+ * - WU state reading (YAML + state store)
10
+ * - Context computation (unified context model)
11
+ *
12
+ * @module
13
+ */
14
+ export * from './location-resolver.js';
15
+ export * from './git-state-reader.js';
16
+ export * from './wu-state-reader.js';
17
+ export * from './context-computer.js';
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Location Resolver for WU Context
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Detects whether the current working directory is:
7
+ * - Main checkout (primary repo clone)
8
+ * - Worktree (isolated workspace for a WU)
9
+ * - Detached HEAD
10
+ * - Unknown (not a git repository)
11
+ *
12
+ * Uses simple-git library (NOT execSync) per library-first mandate.
13
+ *
14
+ * @module
15
+ */
16
+ import { type LocationType } from '../wu-constants.js';
17
+ /**
18
+ * Location context information for WU operations.
19
+ *
20
+ * Captures where the command is being run from and related paths.
21
+ */
22
+ export interface LocationContext {
23
+ /** Location type: 'main', 'worktree', 'detached', or 'unknown' */
24
+ type: LocationType;
25
+ /** Absolute path to current working directory */
26
+ cwd: string;
27
+ /** Absolute path to git root (top-level of working tree) */
28
+ gitRoot: string;
29
+ /** Absolute path to main checkout (primary repo) */
30
+ mainCheckout: string;
31
+ /** Worktree name if in a worktree (e.g., 'framework-core-wu-1090') */
32
+ worktreeName: string | null;
33
+ /** WU ID extracted from worktree path (e.g., 'WU-1090') */
34
+ worktreeWuId: string | null;
35
+ }
36
+ /**
37
+ * Resolve current location context using simple-git library.
38
+ *
39
+ * Detection logic:
40
+ * 1. Use simple-git revparse to find git root and git dir
41
+ * 2. If .git is a file (not dir), we're in a worktree
42
+ * 3. Parse worktree path to extract WU ID
43
+ * 4. Find main checkout via simple-git worktree list
44
+ *
45
+ * @param cwd - Current working directory (defaults to process.cwd())
46
+ * @returns Promise<LocationContext> - Resolved location context
47
+ */
48
+ export declare function resolveLocation(cwd?: string): Promise<LocationContext>;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Location Resolver for WU Context
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Detects whether the current working directory is:
7
+ * - Main checkout (primary repo clone)
8
+ * - Worktree (isolated workspace for a WU)
9
+ * - Detached HEAD
10
+ * - Unknown (not a git repository)
11
+ *
12
+ * Uses simple-git library (NOT execSync) per library-first mandate.
13
+ *
14
+ * @module
15
+ */
16
+ import { simpleGit } from 'simple-git';
17
+ import { statSync } from 'node:fs';
18
+ import { resolve } from 'node:path';
19
+ import { CONTEXT_VALIDATION, PATTERNS } from '../wu-constants.js';
20
+ const { LOCATION_TYPES } = CONTEXT_VALIDATION;
21
+ /**
22
+ * Resolve current location context using simple-git library.
23
+ *
24
+ * Detection logic:
25
+ * 1. Use simple-git revparse to find git root and git dir
26
+ * 2. If .git is a file (not dir), we're in a worktree
27
+ * 3. Parse worktree path to extract WU ID
28
+ * 4. Find main checkout via simple-git worktree list
29
+ *
30
+ * @param cwd - Current working directory (defaults to process.cwd())
31
+ * @returns Promise<LocationContext> - Resolved location context
32
+ */
33
+ export async function resolveLocation(cwd = process.cwd()) {
34
+ const resolvedCwd = resolve(cwd);
35
+ try {
36
+ const git = simpleGit(resolvedCwd);
37
+ // Get git root and git dir using simple-git revparse
38
+ const gitRoot = (await git.revparse(['--show-toplevel'])).trim();
39
+ const gitDir = (await git.revparse(['--git-dir'])).trim();
40
+ // Detect if we're in a worktree (in worktrees, .git is a file not a dir)
41
+ const isWorktree = isGitDirFile(gitDir);
42
+ const mainCheckout = isWorktree ? await findMainCheckout(git) : gitRoot;
43
+ // Parse worktree info
44
+ const worktreeName = isWorktree ? parseWorktreeName(gitRoot, mainCheckout) : null;
45
+ const worktreeWuId = worktreeName ? parseWuIdFromWorktree(worktreeName) : null;
46
+ // Check if HEAD is detached (WU-1096)
47
+ const isDetached = await isHeadDetached(git);
48
+ // Determine location type
49
+ const type = determineLocationType(gitRoot, mainCheckout, isWorktree, isDetached);
50
+ return {
51
+ type,
52
+ cwd: resolvedCwd,
53
+ gitRoot,
54
+ mainCheckout,
55
+ worktreeName,
56
+ worktreeWuId,
57
+ };
58
+ }
59
+ catch {
60
+ // Not a git repository or other error
61
+ return {
62
+ type: LOCATION_TYPES.UNKNOWN,
63
+ cwd: resolvedCwd,
64
+ gitRoot: resolvedCwd,
65
+ mainCheckout: resolvedCwd,
66
+ worktreeName: null,
67
+ worktreeWuId: null,
68
+ };
69
+ }
70
+ }
71
+ /**
72
+ * Check if git dir is a file (worktree) vs directory (main checkout)
73
+ *
74
+ * In a main checkout, .git is a directory containing the repository data.
75
+ * In a worktree, .git is a file that points to the main .git directory.
76
+ */
77
+ function isGitDirFile(gitDir) {
78
+ try {
79
+ const stat = statSync(gitDir);
80
+ return stat.isFile();
81
+ }
82
+ catch {
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * Find main checkout path using simple-git worktree list
88
+ *
89
+ * The first worktree listed is always the main checkout.
90
+ */
91
+ async function findMainCheckout(git) {
92
+ try {
93
+ // simple-git doesn't have direct worktree support, use raw command
94
+ const result = await git.raw(['worktree', 'list', '--porcelain']);
95
+ // First worktree listed is always the main checkout
96
+ // Format: "worktree /path/to/repo\nHEAD ...\nbranch ...\n\nworktree ..."
97
+ const match = result.match(/^worktree (.+)$/m);
98
+ return match ? match[1] : process.cwd();
99
+ }
100
+ catch {
101
+ return process.cwd();
102
+ }
103
+ }
104
+ /**
105
+ * Extract worktree name from path
106
+ *
107
+ * @example
108
+ * parseWorktreeName('/repo/worktrees/framework-core-wu-1090', '/repo')
109
+ * // Returns 'framework-core-wu-1090'
110
+ */
111
+ function parseWorktreeName(gitRoot, mainCheckout) {
112
+ // Remove main checkout prefix to get relative path
113
+ let relativePath = gitRoot;
114
+ if (gitRoot.startsWith(mainCheckout)) {
115
+ relativePath = gitRoot.slice(mainCheckout.length);
116
+ }
117
+ // Remove leading slashes
118
+ relativePath = relativePath.replace(/^\/+/, '');
119
+ // The worktree name is the last path component
120
+ const parts = relativePath.split('/');
121
+ const lastPart = parts[parts.length - 1];
122
+ return lastPart || null;
123
+ }
124
+ /**
125
+ * Extract WU ID from worktree name
126
+ *
127
+ * Uses PATTERNS.WU_ID_EXTRACT_CI (case-insensitive) from wu-constants.ts
128
+ * because worktree names use lowercase (e.g., 'framework-core-wu-1090').
129
+ *
130
+ * @example
131
+ * parseWuIdFromWorktree('framework-core-wu-1090') // 'WU-1090'
132
+ * parseWuIdFromWorktree('some-feature') // null
133
+ */
134
+ function parseWuIdFromWorktree(worktreeName) {
135
+ // Use case-insensitive pattern for worktree names
136
+ const match = worktreeName.match(PATTERNS.WU_ID_EXTRACT_CI);
137
+ return match ? match[0].toUpperCase() : null;
138
+ }
139
+ /**
140
+ * Check if HEAD is detached using git symbolic-ref
141
+ *
142
+ * WU-1096: Detect detached HEAD state and return DETACHED location type.
143
+ *
144
+ * When HEAD is attached to a branch, `git symbolic-ref HEAD` returns
145
+ * the ref name (e.g., 'refs/heads/main'). When detached, it fails
146
+ * with 'fatal: ref HEAD is not a symbolic ref'.
147
+ *
148
+ * @param git - SimpleGit instance
149
+ * @returns Promise<boolean> - true if HEAD is detached
150
+ */
151
+ async function isHeadDetached(git) {
152
+ try {
153
+ // git symbolic-ref HEAD succeeds when HEAD is attached to a branch
154
+ await git.raw(['symbolic-ref', 'HEAD']);
155
+ return false; // HEAD is attached
156
+ }
157
+ catch {
158
+ // git symbolic-ref HEAD fails when HEAD is detached
159
+ return true;
160
+ }
161
+ }
162
+ /**
163
+ * Determine location type from context
164
+ *
165
+ * WU-1096: Added isDetached parameter to detect detached HEAD state.
166
+ */
167
+ function determineLocationType(gitRoot, mainCheckout, isWorktree, isDetached = false) {
168
+ if (isWorktree)
169
+ return LOCATION_TYPES.WORKTREE;
170
+ if (isDetached)
171
+ return LOCATION_TYPES.DETACHED;
172
+ if (gitRoot === mainCheckout)
173
+ return LOCATION_TYPES.MAIN;
174
+ return LOCATION_TYPES.UNKNOWN;
175
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * WU State Reader for WU Context
3
+ *
4
+ * WU-1090: Context-aware state machine for WU lifecycle commands
5
+ *
6
+ * Reads WU state from YAML file and optionally cross-references
7
+ * with state store for inconsistency detection.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Result of reading WU state.
13
+ */
14
+ export interface WuStateResult {
15
+ /** WU ID (uppercase, e.g., 'WU-1090') */
16
+ id: string;
17
+ /** Current status from YAML */
18
+ status: string;
19
+ /** Lane name */
20
+ lane: string;
21
+ /** WU title */
22
+ title: string;
23
+ /** Absolute path to WU YAML file */
24
+ yamlPath: string;
25
+ /** Whether YAML and state store are consistent */
26
+ isConsistent: boolean;
27
+ /** Reason for inconsistency if not consistent */
28
+ inconsistencyReason: string | null;
29
+ }
30
+ /**
31
+ * Read WU state from YAML and detect inconsistencies.
32
+ *
33
+ * @param wuId - WU ID (e.g., 'WU-1090' or 'wu-1090')
34
+ * @param repoRoot - Repository root path
35
+ * @returns WuStateResult or null if WU not found
36
+ */
37
+ export declare function readWuState(wuId: string, repoRoot: string): Promise<WuStateResult | null>;