@lumenflow/core 1.6.0 → 2.1.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/index.d.ts CHANGED
@@ -58,6 +58,8 @@ export type { LocationType, ValidationErrorCode, RecoveryActionType, RecoveryIss
58
58
  export type { ILocationResolver, IGitStateReader, IWuStateReader } from './ports/context.ports.js';
59
59
  export type { ICommandRegistry } from './ports/validation.ports.js';
60
60
  export type { IRecoveryAnalyzer } from './ports/recovery.ports.js';
61
+ export type { IWuGitAdapter, IWuStatusCheckResult, IBranchValidationResult, IWuYamlReader, IWuYamlWriter, IWuStateStore, IWuCheckpointManager, IWuPaths, } from './ports/wu-helpers.ports.js';
62
+ export type { IGitAdapter, IPhiScanner, PHIMatch, PHIScanResult, PHIScanOptions, MergeOptions, MergeResult, PushOptions, DeleteBranchOptions, WorktreeRemoveOptions, } from './ports/git-validator.ports.js';
61
63
  export { LOCATION_TYPE_VALUES, LocationTypeSchema, LocationContextSchema, GitStateSchema, WuStateResultSchema, SessionStateSchema, WuContextSchema, } from './domain/context.schemas.js';
62
64
  export { VALIDATION_ERROR_CODE_VALUES, ValidationErrorCodeSchema, PREDICATE_SEVERITY_VALUES, PredicateSeveritySchema, ValidationErrorSchema, ValidationWarningSchema, ValidationResultSchema, CommandPredicateConfigSchema, CommandDefinitionConfigSchema, type CommandPredicateConfig, type CommandDefinitionConfig, } from './domain/validation.schemas.js';
63
65
  export { RECOVERY_ISSUE_CODE_VALUES, RecoveryIssueCodeSchema, RECOVERY_ACTION_TYPE_VALUES, RecoveryActionTypeSchema, RecoveryIssueSchema, RecoveryActionSchema, RecoveryAnalysisSchema, } from './domain/recovery.schemas.js';
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Core Tools Ports
3
+ *
4
+ * WU-1101: INIT-003 Phase 2a - Migrate tools/lib/core/ to @lumenflow/core
5
+ *
6
+ * Port interfaces for core tool modules migrated from PatientPath tools/lib/core/.
7
+ * These abstractions allow external users to inject custom implementations.
8
+ *
9
+ * Hexagonal Architecture - Input Ports:
10
+ * - IToolRunner: Execute tools with validation and guards
11
+ * - IWorktreeGuard: Detect and enforce worktree context
12
+ * - IScopeChecker: Validate file paths against WU code_paths
13
+ *
14
+ * Current Implementations:
15
+ * - runTool, ToolRunner (tool-runner.ts)
16
+ * - getWUContext, assertWorktreeRequired, isInWorktree, isMainBranch (worktree-guard.ts)
17
+ * - getActiveScope, isPathInScope, assertPathInScope (scope-checker.ts)
18
+ *
19
+ * @module ports/core-tools
20
+ */
21
+ import type { ZodType } from 'zod';
22
+ import type { ToolOutput, ToolError } from '../core/tool.schemas.js';
23
+ import type { PermissionLevel, ToolDomain } from '../core/tool.constants.js';
24
+ /**
25
+ * Tool metadata interface
26
+ */
27
+ export interface IToolMetadata {
28
+ /** Tool name (unique identifier) */
29
+ name: string;
30
+ /** Human-readable description */
31
+ description: string;
32
+ /** Tool domain classification */
33
+ domain?: ToolDomain;
34
+ /** Required permission level */
35
+ permission: PermissionLevel;
36
+ /** Tool version */
37
+ version?: string;
38
+ }
39
+ /**
40
+ * Tool definition interface
41
+ */
42
+ export interface IToolDefinition {
43
+ /** Tool metadata */
44
+ metadata: IToolMetadata;
45
+ /** Zod schema for input validation */
46
+ inputSchema?: ZodType;
47
+ /** Zod schema for output validation */
48
+ outputSchema?: ZodType;
49
+ /** Tool execution function */
50
+ execute: (input: unknown, context?: unknown) => Promise<ToolOutput>;
51
+ }
52
+ /**
53
+ * Tool configuration options
54
+ */
55
+ export interface IToolConfigOptions {
56
+ /** Require worktree context for execution */
57
+ requiresWorktree?: boolean;
58
+ /** Require scope validation for file paths */
59
+ requiresScope?: boolean;
60
+ /** Enable audit logging */
61
+ enableAuditLog?: boolean;
62
+ /** Execution timeout in milliseconds */
63
+ timeoutMs?: number;
64
+ }
65
+ /**
66
+ * Tool execution options
67
+ */
68
+ export interface IRunToolOptions {
69
+ /** Injected dependencies (for testing) */
70
+ dependencies?: Record<string, unknown>;
71
+ /** Configuration overrides */
72
+ config?: IToolConfigOptions;
73
+ /** Execution context (sessionId, userId, etc.) */
74
+ context?: Record<string, unknown>;
75
+ }
76
+ /**
77
+ * Tool Runner Port Interface
78
+ *
79
+ * Provides unified tool execution with validation, guards, and audit logging.
80
+ * Integrates worktree context detection and scope validation.
81
+ *
82
+ * @example
83
+ * // Direct tool execution
84
+ * const result = await runner.runTool(toolDefinition, { message: 'hello' });
85
+ *
86
+ * @example
87
+ * // Registry-based execution
88
+ * runner.register(myTool);
89
+ * const result = await runner.run('tool:name', { arg: 'value' });
90
+ */
91
+ export interface IToolRunner {
92
+ /**
93
+ * Execute a tool with full validation and guards.
94
+ *
95
+ * @param tool - Tool definition
96
+ * @param input - Tool input arguments
97
+ * @param options - Execution options
98
+ * @returns Tool output (success/failure with data/error)
99
+ */
100
+ runTool(tool: IToolDefinition, input: unknown, options?: IRunToolOptions): Promise<ToolOutput>;
101
+ /**
102
+ * Register a tool definition.
103
+ *
104
+ * @param tool - Tool definition
105
+ * @throws Error if tool with same name already registered
106
+ */
107
+ register(tool: IToolDefinition): void;
108
+ /**
109
+ * Check if a tool is registered.
110
+ *
111
+ * @param name - Tool name
112
+ * @returns True if tool exists
113
+ */
114
+ hasTool(name: string): boolean;
115
+ /**
116
+ * Run a registered tool by name.
117
+ *
118
+ * @param name - Tool name
119
+ * @param input - Tool input
120
+ * @param options - Execution options
121
+ * @returns Tool output
122
+ */
123
+ run(name: string, input: unknown, options?: IRunToolOptions): Promise<ToolOutput>;
124
+ /**
125
+ * List all registered tools.
126
+ *
127
+ * @param options - Filter options
128
+ * @returns Array of tool metadata
129
+ */
130
+ listTools(options?: {
131
+ domain?: string;
132
+ }): IToolMetadata[];
133
+ }
134
+ /**
135
+ * WU context from worktree detection
136
+ */
137
+ export interface IWUContext {
138
+ /** WU identifier (e.g., 'WU-1101') */
139
+ wuId: string;
140
+ /** Lane name in kebab-case (e.g., 'framework-core') */
141
+ lane: string;
142
+ /** Worktree path relative to repo root, or null if on lane branch */
143
+ worktreePath: string | null;
144
+ }
145
+ /**
146
+ * Worktree detection options
147
+ */
148
+ export interface IWorktreeOptions {
149
+ /** Current working directory (defaults to process.cwd()) */
150
+ cwd?: string;
151
+ /** Git adapter instance (for testing) */
152
+ git?: {
153
+ getCurrentBranch(): Promise<string>;
154
+ };
155
+ /** Operation name for error messages */
156
+ operation?: string;
157
+ }
158
+ /**
159
+ * Worktree Guard Port Interface
160
+ *
161
+ * Provides runtime guards to enforce worktree discipline:
162
+ * - Detect if current directory is inside a worktree
163
+ * - Extract WU ID and lane from worktree path or git branch
164
+ * - Block write operations when not in worktree
165
+ *
166
+ * @example
167
+ * // Check worktree context
168
+ * const context = await guard.getWUContext();
169
+ * if (context) {
170
+ * console.log(`Working on ${context.wuId} in ${context.lane}`);
171
+ * }
172
+ *
173
+ * @example
174
+ * // Assert worktree for write operation
175
+ * await guard.assertWorktreeRequired({ operation: 'file:write' });
176
+ */
177
+ export interface IWorktreeGuard {
178
+ /**
179
+ * Check if current directory is inside a worktree.
180
+ *
181
+ * @param options - Detection options
182
+ * @returns True if inside a worktree directory
183
+ */
184
+ isInWorktree(options?: IWorktreeOptions): boolean;
185
+ /**
186
+ * Check if on main or master branch.
187
+ *
188
+ * @param options - Detection options
189
+ * @returns True if on main/master branch
190
+ */
191
+ isMainBranch(options?: IWorktreeOptions): Promise<boolean>;
192
+ /**
193
+ * Get WU context from worktree path or git branch.
194
+ *
195
+ * @param options - Detection options
196
+ * @returns WU context or null if not in WU workspace
197
+ */
198
+ getWUContext(options?: IWorktreeOptions): Promise<IWUContext | null>;
199
+ /**
200
+ * Assert that current context is inside a worktree or on a lane branch.
201
+ * Throws if on main branch in main checkout.
202
+ *
203
+ * @param options - Detection options
204
+ * @throws Error if not in worktree and on main branch
205
+ */
206
+ assertWorktreeRequired(options?: IWorktreeOptions): Promise<void>;
207
+ }
208
+ /**
209
+ * WU scope with code paths
210
+ */
211
+ export interface IWUScope {
212
+ /** WU identifier */
213
+ wuId: string;
214
+ /** Allowed file paths/glob patterns */
215
+ code_paths: string[];
216
+ }
217
+ /**
218
+ * Scope Checker Port Interface
219
+ *
220
+ * Provides runtime validation that file modifications stay within WU code_paths.
221
+ * Prevents scope creep and ensures agents only modify authorized files.
222
+ *
223
+ * @example
224
+ * // Check if path is in scope
225
+ * const scope = await checker.getActiveScope();
226
+ * if (checker.isPathInScope('src/file.ts', scope)) {
227
+ * // Safe to modify
228
+ * }
229
+ *
230
+ * @example
231
+ * // Assert path is in scope (throws if not)
232
+ * await checker.assertPathInScope('src/file.ts', scope, 'file:write');
233
+ */
234
+ export interface IScopeChecker {
235
+ /**
236
+ * Get active WU scope (WU ID + code_paths).
237
+ *
238
+ * @param options - Options for dependency injection
239
+ * @returns Scope object or null if not in WU workspace
240
+ */
241
+ getActiveScope(options?: {
242
+ getWUContext?: () => Promise<IWUContext | null>;
243
+ loadWUYaml?: (wuId: string) => {
244
+ code_paths?: string[];
245
+ };
246
+ }): Promise<IWUScope | null>;
247
+ /**
248
+ * Check if a file path is within WU scope.
249
+ *
250
+ * @param filePath - File path to check
251
+ * @param scope - Scope object from getActiveScope()
252
+ * @returns True if path is in scope
253
+ */
254
+ isPathInScope(filePath: string, scope: IWUScope | null): boolean;
255
+ /**
256
+ * Assert that a file path is within WU scope.
257
+ * Throws if path is outside scope or no scope available.
258
+ *
259
+ * @param filePath - File path to check
260
+ * @param scope - Scope object from getActiveScope()
261
+ * @param operation - Operation name for error message
262
+ * @throws Error if path is outside scope
263
+ */
264
+ assertPathInScope(filePath: string, scope: IWUScope | null, operation?: string): void;
265
+ }
266
+ export type { ToolOutput, ToolError };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Core Tools Ports
3
+ *
4
+ * WU-1101: INIT-003 Phase 2a - Migrate tools/lib/core/ to @lumenflow/core
5
+ *
6
+ * Port interfaces for core tool modules migrated from PatientPath tools/lib/core/.
7
+ * These abstractions allow external users to inject custom implementations.
8
+ *
9
+ * Hexagonal Architecture - Input Ports:
10
+ * - IToolRunner: Execute tools with validation and guards
11
+ * - IWorktreeGuard: Detect and enforce worktree context
12
+ * - IScopeChecker: Validate file paths against WU code_paths
13
+ *
14
+ * Current Implementations:
15
+ * - runTool, ToolRunner (tool-runner.ts)
16
+ * - getWUContext, assertWorktreeRequired, isInWorktree, isMainBranch (worktree-guard.ts)
17
+ * - getActiveScope, isPathInScope, assertPathInScope (scope-checker.ts)
18
+ *
19
+ * @module ports/core-tools
20
+ */
21
+ export {};
@@ -0,0 +1,314 @@
1
+ /**
2
+ * Git Adapter and Validator Port Interfaces
3
+ *
4
+ * WU-1103: INIT-003 Phase 2c - Migrate git & validator modules
5
+ *
6
+ * Hexagonal Architecture - Input Ports:
7
+ * These abstractions allow external users to inject custom implementations
8
+ * for git operations and PHI scanning.
9
+ *
10
+ * @module ports/git-validator
11
+ */
12
+ /**
13
+ * Merge options for git merge operations
14
+ */
15
+ export interface MergeOptions {
16
+ /** Fast-forward only merge (--ff-only flag) */
17
+ ffOnly?: boolean;
18
+ }
19
+ /**
20
+ * Merge result from git merge operations
21
+ */
22
+ export interface MergeResult {
23
+ /** Whether the merge succeeded */
24
+ success: boolean;
25
+ }
26
+ /**
27
+ * Push options for git push operations
28
+ */
29
+ export interface PushOptions {
30
+ /** Set upstream tracking (-u flag) */
31
+ setUpstream?: boolean;
32
+ }
33
+ /**
34
+ * Delete branch options
35
+ */
36
+ export interface DeleteBranchOptions {
37
+ /** Force delete (use -D instead of -d) */
38
+ force?: boolean;
39
+ }
40
+ /**
41
+ * Worktree remove options
42
+ */
43
+ export interface WorktreeRemoveOptions {
44
+ /** Force removal */
45
+ force?: boolean;
46
+ }
47
+ /**
48
+ * Git Adapter Port Interface
49
+ *
50
+ * Abstracts git operations to allow dependency injection and testing.
51
+ * The default implementation uses simple-git library.
52
+ *
53
+ * @example
54
+ * // Custom implementation for testing
55
+ * const mockGit: IGitAdapter = {
56
+ * getCurrentBranch: vi.fn().mockResolvedValue('main'),
57
+ * getStatus: vi.fn().mockResolvedValue(''),
58
+ * // ... other methods
59
+ * };
60
+ *
61
+ * @example
62
+ * // Using default implementation
63
+ * import { GitAdapter } from './git-adapter.js';
64
+ * const adapter: IGitAdapter = new GitAdapter({ baseDir: '/path' });
65
+ */
66
+ export interface IGitAdapter {
67
+ /**
68
+ * Get current branch name
69
+ * @returns Promise resolving to branch name
70
+ * @example
71
+ * await git.getCurrentBranch(); // "lane/operations/wu-1213"
72
+ */
73
+ getCurrentBranch(): Promise<string>;
74
+ /**
75
+ * Get git status (porcelain format string)
76
+ * @returns Promise resolving to status output in porcelain format
77
+ * @example
78
+ * await git.getStatus(); // " M file.txt\n?? untracked.txt"
79
+ */
80
+ getStatus(): Promise<string>;
81
+ /**
82
+ * Check if a branch exists
83
+ * @param branch - Branch name to check
84
+ * @returns Promise resolving to true if branch exists
85
+ * @example
86
+ * await git.branchExists('main'); // true
87
+ */
88
+ branchExists(branch: string): Promise<boolean>;
89
+ /**
90
+ * Check if a remote branch exists
91
+ * @param remote - Remote name (e.g., 'origin')
92
+ * @param branch - Branch name
93
+ * @returns Promise resolving to true if remote branch exists
94
+ * @example
95
+ * await git.remoteBranchExists('origin', 'feature'); // true/false
96
+ */
97
+ remoteBranchExists(remote: string, branch: string): Promise<boolean>;
98
+ /**
99
+ * Check if working tree is clean (no uncommitted changes)
100
+ * @returns Promise resolving to true if clean
101
+ * @example
102
+ * await git.isClean(); // true or false
103
+ */
104
+ isClean(): Promise<boolean>;
105
+ /**
106
+ * Fetch from remote
107
+ * @param remote - Remote name (optional, defaults to fetching all)
108
+ * @param branch - Branch name (optional)
109
+ * @example
110
+ * await git.fetch('origin', 'main');
111
+ * await git.fetch(); // Fetch all remotes
112
+ */
113
+ fetch(remote?: string, branch?: string): Promise<void>;
114
+ /**
115
+ * Pull from remote branch
116
+ * @param remote - Remote name
117
+ * @param branch - Branch name
118
+ * @example
119
+ * await git.pull('origin', 'main');
120
+ */
121
+ pull(remote: string, branch: string): Promise<void>;
122
+ /**
123
+ * Add files to staging area
124
+ * @param files - Files to add (single file or array)
125
+ * @example
126
+ * await git.add('file.txt');
127
+ * await git.add(['file1.txt', 'file2.txt']);
128
+ */
129
+ add(files: string | string[]): Promise<void>;
130
+ /**
131
+ * Commit changes
132
+ * @param message - Commit message
133
+ * @example
134
+ * await git.commit('feat: add new feature');
135
+ */
136
+ commit(message: string): Promise<void>;
137
+ /**
138
+ * Push to remote
139
+ * @param remote - Remote name (defaults to 'origin')
140
+ * @param branch - Branch name (optional, uses current branch if not specified)
141
+ * @param options - Push options
142
+ * @example
143
+ * await git.push('origin', 'main');
144
+ * await git.push('origin', 'feature', { setUpstream: true });
145
+ */
146
+ push(remote?: string, branch?: string, options?: PushOptions): Promise<void>;
147
+ /**
148
+ * Checkout a branch
149
+ * @param branch - Branch name
150
+ * @example
151
+ * await git.checkout('main');
152
+ */
153
+ checkout(branch: string): Promise<void>;
154
+ /**
155
+ * Create and checkout a new branch
156
+ * @param branch - Branch name
157
+ * @param startPoint - Starting commit (optional, defaults to HEAD)
158
+ * @example
159
+ * await git.createBranch('feature/new-feature');
160
+ * await git.createBranch('hotfix/bug', 'main');
161
+ */
162
+ createBranch(branch: string, startPoint?: string): Promise<void>;
163
+ /**
164
+ * Delete a branch
165
+ * @param branch - Branch name
166
+ * @param options - Delete options
167
+ * @example
168
+ * await git.deleteBranch('feature');
169
+ * await git.deleteBranch('feature', { force: true });
170
+ */
171
+ deleteBranch(branch: string, options?: DeleteBranchOptions): Promise<void>;
172
+ /**
173
+ * Merge a branch
174
+ * @param branch - Branch to merge
175
+ * @param options - Merge options
176
+ * @returns Promise resolving to merge result
177
+ * @example
178
+ * await git.merge('feature-branch');
179
+ * await git.merge('feature-branch', { ffOnly: true });
180
+ */
181
+ merge(branch: string, options?: MergeOptions): Promise<MergeResult>;
182
+ /**
183
+ * Get commit hash
184
+ * @param ref - Git ref (optional, defaults to 'HEAD')
185
+ * @returns Promise resolving to commit hash
186
+ * @example
187
+ * await git.getCommitHash(); // "a1b2c3d..."
188
+ * await git.getCommitHash('main'); // "e4f5g6h..."
189
+ */
190
+ getCommitHash(ref?: string): Promise<string>;
191
+ /**
192
+ * Add a worktree with new branch
193
+ * @param path - Worktree path
194
+ * @param branch - Branch name
195
+ * @param startPoint - Starting commit (optional, defaults to HEAD)
196
+ * @example
197
+ * await git.worktreeAdd('worktrees/feature', 'feature-branch', 'main');
198
+ */
199
+ worktreeAdd(path: string, branch: string, startPoint?: string): Promise<void>;
200
+ /**
201
+ * Remove a worktree
202
+ * @param worktreePath - Worktree path
203
+ * @param options - Remove options
204
+ * @example
205
+ * await git.worktreeRemove('worktrees/feature');
206
+ * await git.worktreeRemove('worktrees/feature', { force: true });
207
+ */
208
+ worktreeRemove(worktreePath: string, options?: WorktreeRemoveOptions): Promise<void>;
209
+ /**
210
+ * List all worktrees
211
+ * @returns Promise resolving to worktree list in porcelain format
212
+ * @example
213
+ * await git.worktreeList();
214
+ */
215
+ worktreeList(): Promise<string>;
216
+ /**
217
+ * Execute arbitrary git command via raw()
218
+ * @param args - Git command arguments
219
+ * @returns Promise resolving to command output
220
+ * @example
221
+ * await git.raw(['status', '--porcelain']);
222
+ */
223
+ raw(args: string[]): Promise<string>;
224
+ }
225
+ /**
226
+ * PHI (Protected Health Information) match result
227
+ */
228
+ export interface PHIMatch {
229
+ /** PHI type identifier (e.g., 'NHS_NUMBER', 'POSTCODE_MEDICAL_CONTEXT') */
230
+ type: string;
231
+ /** The matched value */
232
+ value: string;
233
+ /** Start position in content */
234
+ startIndex: number;
235
+ /** End position in content */
236
+ endIndex: number;
237
+ /** Medical keyword that triggered postcode detection (optional) */
238
+ medicalKeyword?: string;
239
+ }
240
+ /**
241
+ * PHI scan result
242
+ */
243
+ export interface PHIScanResult {
244
+ /** Whether PHI was detected */
245
+ hasPHI: boolean;
246
+ /** Array of PHI matches found */
247
+ matches: PHIMatch[];
248
+ /** Non-blocking warnings (e.g., test data detected) */
249
+ warnings: string[];
250
+ }
251
+ /**
252
+ * Options for PHI scanning
253
+ */
254
+ export interface PHIScanOptions {
255
+ /** File path for exclusion check */
256
+ filePath?: string;
257
+ }
258
+ /**
259
+ * PHI Scanner Port Interface
260
+ *
261
+ * Abstracts PHI (Protected Health Information) detection to allow
262
+ * dependency injection and testing. Detects NHS numbers and UK
263
+ * postcodes in medical context.
264
+ *
265
+ * @example
266
+ * // Custom implementation for testing
267
+ * const mockScanner: IPhiScanner = {
268
+ * scanForPHI: vi.fn().mockReturnValue({ hasPHI: false, matches: [], warnings: [] }),
269
+ * isPathExcluded: vi.fn().mockReturnValue(false),
270
+ * formatPHISummary: vi.fn().mockReturnValue('No PHI detected'),
271
+ * };
272
+ *
273
+ * @example
274
+ * // Using default implementation
275
+ * import { scanForPHI, isPathExcluded, formatPHISummary } from './validators/phi-scanner.js';
276
+ * const scanner: IPhiScanner = { scanForPHI, isPathExcluded, formatPHISummary };
277
+ */
278
+ export interface IPhiScanner {
279
+ /**
280
+ * Scan content for PHI (Protected Health Information)
281
+ *
282
+ * Detects:
283
+ * - Valid NHS numbers (validated with Modulus 11 checksum)
284
+ * - UK postcodes in medical context
285
+ *
286
+ * @param content - Content to scan
287
+ * @param options - Scan options
288
+ * @returns Scan result with matches and warnings
289
+ * @example
290
+ * scanner.scanForPHI('Patient NHS: 2983396339'); // { hasPHI: true, matches: [...], warnings: [] }
291
+ */
292
+ scanForPHI(content: string, options?: PHIScanOptions): PHIScanResult;
293
+ /**
294
+ * Check if a file path should be excluded from PHI scanning
295
+ *
296
+ * Test files, fixtures, mocks, and documentation are typically excluded.
297
+ *
298
+ * @param filePath - Path to check
299
+ * @returns True if path should be excluded
300
+ * @example
301
+ * scanner.isPathExcluded('src/__tests__/file.test.ts'); // true
302
+ * scanner.isPathExcluded('src/utils/helper.ts'); // false
303
+ */
304
+ isPathExcluded(filePath: string | null | undefined): boolean;
305
+ /**
306
+ * Create a human-readable summary of PHI matches
307
+ *
308
+ * @param matches - PHI matches from scanForPHI
309
+ * @returns Summary message
310
+ * @example
311
+ * scanner.formatPHISummary([{ type: 'NHS_NUMBER', ... }]); // "PHI detected: 1 NHS number"
312
+ */
313
+ formatPHISummary(matches: PHIMatch[]): string;
314
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Git Adapter and Validator Port Interfaces
3
+ *
4
+ * WU-1103: INIT-003 Phase 2c - Migrate git & validator modules
5
+ *
6
+ * Hexagonal Architecture - Input Ports:
7
+ * These abstractions allow external users to inject custom implementations
8
+ * for git operations and PHI scanning.
9
+ *
10
+ * @module ports/git-validator
11
+ */
12
+ export {};
@@ -2,8 +2,11 @@
2
2
  * Ports Index
3
3
  *
4
4
  * WU-1093: INIT-002 Phase 1 - Define ports and domain schemas
5
+ * WU-1101: INIT-003 Phase 2a - Add core tools ports
6
+ * WU-1103: INIT-003 Phase 2c - Add git adapter and validator ports
5
7
  *
6
- * Re-exports all port interfaces for the context-aware validation system.
8
+ * Re-exports all port interfaces for the context-aware validation system
9
+ * and core tools (tool-runner, worktree-guard, scope-checker).
7
10
  *
8
11
  * @module ports
9
12
  */
@@ -12,3 +15,6 @@ export * from './validation.ports.js';
12
15
  export * from './recovery.ports.js';
13
16
  export * from './metrics-collector.port.js';
14
17
  export * from './dashboard-renderer.port.js';
18
+ export * from './core-tools.ports.js';
19
+ export * from './wu-helpers.ports.js';
20
+ export * from './git-validator.ports.js';
@@ -2,8 +2,11 @@
2
2
  * Ports Index
3
3
  *
4
4
  * WU-1093: INIT-002 Phase 1 - Define ports and domain schemas
5
+ * WU-1101: INIT-003 Phase 2a - Add core tools ports
6
+ * WU-1103: INIT-003 Phase 2c - Add git adapter and validator ports
5
7
  *
6
- * Re-exports all port interfaces for the context-aware validation system.
8
+ * Re-exports all port interfaces for the context-aware validation system
9
+ * and core tools (tool-runner, worktree-guard, scope-checker).
7
10
  *
8
11
  * @module ports
9
12
  */
@@ -12,3 +15,6 @@ export * from './validation.ports.js';
12
15
  export * from './recovery.ports.js';
13
16
  export * from './metrics-collector.port.js';
14
17
  export * from './dashboard-renderer.port.js';
18
+ export * from './core-tools.ports.js';
19
+ export * from './wu-helpers.ports.js';
20
+ export * from './git-validator.ports.js';
@@ -0,0 +1,224 @@
1
+ /**
2
+ * WU Helpers Port Interfaces
3
+ *
4
+ * WU-1102: INIT-003 Phase 2b - Port interfaces for WU helper modules
5
+ *
6
+ * Hexagonal Architecture - Input Ports:
7
+ * These abstractions allow external users to inject custom implementations
8
+ * for WU lifecycle operations.
9
+ *
10
+ * @module ports/wu-helpers
11
+ */
12
+ /**
13
+ * Git adapter interface for ensureOnMain and ensureMainUpToDate operations
14
+ */
15
+ export interface IWuGitAdapter {
16
+ /**
17
+ * Get the current git branch name
18
+ * @returns Promise resolving to branch name
19
+ */
20
+ getCurrentBranch(): Promise<string>;
21
+ /**
22
+ * Fetch from a remote
23
+ * @param remote - Remote name (e.g., 'origin')
24
+ * @param branch - Branch name (e.g., 'main')
25
+ */
26
+ fetch(remote: string, branch: string): Promise<void>;
27
+ /**
28
+ * Get commit hash for a ref
29
+ * @param ref - Git ref (branch, tag, or commit)
30
+ * @returns Promise resolving to commit hash
31
+ */
32
+ getCommitHash(ref: string): Promise<string>;
33
+ }
34
+ /**
35
+ * WU status check result
36
+ */
37
+ export interface IWuStatusCheckResult {
38
+ /** Whether the operation is allowed */
39
+ allowed: boolean;
40
+ /** Current WU status */
41
+ status: string | null;
42
+ /** Error message if not allowed */
43
+ error: string | null;
44
+ }
45
+ /**
46
+ * Branch validation result
47
+ */
48
+ export interface IBranchValidationResult {
49
+ /** Whether the branch name is valid */
50
+ valid: boolean;
51
+ /** Lane name (kebab-case) or null */
52
+ lane: string | null;
53
+ /** WU ID (uppercase) or null */
54
+ wuid: string | null;
55
+ /** Error message if invalid */
56
+ error: string | null;
57
+ }
58
+ /**
59
+ * WU YAML reader interface
60
+ */
61
+ export interface IWuYamlReader {
62
+ /**
63
+ * Read and parse WU YAML file
64
+ * @param wuPath - Path to WU YAML file
65
+ * @param expectedId - Expected WU ID for validation
66
+ * @returns Parsed WU document
67
+ */
68
+ readWU(wuPath: string, expectedId: string): unknown;
69
+ /**
70
+ * Read YAML file without ID validation
71
+ * @param yamlPath - Path to YAML file
72
+ * @returns Parsed document
73
+ */
74
+ readWURaw(yamlPath: string): unknown;
75
+ }
76
+ /**
77
+ * WU YAML writer interface
78
+ */
79
+ export interface IWuYamlWriter {
80
+ /**
81
+ * Write WU document to file
82
+ * @param wuPath - Path to WU YAML file
83
+ * @param doc - Document to write
84
+ */
85
+ writeWU(wuPath: string, doc: unknown): void;
86
+ }
87
+ /**
88
+ * WU state store interface (subset for dependency injection)
89
+ */
90
+ export interface IWuStateStore {
91
+ /**
92
+ * Load state from events file
93
+ */
94
+ load(): Promise<void>;
95
+ /**
96
+ * Claim a WU (transition to in_progress)
97
+ * @param wuId - WU identifier
98
+ * @param lane - Lane name
99
+ * @param title - WU title
100
+ */
101
+ claim(wuId: string, lane: string, title: string): Promise<void>;
102
+ /**
103
+ * Complete a WU (transition to done)
104
+ * @param wuId - WU identifier
105
+ */
106
+ complete(wuId: string): Promise<void>;
107
+ /**
108
+ * Block a WU (transition to blocked)
109
+ * @param wuId - WU identifier
110
+ * @param reason - Block reason
111
+ */
112
+ block(wuId: string, reason: string): Promise<void>;
113
+ /**
114
+ * Unblock a WU (transition back to in_progress)
115
+ * @param wuId - WU identifier
116
+ */
117
+ unblock(wuId: string): Promise<void>;
118
+ /**
119
+ * Release a WU (transition to ready)
120
+ * @param wuId - WU identifier
121
+ * @param reason - Release reason
122
+ */
123
+ release(wuId: string, reason: string): Promise<void>;
124
+ /**
125
+ * Get WU state entry
126
+ * @param wuId - WU identifier
127
+ * @returns State entry or undefined
128
+ */
129
+ getWUState(wuId: string): {
130
+ status: string;
131
+ lane: string;
132
+ title: string;
133
+ } | undefined;
134
+ /**
135
+ * Get all WU IDs by status
136
+ * @param status - Status to filter by
137
+ * @returns Set of WU IDs
138
+ */
139
+ getByStatus(status: string): Set<string>;
140
+ /**
141
+ * Get all WU IDs by lane
142
+ * @param lane - Lane to filter by
143
+ * @returns Set of WU IDs
144
+ */
145
+ getByLane(lane: string): Set<string>;
146
+ }
147
+ /**
148
+ * WU checkpoint interface
149
+ */
150
+ export interface IWuCheckpointManager {
151
+ /**
152
+ * Create a pre-gates checkpoint
153
+ */
154
+ createPreGatesCheckpoint(params: {
155
+ wuId: string;
156
+ worktreePath: string;
157
+ branchName: string;
158
+ gatesPassed?: boolean;
159
+ }, options?: {
160
+ baseDir?: string;
161
+ }): Promise<{
162
+ checkpointId: string;
163
+ gatesPassed: boolean;
164
+ }>;
165
+ /**
166
+ * Mark checkpoint as gates passed
167
+ * @param wuId - WU identifier
168
+ * @returns True if updated
169
+ */
170
+ markGatesPassed(wuId: string, options?: {
171
+ baseDir?: string;
172
+ }): boolean;
173
+ /**
174
+ * Get checkpoint for a WU
175
+ * @param wuId - WU identifier
176
+ * @returns Checkpoint or null
177
+ */
178
+ getCheckpoint(wuId: string, options?: {
179
+ baseDir?: string;
180
+ }): {
181
+ gatesPassed: boolean;
182
+ worktreeHeadSha: string;
183
+ } | null;
184
+ /**
185
+ * Clear checkpoint
186
+ * @param wuId - WU identifier
187
+ */
188
+ clearCheckpoint(wuId: string, options?: {
189
+ baseDir?: string;
190
+ }): void;
191
+ /**
192
+ * Check if gates can be skipped
193
+ */
194
+ canSkipGates(wuId: string, options?: {
195
+ baseDir?: string;
196
+ currentHeadSha?: string;
197
+ }): {
198
+ canSkip: boolean;
199
+ reason?: string;
200
+ };
201
+ }
202
+ /**
203
+ * WU paths interface
204
+ */
205
+ export interface IWuPaths {
206
+ /** Get path to WU YAML file */
207
+ WU(id: string): string;
208
+ /** Get path to WU directory */
209
+ WU_DIR(): string;
210
+ /** Get path to status.md */
211
+ STATUS(): string;
212
+ /** Get path to backlog.md */
213
+ BACKLOG(): string;
214
+ /** Get path to stamps directory */
215
+ STAMPS_DIR(): string;
216
+ /** Get path to WU done stamp file */
217
+ STAMP(id: string): string;
218
+ /** Get path to state directory */
219
+ STATE_DIR(): string;
220
+ /** Get path to initiatives directory */
221
+ INITIATIVES_DIR(): string;
222
+ /** Get path to worktrees directory */
223
+ WORKTREES_DIR(): string;
224
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * WU Helpers Port Interfaces
3
+ *
4
+ * WU-1102: INIT-003 Phase 2b - Port interfaces for WU helper modules
5
+ *
6
+ * Hexagonal Architecture - Input Ports:
7
+ * These abstractions allow external users to inject custom implementations
8
+ * for WU lifecycle operations.
9
+ *
10
+ * @module ports/wu-helpers
11
+ */
12
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/core",
3
- "version": "1.6.0",
3
+ "version": "2.1.0",
4
4
  "description": "Core WU lifecycle tools for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -78,7 +78,9 @@
78
78
  "micromatch": "^4.0.8",
79
79
  "minimatch": "^10.1.1",
80
80
  "ms": "^2.1.3",
81
+ "nhs-number-validator": "^1.1.2",
81
82
  "picocolors": "^1.1.1",
83
+ "postcode": "^5.1.0",
82
84
  "pretty-ms": "^9.2.0",
83
85
  "ps-list": "^8.1.1",
84
86
  "simple-git": "^3.30.0",
@@ -92,7 +94,7 @@
92
94
  "vitest": "^4.0.17"
93
95
  },
94
96
  "peerDependencies": {
95
- "@lumenflow/memory": "1.6.0"
97
+ "@lumenflow/memory": "2.1.0"
96
98
  },
97
99
  "peerDependenciesMeta": {
98
100
  "@lumenflow/memory": {