@lumenflow/cli 5.4.0 → 5.7.12

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 (227) hide show
  1. package/README.md +42 -40
  2. package/dist/db-journal-recover.js +400 -0
  3. package/dist/db-journal-recover.js.map +1 -0
  4. package/dist/docs-sync.js +8 -3
  5. package/dist/docs-sync.js.map +1 -1
  6. package/dist/gate-defaults.js +191 -9
  7. package/dist/gate-defaults.js.map +1 -1
  8. package/dist/gate-registry.js.map +1 -1
  9. package/dist/gates/monolithic-file-contention-guard.js +167 -0
  10. package/dist/gates/monolithic-file-contention-guard.js.map +1 -0
  11. package/dist/gates/prod-migration-drift.js +207 -0
  12. package/dist/gates/prod-migration-drift.js.map +1 -0
  13. package/dist/gates/test-over-deletion-guard.js +255 -0
  14. package/dist/gates/test-over-deletion-guard.js.map +1 -0
  15. package/dist/gates-runners.js +401 -2
  16. package/dist/gates-runners.js.map +1 -1
  17. package/dist/gates.js +349 -4
  18. package/dist/gates.js.map +1 -1
  19. package/dist/lumenflow-setup.js +144 -0
  20. package/dist/lumenflow-setup.js.map +1 -0
  21. package/dist/lumenflow-upgrade.js +2 -1
  22. package/dist/lumenflow-upgrade.js.map +1 -1
  23. package/dist/mem-create.js +10 -1
  24. package/dist/mem-create.js.map +1 -1
  25. package/dist/mem-signal.js +21 -4
  26. package/dist/mem-signal.js.map +1 -1
  27. package/dist/metrics-cli.js +19 -2
  28. package/dist/metrics-cli.js.map +1 -1
  29. package/dist/metrics-snapshot.js +25 -2
  30. package/dist/metrics-snapshot.js.map +1 -1
  31. package/dist/orchestrate-initiative.js +28 -3
  32. package/dist/orchestrate-initiative.js.map +1 -1
  33. package/dist/public-manifest.js +17 -0
  34. package/dist/public-manifest.js.map +1 -1
  35. package/dist/release.js +53 -18
  36. package/dist/release.js.map +1 -1
  37. package/dist/wu-done-gates.js +121 -8
  38. package/dist/wu-done-gates.js.map +1 -1
  39. package/dist/wu-done.js +30 -6
  40. package/dist/wu-done.js.map +1 -1
  41. package/dist/wu-edit-operations.js +74 -0
  42. package/dist/wu-edit-operations.js.map +1 -1
  43. package/dist/wu-edit-validators.js +58 -0
  44. package/dist/wu-edit-validators.js.map +1 -1
  45. package/dist/wu-edit.js +106 -4
  46. package/dist/wu-edit.js.map +1 -1
  47. package/dist/wu-prep.js +132 -8
  48. package/dist/wu-prep.js.map +1 -1
  49. package/dist/wu-recover.js +6 -0
  50. package/dist/wu-recover.js.map +1 -1
  51. package/dist/wu-release.js +120 -2
  52. package/dist/wu-release.js.map +1 -1
  53. package/dist/wu-sizing-validation.js +47 -17
  54. package/dist/wu-sizing-validation.js.map +1 -1
  55. package/dist/wu-status.js +33 -0
  56. package/dist/wu-status.js.map +1 -1
  57. package/package.json +13 -11
  58. package/packs/agent-runtime/package.json +1 -1
  59. package/packs/sidekick/package.json +1 -1
  60. package/packs/software-delivery/package.json +1 -1
  61. package/templates/core/AGENTS.md.template +162 -26
  62. package/templates/core/LUMENFLOW.md.template +381 -70
  63. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +0 -5
  64. package/templates/core/ai/onboarding/agent-safety-card.md.template +63 -17
  65. package/templates/core/ai/onboarding/initiative-orchestration.md.template +4 -0
  66. package/templates/core/ai/onboarding/release-process.md.template +7 -7
  67. package/templates/core/ai/onboarding/vendor-support.md.template +74 -10
  68. package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +1 -1
  69. package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +28 -0
  70. package/packs/agent-runtime/agent-heartbeat.ts +0 -163
  71. package/packs/agent-runtime/auto-session-integration.ts +0 -888
  72. package/packs/agent-runtime/capability-factory.ts +0 -104
  73. package/packs/agent-runtime/constants.ts +0 -21
  74. package/packs/agent-runtime/delegation-registry-schema.ts +0 -220
  75. package/packs/agent-runtime/delegation-registry-store.ts +0 -269
  76. package/packs/agent-runtime/delegation-tree.ts +0 -328
  77. package/packs/agent-runtime/index.ts +0 -20
  78. package/packs/agent-runtime/manifest.ts +0 -348
  79. package/packs/agent-runtime/memory-coordination-contract.ts +0 -86
  80. package/packs/agent-runtime/orchestration.ts +0 -2027
  81. package/packs/agent-runtime/pack-registration.ts +0 -110
  82. package/packs/agent-runtime/policy-factory.ts +0 -165
  83. package/packs/agent-runtime/remote-controls/index.ts +0 -7
  84. package/packs/agent-runtime/remote-controls/operations.ts +0 -405
  85. package/packs/agent-runtime/remote-controls/port.ts +0 -48
  86. package/packs/agent-runtime/remote-controls/state-store.ts +0 -258
  87. package/packs/agent-runtime/remote-controls/types.ts +0 -105
  88. package/packs/agent-runtime/session-schema.ts +0 -467
  89. package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +0 -793
  90. package/packs/agent-runtime/tool-impl/index.ts +0 -6
  91. package/packs/agent-runtime/tool-impl/provider-adapters.ts +0 -1245
  92. package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +0 -256
  93. package/packs/agent-runtime/tool-impl/remote-controls.ts +0 -273
  94. package/packs/agent-runtime/tools/index.ts +0 -4
  95. package/packs/agent-runtime/tools/types.ts +0 -47
  96. package/packs/agent-runtime/turn-lifecycle-events.ts +0 -590
  97. package/packs/agent-runtime/types.ts +0 -128
  98. package/packs/agent-runtime/vitest.config.ts +0 -11
  99. package/packs/sidekick/channel-ingress.ts +0 -137
  100. package/packs/sidekick/constants.ts +0 -10
  101. package/packs/sidekick/index.ts +0 -8
  102. package/packs/sidekick/manifest-schema.ts +0 -49
  103. package/packs/sidekick/manifest.ts +0 -512
  104. package/packs/sidekick/pack-registration.ts +0 -110
  105. package/packs/sidekick/policy-factory.ts +0 -38
  106. package/packs/sidekick/sidekick-events.ts +0 -694
  107. package/packs/sidekick/src/adapters/cloud-queue.ts +0 -101
  108. package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +0 -386
  109. package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +0 -228
  110. package/packs/sidekick/src/domain/channel.types.ts +0 -64
  111. package/packs/sidekick/src/ports/channel-bridge.port.ts +0 -92
  112. package/packs/sidekick/src/routines/commit.ts +0 -74
  113. package/packs/sidekick/tool-impl/channel-tools.ts +0 -577
  114. package/packs/sidekick/tool-impl/channel-transports.ts +0 -75
  115. package/packs/sidekick/tool-impl/index.ts +0 -29
  116. package/packs/sidekick/tool-impl/memory-tools.ts +0 -290
  117. package/packs/sidekick/tool-impl/routine-commit.ts +0 -102
  118. package/packs/sidekick/tool-impl/routine-tools.ts +0 -440
  119. package/packs/sidekick/tool-impl/runtime-context.ts +0 -28
  120. package/packs/sidekick/tool-impl/shared.ts +0 -125
  121. package/packs/sidekick/tool-impl/storage.ts +0 -325
  122. package/packs/sidekick/tool-impl/system-tools.ts +0 -160
  123. package/packs/sidekick/tool-impl/task-tools.ts +0 -506
  124. package/packs/sidekick/tools/channel-tools.ts +0 -53
  125. package/packs/sidekick/tools/index.ts +0 -9
  126. package/packs/sidekick/tools/memory-tools.ts +0 -53
  127. package/packs/sidekick/tools/routine-tools.ts +0 -53
  128. package/packs/sidekick/tools/system-tools.ts +0 -47
  129. package/packs/sidekick/tools/task-tools.ts +0 -61
  130. package/packs/sidekick/tools/types.ts +0 -57
  131. package/packs/sidekick/vitest.config.ts +0 -11
  132. package/packs/software-delivery/constants.ts +0 -10
  133. package/packs/software-delivery/extensions.ts +0 -140
  134. package/packs/software-delivery/gate-policies.ts +0 -134
  135. package/packs/software-delivery/index.ts +0 -8
  136. package/packs/software-delivery/manifest-schema.ts +0 -268
  137. package/packs/software-delivery/manifest.ts +0 -657
  138. package/packs/software-delivery/pack-registration.ts +0 -113
  139. package/packs/software-delivery/src/commands/index.ts +0 -5
  140. package/packs/software-delivery/src/config/delivery-review-contract.ts +0 -256
  141. package/packs/software-delivery/src/config/env-accessors.ts +0 -66
  142. package/packs/software-delivery/src/config/index.ts +0 -8
  143. package/packs/software-delivery/src/config/normalize-config-keys.ts +0 -9
  144. package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +0 -460
  145. package/packs/software-delivery/src/config/workspace-reader.ts +0 -375
  146. package/packs/software-delivery/src/constants/backlog-patterns.ts +0 -31
  147. package/packs/software-delivery/src/constants/client-ids.ts +0 -19
  148. package/packs/software-delivery/src/constants/config-contract.ts +0 -7
  149. package/packs/software-delivery/src/constants/docs-layout-presets.ts +0 -50
  150. package/packs/software-delivery/src/constants/duration-constants.ts +0 -20
  151. package/packs/software-delivery/src/constants/gate-constants.ts +0 -32
  152. package/packs/software-delivery/src/constants/index.ts +0 -29
  153. package/packs/software-delivery/src/constants/lock-constants.ts +0 -35
  154. package/packs/software-delivery/src/constants/object-guards.ts +0 -12
  155. package/packs/software-delivery/src/constants/section-headings.ts +0 -107
  156. package/packs/software-delivery/src/constants/wu-cli-constants.ts +0 -488
  157. package/packs/software-delivery/src/constants/wu-domain-constants.ts +0 -466
  158. package/packs/software-delivery/src/constants/wu-git-constants.ts +0 -7
  159. package/packs/software-delivery/src/constants/wu-id-format.ts +0 -327
  160. package/packs/software-delivery/src/constants/wu-paths-constants.ts +0 -384
  161. package/packs/software-delivery/src/constants/wu-statuses.ts +0 -287
  162. package/packs/software-delivery/src/constants/wu-type-helpers.ts +0 -67
  163. package/packs/software-delivery/src/constants/wu-ui-constants.ts +0 -267
  164. package/packs/software-delivery/src/constants/wu-validation-constants.ts +0 -73
  165. package/packs/software-delivery/src/domain/index.ts +0 -5
  166. package/packs/software-delivery/src/domain/orchestration.constants.ts +0 -166
  167. package/packs/software-delivery/src/domain/orchestration.schemas.ts +0 -238
  168. package/packs/software-delivery/src/domain/orchestration.types.ts +0 -176
  169. package/packs/software-delivery/src/methodology/incremental-test.ts +0 -122
  170. package/packs/software-delivery/src/methodology/index.ts +0 -6
  171. package/packs/software-delivery/src/methodology/manual-test-validator.ts +0 -292
  172. package/packs/software-delivery/src/policy/coverage-gate.ts +0 -270
  173. package/packs/software-delivery/src/policy/gates-agent-mode.ts +0 -223
  174. package/packs/software-delivery/src/policy/gates-config-internal.ts +0 -121
  175. package/packs/software-delivery/src/policy/gates-config.ts +0 -300
  176. package/packs/software-delivery/src/policy/gates-coverage.ts +0 -356
  177. package/packs/software-delivery/src/policy/gates-presets.ts +0 -134
  178. package/packs/software-delivery/src/policy/gates-schemas.ts +0 -173
  179. package/packs/software-delivery/src/policy/index.ts +0 -22
  180. package/packs/software-delivery/src/policy/package-manager-resolver.ts +0 -319
  181. package/packs/software-delivery/src/policy/resolve-policy.ts +0 -601
  182. package/packs/software-delivery/src/ports/config.ports.ts +0 -90
  183. package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +0 -125
  184. package/packs/software-delivery/src/ports/index.ts +0 -10
  185. package/packs/software-delivery/src/ports/sync-validator.ports.ts +0 -59
  186. package/packs/software-delivery/src/ports/wu-helpers.ports.ts +0 -168
  187. package/packs/software-delivery/src/ports/wu-state.ports.ts +0 -241
  188. package/packs/software-delivery/src/primitives/index.ts +0 -5
  189. package/packs/software-delivery/src/runtime/index.ts +0 -6
  190. package/packs/software-delivery/src/runtime/work-classifier.ts +0 -561
  191. package/packs/software-delivery/src/sandbox/index.ts +0 -10
  192. package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +0 -118
  193. package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +0 -88
  194. package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +0 -154
  195. package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +0 -47
  196. package/packs/software-delivery/src/sandbox/sandbox-profile.ts +0 -153
  197. package/packs/software-delivery/src/schemas/index.ts +0 -5
  198. package/packs/software-delivery/src/state/date-utils.ts +0 -158
  199. package/packs/software-delivery/src/state/index.ts +0 -15
  200. package/packs/software-delivery/src/state/state-machine.ts +0 -119
  201. package/packs/software-delivery/src/state/wu-doc-types.ts +0 -51
  202. package/packs/software-delivery/src/state/wu-paths.ts +0 -381
  203. package/packs/software-delivery/src/state/wu-schema.ts +0 -1139
  204. package/packs/software-delivery/src/state/wu-state-schema.ts +0 -255
  205. package/packs/software-delivery/src/state/wu-yaml.ts +0 -338
  206. package/packs/software-delivery/tool-impl/agent-tools.ts +0 -263
  207. package/packs/software-delivery/tool-impl/delegation-tools.ts +0 -66
  208. package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +0 -219
  209. package/packs/software-delivery/tool-impl/git-runner.ts +0 -113
  210. package/packs/software-delivery/tool-impl/git-tools.ts +0 -316
  211. package/packs/software-delivery/tool-impl/index.ts +0 -15
  212. package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +0 -720
  213. package/packs/software-delivery/tool-impl/lane-lock.ts +0 -246
  214. package/packs/software-delivery/tool-impl/memory-tools.ts +0 -470
  215. package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +0 -21
  216. package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +0 -329
  217. package/packs/software-delivery/tool-impl/runtime-native-tools.ts +0 -687
  218. package/packs/software-delivery/tool-impl/worker-loader.ts +0 -52
  219. package/packs/software-delivery/tool-impl/worktree-tools.ts +0 -46
  220. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +0 -807
  221. package/packs/software-delivery/tools/delegation-tools.ts +0 -23
  222. package/packs/software-delivery/tools/git-tools.ts +0 -55
  223. package/packs/software-delivery/tools/index.ts +0 -8
  224. package/packs/software-delivery/tools/lane-lock-tool.ts +0 -37
  225. package/packs/software-delivery/tools/types.ts +0 -71
  226. package/packs/software-delivery/tools/worktree-tools.ts +0 -49
  227. package/packs/software-delivery/vitest.config.ts +0 -11
@@ -1,561 +0,0 @@
1
- // Copyright (c) 2026 Hellmai Ltd
2
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
3
-
4
- /**
5
- * Signal-Based Work Classifier
6
- *
7
- * WU-1899: Determines work domain (ui/backend/docs/infra/mixed) from
8
- * multiple weighted signals: code_paths patterns (weight 1.0), lane hints
9
- * (0.6), type (0.3), description keywords (0.2).
10
- *
11
- * Returns abstract capability tags (not client skill names) for
12
- * vendor-agnostic design. Configurable via methodology.work_classification
13
- * in workspace.yaml with sensible built-in defaults.
14
- *
15
- * @module work-classifier
16
- */
17
-
18
- import { minimatch } from 'minimatch';
19
-
20
- // ─── Constants ───────────────────────────────────────────────────────
21
-
22
- /**
23
- * Work domain enum-style constants
24
- */
25
- export const WORK_DOMAINS = {
26
- UI: 'ui',
27
- BACKEND: 'backend',
28
- DOCS: 'docs',
29
- INFRA: 'infra',
30
- MIXED: 'mixed',
31
- } as const;
32
-
33
- export type WorkDomain = (typeof WORK_DOMAINS)[keyof typeof WORK_DOMAINS];
34
-
35
- /**
36
- * Classifier-produced methodology hints consumed by prompt guidance builders.
37
- */
38
- export const TEST_METHODOLOGY_HINTS = {
39
- SMOKE_TEST: 'smoke-test',
40
- STRUCTURED_CONTENT: 'structured-content',
41
- } as const;
42
-
43
- export type TestMethodologyHint =
44
- (typeof TEST_METHODOLOGY_HINTS)[keyof typeof TEST_METHODOLOGY_HINTS];
45
-
46
- /**
47
- * Signal weights for each source type.
48
- * Confidence = max(matched_signal_weights), not sum.
49
- */
50
- export const SIGNAL_WEIGHTS = {
51
- CODE_PATHS: 1.0,
52
- LANE: 0.6,
53
- TYPE: 0.3,
54
- DESCRIPTION: 0.2,
55
- } as const;
56
-
57
- /**
58
- * Confidence threshold: domain only assigned when max signal weight >= this.
59
- */
60
- const CONFIDENCE_THRESHOLD = 0.3;
61
-
62
- /**
63
- * Smoke-test hint threshold: testMethodologyHint only assigned when
64
- * UI confidence >= this value.
65
- */
66
- const SMOKE_TEST_THRESHOLD = 0.5;
67
-
68
- /**
69
- * Structured-content patterns used to detect non-code work.
70
- *
71
- * When all code_paths match these patterns, classifier emits
72
- * testMethodologyHint: 'structured-content' to avoid forcing TDD
73
- * for content/config-only changes.
74
- */
75
- const STRUCTURED_CONTENT_CODE_PATH_PATTERNS: readonly string[] = Object.freeze([
76
- '**/*.yaml',
77
- '**/*.yml',
78
- '**/*.json',
79
- '**/*.md',
80
- '**/*.mdx',
81
- ]);
82
-
83
- // ─── Default Patterns ────────────────────────────────────────────────
84
-
85
- /**
86
- * Built-in default UI code path patterns (minimatch glob patterns).
87
- * CSS/SCSS/LESS, components/pages directories, app page/layout files,
88
- * module CSS, styled components.
89
- */
90
- export const DEFAULT_UI_CODE_PATH_PATTERNS: readonly string[] = Object.freeze([
91
- // Stylesheets
92
- '**/*.css',
93
- '**/*.scss',
94
- '**/*.less',
95
- '**/*.module.css',
96
- '**/*.module.scss',
97
- // Styled components
98
- '**/*.styled.ts',
99
- '**/*.styled.tsx',
100
- '**/*.styled.js',
101
- '**/*.styled.jsx',
102
- // Component / page directories
103
- '**/components/**',
104
- '**/pages/**',
105
- ]);
106
-
107
- /**
108
- * Built-in default UI lane hints.
109
- * Matched against the lane parent (part before the colon).
110
- */
111
- export const DEFAULT_UI_LANE_HINTS: readonly string[] = Object.freeze([
112
- 'Experience',
113
- 'Frontend',
114
- 'UI',
115
- 'Design',
116
- ]);
117
-
118
- /**
119
- * Built-in docs code path patterns.
120
- */
121
- const DEFAULT_DOCS_CODE_PATH_PATTERNS: readonly string[] = Object.freeze([
122
- 'docs/**',
123
- '**/*.md',
124
- '**/*.mdx',
125
- 'README*',
126
- 'CHANGELOG*',
127
- ]);
128
-
129
- /**
130
- * Built-in docs lane hints.
131
- */
132
- const DEFAULT_DOCS_LANE_HINTS: readonly string[] = Object.freeze([
133
- 'Content',
134
- 'Documentation',
135
- 'Docs',
136
- ]);
137
-
138
- /**
139
- * Built-in infra code path patterns.
140
- */
141
- const DEFAULT_INFRA_CODE_PATH_PATTERNS: readonly string[] = Object.freeze([
142
- '.github/**',
143
- 'Dockerfile*',
144
- 'docker-compose*',
145
- '**/terraform/**',
146
- '**/k8s/**',
147
- '**/kubernetes/**',
148
- 'infrastructure/**',
149
- ]);
150
-
151
- /**
152
- * Built-in infra lane hints.
153
- */
154
- const DEFAULT_INFRA_LANE_HINTS: readonly string[] = Object.freeze([
155
- 'Operations',
156
- 'Infrastructure',
157
- 'DevOps',
158
- 'Platform',
159
- ]);
160
-
161
- /**
162
- * UI-related description keywords (case-insensitive).
163
- */
164
- const UI_DESCRIPTION_KEYWORDS: readonly string[] = Object.freeze([
165
- 'css',
166
- 'scss',
167
- 'less',
168
- 'stylesheet',
169
- 'component',
170
- 'layout',
171
- 'responsive',
172
- 'ui',
173
- 'frontend',
174
- 'styled',
175
- 'animation',
176
- 'theme',
177
- ]);
178
-
179
- /**
180
- * Docs-related description keywords (case-insensitive).
181
- */
182
- const DOCS_DESCRIPTION_KEYWORDS: readonly string[] = Object.freeze([
183
- 'documentation',
184
- 'readme',
185
- 'changelog',
186
- 'docs',
187
- 'guide',
188
- 'tutorial',
189
- ]);
190
-
191
- /**
192
- * Infra-related description keywords (case-insensitive).
193
- */
194
- const INFRA_DESCRIPTION_KEYWORDS: readonly string[] = Object.freeze([
195
- 'docker',
196
- 'kubernetes',
197
- 'terraform',
198
- 'ci/cd',
199
- 'pipeline',
200
- 'deploy',
201
- 'infrastructure',
202
- ]);
203
-
204
- // ─── Types ───────────────────────────────────────────────────────────
205
-
206
- /**
207
- * A signal that contributed to the classification.
208
- */
209
- export interface WorkSignal {
210
- /** Signal source: 'code_paths' | 'lane' | 'type' | 'description' */
211
- source: string;
212
- /** The domain this signal points to */
213
- domain: WorkDomain;
214
- /** The weight of this signal */
215
- weight: number;
216
- /** What matched (e.g., the pattern or keyword) */
217
- match: string;
218
- }
219
-
220
- /**
221
- * Result of classifyWork.
222
- */
223
- export interface WorkClassification {
224
- /** Detected work domain */
225
- domain: WorkDomain;
226
- /** Confidence score (max signal weight, 0 if no match) */
227
- confidence: number;
228
- /** Individual signals that contributed */
229
- signals: WorkSignal[];
230
- /** Abstract capability tags (NOT client skill names) */
231
- capabilities: string[];
232
- /** Test methodology hint, e.g. smoke-test (UI) or structured-content (non-code content) */
233
- testMethodologyHint?: TestMethodologyHint;
234
- }
235
-
236
- /**
237
- * Optional configuration to extend defaults.
238
- * Maps to methodology.work_classification in workspace.yaml.
239
- */
240
- export interface WorkClassificationConfig {
241
- ui?: {
242
- /** Additional code_path_patterns (extend defaults) */
243
- code_path_patterns?: string[];
244
- /** Additional lane_hints (extend defaults) */
245
- lane_hints?: string[];
246
- };
247
- }
248
-
249
- // ─── Internals ───────────────────────────────────────────────────────
250
-
251
- /**
252
- * Tracks per-domain signals collected during classification.
253
- */
254
- interface DomainSignals {
255
- domain: WorkDomain;
256
- maxWeight: number;
257
- signals: WorkSignal[];
258
- }
259
-
260
- function createDomainSignals(domain: WorkDomain): DomainSignals {
261
- return { domain, maxWeight: 0, signals: [] };
262
- }
263
-
264
- function addSignal(ds: DomainSignals, signal: WorkSignal): void {
265
- ds.signals.push(signal);
266
- if (signal.weight > ds.maxWeight) {
267
- ds.maxWeight = signal.weight;
268
- }
269
- }
270
-
271
- /**
272
- * Check if any code_paths match a set of glob patterns.
273
- */
274
- function matchCodePaths(codePaths: string[], patterns: readonly string[]): string | undefined {
275
- for (const path of codePaths) {
276
- for (const pattern of patterns) {
277
- if (minimatch(path, pattern)) {
278
- return pattern;
279
- }
280
- }
281
- }
282
- return undefined;
283
- }
284
-
285
- /**
286
- * Check whether all code_paths are structured content (yaml/json/markdown).
287
- */
288
- function isStructuredContentOnly(codePaths: string[]): boolean {
289
- return (
290
- codePaths.length > 0 &&
291
- codePaths.every((path) =>
292
- STRUCTURED_CONTENT_CODE_PATH_PATTERNS.some((pattern) =>
293
- minimatch(path, pattern, { nocase: true }),
294
- ),
295
- )
296
- );
297
- }
298
-
299
- /**
300
- * Check if the lane parent matches any hints (case-insensitive).
301
- */
302
- function matchLaneHint(lane: string, hints: readonly string[]): string | undefined {
303
- const parts = lane.split(':');
304
- const laneParent = (parts[0] ?? '').trim().toLowerCase();
305
- const laneSublane = parts.length > 1 ? (parts[1] ?? '').trim().toLowerCase() : '';
306
-
307
- for (const hint of hints) {
308
- const hintLower = hint.toLowerCase();
309
- if (laneParent === hintLower || laneSublane === hintLower) {
310
- return hint;
311
- }
312
- }
313
- return undefined;
314
- }
315
-
316
- /**
317
- * Check if description contains any keywords (case-insensitive, word boundary).
318
- */
319
- function matchDescriptionKeywords(
320
- description: string,
321
- keywords: readonly string[],
322
- ): string | undefined {
323
- const descLower = description.toLowerCase();
324
- for (const keyword of keywords) {
325
- if (descLower.includes(keyword.toLowerCase())) {
326
- return keyword;
327
- }
328
- }
329
- return undefined;
330
- }
331
-
332
- /**
333
- * Domain-to-capabilities data map (OCP-compliant).
334
- * Add new domains by extending the record, no switch modification needed.
335
- */
336
- const DOMAIN_CAPABILITIES: Record<WorkDomain, string[]> = {
337
- [WORK_DOMAINS.UI]: ['ui-design-awareness', 'component-reuse-check'],
338
- [WORK_DOMAINS.DOCS]: ['documentation-structure', 'link-validation'],
339
- [WORK_DOMAINS.INFRA]: ['infrastructure-review', 'security-check'],
340
- [WORK_DOMAINS.MIXED]: ['cross-domain-awareness'],
341
- [WORK_DOMAINS.BACKEND]: [],
342
- };
343
-
344
- /**
345
- * Map work domain to capabilities (abstract, vendor-agnostic).
346
- */
347
- function getCapabilities(domain: WorkDomain): string[] {
348
- return DOMAIN_CAPABILITIES[domain] ?? [];
349
- }
350
-
351
- // ─── Public API ──────────────────────────────────────────────────────
352
-
353
- /**
354
- * Classify work domain from multiple weighted signals.
355
- *
356
- * Signal weights:
357
- * - code_paths patterns: 1.0
358
- * - lane hints: 0.6
359
- * - WU type: 0.3
360
- * - description keywords: 0.2
361
- *
362
- * Confidence = max(matched signal weights for winning domain).
363
- * Domain only assigned when confidence >= 0.3.
364
- *
365
- * @param doc - WU document (code_paths, lane, type, description)
366
- * @param config - Optional config to extend default patterns
367
- * @returns WorkClassification with domain, confidence, signals, capabilities, testMethodologyHint
368
- */
369
- export function classifyWork(
370
- doc: {
371
- code_paths?: string[];
372
- lane?: string;
373
- type?: string;
374
- description?: string;
375
- },
376
- config?: WorkClassificationConfig,
377
- ): WorkClassification {
378
- const codePaths = doc.code_paths ?? [];
379
- const lane = doc.lane ?? '';
380
- const type = doc.type ?? '';
381
- const description = doc.description ?? '';
382
- const structuredContentOnly = isStructuredContentOnly(codePaths);
383
-
384
- // Merge config patterns with defaults (config extends, not replaces)
385
- const uiCodePathPatterns = [
386
- ...DEFAULT_UI_CODE_PATH_PATTERNS,
387
- ...(config?.ui?.code_path_patterns ?? []),
388
- ];
389
- const uiLaneHints = [...DEFAULT_UI_LANE_HINTS, ...(config?.ui?.lane_hints ?? [])];
390
-
391
- // Collect signals per domain
392
- const ui = createDomainSignals(WORK_DOMAINS.UI);
393
- const docs = createDomainSignals(WORK_DOMAINS.DOCS);
394
- const infra = createDomainSignals(WORK_DOMAINS.INFRA);
395
-
396
- // ── Signal 1: code_paths (weight 1.0) ──
397
-
398
- if (codePaths.length > 0) {
399
- const uiMatch = matchCodePaths(codePaths, uiCodePathPatterns);
400
- if (uiMatch) {
401
- addSignal(ui, {
402
- source: 'code_paths',
403
- domain: WORK_DOMAINS.UI,
404
- weight: SIGNAL_WEIGHTS.CODE_PATHS,
405
- match: uiMatch,
406
- });
407
- }
408
-
409
- const docsMatch = matchCodePaths(codePaths, DEFAULT_DOCS_CODE_PATH_PATTERNS);
410
- if (docsMatch) {
411
- addSignal(docs, {
412
- source: 'code_paths',
413
- domain: WORK_DOMAINS.DOCS,
414
- weight: SIGNAL_WEIGHTS.CODE_PATHS,
415
- match: docsMatch,
416
- });
417
- }
418
-
419
- const infraMatch = matchCodePaths(codePaths, DEFAULT_INFRA_CODE_PATH_PATTERNS);
420
- if (infraMatch) {
421
- addSignal(infra, {
422
- source: 'code_paths',
423
- domain: WORK_DOMAINS.INFRA,
424
- weight: SIGNAL_WEIGHTS.CODE_PATHS,
425
- match: infraMatch,
426
- });
427
- }
428
- }
429
-
430
- // ── Signal 2: lane hints (weight 0.6) ──
431
-
432
- if (lane) {
433
- const uiLaneMatch = matchLaneHint(lane, uiLaneHints);
434
- if (uiLaneMatch) {
435
- addSignal(ui, {
436
- source: 'lane',
437
- domain: WORK_DOMAINS.UI,
438
- weight: SIGNAL_WEIGHTS.LANE,
439
- match: uiLaneMatch,
440
- });
441
- }
442
-
443
- const docsLaneMatch = matchLaneHint(lane, DEFAULT_DOCS_LANE_HINTS);
444
- if (docsLaneMatch) {
445
- addSignal(docs, {
446
- source: 'lane',
447
- domain: WORK_DOMAINS.DOCS,
448
- weight: SIGNAL_WEIGHTS.LANE,
449
- match: docsLaneMatch,
450
- });
451
- }
452
-
453
- const infraLaneMatch = matchLaneHint(lane, DEFAULT_INFRA_LANE_HINTS);
454
- if (infraLaneMatch) {
455
- addSignal(infra, {
456
- source: 'lane',
457
- domain: WORK_DOMAINS.INFRA,
458
- weight: SIGNAL_WEIGHTS.LANE,
459
- match: infraLaneMatch,
460
- });
461
- }
462
- }
463
-
464
- // ── Signal 3: WU type (weight 0.3) ──
465
-
466
- if (type === 'documentation') {
467
- addSignal(docs, {
468
- source: 'type',
469
- domain: WORK_DOMAINS.DOCS,
470
- weight: SIGNAL_WEIGHTS.TYPE,
471
- match: 'documentation',
472
- });
473
- }
474
-
475
- // ── Signal 4: description keywords (weight 0.2) ──
476
-
477
- if (description) {
478
- const uiKeywordMatch = matchDescriptionKeywords(description, UI_DESCRIPTION_KEYWORDS);
479
- if (uiKeywordMatch) {
480
- addSignal(ui, {
481
- source: 'description',
482
- domain: WORK_DOMAINS.UI,
483
- weight: SIGNAL_WEIGHTS.DESCRIPTION,
484
- match: uiKeywordMatch,
485
- });
486
- }
487
-
488
- const docsKeywordMatch = matchDescriptionKeywords(description, DOCS_DESCRIPTION_KEYWORDS);
489
- if (docsKeywordMatch) {
490
- addSignal(docs, {
491
- source: 'description',
492
- domain: WORK_DOMAINS.DOCS,
493
- weight: SIGNAL_WEIGHTS.DESCRIPTION,
494
- match: docsKeywordMatch,
495
- });
496
- }
497
-
498
- const infraKeywordMatch = matchDescriptionKeywords(description, INFRA_DESCRIPTION_KEYWORDS);
499
- if (infraKeywordMatch) {
500
- addSignal(infra, {
501
- source: 'description',
502
- domain: WORK_DOMAINS.INFRA,
503
- weight: SIGNAL_WEIGHTS.DESCRIPTION,
504
- match: infraKeywordMatch,
505
- });
506
- }
507
- }
508
-
509
- // ── Determine winning domain ──
510
-
511
- const candidates = [ui, docs, infra].filter((d) => d.maxWeight >= CONFIDENCE_THRESHOLD);
512
-
513
- // Check for mixed: multiple domains with strong code_paths signals
514
- const strongCodePathDomains = [ui, docs, infra].filter((d) =>
515
- d.signals.some((s) => s.source === 'code_paths'),
516
- );
517
-
518
- if (strongCodePathDomains.length > 1) {
519
- // Multiple code_paths domains detected - classify as mixed
520
- const allSignals = strongCodePathDomains.flatMap((d) => d.signals);
521
- return {
522
- domain: WORK_DOMAINS.MIXED,
523
- confidence: Math.max(...strongCodePathDomains.map((d) => d.maxWeight)),
524
- signals: allSignals,
525
- capabilities: getCapabilities(WORK_DOMAINS.MIXED),
526
- testMethodologyHint: undefined,
527
- };
528
- }
529
-
530
- if (candidates.length === 0) {
531
- // No signals above threshold - default to backend
532
- return {
533
- domain: WORK_DOMAINS.BACKEND,
534
- confidence: 0,
535
- signals: [],
536
- capabilities: [],
537
- testMethodologyHint: structuredContentOnly
538
- ? TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT
539
- : undefined,
540
- };
541
- }
542
-
543
- // Pick the domain with highest max weight
544
- candidates.sort((a, b) => b.maxWeight - a.maxWeight);
545
- const winner = candidates[0] as DomainSignals;
546
-
547
- const testMethodologyHint =
548
- winner.domain === WORK_DOMAINS.UI && winner.maxWeight >= SMOKE_TEST_THRESHOLD
549
- ? TEST_METHODOLOGY_HINTS.SMOKE_TEST
550
- : structuredContentOnly
551
- ? TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT
552
- : undefined;
553
-
554
- return {
555
- domain: winner.domain,
556
- confidence: winner.maxWeight,
557
- signals: winner.signals,
558
- capabilities: getCapabilities(winner.domain),
559
- testMethodologyHint,
560
- };
561
- }
@@ -1,10 +0,0 @@
1
- // Copyright (c) 2026 Hellmai Ltd
2
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
3
-
4
- // Barrel for pack-local sandbox modules. Populated by Layer 4 of INIT-058.
5
- // WU-2678: moved sandbox-profile, sandbox-allowlist, sandbox-backend-{linux,macos,windows} from @lumenflow/core.
6
- export * from './sandbox-allowlist.js';
7
- export * from './sandbox-profile.js';
8
- export * from './sandbox-backend-linux.js';
9
- export * from './sandbox-backend-macos.js';
10
- export * from './sandbox-backend-windows.js';
@@ -1,118 +0,0 @@
1
- // Copyright (c) 2026 Hellmai Ltd
2
- // SPDX-License-Identifier: LicenseRef-LumenFlow-Proprietary
3
-
4
- import fs from 'node:fs';
5
- import path from 'node:path';
6
-
7
- export interface SandboxAllowlistEntry {
8
- originalPath: string;
9
- normalizedPath: string;
10
- canonicalPath: string;
11
- }
12
-
13
- export interface SandboxAllowlist {
14
- projectRoot: string;
15
- writableRoots: SandboxAllowlistEntry[];
16
- }
17
-
18
- export interface BuildSandboxAllowlistInput {
19
- projectRoot: string;
20
- writableRoots: string[];
21
- }
22
-
23
- function normalizeAbsolutePath(targetPath: string): string {
24
- const normalized = path.resolve(targetPath);
25
-
26
- if (normalized.length > 1 && normalized.endsWith(path.sep)) {
27
- return normalized.slice(0, -1);
28
- }
29
-
30
- return normalized;
31
- }
32
-
33
- function toComparisonKey(targetPath: string): string {
34
- return process.platform === 'win32' ? targetPath.toLowerCase() : targetPath;
35
- }
36
-
37
- function findNearestExistingAncestor(targetPath: string): {
38
- ancestor: string;
39
- suffixParts: string[];
40
- } {
41
- let cursor = normalizeAbsolutePath(targetPath);
42
- const suffixParts: string[] = [];
43
-
44
- while (!fs.existsSync(cursor)) {
45
- const parent = path.dirname(cursor);
46
- if (parent === cursor) {
47
- break;
48
- }
49
-
50
- suffixParts.unshift(path.basename(cursor));
51
- cursor = parent;
52
- }
53
-
54
- return { ancestor: cursor, suffixParts };
55
- }
56
-
57
- function resolveCanonicalPathForWrite(targetPath: string): string {
58
- const normalized = normalizeAbsolutePath(targetPath);
59
-
60
- if (fs.existsSync(normalized)) {
61
- return normalizeAbsolutePath(fs.realpathSync.native(normalized));
62
- }
63
-
64
- const { ancestor, suffixParts } = findNearestExistingAncestor(normalized);
65
- const canonicalAncestor = fs.existsSync(ancestor)
66
- ? normalizeAbsolutePath(fs.realpathSync.native(ancestor))
67
- : normalizeAbsolutePath(ancestor);
68
-
69
- return normalizeAbsolutePath(path.join(canonicalAncestor, ...suffixParts));
70
- }
71
-
72
- function resolveRoot(projectRoot: string, writableRoot: string): string {
73
- if (path.isAbsolute(writableRoot)) {
74
- return normalizeAbsolutePath(writableRoot);
75
- }
76
-
77
- return normalizeAbsolutePath(path.resolve(projectRoot, writableRoot));
78
- }
79
-
80
- function isWithinRoot(candidatePath: string, rootPath: string): boolean {
81
- const candidateKey = toComparisonKey(candidatePath);
82
- const rootKey = toComparisonKey(rootPath);
83
-
84
- return candidateKey === rootKey || candidateKey.startsWith(`${rootKey}${path.sep}`);
85
- }
86
-
87
- export function buildSandboxAllowlist(input: BuildSandboxAllowlistInput): SandboxAllowlist {
88
- const projectRoot = normalizeAbsolutePath(input.projectRoot);
89
- const writableRoots = input.writableRoots.map((writableRoot) => {
90
- const normalizedPath = resolveRoot(projectRoot, writableRoot);
91
-
92
- return {
93
- originalPath: writableRoot,
94
- normalizedPath,
95
- canonicalPath: resolveCanonicalPathForWrite(normalizedPath),
96
- };
97
- });
98
-
99
- return {
100
- projectRoot,
101
- writableRoots,
102
- };
103
- }
104
-
105
- export function isWritePathAllowed(allowlist: SandboxAllowlist, targetPath: string): boolean {
106
- const normalizedTargetPath = normalizeAbsolutePath(targetPath);
107
- const canonicalTargetPath = resolveCanonicalPathForWrite(normalizedTargetPath);
108
-
109
- return allowlist.writableRoots.some((entry) => {
110
- const normalizedMatch = isWithinRoot(normalizedTargetPath, entry.normalizedPath);
111
- const canonicalMatch = isWithinRoot(canonicalTargetPath, entry.canonicalPath);
112
-
113
- // Require both checks:
114
- // - normalized path guards lexical traversal tricks (../)
115
- // - canonical path guards symlink escapes to outside the allowed root
116
- return normalizedMatch && canonicalMatch;
117
- });
118
- }