@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.
- package/README.md +325 -1
- package/dist/adapters/context-adapters.d.ts +90 -0
- package/dist/adapters/context-adapters.js +99 -0
- package/dist/adapters/index.d.ts +14 -0
- package/dist/adapters/index.js +18 -0
- package/dist/adapters/recovery-adapters.d.ts +40 -0
- package/dist/adapters/recovery-adapters.js +43 -0
- package/dist/adapters/validation-adapters.d.ts +52 -0
- package/dist/adapters/validation-adapters.js +59 -0
- package/dist/context/context-computer.d.ts +46 -0
- package/dist/context/context-computer.js +125 -0
- package/dist/context/git-state-reader.d.ts +51 -0
- package/dist/context/git-state-reader.js +61 -0
- package/dist/context/index.d.ts +17 -0
- package/dist/context/index.js +17 -0
- package/dist/context/location-resolver.d.ts +48 -0
- package/dist/context/location-resolver.js +175 -0
- package/dist/context/wu-state-reader.d.ts +37 -0
- package/dist/context/wu-state-reader.js +76 -0
- package/dist/context-di.d.ts +184 -0
- package/dist/context-di.js +178 -0
- package/dist/context-validation-integration.d.ts +77 -0
- package/dist/context-validation-integration.js +157 -0
- package/dist/domain/context.schemas.d.ts +147 -0
- package/dist/domain/context.schemas.js +126 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.js +18 -0
- package/dist/domain/recovery.schemas.d.ts +115 -0
- package/dist/domain/recovery.schemas.js +83 -0
- package/dist/domain/validation.schemas.d.ts +146 -0
- package/dist/domain/validation.schemas.js +114 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +43 -0
- package/dist/lumenflow-config-schema.d.ts +41 -0
- package/dist/lumenflow-config-schema.js +37 -0
- package/dist/ports/context.ports.d.ts +135 -0
- package/dist/ports/context.ports.js +21 -0
- package/dist/ports/core-tools.ports.d.ts +266 -0
- package/dist/ports/core-tools.ports.js +21 -0
- package/dist/ports/git-validator.ports.d.ts +314 -0
- package/dist/ports/git-validator.ports.js +12 -0
- package/dist/ports/index.d.ts +20 -0
- package/dist/ports/index.js +20 -0
- package/dist/ports/recovery.ports.d.ts +58 -0
- package/dist/ports/recovery.ports.js +17 -0
- package/dist/ports/validation.ports.d.ts +74 -0
- package/dist/ports/validation.ports.js +17 -0
- package/dist/ports/wu-helpers.ports.d.ts +224 -0
- package/dist/ports/wu-helpers.ports.js +12 -0
- package/dist/recovery/index.d.ts +11 -0
- package/dist/recovery/index.js +11 -0
- package/dist/recovery/recovery-analyzer.d.ts +66 -0
- package/dist/recovery/recovery-analyzer.js +129 -0
- package/dist/usecases/analyze-recovery.usecase.d.ts +42 -0
- package/dist/usecases/analyze-recovery.usecase.js +45 -0
- package/dist/usecases/compute-context.usecase.d.ts +62 -0
- package/dist/usecases/compute-context.usecase.js +101 -0
- package/dist/usecases/index.d.ts +14 -0
- package/dist/usecases/index.js +18 -0
- package/dist/usecases/validate-command.usecase.d.ts +55 -0
- package/dist/usecases/validate-command.usecase.js +154 -0
- package/dist/validation/command-registry.d.ts +38 -0
- package/dist/validation/command-registry.js +229 -0
- package/dist/validation/index.d.ts +15 -0
- package/dist/validation/index.js +15 -0
- package/dist/validation/types.d.ts +135 -0
- package/dist/validation/types.js +11 -0
- package/dist/validation/validate-command.d.ts +27 -0
- package/dist/validation/validate-command.js +160 -0
- package/dist/wu-constants.d.ts +136 -0
- package/dist/wu-constants.js +124 -0
- package/dist/wu-helpers.d.ts +5 -1
- package/dist/wu-helpers.js +12 -1
- 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>;
|