@lumenflow/core 1.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 (263) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +119 -0
  3. package/dist/active-wu-detector.d.ts +33 -0
  4. package/dist/active-wu-detector.js +106 -0
  5. package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
  6. package/dist/adapters/filesystem-metrics.adapter.js +519 -0
  7. package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
  8. package/dist/adapters/terminal-renderer.adapter.js +337 -0
  9. package/dist/arg-parser.d.ts +63 -0
  10. package/dist/arg-parser.js +560 -0
  11. package/dist/backlog-editor.d.ts +98 -0
  12. package/dist/backlog-editor.js +179 -0
  13. package/dist/backlog-generator.d.ts +111 -0
  14. package/dist/backlog-generator.js +381 -0
  15. package/dist/backlog-parser.d.ts +45 -0
  16. package/dist/backlog-parser.js +102 -0
  17. package/dist/backlog-sync-validator.d.ts +78 -0
  18. package/dist/backlog-sync-validator.js +294 -0
  19. package/dist/branch-drift.d.ts +34 -0
  20. package/dist/branch-drift.js +51 -0
  21. package/dist/cleanup-install-config.d.ts +33 -0
  22. package/dist/cleanup-install-config.js +37 -0
  23. package/dist/cleanup-lock.d.ts +139 -0
  24. package/dist/cleanup-lock.js +313 -0
  25. package/dist/code-path-validator.d.ts +146 -0
  26. package/dist/code-path-validator.js +537 -0
  27. package/dist/code-paths-overlap.d.ts +55 -0
  28. package/dist/code-paths-overlap.js +245 -0
  29. package/dist/commands-logger.d.ts +77 -0
  30. package/dist/commands-logger.js +254 -0
  31. package/dist/commit-message-utils.d.ts +25 -0
  32. package/dist/commit-message-utils.js +41 -0
  33. package/dist/compliance-parser.d.ts +150 -0
  34. package/dist/compliance-parser.js +507 -0
  35. package/dist/constants/backlog-patterns.d.ts +20 -0
  36. package/dist/constants/backlog-patterns.js +23 -0
  37. package/dist/constants/dora-constants.d.ts +49 -0
  38. package/dist/constants/dora-constants.js +53 -0
  39. package/dist/constants/gate-constants.d.ts +15 -0
  40. package/dist/constants/gate-constants.js +15 -0
  41. package/dist/constants/linter-constants.d.ts +16 -0
  42. package/dist/constants/linter-constants.js +16 -0
  43. package/dist/constants/tokenizer-constants.d.ts +15 -0
  44. package/dist/constants/tokenizer-constants.js +15 -0
  45. package/dist/core/scope-checker.d.ts +97 -0
  46. package/dist/core/scope-checker.js +163 -0
  47. package/dist/core/tool-runner.d.ts +161 -0
  48. package/dist/core/tool-runner.js +393 -0
  49. package/dist/core/tool.constants.d.ts +105 -0
  50. package/dist/core/tool.constants.js +101 -0
  51. package/dist/core/tool.schemas.d.ts +226 -0
  52. package/dist/core/tool.schemas.js +226 -0
  53. package/dist/core/worktree-guard.d.ts +130 -0
  54. package/dist/core/worktree-guard.js +242 -0
  55. package/dist/coverage-gate.d.ts +108 -0
  56. package/dist/coverage-gate.js +196 -0
  57. package/dist/date-utils.d.ts +75 -0
  58. package/dist/date-utils.js +140 -0
  59. package/dist/dependency-graph.d.ts +142 -0
  60. package/dist/dependency-graph.js +550 -0
  61. package/dist/dependency-guard.d.ts +54 -0
  62. package/dist/dependency-guard.js +142 -0
  63. package/dist/dependency-validator.d.ts +105 -0
  64. package/dist/dependency-validator.js +154 -0
  65. package/dist/docs-path-validator.d.ts +36 -0
  66. package/dist/docs-path-validator.js +95 -0
  67. package/dist/domain/orchestration.constants.d.ts +99 -0
  68. package/dist/domain/orchestration.constants.js +97 -0
  69. package/dist/domain/orchestration.schemas.d.ts +280 -0
  70. package/dist/domain/orchestration.schemas.js +211 -0
  71. package/dist/domain/orchestration.types.d.ts +133 -0
  72. package/dist/domain/orchestration.types.js +12 -0
  73. package/dist/error-handler.d.ts +116 -0
  74. package/dist/error-handler.js +136 -0
  75. package/dist/file-classifiers.d.ts +62 -0
  76. package/dist/file-classifiers.js +108 -0
  77. package/dist/gates-agent-mode.d.ts +81 -0
  78. package/dist/gates-agent-mode.js +94 -0
  79. package/dist/generate-traceability.d.ts +107 -0
  80. package/dist/generate-traceability.js +411 -0
  81. package/dist/git-adapter.d.ts +395 -0
  82. package/dist/git-adapter.js +649 -0
  83. package/dist/git-staged-validator.d.ts +32 -0
  84. package/dist/git-staged-validator.js +48 -0
  85. package/dist/hardcoded-strings.d.ts +61 -0
  86. package/dist/hardcoded-strings.js +270 -0
  87. package/dist/incremental-lint.d.ts +78 -0
  88. package/dist/incremental-lint.js +129 -0
  89. package/dist/incremental-test.d.ts +39 -0
  90. package/dist/incremental-test.js +61 -0
  91. package/dist/index.d.ts +42 -0
  92. package/dist/index.js +61 -0
  93. package/dist/invariants/check-automated-tests.d.ts +50 -0
  94. package/dist/invariants/check-automated-tests.js +166 -0
  95. package/dist/invariants-runner.d.ts +103 -0
  96. package/dist/invariants-runner.js +527 -0
  97. package/dist/lane-checker.d.ts +50 -0
  98. package/dist/lane-checker.js +319 -0
  99. package/dist/lane-inference.d.ts +39 -0
  100. package/dist/lane-inference.js +195 -0
  101. package/dist/lane-lock.d.ts +211 -0
  102. package/dist/lane-lock.js +474 -0
  103. package/dist/lane-validator.d.ts +48 -0
  104. package/dist/lane-validator.js +114 -0
  105. package/dist/logs-lib.d.ts +104 -0
  106. package/dist/logs-lib.js +207 -0
  107. package/dist/lumenflow-config-schema.d.ts +272 -0
  108. package/dist/lumenflow-config-schema.js +207 -0
  109. package/dist/lumenflow-config.d.ts +95 -0
  110. package/dist/lumenflow-config.js +236 -0
  111. package/dist/manual-test-validator.d.ts +80 -0
  112. package/dist/manual-test-validator.js +200 -0
  113. package/dist/merge-lock.d.ts +115 -0
  114. package/dist/merge-lock.js +251 -0
  115. package/dist/micro-worktree.d.ts +159 -0
  116. package/dist/micro-worktree.js +427 -0
  117. package/dist/migration-deployer.d.ts +69 -0
  118. package/dist/migration-deployer.js +151 -0
  119. package/dist/orchestration-advisory-loader.d.ts +28 -0
  120. package/dist/orchestration-advisory-loader.js +87 -0
  121. package/dist/orchestration-advisory.d.ts +58 -0
  122. package/dist/orchestration-advisory.js +94 -0
  123. package/dist/orchestration-di.d.ts +48 -0
  124. package/dist/orchestration-di.js +57 -0
  125. package/dist/orchestration-rules.d.ts +57 -0
  126. package/dist/orchestration-rules.js +201 -0
  127. package/dist/orphan-detector.d.ts +131 -0
  128. package/dist/orphan-detector.js +226 -0
  129. package/dist/path-classifiers.d.ts +57 -0
  130. package/dist/path-classifiers.js +93 -0
  131. package/dist/piped-command-detector.d.ts +34 -0
  132. package/dist/piped-command-detector.js +64 -0
  133. package/dist/ports/dashboard-renderer.port.d.ts +112 -0
  134. package/dist/ports/dashboard-renderer.port.js +25 -0
  135. package/dist/ports/metrics-collector.port.d.ts +132 -0
  136. package/dist/ports/metrics-collector.port.js +26 -0
  137. package/dist/process-detector.d.ts +84 -0
  138. package/dist/process-detector.js +172 -0
  139. package/dist/prompt-linter.d.ts +72 -0
  140. package/dist/prompt-linter.js +312 -0
  141. package/dist/prompt-monitor.d.ts +15 -0
  142. package/dist/prompt-monitor.js +205 -0
  143. package/dist/rebase-artifact-cleanup.d.ts +145 -0
  144. package/dist/rebase-artifact-cleanup.js +433 -0
  145. package/dist/retry-strategy.d.ts +189 -0
  146. package/dist/retry-strategy.js +283 -0
  147. package/dist/risk-detector.d.ts +108 -0
  148. package/dist/risk-detector.js +252 -0
  149. package/dist/rollback-utils.d.ts +76 -0
  150. package/dist/rollback-utils.js +104 -0
  151. package/dist/section-headings.d.ts +43 -0
  152. package/dist/section-headings.js +49 -0
  153. package/dist/spawn-escalation.d.ts +90 -0
  154. package/dist/spawn-escalation.js +253 -0
  155. package/dist/spawn-monitor.d.ts +229 -0
  156. package/dist/spawn-monitor.js +672 -0
  157. package/dist/spawn-recovery.d.ts +82 -0
  158. package/dist/spawn-recovery.js +298 -0
  159. package/dist/spawn-registry-schema.d.ts +98 -0
  160. package/dist/spawn-registry-schema.js +108 -0
  161. package/dist/spawn-registry-store.d.ts +146 -0
  162. package/dist/spawn-registry-store.js +273 -0
  163. package/dist/spawn-tree.d.ts +121 -0
  164. package/dist/spawn-tree.js +285 -0
  165. package/dist/stamp-status-validator.d.ts +84 -0
  166. package/dist/stamp-status-validator.js +134 -0
  167. package/dist/stamp-utils.d.ts +100 -0
  168. package/dist/stamp-utils.js +229 -0
  169. package/dist/state-machine.d.ts +26 -0
  170. package/dist/state-machine.js +83 -0
  171. package/dist/system-map-validator.d.ts +80 -0
  172. package/dist/system-map-validator.js +272 -0
  173. package/dist/telemetry.d.ts +80 -0
  174. package/dist/telemetry.js +213 -0
  175. package/dist/token-counter.d.ts +51 -0
  176. package/dist/token-counter.js +145 -0
  177. package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
  178. package/dist/usecases/get-dashboard-data.usecase.js +61 -0
  179. package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
  180. package/dist/usecases/get-suggestions.usecase.js +153 -0
  181. package/dist/user-normalizer.d.ts +41 -0
  182. package/dist/user-normalizer.js +141 -0
  183. package/dist/validators/phi-constants.d.ts +97 -0
  184. package/dist/validators/phi-constants.js +152 -0
  185. package/dist/validators/phi-scanner.d.ts +58 -0
  186. package/dist/validators/phi-scanner.js +215 -0
  187. package/dist/worktree-ownership.d.ts +50 -0
  188. package/dist/worktree-ownership.js +74 -0
  189. package/dist/worktree-scanner.d.ts +103 -0
  190. package/dist/worktree-scanner.js +168 -0
  191. package/dist/worktree-symlink.d.ts +99 -0
  192. package/dist/worktree-symlink.js +359 -0
  193. package/dist/wu-backlog-updater.d.ts +17 -0
  194. package/dist/wu-backlog-updater.js +37 -0
  195. package/dist/wu-checkpoint.d.ts +124 -0
  196. package/dist/wu-checkpoint.js +233 -0
  197. package/dist/wu-claim-helpers.d.ts +26 -0
  198. package/dist/wu-claim-helpers.js +63 -0
  199. package/dist/wu-claim-resume.d.ts +106 -0
  200. package/dist/wu-claim-resume.js +276 -0
  201. package/dist/wu-consistency-checker.d.ts +95 -0
  202. package/dist/wu-consistency-checker.js +567 -0
  203. package/dist/wu-constants.d.ts +1275 -0
  204. package/dist/wu-constants.js +1382 -0
  205. package/dist/wu-create-validators.d.ts +42 -0
  206. package/dist/wu-create-validators.js +93 -0
  207. package/dist/wu-done-branch-only.d.ts +63 -0
  208. package/dist/wu-done-branch-only.js +191 -0
  209. package/dist/wu-done-messages.d.ts +119 -0
  210. package/dist/wu-done-messages.js +185 -0
  211. package/dist/wu-done-pr.d.ts +72 -0
  212. package/dist/wu-done-pr.js +174 -0
  213. package/dist/wu-done-retry-helpers.d.ts +85 -0
  214. package/dist/wu-done-retry-helpers.js +172 -0
  215. package/dist/wu-done-ui.d.ts +37 -0
  216. package/dist/wu-done-ui.js +69 -0
  217. package/dist/wu-done-validators.d.ts +411 -0
  218. package/dist/wu-done-validators.js +1229 -0
  219. package/dist/wu-done-worktree.d.ts +182 -0
  220. package/dist/wu-done-worktree.js +1097 -0
  221. package/dist/wu-helpers.d.ts +128 -0
  222. package/dist/wu-helpers.js +248 -0
  223. package/dist/wu-lint.d.ts +70 -0
  224. package/dist/wu-lint.js +234 -0
  225. package/dist/wu-paths.d.ts +171 -0
  226. package/dist/wu-paths.js +178 -0
  227. package/dist/wu-preflight-validators.d.ts +86 -0
  228. package/dist/wu-preflight-validators.js +251 -0
  229. package/dist/wu-recovery.d.ts +138 -0
  230. package/dist/wu-recovery.js +341 -0
  231. package/dist/wu-repair-core.d.ts +131 -0
  232. package/dist/wu-repair-core.js +669 -0
  233. package/dist/wu-schema-normalization.d.ts +17 -0
  234. package/dist/wu-schema-normalization.js +82 -0
  235. package/dist/wu-schema.d.ts +793 -0
  236. package/dist/wu-schema.js +881 -0
  237. package/dist/wu-spawn-helpers.d.ts +121 -0
  238. package/dist/wu-spawn-helpers.js +271 -0
  239. package/dist/wu-spawn.d.ts +158 -0
  240. package/dist/wu-spawn.js +1306 -0
  241. package/dist/wu-state-schema.d.ts +213 -0
  242. package/dist/wu-state-schema.js +156 -0
  243. package/dist/wu-state-store.d.ts +264 -0
  244. package/dist/wu-state-store.js +691 -0
  245. package/dist/wu-status-transition.d.ts +63 -0
  246. package/dist/wu-status-transition.js +382 -0
  247. package/dist/wu-status-updater.d.ts +25 -0
  248. package/dist/wu-status-updater.js +116 -0
  249. package/dist/wu-transaction-collectors.d.ts +116 -0
  250. package/dist/wu-transaction-collectors.js +272 -0
  251. package/dist/wu-transaction.d.ts +170 -0
  252. package/dist/wu-transaction.js +273 -0
  253. package/dist/wu-validation-constants.d.ts +60 -0
  254. package/dist/wu-validation-constants.js +66 -0
  255. package/dist/wu-validation.d.ts +118 -0
  256. package/dist/wu-validation.js +243 -0
  257. package/dist/wu-validator.d.ts +62 -0
  258. package/dist/wu-validator.js +325 -0
  259. package/dist/wu-yaml-fixer.d.ts +97 -0
  260. package/dist/wu-yaml-fixer.js +264 -0
  261. package/dist/wu-yaml.d.ts +86 -0
  262. package/dist/wu-yaml.js +222 -0
  263. package/package.json +114 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * GetSuggestions Use Case
3
+ *
4
+ * Applies orchestration rules to generate recommendations for next actions.
5
+ * Combines WU progress analysis with code path detection.
6
+ *
7
+ * @module get-suggestions.usecase
8
+ * @see {@link ../orchestration-rules.ts} - Rule functions
9
+ * @see {@link ../ports/metrics-collector.port.ts} - Port interface
10
+ */
11
+ import type { IMetricsCollector } from '../ports/metrics-collector.port.js';
12
+ import type { Suggestion } from '../domain/orchestration.types.js';
13
+ /**
14
+ * Bottleneck scores mapping WU IDs to their impact scores.
15
+ * Impact score = number of downstream WUs blocked by this WU.
16
+ */
17
+ export type BottleneckScores = Record<string, number>;
18
+ /**
19
+ * Options for the GetSuggestions use case.
20
+ */
21
+ export interface GetSuggestionsOptions {
22
+ /**
23
+ * Code paths to analyse for mandatory agent triggers.
24
+ * When provided, detects which agents should be invoked.
25
+ */
26
+ codePaths?: string[];
27
+ /**
28
+ * Bottleneck impact scores from flow:bottlenecks analysis.
29
+ * When provided, suggestions for high-impact WUs are ranked higher
30
+ * within the same priority level.
31
+ *
32
+ * @see flow-bottlenecks.mjs for score calculation
33
+ */
34
+ bottleneckScores?: BottleneckScores;
35
+ }
36
+ /**
37
+ * Use case for generating orchestration suggestions.
38
+ *
39
+ * Combines two sources of suggestions:
40
+ * 1. WU progress analysis - suggests agents based on current state
41
+ * 2. Code path analysis - detects mandatory agents from file patterns
42
+ *
43
+ * @example
44
+ * const collector = new FileSystemMetricsCollector(basePath);
45
+ * const useCase = new GetSuggestionsUseCase(collector);
46
+ *
47
+ * // Get suggestions based on current WU state
48
+ * const suggestions = await useCase.execute();
49
+ *
50
+ * // Get suggestions including code path analysis
51
+ * const suggestionsWithPaths = await useCase.execute({
52
+ * codePaths: ['supabase/migrations/001.sql', 'src/prompts/system.ts']
53
+ * });
54
+ */
55
+ export declare class GetSuggestionsUseCase {
56
+ private readonly metricsCollector;
57
+ constructor(metricsCollector: IMetricsCollector);
58
+ /**
59
+ * Execute the use case to generate suggestions.
60
+ *
61
+ * @param options - Optional configuration including code paths and bottleneck scores
62
+ * @returns Promise resolving to prioritised suggestions
63
+ * @throws Error if collector methods fail
64
+ */
65
+ execute(options?: GetSuggestionsOptions): Promise<Suggestion[]>;
66
+ /**
67
+ * Enrich suggestions with impact score information in reason field.
68
+ *
69
+ * @param suggestions - Suggestions to enrich
70
+ * @param bottleneckScores - WU ID to impact score mapping
71
+ * @returns Enriched suggestions
72
+ */
73
+ private enrichWithImpactScores;
74
+ /**
75
+ * Extract WU ID from a suggestion command.
76
+ *
77
+ * @param command - Command string containing WU ID
78
+ * @returns WU ID or null if not found
79
+ */
80
+ private extractWuIdFromCommand;
81
+ /**
82
+ * Sort suggestions by priority, then by impact score within same priority.
83
+ *
84
+ * @param suggestions - Suggestions to sort
85
+ * @param bottleneckScores - WU ID to impact score mapping
86
+ * @returns Sorted suggestions
87
+ */
88
+ private sortSuggestions;
89
+ /**
90
+ * Generate suggestions for mandatory agents detected from code paths.
91
+ *
92
+ * Avoids duplicating suggestions that already exist from WU progress analysis.
93
+ *
94
+ * @param mandatoryAgents - Agents detected from code paths
95
+ * @param existingSuggestions - Suggestions already generated
96
+ * @param wuId - WU ID for the suggestion
97
+ * @returns Additional suggestions for mandatory agents
98
+ */
99
+ private generateMandatoryAgentSuggestions;
100
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * GetSuggestions Use Case
3
+ *
4
+ * Applies orchestration rules to generate recommendations for next actions.
5
+ * Combines WU progress analysis with code path detection.
6
+ *
7
+ * @module get-suggestions.usecase
8
+ * @see {@link ../orchestration-rules.ts} - Rule functions
9
+ * @see {@link ../ports/metrics-collector.port.ts} - Port interface
10
+ */
11
+ import { detectMandatoryAgents, generateSuggestions } from '../orchestration-rules.js';
12
+ /**
13
+ * Use case for generating orchestration suggestions.
14
+ *
15
+ * Combines two sources of suggestions:
16
+ * 1. WU progress analysis - suggests agents based on current state
17
+ * 2. Code path analysis - detects mandatory agents from file patterns
18
+ *
19
+ * @example
20
+ * const collector = new FileSystemMetricsCollector(basePath);
21
+ * const useCase = new GetSuggestionsUseCase(collector);
22
+ *
23
+ * // Get suggestions based on current WU state
24
+ * const suggestions = await useCase.execute();
25
+ *
26
+ * // Get suggestions including code path analysis
27
+ * const suggestionsWithPaths = await useCase.execute({
28
+ * codePaths: ['supabase/migrations/001.sql', 'src/prompts/system.ts']
29
+ * });
30
+ */
31
+ export class GetSuggestionsUseCase {
32
+ metricsCollector;
33
+ constructor(metricsCollector) {
34
+ this.metricsCollector = metricsCollector;
35
+ }
36
+ /**
37
+ * Execute the use case to generate suggestions.
38
+ *
39
+ * @param options - Optional configuration including code paths and bottleneck scores
40
+ * @returns Promise resolving to prioritised suggestions
41
+ * @throws Error if collector methods fail
42
+ */
43
+ async execute(options = {}) {
44
+ const { codePaths = [], bottleneckScores = {} } = options;
45
+ const [wuProgress, agentMetrics] = await Promise.all([
46
+ this.metricsCollector.getWUProgress(),
47
+ this.metricsCollector.getAgentMetrics(),
48
+ ]);
49
+ // Generate suggestions from WU progress
50
+ let progressSuggestions = generateSuggestions(wuProgress, agentMetrics);
51
+ // Enrich suggestions with impact scores if available (WU-1596)
52
+ progressSuggestions = this.enrichWithImpactScores(progressSuggestions, bottleneckScores);
53
+ // Detect mandatory agents from code paths
54
+ const mandatoryAgents = detectMandatoryAgents(codePaths);
55
+ // Generate additional suggestions for detected mandatory agents
56
+ const pathSuggestions = this.generateMandatoryAgentSuggestions(mandatoryAgents, progressSuggestions, wuProgress.length > 0 ? wuProgress[0].wuId : 'current');
57
+ // Combine and deduplicate suggestions
58
+ const allSuggestions = [...progressSuggestions, ...pathSuggestions];
59
+ // Sort by priority (high > medium > low), then by impact score (descending)
60
+ return this.sortSuggestions(allSuggestions, bottleneckScores);
61
+ }
62
+ /**
63
+ * Enrich suggestions with impact score information in reason field.
64
+ *
65
+ * @param suggestions - Suggestions to enrich
66
+ * @param bottleneckScores - WU ID to impact score mapping
67
+ * @returns Enriched suggestions
68
+ */
69
+ enrichWithImpactScores(suggestions, bottleneckScores) {
70
+ if (Object.keys(bottleneckScores).length === 0) {
71
+ return suggestions;
72
+ }
73
+ return suggestions.map((suggestion) => {
74
+ const wuId = this.extractWuIdFromCommand(suggestion.command);
75
+ if (!wuId) {
76
+ return suggestion;
77
+ }
78
+ const impactScore = bottleneckScores[wuId];
79
+ if (impactScore === undefined || impactScore === 0) {
80
+ return suggestion;
81
+ }
82
+ // Enrich reason with impact score
83
+ return {
84
+ ...suggestion,
85
+ reason: `${suggestion.reason} (blocks ${impactScore} downstream WU${impactScore === 1 ? '' : 's'})`,
86
+ };
87
+ });
88
+ }
89
+ /**
90
+ * Extract WU ID from a suggestion command.
91
+ *
92
+ * @param command - Command string containing WU ID
93
+ * @returns WU ID or null if not found
94
+ */
95
+ extractWuIdFromCommand(command) {
96
+ if (!command) {
97
+ return null;
98
+ }
99
+ const match = command.match(/WU-\d+/);
100
+ return match ? match[0] : null;
101
+ }
102
+ /**
103
+ * Sort suggestions by priority, then by impact score within same priority.
104
+ *
105
+ * @param suggestions - Suggestions to sort
106
+ * @param bottleneckScores - WU ID to impact score mapping
107
+ * @returns Sorted suggestions
108
+ */
109
+ sortSuggestions(suggestions, bottleneckScores) {
110
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
111
+ return suggestions.sort((a, b) => {
112
+ // First sort by priority
113
+ const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
114
+ if (priorityDiff !== 0) {
115
+ return priorityDiff;
116
+ }
117
+ // Within same priority, sort by impact score (higher score first)
118
+ const aWuId = this.extractWuIdFromCommand(a.command);
119
+ const bWuId = this.extractWuIdFromCommand(b.command);
120
+ const aScore = aWuId ? (bottleneckScores[aWuId] ?? 0) : 0;
121
+ const bScore = bWuId ? (bottleneckScores[bWuId] ?? 0) : 0;
122
+ return bScore - aScore; // Descending order (higher score first)
123
+ });
124
+ }
125
+ /**
126
+ * Generate suggestions for mandatory agents detected from code paths.
127
+ *
128
+ * Avoids duplicating suggestions that already exist from WU progress analysis.
129
+ *
130
+ * @param mandatoryAgents - Agents detected from code paths
131
+ * @param existingSuggestions - Suggestions already generated
132
+ * @param wuId - WU ID for the suggestion
133
+ * @returns Additional suggestions for mandatory agents
134
+ */
135
+ generateMandatoryAgentSuggestions(mandatoryAgents, existingSuggestions, wuId) {
136
+ const suggestions = [];
137
+ let nextId = existingSuggestions.length + 1;
138
+ for (const agentName of mandatoryAgents) {
139
+ // Check if suggestion already exists for this agent
140
+ const alreadyExists = existingSuggestions.some((s) => s.action.toLowerCase().includes(agentName));
141
+ if (!alreadyExists) {
142
+ suggestions.push({
143
+ id: `sug-${(nextId++).toString().padStart(3, '0')}`,
144
+ priority: 'high',
145
+ action: `Run ${agentName}`,
146
+ reason: `Code paths indicate ${agentName} is required`,
147
+ command: `pnpm orchestrate:run ${agentName} --wu ${wuId}`,
148
+ });
149
+ }
150
+ }
151
+ return suggestions;
152
+ }
153
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * WU-1333: User normalizer utilities
3
+ *
4
+ * Provides email normalization and domain inference for WU ownership.
5
+ * Converts plain usernames (e.g., "tom") to email format (e.g., "tom@hellm.ai")
6
+ * using domain from git config or .lumenflow.config.yaml.
7
+ */
8
+ /**
9
+ * Default domain fallback when git config and lumenflow config unavailable
10
+ */
11
+ export declare const DEFAULT_DOMAIN = "patientpath.co.uk";
12
+ /**
13
+ * Check if a value is a valid email address (simple check)
14
+ *
15
+ * @param {string|null|undefined} value - Value to check
16
+ * @returns {boolean} True if value is a valid email
17
+ */
18
+ export declare function isValidEmail(value: any): boolean;
19
+ /**
20
+ * Infer the default email domain from available sources
21
+ *
22
+ * Priority:
23
+ * 1. Git config user.email domain
24
+ * 2. .lumenflow.config.yaml OWNER_EMAIL domain
25
+ * 3. DEFAULT_DOMAIN constant
26
+ *
27
+ * @param {string} [cwd] - Working directory for config lookup
28
+ * @returns {Promise<string>} Inferred domain (never null/undefined)
29
+ */
30
+ export declare function inferDefaultDomain(cwd?: string): Promise<any>;
31
+ /**
32
+ * Normalize a username or email to full email format
33
+ *
34
+ * If value is already a valid email, returns it unchanged (with normalization).
35
+ * If value is a plain username, appends the default domain.
36
+ *
37
+ * @param {string|null|undefined} value - Username or email
38
+ * @param {string} [domain] - Optional domain override
39
+ * @returns {Promise<string>} Normalized email address, or empty string for null/undefined
40
+ */
41
+ export declare function normalizeToEmail(value: any, domain: any): Promise<string>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * WU-1333: User normalizer utilities
3
+ *
4
+ * Provides email normalization and domain inference for WU ownership.
5
+ * Converts plain usernames (e.g., "tom") to email format (e.g., "tom@hellm.ai")
6
+ * using domain from git config or .lumenflow.config.yaml.
7
+ */
8
+ import { readFile, access } from 'node:fs/promises';
9
+ import { join } from 'node:path';
10
+ import { getGitForCwd } from './git-adapter.js';
11
+ /**
12
+ * Default domain fallback when git config and lumenflow config unavailable
13
+ */
14
+ export const DEFAULT_DOMAIN = 'patientpath.co.uk';
15
+ /**
16
+ * Minimum length for a valid email local part
17
+ */
18
+ const MIN_LOCAL_PART_LENGTH = 1;
19
+ /**
20
+ * Check if a value is a valid email address (simple check)
21
+ *
22
+ * @param {string|null|undefined} value - Value to check
23
+ * @returns {boolean} True if value is a valid email
24
+ */
25
+ export function isValidEmail(value) {
26
+ if (!value || typeof value !== 'string') {
27
+ return false;
28
+ }
29
+ const str = value.trim();
30
+ // Simple email validation: must have @ with content before and after
31
+ const atIndex = str.indexOf('@');
32
+ return atIndex > 0 && atIndex < str.length - 1;
33
+ }
34
+ /**
35
+ * Extract domain from an email address
36
+ *
37
+ * @param {string} email - Email address
38
+ * @returns {string|null} Domain part or null if invalid
39
+ */
40
+ function extractDomain(email) {
41
+ if (!isValidEmail(email)) {
42
+ return null;
43
+ }
44
+ const atIndex = email.indexOf('@');
45
+ return email.slice(atIndex + 1);
46
+ }
47
+ /**
48
+ * Try to get domain from git config user.email
49
+ *
50
+ * @returns {Promise<string|null>} Domain from git config or null
51
+ */
52
+ async function getDomainFromGitConfig() {
53
+ try {
54
+ const git = getGitForCwd();
55
+ const email = await git.getConfigValue('user.email');
56
+ return extractDomain(email?.trim() || '');
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ /**
63
+ * Try to get domain from .lumenflow.config.yaml OWNER_EMAIL
64
+ *
65
+ * @param {string} [cwd] - Working directory to search from
66
+ * @returns {Promise<string|null>} Domain from config or null
67
+ */
68
+ async function getDomainFromLumenflowConfig(cwd = process.cwd()) {
69
+ const configPath = join(cwd, '.lumenflow.config.yaml');
70
+ try {
71
+ await access(configPath);
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ try {
77
+ const content = await readFile(configPath, { encoding: 'utf-8' });
78
+ // Simple pattern match for OWNER_EMAIL (avoid full YAML parse for performance)
79
+ // Looking for: OWNER_EMAIL: "email@domain"
80
+ const match = content.match(/OWNER_EMAIL:\s*["']?([^"'\s]+)["']?/i);
81
+ if (match && match[1]) {
82
+ return extractDomain(match[1]);
83
+ }
84
+ return null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ /**
91
+ * Infer the default email domain from available sources
92
+ *
93
+ * Priority:
94
+ * 1. Git config user.email domain
95
+ * 2. .lumenflow.config.yaml OWNER_EMAIL domain
96
+ * 3. DEFAULT_DOMAIN constant
97
+ *
98
+ * @param {string} [cwd] - Working directory for config lookup
99
+ * @returns {Promise<string>} Inferred domain (never null/undefined)
100
+ */
101
+ export async function inferDefaultDomain(cwd = process.cwd()) {
102
+ // Try git config first
103
+ const gitDomain = await getDomainFromGitConfig();
104
+ if (gitDomain) {
105
+ return gitDomain;
106
+ }
107
+ // Try lumenflow config
108
+ const configDomain = await getDomainFromLumenflowConfig(cwd);
109
+ if (configDomain) {
110
+ return configDomain;
111
+ }
112
+ // Fallback to default
113
+ return DEFAULT_DOMAIN;
114
+ }
115
+ /**
116
+ * Normalize a username or email to full email format
117
+ *
118
+ * If value is already a valid email, returns it unchanged (with normalization).
119
+ * If value is a plain username, appends the default domain.
120
+ *
121
+ * @param {string|null|undefined} value - Username or email
122
+ * @param {string} [domain] - Optional domain override
123
+ * @returns {Promise<string>} Normalized email address, or empty string for null/undefined
124
+ */
125
+ export async function normalizeToEmail(value, domain) {
126
+ // Handle null/undefined/empty
127
+ if (!value) {
128
+ return '';
129
+ }
130
+ const str = String(value).trim().toLowerCase();
131
+ if (str.length < MIN_LOCAL_PART_LENGTH) {
132
+ return '';
133
+ }
134
+ // Already a valid email - return as-is (normalized)
135
+ if (isValidEmail(str)) {
136
+ return str;
137
+ }
138
+ // Plain username - append domain
139
+ const effectiveDomain = domain || (await inferDefaultDomain());
140
+ return `${str}@${effectiveDomain}`;
141
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * PHI (Protected Health Information) Detection Constants
3
+ *
4
+ * Centralized constants for PHI scanner to detect NHS numbers and UK postcodes
5
+ * in medical context. Uses library-first approach with nhs-number-validator
6
+ * and postcode packages for validation.
7
+ *
8
+ * Part of WU-1404: PHI Scanner Integration
9
+ *
10
+ * @see {@link https://digital.nhs.uk/data-and-information/data-tools-and-services/data-services/nhs-number} NHS Number format
11
+ * @see {@link https://github.com/ideal-postcodes/postcode} Postcode validation library
12
+ */
13
+ /**
14
+ * PHI type identifiers for categorizing detected PHI
15
+ */
16
+ export declare const PHI_TYPES: {
17
+ /** NHS number (10 digits with Modulus 11 checksum) */
18
+ NHS_NUMBER: string;
19
+ /** UK postcode detected in medical context */
20
+ POSTCODE_MEDICAL_CONTEXT: string;
21
+ };
22
+ /**
23
+ * Medical context keywords for postcode detection
24
+ *
25
+ * Postcodes are only flagged as PHI when they appear within proximity
26
+ * of these medical context keywords. This reduces false positives from
27
+ * legitimate postcode usage (e.g., hospital addresses in documentation).
28
+ */
29
+ export declare const MEDICAL_CONTEXT_KEYWORDS: string[];
30
+ /**
31
+ * Context window size for medical keyword proximity detection
32
+ *
33
+ * When a postcode is found, we search this many characters before
34
+ * and after the postcode for medical context keywords.
35
+ */
36
+ export declare const MEDICAL_CONTEXT_WINDOW_SIZE = 100;
37
+ /**
38
+ * Known test NHS numbers that should not trigger PHI detection
39
+ *
40
+ * These are official test NHS numbers or commonly used in test fixtures.
41
+ * Source: NHS Digital test data guidelines
42
+ */
43
+ export declare const TEST_NHS_NUMBERS: string[];
44
+ /**
45
+ * Prefix for NHS test numbers (numbers starting with 999 are reserved for testing)
46
+ */
47
+ export declare const NHS_TEST_PREFIX = "999";
48
+ /**
49
+ * Known test postcodes that should not trigger PHI detection
50
+ *
51
+ * These are commonly used in examples, documentation, and test fixtures.
52
+ */
53
+ export declare const TEST_POSTCODES: string[];
54
+ /**
55
+ * Content markers that indicate test/example data
56
+ *
57
+ * Content containing these markers should not trigger PHI detection.
58
+ */
59
+ export declare const TEST_DATA_MARKERS: string[];
60
+ /**
61
+ * File path patterns that should be excluded from PHI scanning
62
+ *
63
+ * These patterns indicate test/fixture files where PHI patterns
64
+ * may legitimately appear for testing purposes.
65
+ */
66
+ export declare const EXCLUDED_PATH_PATTERNS: RegExp[];
67
+ /**
68
+ * Candidate extraction pattern for NHS numbers
69
+ *
70
+ * Extracts 10-digit numeric sequences that could be NHS numbers.
71
+ * The library validates these with Modulus 11 checksum.
72
+ *
73
+ * NHS numbers may appear with or without spaces:
74
+ * - 1234567890
75
+ * - 123 456 7890
76
+ * - 123-456-7890
77
+ */
78
+ export declare const NHS_CANDIDATE_PATTERN: RegExp;
79
+ /**
80
+ * PHI detection result structure
81
+ *
82
+ * @typedef {Object} PHIMatch
83
+ * @property {string} type - PHI type from PHI_TYPES
84
+ * @property {string} value - The matched value (may be masked)
85
+ * @property {number} startIndex - Start position in content
86
+ * @property {number} endIndex - End position in content
87
+ * @property {string} [context] - Surrounding context (optional)
88
+ * @property {string} [medicalKeyword] - Medical keyword that triggered postcode detection (optional)
89
+ */
90
+ /**
91
+ * Scan result structure
92
+ *
93
+ * @typedef {Object} PHIScanResult
94
+ * @property {boolean} hasPHI - Whether PHI was detected
95
+ * @property {PHIMatch[]} matches - Array of PHI matches found
96
+ * @property {string[]} warnings - Non-blocking warnings (e.g., test data detected)
97
+ */
@@ -0,0 +1,152 @@
1
+ /**
2
+ * PHI (Protected Health Information) Detection Constants
3
+ *
4
+ * Centralized constants for PHI scanner to detect NHS numbers and UK postcodes
5
+ * in medical context. Uses library-first approach with nhs-number-validator
6
+ * and postcode packages for validation.
7
+ *
8
+ * Part of WU-1404: PHI Scanner Integration
9
+ *
10
+ * @see {@link https://digital.nhs.uk/data-and-information/data-tools-and-services/data-services/nhs-number} NHS Number format
11
+ * @see {@link https://github.com/ideal-postcodes/postcode} Postcode validation library
12
+ */
13
+ /**
14
+ * PHI type identifiers for categorizing detected PHI
15
+ */
16
+ export const PHI_TYPES = {
17
+ /** NHS number (10 digits with Modulus 11 checksum) */
18
+ NHS_NUMBER: 'NHS_NUMBER',
19
+ /** UK postcode detected in medical context */
20
+ POSTCODE_MEDICAL_CONTEXT: 'POSTCODE_MEDICAL_CONTEXT',
21
+ };
22
+ /**
23
+ * Medical context keywords for postcode detection
24
+ *
25
+ * Postcodes are only flagged as PHI when they appear within proximity
26
+ * of these medical context keywords. This reduces false positives from
27
+ * legitimate postcode usage (e.g., hospital addresses in documentation).
28
+ */
29
+ export const MEDICAL_CONTEXT_KEYWORDS = [
30
+ 'patient',
31
+ 'medical record',
32
+ 'gp surgery',
33
+ 'nhs',
34
+ 'hospital',
35
+ 'clinic',
36
+ 'registered address',
37
+ 'home address',
38
+ 'next of kin',
39
+ 'emergency contact',
40
+ 'admission',
41
+ 'discharge',
42
+ 'referral',
43
+ 'diagnosis',
44
+ 'treatment',
45
+ ];
46
+ /**
47
+ * Context window size for medical keyword proximity detection
48
+ *
49
+ * When a postcode is found, we search this many characters before
50
+ * and after the postcode for medical context keywords.
51
+ */
52
+ export const MEDICAL_CONTEXT_WINDOW_SIZE = 100;
53
+ /**
54
+ * Known test NHS numbers that should not trigger PHI detection
55
+ *
56
+ * These are official test NHS numbers or commonly used in test fixtures.
57
+ * Source: NHS Digital test data guidelines
58
+ */
59
+ export const TEST_NHS_NUMBERS = [
60
+ '4505577104', // Common test NHS number
61
+ '9999999999', // Range reserved for testing (999 prefix)
62
+ '9990000001', // Test range
63
+ '9990000002',
64
+ '9990000003',
65
+ ];
66
+ /**
67
+ * Prefix for NHS test numbers (numbers starting with 999 are reserved for testing)
68
+ */
69
+ export const NHS_TEST_PREFIX = '999';
70
+ /**
71
+ * Known test postcodes that should not trigger PHI detection
72
+ *
73
+ * These are commonly used in examples, documentation, and test fixtures.
74
+ */
75
+ export const TEST_POSTCODES = [
76
+ 'SW1A 1AA', // Buckingham Palace - often used in examples
77
+ 'SW1A1AA', // Same without space
78
+ 'EC1A 1BB', // Commonly used test postcode
79
+ 'EC1A1BB',
80
+ 'W1A 1AA', // BBC Broadcasting House - frequently in docs
81
+ 'W1A1AA',
82
+ ];
83
+ /**
84
+ * Content markers that indicate test/example data
85
+ *
86
+ * Content containing these markers should not trigger PHI detection.
87
+ */
88
+ export const TEST_DATA_MARKERS = [
89
+ '[TEST]',
90
+ '[EXAMPLE]',
91
+ '[PLACEHOLDER]',
92
+ '[SAMPLE]',
93
+ '// test data',
94
+ '/* test data */',
95
+ '# test data',
96
+ 'test-data',
97
+ 'testData',
98
+ 'TEST_DATA',
99
+ 'example-data',
100
+ 'sample-data',
101
+ 'mock-data',
102
+ 'fixture',
103
+ ];
104
+ /**
105
+ * File path patterns that should be excluded from PHI scanning
106
+ *
107
+ * These patterns indicate test/fixture files where PHI patterns
108
+ * may legitimately appear for testing purposes.
109
+ */
110
+ export const EXCLUDED_PATH_PATTERNS = [
111
+ /\/__tests__\//i,
112
+ /\/test\//i,
113
+ /\/tests\//i,
114
+ /\.test\./i,
115
+ /\.spec\./i,
116
+ /\/fixtures?\//i,
117
+ /\/mocks?\//i,
118
+ /\/__mocks__\//i,
119
+ /\/VCR\/cassettes\//i,
120
+ /\.md$/i, // Documentation files have different risk profile
121
+ ];
122
+ /**
123
+ * Candidate extraction pattern for NHS numbers
124
+ *
125
+ * Extracts 10-digit numeric sequences that could be NHS numbers.
126
+ * The library validates these with Modulus 11 checksum.
127
+ *
128
+ * NHS numbers may appear with or without spaces:
129
+ * - 1234567890
130
+ * - 123 456 7890
131
+ * - 123-456-7890
132
+ */
133
+ export const NHS_CANDIDATE_PATTERN = /\b(\d{3}[\s-]?\d{3}[\s-]?\d{4})\b/g;
134
+ /**
135
+ * PHI detection result structure
136
+ *
137
+ * @typedef {Object} PHIMatch
138
+ * @property {string} type - PHI type from PHI_TYPES
139
+ * @property {string} value - The matched value (may be masked)
140
+ * @property {number} startIndex - Start position in content
141
+ * @property {number} endIndex - End position in content
142
+ * @property {string} [context] - Surrounding context (optional)
143
+ * @property {string} [medicalKeyword] - Medical keyword that triggered postcode detection (optional)
144
+ */
145
+ /**
146
+ * Scan result structure
147
+ *
148
+ * @typedef {Object} PHIScanResult
149
+ * @property {boolean} hasPHI - Whether PHI was detected
150
+ * @property {PHIMatch[]} matches - Array of PHI matches found
151
+ * @property {string[]} warnings - Non-blocking warnings (e.g., test data detected)
152
+ */