@lumenflow/cli 4.24.0 → 5.0.1

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 (287) hide show
  1. package/README.md +54 -52
  2. package/dist/agent-issues-query.js +10 -2
  3. package/dist/agent-issues-query.js.map +1 -1
  4. package/dist/agent-runtime-enrollment-events.js +44 -0
  5. package/dist/agent-runtime-enrollment-events.js.map +1 -0
  6. package/dist/agent-session-end.js +47 -0
  7. package/dist/agent-session-end.js.map +1 -1
  8. package/dist/agent-session-heartbeat.js +250 -0
  9. package/dist/agent-session-heartbeat.js.map +1 -0
  10. package/dist/agent-session.js +299 -5
  11. package/dist/agent-session.js.map +1 -1
  12. package/dist/capacity-snapshot-emitter.js +73 -0
  13. package/dist/capacity-snapshot-emitter.js.map +1 -0
  14. package/dist/claim-queue.js +276 -0
  15. package/dist/claim-queue.js.map +1 -0
  16. package/dist/config-set.js +22 -3
  17. package/dist/config-set.js.map +1 -1
  18. package/dist/control-plane-sidecar-runner.js +145 -0
  19. package/dist/control-plane-sidecar-runner.js.map +1 -0
  20. package/dist/delegation-list.js +160 -1
  21. package/dist/delegation-list.js.map +1 -1
  22. package/dist/delegation-role-resolver.js +69 -0
  23. package/dist/delegation-role-resolver.js.map +1 -0
  24. package/dist/docs-generate-pack-reference.js +500 -0
  25. package/dist/docs-generate-pack-reference.js.map +1 -0
  26. package/dist/docs-sync.js +116 -1
  27. package/dist/docs-sync.js.map +1 -1
  28. package/dist/file-edit.js +28 -8
  29. package/dist/file-edit.js.map +1 -1
  30. package/dist/file-write.js +29 -5
  31. package/dist/file-write.js.map +1 -1
  32. package/dist/gate-co-change.js +25 -7
  33. package/dist/gate-co-change.js.map +1 -1
  34. package/dist/gate-conditional.js +19 -7
  35. package/dist/gate-conditional.js.map +1 -1
  36. package/dist/gates-runners.js +42 -33
  37. package/dist/gates-runners.js.map +1 -1
  38. package/dist/gates-utils.js +34 -20
  39. package/dist/gates-utils.js.map +1 -1
  40. package/dist/gates.js +79 -7
  41. package/dist/gates.js.map +1 -1
  42. package/dist/hooks/config-resolver.js +10 -1
  43. package/dist/hooks/config-resolver.js.map +1 -1
  44. package/dist/init-package-config.js +1 -1
  45. package/dist/init-package-config.js.map +1 -1
  46. package/dist/init-scaffolding.js +5 -1
  47. package/dist/init-scaffolding.js.map +1 -1
  48. package/dist/init-templates.js +10 -0
  49. package/dist/init-templates.js.map +1 -1
  50. package/dist/init.js +1 -1
  51. package/dist/init.js.map +1 -1
  52. package/dist/initiative-create.js +17 -0
  53. package/dist/initiative-create.js.map +1 -1
  54. package/dist/initiative-remove-wu.js +17 -3
  55. package/dist/initiative-remove-wu.js.map +1 -1
  56. package/dist/kernel-event-sync/emitters.js +104 -0
  57. package/dist/kernel-event-sync/emitters.js.map +1 -0
  58. package/dist/kernel-event-sync/index.js +13 -0
  59. package/dist/kernel-event-sync/index.js.map +1 -0
  60. package/dist/kernel-event-sync/lifecycle-emitters.js +160 -0
  61. package/dist/kernel-event-sync/lifecycle-emitters.js.map +1 -0
  62. package/dist/kernel-event-sync/narrow-emissions.js +89 -0
  63. package/dist/kernel-event-sync/narrow-emissions.js.map +1 -0
  64. package/dist/kernel-event-sync/software-delivery-emitters.js +297 -0
  65. package/dist/kernel-event-sync/software-delivery-emitters.js.map +1 -0
  66. package/dist/lane-lock.js +14 -1
  67. package/dist/lane-lock.js.map +1 -1
  68. package/dist/lane-suggest.js +21 -0
  69. package/dist/lane-suggest.js.map +1 -1
  70. package/dist/lumenflow-upgrade.js +7 -5
  71. package/dist/lumenflow-upgrade.js.map +1 -1
  72. package/dist/mem-context.js +145 -0
  73. package/dist/mem-context.js.map +1 -1
  74. package/dist/mem-create.js +39 -6
  75. package/dist/mem-create.js.map +1 -1
  76. package/dist/mem-inbox.js +16 -0
  77. package/dist/mem-inbox.js.map +1 -1
  78. package/dist/mem-roster.js +95 -0
  79. package/dist/mem-roster.js.map +1 -0
  80. package/dist/mem-signal.js +97 -2
  81. package/dist/mem-signal.js.map +1 -1
  82. package/dist/metrics-snapshot.js +3 -2
  83. package/dist/metrics-snapshot.js.map +1 -1
  84. package/dist/orchestrate-init-status.js +117 -2
  85. package/dist/orchestrate-init-status.js.map +1 -1
  86. package/dist/orchestrate-initiative.js +83 -10
  87. package/dist/orchestrate-initiative.js.map +1 -1
  88. package/dist/orchestrate-monitor-quality.js +289 -0
  89. package/dist/orchestrate-monitor-quality.js.map +1 -0
  90. package/dist/orchestrate-monitor.js +85 -0
  91. package/dist/orchestrate-monitor.js.map +1 -1
  92. package/dist/pack-validate.js +127 -2
  93. package/dist/pack-validate.js.map +1 -1
  94. package/dist/plan-create.js +18 -0
  95. package/dist/plan-create.js.map +1 -1
  96. package/dist/plan-link.js +13 -0
  97. package/dist/plan-link.js.map +1 -1
  98. package/dist/plan-promote.js +14 -0
  99. package/dist/plan-promote.js.map +1 -1
  100. package/dist/pre-commit-check.js +4 -3
  101. package/dist/pre-commit-check.js.map +1 -1
  102. package/dist/public-manifest.js +17 -3
  103. package/dist/public-manifest.js.map +1 -1
  104. package/dist/release.js +28 -10
  105. package/dist/release.js.map +1 -1
  106. package/dist/session-cross-link.js +139 -0
  107. package/dist/session-cross-link.js.map +1 -0
  108. package/dist/sidecar-manager.js +208 -0
  109. package/dist/sidecar-manager.js.map +1 -0
  110. package/dist/state-path-resolvers.js +18 -0
  111. package/dist/state-path-resolvers.js.map +1 -1
  112. package/dist/stream-heartbeat.js +151 -0
  113. package/dist/stream-heartbeat.js.map +1 -0
  114. package/dist/sync-templates.js +56 -2
  115. package/dist/sync-templates.js.map +1 -1
  116. package/dist/wu-block.js +47 -5
  117. package/dist/wu-block.js.map +1 -1
  118. package/dist/wu-claim-branch.js +8 -4
  119. package/dist/wu-claim-branch.js.map +1 -1
  120. package/dist/wu-claim-state.js +5 -3
  121. package/dist/wu-claim-state.js.map +1 -1
  122. package/dist/wu-claim-worktree.js +5 -3
  123. package/dist/wu-claim-worktree.js.map +1 -1
  124. package/dist/wu-claim.js +261 -9
  125. package/dist/wu-claim.js.map +1 -1
  126. package/dist/wu-done-auto-cleanup.js +3 -2
  127. package/dist/wu-done-auto-cleanup.js.map +1 -1
  128. package/dist/wu-done-git-ops.js +12 -8
  129. package/dist/wu-done-git-ops.js.map +1 -1
  130. package/dist/wu-done-preflight.js +3 -3
  131. package/dist/wu-done-preflight.js.map +1 -1
  132. package/dist/wu-done.js +46 -10
  133. package/dist/wu-done.js.map +1 -1
  134. package/dist/wu-lifecycle-sync/gate-scope-resolver.js +16 -0
  135. package/dist/wu-lifecycle-sync/gate-scope-resolver.js.map +1 -0
  136. package/dist/wu-lifecycle-sync/kernel-event-sync-shim.js +10 -0
  137. package/dist/wu-lifecycle-sync/kernel-event-sync-shim.js.map +1 -0
  138. package/dist/wu-prep.js +363 -22
  139. package/dist/wu-prep.js.map +1 -1
  140. package/dist/wu-prune.js +68 -27
  141. package/dist/wu-prune.js.map +1 -1
  142. package/dist/wu-release.js +34 -3
  143. package/dist/wu-release.js.map +1 -1
  144. package/dist/wu-review.js +167 -0
  145. package/dist/wu-review.js.map +1 -0
  146. package/dist/wu-spawn-prompt-builders.js +296 -40
  147. package/dist/wu-spawn-prompt-builders.js.map +1 -1
  148. package/dist/wu-spawn-strategy-resolver.js +126 -14
  149. package/dist/wu-spawn-strategy-resolver.js.map +1 -1
  150. package/dist/wu-unblock.js +52 -22
  151. package/dist/wu-unblock.js.map +1 -1
  152. package/package.json +13 -8
  153. package/packs/agent-runtime/agent-heartbeat.ts +163 -0
  154. package/packs/agent-runtime/auto-session-integration.ts +874 -0
  155. package/packs/agent-runtime/delegation-registry-schema.ts +220 -0
  156. package/packs/agent-runtime/delegation-registry-store.ts +269 -0
  157. package/packs/agent-runtime/delegation-tree.ts +328 -0
  158. package/packs/agent-runtime/index.ts +9 -0
  159. package/packs/agent-runtime/manifest.ts +109 -19
  160. package/packs/agent-runtime/manifest.yaml +150 -0
  161. package/packs/agent-runtime/memory-coordination-contract.ts +86 -0
  162. package/packs/agent-runtime/memory.d.ts +19 -0
  163. package/packs/agent-runtime/orchestration.ts +238 -23
  164. package/packs/agent-runtime/package.json +11 -2
  165. package/packs/agent-runtime/remote-controls/index.ts +7 -0
  166. package/packs/agent-runtime/remote-controls/operations.ts +399 -0
  167. package/packs/agent-runtime/remote-controls/port.ts +48 -0
  168. package/packs/agent-runtime/remote-controls/state-store.ts +258 -0
  169. package/packs/agent-runtime/remote-controls/types.ts +105 -0
  170. package/packs/agent-runtime/session-schema.ts +423 -0
  171. package/packs/agent-runtime/tool-impl/index.ts +1 -0
  172. package/packs/agent-runtime/tool-impl/remote-controls.mock.ts +252 -0
  173. package/packs/agent-runtime/tool-impl/remote-controls.ts +273 -0
  174. package/packs/agent-runtime/tsconfig.json +1 -1
  175. package/packs/agent-runtime/turn-lifecycle-events.ts +501 -0
  176. package/packs/sidekick/channel-ingress.ts +137 -0
  177. package/packs/sidekick/manifest.ts +74 -0
  178. package/packs/sidekick/manifest.yaml +88 -0
  179. package/packs/sidekick/package.json +3 -1
  180. package/packs/sidekick/sidekick-events.ts +517 -0
  181. package/packs/sidekick/src/adapters/cloud-queue.ts +101 -0
  182. package/packs/sidekick/src/adapters/control-plane-bridge.adapter.ts +378 -0
  183. package/packs/sidekick/src/adapters/filesystem-bridge.adapter.ts +224 -0
  184. package/packs/sidekick/src/domain/channel.types.ts +84 -0
  185. package/packs/sidekick/src/ports/channel-bridge.port.ts +75 -0
  186. package/packs/sidekick/src/routines/commit.ts +74 -0
  187. package/packs/sidekick/tool-impl/channel-tools.ts +47 -0
  188. package/packs/sidekick/tool-impl/memory-tools.ts +17 -0
  189. package/packs/sidekick/tool-impl/routine-commit.ts +102 -0
  190. package/packs/sidekick/tool-impl/routine-tools.ts +67 -7
  191. package/packs/sidekick/tool-impl/runtime-context.ts +4 -0
  192. package/packs/sidekick/tool-impl/storage.ts +3 -0
  193. package/packs/sidekick/tool-impl/system-tools.ts +7 -0
  194. package/packs/sidekick/tool-impl/task-tools.ts +46 -0
  195. package/packs/sidekick/tsconfig.json +1 -1
  196. package/packs/software-delivery/manifest-schema.ts +30 -0
  197. package/packs/software-delivery/manifest.ts +160 -11
  198. package/packs/software-delivery/manifest.yaml +210 -230
  199. package/packs/software-delivery/package.json +88 -3
  200. package/packs/software-delivery/src/commands/index.ts +5 -0
  201. package/packs/software-delivery/src/config/delivery-review-contract.ts +20 -0
  202. package/packs/software-delivery/src/config/env-accessors.ts +19 -0
  203. package/packs/software-delivery/src/config/index.ts +8 -0
  204. package/packs/software-delivery/src/config/normalize-config-keys.ts +19 -0
  205. package/packs/software-delivery/src/config/schemas/lumenflow-config-schema-types.ts +436 -0
  206. package/packs/software-delivery/src/config/workspace-reader.ts +310 -0
  207. package/packs/software-delivery/src/constants/backlog-patterns.ts +31 -0
  208. package/packs/software-delivery/src/constants/client-ids.ts +19 -0
  209. package/packs/software-delivery/src/constants/config-contract.ts +7 -0
  210. package/packs/software-delivery/src/constants/docs-layout-presets.ts +50 -0
  211. package/packs/software-delivery/src/constants/duration-constants.ts +20 -0
  212. package/packs/software-delivery/src/constants/gate-constants.ts +32 -0
  213. package/packs/software-delivery/src/constants/index.ts +29 -0
  214. package/packs/software-delivery/src/constants/lock-constants.ts +35 -0
  215. package/packs/software-delivery/src/constants/object-guards.ts +12 -0
  216. package/packs/software-delivery/src/constants/section-headings.ts +107 -0
  217. package/packs/software-delivery/src/constants/wu-cli-constants.ts +485 -0
  218. package/packs/software-delivery/src/constants/wu-domain-constants.ts +466 -0
  219. package/packs/software-delivery/src/constants/wu-git-constants.ts +7 -0
  220. package/packs/software-delivery/src/constants/wu-id-format.ts +327 -0
  221. package/packs/software-delivery/src/constants/wu-paths-constants.ts +358 -0
  222. package/packs/software-delivery/src/constants/wu-statuses.ts +287 -0
  223. package/packs/software-delivery/src/constants/wu-type-helpers.ts +67 -0
  224. package/packs/software-delivery/src/constants/wu-ui-constants.ts +267 -0
  225. package/packs/software-delivery/src/constants/wu-validation-constants.ts +73 -0
  226. package/packs/software-delivery/src/domain/index.ts +5 -0
  227. package/packs/software-delivery/src/domain/orchestration.constants.ts +168 -0
  228. package/packs/software-delivery/src/domain/orchestration.schemas.ts +239 -0
  229. package/packs/software-delivery/src/domain/orchestration.types.ts +178 -0
  230. package/packs/software-delivery/src/methodology/incremental-test.ts +90 -0
  231. package/packs/software-delivery/src/methodology/index.ts +6 -0
  232. package/packs/software-delivery/src/methodology/manual-test-validator.ts +292 -0
  233. package/packs/software-delivery/src/policy/coverage-gate.ts +270 -0
  234. package/packs/software-delivery/src/policy/gates-agent-mode.ts +223 -0
  235. package/packs/software-delivery/src/policy/gates-config-internal.ts +121 -0
  236. package/packs/software-delivery/src/policy/gates-config.ts +293 -0
  237. package/packs/software-delivery/src/policy/gates-coverage.ts +247 -0
  238. package/packs/software-delivery/src/policy/gates-presets.ts +134 -0
  239. package/packs/software-delivery/src/policy/gates-schemas.ts +173 -0
  240. package/packs/software-delivery/src/policy/index.ts +22 -0
  241. package/packs/software-delivery/src/policy/package-manager-resolver.ts +319 -0
  242. package/packs/software-delivery/src/policy/resolve-policy.ts +518 -0
  243. package/packs/software-delivery/src/ports/config.ports.ts +90 -0
  244. package/packs/software-delivery/src/ports/dashboard-renderer.port.ts +125 -0
  245. package/packs/software-delivery/src/ports/index.ts +10 -0
  246. package/packs/software-delivery/src/ports/sync-validator.ports.ts +59 -0
  247. package/packs/software-delivery/src/ports/wu-helpers.ports.ts +168 -0
  248. package/packs/software-delivery/src/ports/wu-state.ports.ts +241 -0
  249. package/packs/software-delivery/src/primitives/index.ts +5 -0
  250. package/packs/software-delivery/src/runtime/index.ts +6 -0
  251. package/packs/software-delivery/src/runtime/work-classifier.ts +561 -0
  252. package/packs/software-delivery/src/sandbox/index.ts +10 -0
  253. package/packs/software-delivery/src/sandbox/sandbox-allowlist.ts +118 -0
  254. package/packs/software-delivery/src/sandbox/sandbox-backend-linux.ts +88 -0
  255. package/packs/software-delivery/src/sandbox/sandbox-backend-macos.ts +154 -0
  256. package/packs/software-delivery/src/sandbox/sandbox-backend-windows.ts +47 -0
  257. package/packs/software-delivery/src/sandbox/sandbox-profile.ts +153 -0
  258. package/packs/software-delivery/src/schemas/index.ts +5 -0
  259. package/packs/software-delivery/src/state/date-utils.ts +158 -0
  260. package/packs/software-delivery/src/state/index.ts +15 -0
  261. package/packs/software-delivery/src/state/state-machine.ts +119 -0
  262. package/packs/software-delivery/src/state/wu-doc-types.ts +51 -0
  263. package/packs/software-delivery/src/state/wu-paths.ts +381 -0
  264. package/packs/software-delivery/src/state/wu-schema.ts +1139 -0
  265. package/packs/software-delivery/src/state/wu-state-schema.ts +255 -0
  266. package/packs/software-delivery/src/state/wu-yaml.ts +338 -0
  267. package/packs/software-delivery/src/types.d.ts +16 -0
  268. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +18 -0
  269. package/packs/software-delivery/tsconfig.json +28 -2
  270. package/templates/core/AGENTS.md.template +76 -17
  271. package/templates/core/LUMENFLOW.md.template +265 -66
  272. package/templates/core/_frameworks/lumenflow/wu-sizing-guide.md.template +180 -116
  273. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +26 -8
  274. package/templates/core/ai/onboarding/existing-project-bootstrap.md.template +171 -0
  275. package/templates/core/ai/onboarding/first-15-mins.md.template +3 -1
  276. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +1 -1
  277. package/templates/core/ai/onboarding/initiative-orchestration.md.template +46 -30
  278. package/templates/core/ai/onboarding/quick-ref-commands.md.template +36 -33
  279. package/templates/core/ai/onboarding/release-process.md.template +8 -7
  280. package/templates/core/ai/onboarding/starting-prompt.md.template +2 -0
  281. package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +62 -0
  282. package/templates/vendors/claude/.claude/CLAUDE.md.template +29 -54
  283. package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +24 -52
  284. package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +24 -52
  285. package/packs/agent-runtime/.turbo/turbo-build.log +0 -4
  286. package/packs/sidekick/.turbo/turbo-build.log +0 -4
  287. package/packs/software-delivery/.turbo/turbo-build.log +0 -4
@@ -0,0 +1,310 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Pack-side workspace.yaml reader.
6
+ *
7
+ * Locates the project root (via kernel primitive) and returns the
8
+ * `software_delivery` section of `workspace.yaml`. Loose typing on purpose —
9
+ * the authoritative schema lives in `@lumenflow/core`; this helper exists to
10
+ * keep the pack core-free for Option 1b prep work (INIT-058).
11
+ */
12
+
13
+ import { readFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+
16
+ import { findProjectRoot } from '@lumenflow/kernel/primitives/project-root';
17
+ import { WORKSPACE_CONFIG_FILE_NAME } from '@lumenflow/kernel/primitives/workspace-constants';
18
+ import { parse as parseYaml } from 'yaml';
19
+
20
+ import {
21
+ AGENTS_DEFAULTS,
22
+ CLEANUP_DEFAULTS,
23
+ CLOUD_DEFAULTS,
24
+ DIRECTORIES_DEFAULTS,
25
+ ESCALATION_DEFAULTS,
26
+ EXPERIMENTAL_DEFAULTS,
27
+ GATES_DEFAULTS,
28
+ GIT_DEFAULTS,
29
+ INITIATIVE_ID_DEFAULTS,
30
+ LUMENFLOW_CONFIG_DEFAULTS,
31
+ MEMORY_DEFAULTS,
32
+ STATE_PATHS_DEFAULTS,
33
+ TELEMETRY_DEFAULTS,
34
+ UI_DEFAULTS,
35
+ WU_DEFAULTS,
36
+ WU_ID_DEFAULTS,
37
+ YAML_DEFAULTS,
38
+ type AgentsConfig,
39
+ type CleanupConfig,
40
+ type CloudConfig,
41
+ type DirectoriesConfig,
42
+ type EscalationConfig,
43
+ type EventArchivalConfig,
44
+ type ExperimentalConfig,
45
+ type GatesConfig,
46
+ type GitConfig,
47
+ type IdFormatConfig,
48
+ type LumenflowConfig,
49
+ type MemoryConfig,
50
+ type PackageManager,
51
+ type PushRetryConfig,
52
+ type StatePathsConfig,
53
+ type TelemetryConfig,
54
+ type TestRunner,
55
+ type UiConfig,
56
+ type WuConfig,
57
+ type YamlConfig,
58
+ } from './schemas/lumenflow-config-schema-types.js';
59
+
60
+ export type SoftwareDeliveryConfig = Record<string, unknown>;
61
+
62
+ /**
63
+ * Read `workspace.yaml` relative to the given `cwd`'s project root and return
64
+ * the `software_delivery` section (or an empty object if absent/empty).
65
+ *
66
+ * @throws If the project root cannot be located or the YAML is malformed.
67
+ */
68
+ export function readSoftwareDeliveryConfig(cwd: string): SoftwareDeliveryConfig {
69
+ const root = findProjectRoot(cwd);
70
+ const raw = readFileSync(join(root, WORKSPACE_CONFIG_FILE_NAME), 'utf8');
71
+ const doc = parseYaml(raw) as Record<string, unknown> | null | undefined;
72
+ const section = doc?.['software_delivery'];
73
+ if (section && typeof section === 'object' && !Array.isArray(section)) {
74
+ return section as SoftwareDeliveryConfig;
75
+ }
76
+ return {};
77
+ }
78
+
79
+ /**
80
+ * Re-export pack-local directory/state-path type aliases. These mirror
81
+ * `@lumenflow/core/schemas/directories-config`; drift is enforced by the
82
+ * integration-tests parity tests.
83
+ */
84
+ export type WorkspaceDirectories = DirectoriesConfig;
85
+ export type WorkspaceStatePaths = StatePathsConfig;
86
+ export type WorkspaceEventArchivalConfig = EventArchivalConfig;
87
+
88
+ function readObjectRecord(cwd: string, key: string): Record<string, unknown> {
89
+ const section = readSoftwareDeliveryConfig(cwd);
90
+ const value = section[key];
91
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
92
+ return value as Record<string, unknown>;
93
+ }
94
+ return {};
95
+ }
96
+
97
+ /**
98
+ * Return the typed directory map for the workspace rooted at `cwd`.
99
+ *
100
+ * User-provided values in `software_delivery.directories` are merged over the
101
+ * built-in defaults. Unknown keys in the user's YAML are ignored by the return
102
+ * type but pass through the spread — the authoritative schema validation still
103
+ * lives in core's `getConfig()` pathway.
104
+ */
105
+ export function getDirectories(cwd: string): WorkspaceDirectories {
106
+ const overrides = readObjectRecord(cwd, 'directories') as Partial<WorkspaceDirectories>;
107
+ return { ...DIRECTORIES_DEFAULTS, ...overrides };
108
+ }
109
+
110
+ /**
111
+ * Return the typed state-paths map for the workspace rooted at `cwd`.
112
+ *
113
+ * User-provided values in `software_delivery.state` are merged over the
114
+ * built-in defaults. `eventArchival` is merged as a nested object so a partial
115
+ * override (e.g. only `archiveAfter`) preserves the remaining defaults.
116
+ */
117
+ export function getStatePaths(cwd: string): WorkspaceStatePaths {
118
+ const overrides = readObjectRecord(cwd, 'state') as Partial<WorkspaceStatePaths>;
119
+ const { eventArchival: overrideArchival, ...flatOverrides } = overrides;
120
+ const eventArchivalOverrides =
121
+ overrideArchival && typeof overrideArchival === 'object' && !Array.isArray(overrideArchival)
122
+ ? (overrideArchival as Partial<WorkspaceEventArchivalConfig>)
123
+ : {};
124
+ return {
125
+ ...STATE_PATHS_DEFAULTS,
126
+ ...flatOverrides,
127
+ eventArchival: {
128
+ ...STATE_PATHS_DEFAULTS.eventArchival,
129
+ ...eventArchivalOverrides,
130
+ },
131
+ };
132
+ }
133
+
134
+ function mergeGit(overrides: Partial<GitConfig> | undefined): GitConfig {
135
+ if (!overrides) return { ...GIT_DEFAULTS, push_retry: { ...GIT_DEFAULTS.push_retry } };
136
+ const { push_retry: overridePushRetry, ...flatOverrides } = overrides;
137
+ const pushRetryOverrides =
138
+ overridePushRetry && typeof overridePushRetry === 'object' && !Array.isArray(overridePushRetry)
139
+ ? (overridePushRetry as Partial<PushRetryConfig>)
140
+ : {};
141
+ return {
142
+ ...GIT_DEFAULTS,
143
+ ...flatOverrides,
144
+ push_retry: {
145
+ ...GIT_DEFAULTS.push_retry,
146
+ ...pushRetryOverrides,
147
+ },
148
+ };
149
+ }
150
+
151
+ function mergeWu(overrides: Partial<WuConfig> | undefined): WuConfig {
152
+ if (!overrides) return { ...WU_DEFAULTS, brief: { ...WU_DEFAULTS.brief } };
153
+ const { brief: overrideBrief, ...flatOverrides } = overrides;
154
+ const briefOverrides =
155
+ overrideBrief && typeof overrideBrief === 'object' && !Array.isArray(overrideBrief)
156
+ ? (overrideBrief as Partial<WuConfig['brief']>)
157
+ : {};
158
+ return {
159
+ ...WU_DEFAULTS,
160
+ ...flatOverrides,
161
+ brief: {
162
+ ...WU_DEFAULTS.brief,
163
+ ...briefOverrides,
164
+ },
165
+ };
166
+ }
167
+
168
+ function mergeSimple<T>(defaults: T, overrides: unknown): T {
169
+ if (overrides && typeof overrides === 'object' && !Array.isArray(overrides)) {
170
+ return { ...defaults, ...(overrides as object) } as T;
171
+ }
172
+ return { ...defaults };
173
+ }
174
+
175
+ function mergeTelemetry(overrides: Partial<TelemetryConfig> | undefined): TelemetryConfig {
176
+ if (!overrides) {
177
+ return { methodology: { ...TELEMETRY_DEFAULTS.methodology } };
178
+ }
179
+ const methodology =
180
+ overrides.methodology && typeof overrides.methodology === 'object'
181
+ ? { ...TELEMETRY_DEFAULTS.methodology, ...overrides.methodology }
182
+ : { ...TELEMETRY_DEFAULTS.methodology };
183
+ return { methodology };
184
+ }
185
+
186
+ function pickEnum<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {
187
+ return typeof value === 'string' && (allowed as readonly string[]).includes(value)
188
+ ? (value as T)
189
+ : fallback;
190
+ }
191
+
192
+ /**
193
+ * Return the fully-typed pack-local `LumenflowConfig` for the workspace rooted
194
+ * at `cwd`.
195
+ *
196
+ * Mirrors the shape produced by `@lumenflow/core`'s
197
+ * `getConfig({ projectRoot: cwd }).parse({})` for every field exposed on
198
+ * `LumenflowConfig`. Drift from core is enforced by
199
+ * `integration-tests/tests/config-parity.test.ts`.
200
+ *
201
+ * Behaviour parity with core:
202
+ * - Reads `workspace.yaml` relative to `cwd`'s project root via the kernel
203
+ * `findProjectRoot` primitive.
204
+ * - Returns defaults when `workspace.yaml` is absent or lacks a
205
+ * `software_delivery` block (graceful fallback, matches core's
206
+ * non-`strictWorkspace` mode).
207
+ * - User-provided values are spread over defaults at the section level, with
208
+ * nested merges for sections that have nested objects (state.eventArchival,
209
+ * git.push_retry, wu.brief, telemetry.methodology).
210
+ */
211
+ export function getConfig(cwd: string): LumenflowConfig {
212
+ let section: SoftwareDeliveryConfig;
213
+ try {
214
+ section = readSoftwareDeliveryConfig(cwd);
215
+ } catch {
216
+ // Graceful fallback — matches core's non-strictWorkspace behaviour when
217
+ // workspace.yaml is absent or unreadable.
218
+ return cloneDefaults();
219
+ }
220
+
221
+ return {
222
+ version: typeof section['version'] === 'string' ? section['version'] : '1.0.0',
223
+ directories: getDirectories(cwd),
224
+ state: getStatePaths(cwd),
225
+ git: mergeGit(section['git'] as Partial<GitConfig> | undefined),
226
+ wu: mergeWu(section['wu'] as Partial<WuConfig> | undefined),
227
+ wuId: mergeSimple<IdFormatConfig>(WU_ID_DEFAULTS, section['wuId']),
228
+ initiativeId: mergeSimple<IdFormatConfig>(INITIATIVE_ID_DEFAULTS, section['initiativeId']),
229
+ gates: mergeSimple<GatesConfig>(GATES_DEFAULTS, section['gates']),
230
+ memory: mergeSimple<MemoryConfig>(MEMORY_DEFAULTS, section['memory']),
231
+ ui: mergeSimple<UiConfig>(UI_DEFAULTS, section['ui']),
232
+ yaml: mergeSimple<YamlConfig>(YAML_DEFAULTS, section['yaml']),
233
+ agents: mergeSimple<AgentsConfig>(AGENTS_DEFAULTS, section['agents']),
234
+ experimental: mergeSimple<ExperimentalConfig>(EXPERIMENTAL_DEFAULTS, section['experimental']),
235
+ cleanup: mergeSimple<CleanupConfig>(CLEANUP_DEFAULTS, section['cleanup']),
236
+ telemetry: mergeTelemetry(section['telemetry'] as Partial<TelemetryConfig> | undefined),
237
+ methodology:
238
+ section['methodology'] && typeof section['methodology'] === 'object'
239
+ ? (section['methodology'] as Record<string, unknown>)
240
+ : undefined,
241
+ cloud: mergeSimple<CloudConfig>(CLOUD_DEFAULTS, section['cloud']),
242
+ lanes:
243
+ section['lanes'] && typeof section['lanes'] === 'object'
244
+ ? (section['lanes'] as Record<string, unknown>)
245
+ : undefined,
246
+ escalation: mergeSimple<EscalationConfig>(ESCALATION_DEFAULTS, section['escalation']),
247
+ package_manager: pickEnum<PackageManager>(
248
+ section['package_manager'],
249
+ ['pnpm', 'npm', 'yarn', 'bun'] as const,
250
+ 'pnpm',
251
+ ),
252
+ test_runner: pickEnum<TestRunner>(
253
+ section['test_runner'],
254
+ ['vitest', 'jest', 'mocha'] as const,
255
+ 'vitest',
256
+ ),
257
+ build_command:
258
+ typeof section['build_command'] === 'string' ? section['build_command'] : 'pnpm build',
259
+ };
260
+ }
261
+
262
+ function cloneDefaults(): LumenflowConfig {
263
+ return {
264
+ ...LUMENFLOW_CONFIG_DEFAULTS,
265
+ directories: { ...DIRECTORIES_DEFAULTS },
266
+ state: {
267
+ ...STATE_PATHS_DEFAULTS,
268
+ eventArchival: { ...STATE_PATHS_DEFAULTS.eventArchival },
269
+ },
270
+ git: { ...GIT_DEFAULTS, push_retry: { ...GIT_DEFAULTS.push_retry } },
271
+ wu: { ...WU_DEFAULTS, brief: { ...WU_DEFAULTS.brief } },
272
+ wuId: { ...WU_ID_DEFAULTS },
273
+ initiativeId: { ...INITIATIVE_ID_DEFAULTS },
274
+ gates: { ...GATES_DEFAULTS },
275
+ memory: { ...MEMORY_DEFAULTS },
276
+ ui: { ...UI_DEFAULTS },
277
+ yaml: { ...YAML_DEFAULTS },
278
+ agents: { ...AGENTS_DEFAULTS },
279
+ experimental: { ...EXPERIMENTAL_DEFAULTS },
280
+ cleanup: { ...CLEANUP_DEFAULTS },
281
+ telemetry: { methodology: { ...TELEMETRY_DEFAULTS.methodology } },
282
+ cloud: { ...CLOUD_DEFAULTS, env_signals: [...CLOUD_DEFAULTS.env_signals] },
283
+ escalation: { ...ESCALATION_DEFAULTS },
284
+ };
285
+ }
286
+
287
+ // Re-export pack-local schema types for downstream consumers migrating off
288
+ // `@lumenflow/core/lumenflow-config-schema`.
289
+ export type {
290
+ AgentsConfig,
291
+ CleanupConfig,
292
+ CloudConfig,
293
+ DirectoriesConfig,
294
+ EscalationConfig,
295
+ EventArchivalConfig,
296
+ ExperimentalConfig,
297
+ GatesConfig,
298
+ GitConfig,
299
+ IdFormatConfig,
300
+ LumenflowConfig,
301
+ MemoryConfig,
302
+ PackageManager,
303
+ PushRetryConfig,
304
+ StatePathsConfig,
305
+ TelemetryConfig,
306
+ TestRunner,
307
+ UiConfig,
308
+ WuConfig,
309
+ YamlConfig,
310
+ } from './schemas/lumenflow-config-schema-types.js';
@@ -0,0 +1,31 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Backlog Parsing Patterns
6
+ *
7
+ * Centralizes patterns for parsing status.md and backlog.md files.
8
+ * Used by lane-checker.ts and related backlog utilities.
9
+ */
10
+
11
+ /** Markdown section header patterns for In Progress section */
12
+ export const IN_PROGRESS_HEADERS = ['## in progress', '## 🔧 in progress'];
13
+
14
+ /**
15
+ * Pattern for extracting WU ID from backlog links.
16
+ * Matches: [WU-123 — Title text](path/to/file.yaml)
17
+ * Captures: WU ID (e.g., "WU-123")
18
+ */
19
+ export const WU_LINK_PATTERN = /\[([A-Z]+-\d+)\s*—\s*[^\]]+\]\([^)]+\)/gi;
20
+
21
+ /**
22
+ * Check if a line matches an In Progress section header.
23
+ * @param {string} line - Line to check (will be trimmed and lowercased)
24
+ * @returns {boolean} True if line is an In Progress header
25
+ */
26
+ export function isInProgressHeader(line: string) {
27
+ const normalized = line.trim().toLowerCase();
28
+ return IN_PROGRESS_HEADERS.some(
29
+ (header) => normalized === header || normalized.startsWith(header),
30
+ );
31
+ }
@@ -0,0 +1,19 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Canonical client IDs for client-specific integrations and config keys.
6
+ *
7
+ * Kept in a standalone module so config schema can depend on these
8
+ * constants without importing context/hook modules.
9
+ */
10
+ export const LUMENFLOW_CLIENT_IDS = {
11
+ CLAUDE_CODE: 'claude-code',
12
+ CODEX_CLI: 'codex-cli',
13
+ CURSOR: 'cursor',
14
+ GEMINI_CLI: 'gemini-cli',
15
+ WINDSURF: 'windsurf',
16
+ } as const;
17
+
18
+ /** Type for supported client IDs with integration support */
19
+ export type LumenflowClientId = (typeof LUMENFLOW_CLIENT_IDS)[keyof typeof LUMENFLOW_CLIENT_IDS];
@@ -0,0 +1,7 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ // WU-2681 (INIT-058 L2e): Shim — real module lives in @lumenflow/kernel/primitives/workspace-constants.
5
+ // Re-export preserves the `@lumenflow/packs-software-delivery/constants/config-contract` subpath
6
+ // for existing consumers (including core shims) until Layer 7 removes the shim.
7
+ export * from '@lumenflow/kernel/primitives/workspace-constants';
@@ -0,0 +1,50 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Canonical docs layout presets used across scaffolding and defaults.
6
+ *
7
+ * Keeping presets in one place avoids path-literal drift between:
8
+ * - schema defaults
9
+ * - init docs-structure mapping
10
+ * - runtime prompt/path helpers
11
+ */
12
+
13
+ function buildDocsLayout(operations: string, tasks: string, adrDir: string) {
14
+ const frameworkRoot = `${operations}/_frameworks/lumenflow`;
15
+ const onboarding = `${frameworkRoot}/agent/onboarding`;
16
+
17
+ return {
18
+ operations,
19
+ tasks,
20
+ adrDir,
21
+ onboarding,
22
+ quickRefLink: `${onboarding}/quick-ref-commands.md`,
23
+ completeGuidePath: `${frameworkRoot}/lumenflow-complete.md`,
24
+ quickRefPath: `${onboarding}/quick-ref-commands.md`,
25
+ startingPromptPath: `${onboarding}/starting-prompt.md`,
26
+ sizingGuidePath: `${frameworkRoot}/wu-sizing-guide.md`,
27
+ governancePath: `${operations}/governance/project-governance.md`,
28
+ } as const;
29
+ }
30
+
31
+ const DOCS_ROOT = 'docs';
32
+ const SIMPLE_TASKS_PATH = `${DOCS_ROOT}/tasks`;
33
+ const SIMPLE_ADR_PATH = `${DOCS_ROOT}/architecture-decisions`;
34
+ const ARC42_OPERATIONS_PATH = [DOCS_ROOT, 'operations'].join('/');
35
+ const ARC42_TASKS_PATH = [ARC42_OPERATIONS_PATH, 'tasks'].join('/');
36
+ const ARC42_ADR_PATH = [DOCS_ROOT, '09-architecture-decisions'].join('/');
37
+
38
+ export const DOCS_LAYOUT_PRESETS = {
39
+ simple: buildDocsLayout(DOCS_ROOT, SIMPLE_TASKS_PATH, SIMPLE_ADR_PATH),
40
+ arc42: buildDocsLayout(ARC42_OPERATIONS_PATH, ARC42_TASKS_PATH, ARC42_ADR_PATH),
41
+ } as const;
42
+
43
+ export type DocsLayoutType = keyof typeof DOCS_LAYOUT_PRESETS;
44
+ export type DocsLayoutPreset = (typeof DOCS_LAYOUT_PRESETS)[DocsLayoutType];
45
+
46
+ export const DEFAULT_DOCS_LAYOUT: DocsLayoutType = 'simple';
47
+
48
+ export function getDocsLayoutPreset(layout: DocsLayoutType): DocsLayoutPreset {
49
+ return DOCS_LAYOUT_PRESETS[layout];
50
+ }
@@ -0,0 +1,20 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Duration Constants — Re-export shim (ADR-012, INIT-058 Layer 1, WU-2664).
6
+ *
7
+ * Moved to `@lumenflow/kernel/primitives/duration-utils`. This shim preserves
8
+ * the legacy import path for in-repo consumers until the barrel slim in
9
+ * Layer 7.
10
+ *
11
+ * @module constants/duration-constants
12
+ */
13
+
14
+ export {
15
+ MS_PER_SECOND,
16
+ MS_PER_MINUTE,
17
+ MS_PER_HOUR,
18
+ MS_PER_DAY,
19
+ DURATION_MS,
20
+ } from '@lumenflow/kernel/primitives/duration-utils';
@@ -0,0 +1,32 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Gate Configuration Constants
6
+ *
7
+ * WU-2010: Centralizes magic numbers for gates, including
8
+ * pre-commit/local gates AND config-driven gate thresholds.
9
+ * Used by gates-pre-commit.ts, gates-local.ts, gates-config.ts,
10
+ * and lumenflow-config-schema.ts.
11
+ */
12
+
13
+ /** Gate execution configuration */
14
+ export const GATE_CONFIG = {
15
+ /** Maximum execution time per gate step (ms) */
16
+ TIMEOUT_MS: 180000,
17
+
18
+ /** Maximum file size allowed in commits (bytes) - 5MB */
19
+ MAX_FILE_SIZE_BYTES: 5 * 1024 * 1024,
20
+
21
+ /** Total number of gates (for progress display) */
22
+ TOTAL_GATES: 14,
23
+ };
24
+
25
+ /** Default minimum code coverage percentage (used by TDD methodology) */
26
+ export const DEFAULT_MIN_COVERAGE = 90;
27
+
28
+ /** Default maximum allowed ESLint warnings before gate failure */
29
+ export const DEFAULT_MAX_ESLINT_WARNINGS = 100;
30
+
31
+ /** Default timeout for individual gate commands in gates-config (ms) */
32
+ export const DEFAULT_GATE_TIMEOUT_MS = 120_000; // 2 minutes
@@ -0,0 +1,29 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ // Barrel for pack-local constants. Populated by Layer 2 of INIT-058.
5
+ // WU-2677: moved wu-statuses, object-guards, config-contract, gate-constants from @lumenflow/core.
6
+ // WU-2674: added wu-ui-constants (leaf supplying EMOJI/STRING_LITERALS to policy/coverage-gate).
7
+ // WU-2678: added wu-paths-constants, docs-layout-presets from @lumenflow/core (L4 sandbox cohort deps).
8
+ // WU-2679: added wu-git-constants, wu-cli-constants, client-ids, wu-domain-constants, wu-type-helpers
9
+ // (L2c finish — eliminates wu-constants barrel as cross-cohort coupling point).
10
+ // WU-2680: added wu-validation-constants (L2d single leaf — unblocks lane-inference.ts migration).
11
+ // WU-2698: added backlog-patterns, duration-constants, section-headings, wu-id-format
12
+ // (L4-prep narrow — pure leaves + rewrites from @lumenflow/core).
13
+ export * from './wu-statuses.js';
14
+ export * from './object-guards.js';
15
+ export * from './config-contract.js';
16
+ export * from './gate-constants.js';
17
+ export * from './wu-ui-constants.js';
18
+ export * from './docs-layout-presets.js';
19
+ export * from './wu-paths-constants.js';
20
+ export * from './wu-git-constants.js';
21
+ export * from './wu-cli-constants.js';
22
+ export * from './client-ids.js';
23
+ export * from './wu-domain-constants.js';
24
+ export * from './wu-type-helpers.js';
25
+ export * from './wu-validation-constants.js';
26
+ export * from './backlog-patterns.js';
27
+ export * from './duration-constants.js';
28
+ export * from './section-headings.js';
29
+ export * from './wu-id-format.js';
@@ -0,0 +1,35 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Shared Lock Constants
6
+ *
7
+ * WU-2010: Consolidated lock-related constants used by both
8
+ * merge-lock.ts and cleanup-lock.ts to eliminate duplication.
9
+ *
10
+ * @module constants/lock-constants
11
+ */
12
+
13
+ /**
14
+ * Default timeout for waiting to acquire a lock (ms).
15
+ * After this time, acquisition fails if the lock is held.
16
+ */
17
+ export const LOCK_TIMEOUT_MS = 30_000; // 30 seconds
18
+
19
+ /**
20
+ * Time after which a merge lock is considered stale (ms).
21
+ * Should be greater than expected merge operation duration.
22
+ */
23
+ export const MERGE_LOCK_STALE_MS = 60_000; // 60 seconds
24
+
25
+ /**
26
+ * Time after which a cleanup lock is considered stale (ms).
27
+ * Cleanup is slower than merge, so a longer timeout is used.
28
+ */
29
+ export const CLEANUP_LOCK_STALE_MS = 5 * 60 * 1_000; // 5 minutes
30
+
31
+ /**
32
+ * Polling interval for lock acquisition retries (ms).
33
+ * Used by both merge and cleanup lock loops.
34
+ */
35
+ export const LOCK_POLL_INTERVAL_MS = 500;
@@ -0,0 +1,12 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Object Guards — Re-export shim (ADR-012, INIT-058 Layer 2f, WU-2685).
6
+ *
7
+ * Relocated to `@lumenflow/kernel/primitives/object-guards` because the
8
+ * utilities are pack-agnostic type predicates. Shim preserves the legacy
9
+ * pack import path for in-repo consumers until Layer 7 barrel slim.
10
+ */
11
+
12
+ export * from '@lumenflow/kernel/primitives/object-guards';
@@ -0,0 +1,107 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ /**
5
+ * Section Headings Constants
6
+ *
7
+ * Centralized section heading defaults for backlog.md and status.md
8
+ * Eliminates magic strings scattered throughout wu-* tools
9
+ *
10
+ * Usage: Use getSectionHeadingsWithDefaults() to get frontmatter-configured
11
+ * headings with sensible fallbacks if frontmatter is missing/malformed
12
+ *
13
+ * WU-2698 (INIT-058 L4-prep narrow): moved from @lumenflow/core/src/section-headings.ts.
14
+ * The `getSectionHeadings` helper (previously imported from core's backlog-parser) is
15
+ * inlined here to keep the pack core-free.
16
+ */
17
+
18
+ import { BACKLOG_SECTIONS, STATUS_SECTIONS } from './wu-ui-constants.js';
19
+
20
+ /**
21
+ * Section configuration from frontmatter.
22
+ */
23
+ interface SectionConfig {
24
+ heading?: string;
25
+ }
26
+
27
+ /**
28
+ * Extract section headings from frontmatter.
29
+ *
30
+ * Inlined from @lumenflow/core/src/backlog-parser.ts (WU-2698) so that this
31
+ * module does not depend on core during the INIT-058 L4-prep migration.
32
+ *
33
+ * @param {object|null} frontmatter - Parsed frontmatter object.
34
+ * @returns {object} Map of section names to heading strings (e.g., {ready: "## 🚀 Ready"}).
35
+ */
36
+ function getSectionHeadings(frontmatter: Record<string, unknown> | null): Record<string, string> {
37
+ if (!frontmatter) {
38
+ return {};
39
+ }
40
+ const sections = (frontmatter as { sections?: Record<string, SectionConfig> }).sections;
41
+ if (!sections) {
42
+ return {};
43
+ }
44
+
45
+ const headings: Record<string, string> = {};
46
+ for (const [sectionName, sectionConfig] of Object.entries(sections)) {
47
+ if (sectionConfig && typeof sectionConfig.heading === 'string') {
48
+ headings[sectionName] = sectionConfig.heading;
49
+ }
50
+ }
51
+
52
+ return headings;
53
+ }
54
+
55
+ /**
56
+ * Default section headings (fallbacks when frontmatter is missing)
57
+ * Re-exports from wu-ui-constants.ts for backwards compatibility
58
+ */
59
+ export const DEFAULT_SECTION_HEADINGS = {
60
+ backlog: {
61
+ ready: BACKLOG_SECTIONS.READY,
62
+ in_progress: BACKLOG_SECTIONS.IN_PROGRESS,
63
+ blocked: BACKLOG_SECTIONS.BLOCKED,
64
+ done: BACKLOG_SECTIONS.DONE,
65
+ },
66
+ status: {
67
+ in_progress: STATUS_SECTIONS.IN_PROGRESS,
68
+ completed: STATUS_SECTIONS.COMPLETED,
69
+ blocked: STATUS_SECTIONS.BLOCKED,
70
+ },
71
+ };
72
+
73
+ /**
74
+ * Get section headings with frontmatter override + defaults
75
+ *
76
+ * Replaces scattered pattern: headings.done || '## ✅ Done'
77
+ * Centralizes fallback logic for consistent heading resolution
78
+ *
79
+ * @param {object|null} frontmatter - Parsed frontmatter from backlog.md/status.md
80
+ * @param {'backlog'|'status'} docType - Document type
81
+ * @returns {object} Section headings (configured or default)
82
+ */
83
+ export function getSectionHeadingsWithDefaults(
84
+ frontmatter: Record<string, unknown> | null,
85
+ docType: keyof typeof DEFAULT_SECTION_HEADINGS = 'backlog',
86
+ ) {
87
+ const defaults = DEFAULT_SECTION_HEADINGS[docType] as {
88
+ ready?: string;
89
+ in_progress?: string;
90
+ blocked?: string;
91
+ done?: string;
92
+ completed?: string;
93
+ };
94
+ const configured = frontmatter ? getSectionHeadings(frontmatter) : {};
95
+
96
+ return {
97
+ ready: configured.ready || defaults.ready || DEFAULT_SECTION_HEADINGS.backlog.ready,
98
+ in_progress:
99
+ configured.in_progress ||
100
+ defaults.in_progress ||
101
+ DEFAULT_SECTION_HEADINGS.backlog.in_progress,
102
+ blocked: configured.blocked || defaults.blocked || DEFAULT_SECTION_HEADINGS.backlog.blocked,
103
+ done: configured.done || defaults.done || DEFAULT_SECTION_HEADINGS.backlog.done,
104
+ completed:
105
+ configured.completed || defaults.completed || DEFAULT_SECTION_HEADINGS.status.completed,
106
+ };
107
+ }