@lumenflow/cli 3.17.7 → 3.18.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 (156) hide show
  1. package/README.md +44 -43
  2. package/dist/chunk-2D2VOCA4.js +37 -0
  3. package/dist/chunk-2D5KFYGX.js +284 -0
  4. package/dist/chunk-2GXVIN57.js +14072 -0
  5. package/dist/chunk-2MQ7HZWZ.js +26 -0
  6. package/dist/chunk-2UFQ3A3C.js +643 -0
  7. package/dist/chunk-3RG5ZIWI.js +10 -0
  8. package/dist/chunk-4N74J3UT.js +15 -0
  9. package/dist/chunk-5GTOXFYR.js +392 -0
  10. package/dist/chunk-5VY6MQMC.js +240 -0
  11. package/dist/chunk-67XVPMRY.js +1297 -0
  12. package/dist/chunk-6HO4GWJE.js +164 -0
  13. package/dist/chunk-6W5XHWYV.js +1890 -0
  14. package/dist/chunk-6X4EMYJQ.js +64 -0
  15. package/dist/chunk-6XYXI2NQ.js +772 -0
  16. package/dist/chunk-7ANSOV6Q.js +285 -0
  17. package/dist/chunk-A624LFLB.js +1380 -0
  18. package/dist/chunk-ADN5NHG4.js +126 -0
  19. package/dist/chunk-B7YJYJKG.js +33 -0
  20. package/dist/chunk-CCLHCPKG.js +210 -0
  21. package/dist/chunk-CK36VROC.js +1584 -0
  22. package/dist/chunk-D3UOFRSB.js +81 -0
  23. package/dist/chunk-DFR4DJBM.js +230 -0
  24. package/dist/chunk-DSYBDHYH.js +79 -0
  25. package/dist/chunk-DWMLTXKQ.js +1176 -0
  26. package/dist/chunk-E3REJTAJ.js +28 -0
  27. package/dist/chunk-EA3IVO64.js +633 -0
  28. package/dist/chunk-EK2AKZKD.js +55 -0
  29. package/dist/chunk-ELD7JTTT.js +343 -0
  30. package/dist/chunk-EX6TT2XI.js +195 -0
  31. package/dist/chunk-EXINSFZE.js +82 -0
  32. package/dist/chunk-EZ6ZBYBM.js +510 -0
  33. package/dist/chunk-FBKAPTJ2.js +16 -0
  34. package/dist/chunk-FVLV5RYH.js +1118 -0
  35. package/dist/chunk-GDNSBQVK.js +2485 -0
  36. package/dist/chunk-GPQHMBNN.js +278 -0
  37. package/dist/chunk-GTFJB67L.js +68 -0
  38. package/dist/chunk-HANJXVKW.js +1127 -0
  39. package/dist/chunk-HEVS5YLD.js +269 -0
  40. package/dist/chunk-HMEVZKPQ.js +9 -0
  41. package/dist/chunk-HRGSYNLM.js +3511 -0
  42. package/dist/chunk-ISZR5N4K.js +60 -0
  43. package/dist/chunk-J6SUPR2C.js +226 -0
  44. package/dist/chunk-JERYVEIZ.js +244 -0
  45. package/dist/chunk-JHHWGL2N.js +87 -0
  46. package/dist/chunk-JONWQUB5.js +775 -0
  47. package/dist/chunk-K2DIWWDM.js +1766 -0
  48. package/dist/chunk-KY4PGL5V.js +969 -0
  49. package/dist/chunk-L737LQ4C.js +1285 -0
  50. package/dist/chunk-LFTWYIB2.js +497 -0
  51. package/dist/chunk-LV47RFNJ.js +41 -0
  52. package/dist/chunk-MKSAITI7.js +15 -0
  53. package/dist/chunk-MZ7RKIX4.js +212 -0
  54. package/dist/chunk-NAP6CFSO.js +84 -0
  55. package/dist/chunk-ND6MY37M.js +16 -0
  56. package/dist/chunk-NMG736UR.js +683 -0
  57. package/dist/chunk-NRAXROED.js +32 -0
  58. package/dist/chunk-NRIZR3A7.js +690 -0
  59. package/dist/chunk-NX43BG3M.js +233 -0
  60. package/dist/chunk-O645XLSI.js +297 -0
  61. package/dist/chunk-OMJD6A3S.js +235 -0
  62. package/dist/chunk-QB6SJD4T.js +430 -0
  63. package/dist/chunk-QFSTL4J3.js +276 -0
  64. package/dist/chunk-QLGDFMFX.js +212 -0
  65. package/dist/chunk-RIAAGL2E.js +13 -0
  66. package/dist/chunk-RWO5XMZ6.js +86 -0
  67. package/dist/chunk-RXRKBBSM.js +149 -0
  68. package/dist/chunk-RZOZMML6.js +363 -0
  69. package/dist/chunk-U7I7FS7T.js +113 -0
  70. package/dist/chunk-UI42RODY.js +717 -0
  71. package/dist/chunk-UTVMVSCO.js +519 -0
  72. package/dist/chunk-V6OJGLBA.js +1746 -0
  73. package/dist/chunk-W2JHVH7D.js +152 -0
  74. package/dist/chunk-WD3Y7VQN.js +280 -0
  75. package/dist/chunk-WOCTQ5MS.js +303 -0
  76. package/dist/chunk-WZR3ZUNN.js +696 -0
  77. package/dist/chunk-XGI665H7.js +150 -0
  78. package/dist/chunk-XKY65P2T.js +304 -0
  79. package/dist/chunk-Y4CQZY65.js +57 -0
  80. package/dist/chunk-YFEXKLVE.js +194 -0
  81. package/dist/chunk-YHO3HS5X.js +287 -0
  82. package/dist/chunk-YLS7AZSX.js +738 -0
  83. package/dist/chunk-ZE473AO6.js +49 -0
  84. package/dist/chunk-ZF747T3O.js +644 -0
  85. package/dist/chunk-ZHCZHZH3.js +43 -0
  86. package/dist/chunk-ZZNZX2XY.js +87 -0
  87. package/dist/config-set.js +10 -1
  88. package/dist/config-set.js.map +1 -1
  89. package/dist/constants-7QAP3VQ4.js +23 -0
  90. package/dist/dist-IY3UUMWK.js +33 -0
  91. package/dist/gate-co-change.js +5 -2
  92. package/dist/gate-co-change.js.map +1 -1
  93. package/dist/init-detection.js +5 -3
  94. package/dist/init-detection.js.map +1 -1
  95. package/dist/init-templates.js +4 -4
  96. package/dist/init-templates.js.map +1 -1
  97. package/dist/initiative-edit.js +8 -3
  98. package/dist/initiative-edit.js.map +1 -1
  99. package/dist/initiative-plan.js +1 -1
  100. package/dist/initiative-plan.js.map +1 -1
  101. package/dist/invariants-runner-W5RGHCSU.js +27 -0
  102. package/dist/lane-lock-6J36HD5O.js +35 -0
  103. package/dist/lumenflow-upgrade.js +49 -0
  104. package/dist/lumenflow-upgrade.js.map +1 -1
  105. package/dist/mem-checkpoint-core-EANG2GVN.js +14 -0
  106. package/dist/mem-signal-core-2LZ2WYHW.js +19 -0
  107. package/dist/memory-store-OLB5FO7K.js +18 -0
  108. package/dist/pre-commit-check.js +1 -1
  109. package/dist/pre-commit-check.js.map +1 -1
  110. package/dist/service-6BYCOCO5.js +13 -0
  111. package/dist/spawn-policy-resolver-NTSZYQ6R.js +17 -0
  112. package/dist/spawn-task-builder-R4E2BHSW.js +22 -0
  113. package/dist/wu-done-pr-WLFFFEPJ.js +25 -0
  114. package/dist/wu-done-validation-3J5E36FE.js +30 -0
  115. package/dist/wu-duplicate-id-detector-5S7JHELK.js +232 -0
  116. package/dist/wu-edit-operations.js +4 -0
  117. package/dist/wu-edit-operations.js.map +1 -1
  118. package/dist/wu-edit-validators.js +4 -0
  119. package/dist/wu-edit-validators.js.map +1 -1
  120. package/dist/wu-edit.js +11 -0
  121. package/dist/wu-edit.js.map +1 -1
  122. package/dist/wu-spawn-strategy-resolver.js +13 -1
  123. package/dist/wu-spawn-strategy-resolver.js.map +1 -1
  124. package/package.json +8 -8
  125. package/packs/agent-runtime/.turbo/turbo-build.log +4 -0
  126. package/packs/agent-runtime/README.md +147 -0
  127. package/packs/agent-runtime/capability-factory.ts +104 -0
  128. package/packs/agent-runtime/config.schema.json +87 -0
  129. package/packs/agent-runtime/constants.ts +21 -0
  130. package/packs/agent-runtime/index.ts +11 -0
  131. package/packs/agent-runtime/manifest.ts +207 -0
  132. package/packs/agent-runtime/manifest.yaml +193 -0
  133. package/packs/agent-runtime/orchestration.ts +1787 -0
  134. package/packs/agent-runtime/pack-registration.ts +110 -0
  135. package/packs/agent-runtime/package.json +57 -0
  136. package/packs/agent-runtime/policy-factory.ts +165 -0
  137. package/packs/agent-runtime/tool-impl/agent-turn-tools.ts +793 -0
  138. package/packs/agent-runtime/tool-impl/index.ts +5 -0
  139. package/packs/agent-runtime/tool-impl/provider-adapters.ts +1245 -0
  140. package/packs/agent-runtime/tools/index.ts +4 -0
  141. package/packs/agent-runtime/tools/types.ts +47 -0
  142. package/packs/agent-runtime/tsconfig.json +20 -0
  143. package/packs/agent-runtime/types.ts +128 -0
  144. package/packs/agent-runtime/vitest.config.ts +11 -0
  145. package/packs/sidekick/.turbo/turbo-build.log +1 -1
  146. package/packs/sidekick/.turbo/turbo-test.log +12 -0
  147. package/packs/sidekick/.turbo/turbo-typecheck.log +4 -0
  148. package/packs/sidekick/package.json +1 -1
  149. package/packs/software-delivery/.turbo/turbo-build.log +1 -1
  150. package/packs/software-delivery/.turbo/turbo-typecheck.log +4 -0
  151. package/packs/software-delivery/package.json +1 -1
  152. package/templates/core/.lumenflow/rules/wu-workflow.md.template +1 -1
  153. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +2 -2
  154. package/templates/core/ai/onboarding/quick-ref-commands.md.template +1 -1
  155. package/templates/core/ai/onboarding/starting-prompt.md.template +1 -1
  156. package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +1 -1
@@ -0,0 +1,690 @@
1
+ import {
2
+ createWuPaths
3
+ } from "./chunk-6HO4GWJE.js";
4
+ import {
5
+ STRING_LITERALS,
6
+ WU_DEFAULTS
7
+ } from "./chunk-DWMLTXKQ.js";
8
+ import {
9
+ WU_EXPOSURE_VALUES,
10
+ WU_STATUS_GROUPS
11
+ } from "./chunk-V6OJGLBA.js";
12
+ import {
13
+ ErrorCodes,
14
+ createError,
15
+ getErrorMessage
16
+ } from "./chunk-RXRKBBSM.js";
17
+
18
+ // ../core/dist/date-utils.js
19
+ import { format } from "date-fns";
20
+ function todayISO() {
21
+ return format(/* @__PURE__ */ new Date(), "yyyy-MM-dd");
22
+ }
23
+ var DATE_FORMAT_ISO = "yyyy-MM-dd";
24
+ function normalizeToDateString(value) {
25
+ if (value == null) {
26
+ return void 0;
27
+ }
28
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
29
+ return value;
30
+ }
31
+ if (value instanceof Date) {
32
+ return format(value, DATE_FORMAT_ISO);
33
+ }
34
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
35
+ return format(new Date(value), DATE_FORMAT_ISO);
36
+ }
37
+ try {
38
+ const date = new Date(String(value));
39
+ if (!isNaN(date.getTime())) {
40
+ return format(date, DATE_FORMAT_ISO);
41
+ }
42
+ } catch {
43
+ }
44
+ return void 0;
45
+ }
46
+ function normalizeISODateTime(value) {
47
+ if (value == null) {
48
+ return void 0;
49
+ }
50
+ if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) {
51
+ return value;
52
+ }
53
+ let dateInput = value;
54
+ if (typeof value === "string" && /^\d+$/.test(value)) {
55
+ dateInput = Number(value);
56
+ }
57
+ const date = new Date(dateInput);
58
+ if (isNaN(date.getTime())) {
59
+ return void 0;
60
+ }
61
+ return date.toISOString();
62
+ }
63
+
64
+ // ../core/dist/wu-schema.js
65
+ import { z } from "zod";
66
+ var VALID_STATUSES = [
67
+ "todo",
68
+ "ready",
69
+ "backlog",
70
+ "in_progress",
71
+ "blocked",
72
+ "done",
73
+ "completed",
74
+ "cancelled",
75
+ "abandoned",
76
+ "deferred",
77
+ "closed",
78
+ "superseded"
79
+ ];
80
+ var PLACEHOLDER_SENTINEL = "[PLACEHOLDER]";
81
+ var MIN_DESCRIPTION_LENGTH = 50;
82
+ var WU_ID_FORMAT_MESSAGE = "Must be WU-XXX format";
83
+ var ACCEPTANCE_REQUIRED_MSG = "At least one acceptance criterion required";
84
+ var NEWLINE_PATTERN = /\\n|\n/;
85
+ var normalizedStringArray = z.array(z.string()).transform((arr) => arr.flatMap((s) => s.split(NEWLINE_PATTERN)).map((s) => s.trim()).filter(Boolean));
86
+ var _normalizedMultilineString = z.string().transform((s) => s.replace(/\\n/g, "\n"));
87
+ var filePathItem = z.string().refine((s) => !s.includes("\n") && !s.includes("\\n"), {
88
+ message: "File path cannot contain newlines - split into separate array items"
89
+ });
90
+ var normalizedCodePaths = normalizedStringArray.pipe(z.array(filePathItem)).default([]);
91
+ var normalizedTestPaths = z.object({
92
+ manual: normalizedStringArray.optional(),
93
+ unit: normalizedStringArray.optional(),
94
+ integration: normalizedStringArray.optional(),
95
+ e2e: normalizedStringArray.optional()
96
+ }).optional();
97
+ var baseDescriptionField = z.string().min(1, "Description is required").transform((s) => s.replace(/\\n/g, "\n")).refine((val) => val.trim().length >= MIN_DESCRIPTION_LENGTH, {
98
+ // WU-1539 fix: Use function message for dynamic interpolation
99
+ message: `Description must be at least ${MIN_DESCRIPTION_LENGTH} characters`
100
+ });
101
+ var strictDescriptionField = z.string().min(1, "Description is required").transform((s) => s.replace(/\\n/g, "\n")).refine((val) => !val.includes(PLACEHOLDER_SENTINEL), {
102
+ message: `Description cannot contain ${PLACEHOLDER_SENTINEL} marker`
103
+ }).refine((val) => val.trim().length >= MIN_DESCRIPTION_LENGTH, {
104
+ // WU-1539 fix: Use function message for dynamic interpolation
105
+ message: `Description must be at least ${MIN_DESCRIPTION_LENGTH} characters`
106
+ });
107
+ var hasItems = (value) => {
108
+ if (Array.isArray(value)) {
109
+ return value.length > 0;
110
+ }
111
+ if (typeof value === "object" && value !== null) {
112
+ return Object.values(value).some(hasItems);
113
+ }
114
+ return false;
115
+ };
116
+ var checkStringsForPlaceholder = (value) => {
117
+ if (typeof value === "string") {
118
+ return !value.includes(PLACEHOLDER_SENTINEL);
119
+ }
120
+ if (Array.isArray(value)) {
121
+ return value.every(checkStringsForPlaceholder);
122
+ }
123
+ if (typeof value === "object" && value !== null) {
124
+ return Object.values(value).every(checkStringsForPlaceholder);
125
+ }
126
+ return true;
127
+ };
128
+ var baseAcceptanceField = z.union([
129
+ // Flat array format (legacy): acceptance: ["item1", "item2"]
130
+ // WU-1750: Normalize embedded newlines: ["1. a\n2. b"] → ["1. a", "2. b"]
131
+ normalizedStringArray.pipe(z.array(z.string()).min(1, ACCEPTANCE_REQUIRED_MSG)),
132
+ // Nested object format (structured): acceptance: { category1: ["item1"], category2: ["item2"] }
133
+ z.record(z.string(), normalizedStringArray).refine((obj) => Object.values(obj).some(hasItems), {
134
+ message: ACCEPTANCE_REQUIRED_MSG
135
+ })
136
+ ]);
137
+ var strictAcceptanceField = z.union([
138
+ // Flat array format (legacy): acceptance: ["item1", "item2"]
139
+ // WU-1750: Normalize embedded newlines: ["1. a\n2. b"] → ["1. a", "2. b"]
140
+ normalizedStringArray.pipe(z.array(z.string()).min(1, ACCEPTANCE_REQUIRED_MSG)).refine((arr) => !arr.some((item) => item.includes(PLACEHOLDER_SENTINEL)), {
141
+ message: `Acceptance criteria cannot contain ${PLACEHOLDER_SENTINEL} markers`
142
+ }),
143
+ // Nested object format (structured): acceptance: { category1: ["item1"], category2: ["item2"] }
144
+ z.record(z.string(), normalizedStringArray).refine((obj) => Object.values(obj).some(hasItems), {
145
+ message: ACCEPTANCE_REQUIRED_MSG
146
+ }).refine((obj) => checkStringsForPlaceholder(obj), {
147
+ message: `Acceptance criteria cannot contain ${PLACEHOLDER_SENTINEL} markers`
148
+ })
149
+ ]);
150
+ var sharedFields = {
151
+ /** WU identifier (e.g., WU-1162) */
152
+ id: z.string().regex(/^WU-\d+$/, "ID must match pattern WU-XXX"),
153
+ /** Short title describing the work */
154
+ title: z.string().min(1, "Title is required"),
155
+ /** Lane assignment (parent or sub-lane) */
156
+ lane: z.string().min(1, "Lane is required"),
157
+ /** Work type classification */
158
+ type: z.enum(["feature", "bug", "documentation", "process", "tooling", "chore", "refactor"], {
159
+ error: "Invalid type"
160
+ }).default(WU_DEFAULTS.type),
161
+ /** Current status in workflow */
162
+ status: z.enum(VALID_STATUSES, {
163
+ error: `Invalid status. Valid values: ${VALID_STATUSES.join(", ")}`
164
+ }).default(WU_DEFAULTS.status),
165
+ /** Priority level */
166
+ priority: z.enum(["P0", "P1", "P2", "P3"], {
167
+ error: "Invalid priority"
168
+ }).default(WU_DEFAULTS.priority),
169
+ /** Creation date (YYYY-MM-DD) */
170
+ created: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Created must be YYYY-MM-DD"),
171
+ /** Files modified by this WU - WU-1750: Normalized to split embedded newlines */
172
+ code_paths: normalizedCodePaths,
173
+ /** Test specifications - WU-1750: All test arrays normalized */
174
+ tests: normalizedTestPaths.default(WU_DEFAULTS.tests),
175
+ /** Output artifacts (stamps, docs, etc.) - WU-1750: Normalized */
176
+ artifacts: normalizedStringArray.optional().default(WU_DEFAULTS.artifacts),
177
+ /** Upstream WU dependencies (informational, legacy field) - WU-1750: Normalized */
178
+ dependencies: normalizedStringArray.optional().default(WU_DEFAULTS.dependencies),
179
+ // === Initiative System Fields (WU-1246) ===
180
+ /** Parent initiative reference (format: INIT-{number} or slug) */
181
+ initiative: z.string().optional(),
182
+ /** Phase number within parent initiative */
183
+ phase: z.number().int().positive().optional(),
184
+ /** WU IDs that this WU blocks (downstream dependencies) - WU-1750: Normalized + validated */
185
+ blocks: normalizedStringArray.pipe(z.array(z.string().regex(/^WU-\d+$/, WU_ID_FORMAT_MESSAGE))).optional(),
186
+ /** WU IDs that block this WU (upstream dependencies) - WU-1750: Normalized + validated */
187
+ blocked_by: normalizedStringArray.pipe(z.array(z.string().regex(/^WU-\d+$/, WU_ID_FORMAT_MESSAGE))).optional(),
188
+ /** Cross-cutting tags (orthogonal to initiative) - WU-1750: Normalized */
189
+ labels: normalizedStringArray.optional(),
190
+ // === End Initiative System Fields ===
191
+ /**
192
+ * WU-1683: First-class plan field, symmetric with initiative `related_plan`.
193
+ * Set via wu:create --plan, wu:edit --plan, or plan:link --id WU-XXX.
194
+ */
195
+ plan: z.string().optional(),
196
+ /**
197
+ * WU-1833: References to plans, design docs, external specifications
198
+ * WU-1834: Supports both flat string array AND nested object format for backwards compatibility
199
+ *
200
+ * Flat format (WU-1833+): ['docs/plans/WU-XXX-plan.md']
201
+ * Nested format (legacy): [{file: 'docs/path.md', section: 'heading'}]
202
+ * Mixed format allowed: ['path.md', {section: 'heading'}]
203
+ * Bare object (WU-428): {file: 'docs/path.md', section: 'heading'}
204
+ */
205
+ spec_refs: z.union([
206
+ // Single object format (WU-428 style): {file: '...', section: '...'}
207
+ z.object({
208
+ file: z.string().optional(),
209
+ section: z.string()
210
+ }),
211
+ // Array format (WU-1833+): strings, objects, or mixed
212
+ z.array(z.union([
213
+ z.string(),
214
+ // Flat format: 'docs/path.md'
215
+ z.object({
216
+ // Nested format: {file: 'path', section: 'heading'}
217
+ file: z.string().optional(),
218
+ section: z.string()
219
+ })
220
+ ]))
221
+ ]).optional(),
222
+ /** Known risks or constraints - WU-1750: Normalized */
223
+ risks: normalizedStringArray.optional().default(WU_DEFAULTS.risks),
224
+ /**
225
+ * Free-form notes - supports string or array (auto-converted to string)
226
+ * WU-1750: Normalizes escaped newlines (\\n → actual newlines)
227
+ */
228
+ notes: z.union([
229
+ z.string(),
230
+ z.array(z.string())
231
+ // Legacy array format - will be converted
232
+ ]).optional().transform((val) => {
233
+ if (Array.isArray(val)) {
234
+ return val.filter((s) => s.trim().length > 0).join(STRING_LITERALS.NEWLINE);
235
+ }
236
+ if (typeof val === "string") {
237
+ return val.replace(/\\n/g, "\n");
238
+ }
239
+ return val ?? WU_DEFAULTS.notes;
240
+ }),
241
+ /** Requires human review before merge */
242
+ requires_review: z.boolean().optional().default(WU_DEFAULTS.requires_review),
243
+ /** Locked state (done WUs only) */
244
+ locked: z.boolean().optional(),
245
+ /** Completion date (done WUs only) - auto-normalized to ISO datetime */
246
+ completed_at: z.string().optional().transform((val) => normalizeISODateTime(val)),
247
+ /** Claimed mode (worktree/branch-only/worktree-pr/branch-pr) */
248
+ claimed_mode: z.enum(["worktree", "branch-only", "worktree-pr", "branch-pr"]).optional(),
249
+ /**
250
+ * WU-1589: Canonical branch name for this WU claim.
251
+ *
252
+ * Set at claim time to record the actual branch used.
253
+ * Used by defaultBranchFrom() as highest-priority source for branch resolution.
254
+ * Essential for branch-pr mode where cloud agents may use non-lane-derived branch names.
255
+ * Cleared on rollback/release/recover when resetting to ready.
256
+ */
257
+ claimed_branch: z.string().optional(),
258
+ /** Assigned agent email */
259
+ assigned_to: z.string().email().optional(),
260
+ /** Claim timestamp - auto-normalized to ISO datetime */
261
+ claimed_at: z.string().optional().transform((val) => normalizeISODateTime(val)),
262
+ /** Block reason (blocked WUs only) */
263
+ blocked_reason: z.string().optional(),
264
+ /** Worktree path (claimed WUs only) */
265
+ worktree_path: z.string().optional(),
266
+ /** Current active session ID (WU-1438: auto-set on claim, cleared on done) */
267
+ session_id: z.string().uuid().optional(),
268
+ /** Agent sessions (issue logging metadata, WU-1231) */
269
+ agent_sessions: z.array(z.object({
270
+ session_id: z.string().uuid(),
271
+ started: z.string().datetime(),
272
+ completed: z.string().datetime().optional(),
273
+ agent_type: z.enum([
274
+ "claude-code",
275
+ "codex-cli",
276
+ "cursor",
277
+ "gemini-cli",
278
+ "windsurf",
279
+ "copilot",
280
+ "other"
281
+ ]),
282
+ context_tier: z.union([z.literal(1), z.literal(2), z.literal(3)]),
283
+ incidents_logged: z.number().int().min(0).default(0),
284
+ incidents_major: z.number().int().min(0).default(0),
285
+ artifacts: z.array(z.string()).optional()
286
+ })).optional(),
287
+ // === Exposure System Fields (WU-1998) ===
288
+ /**
289
+ * WU-1998: Exposure level - defines how the WU exposes functionality to users
290
+ *
291
+ * Valid values:
292
+ * - 'ui': User-facing UI changes (pages, components, widgets)
293
+ * - 'api': API endpoints called by UI or external clients
294
+ * - 'backend-only': Backend-only changes (no user visibility)
295
+ * - 'documentation': Documentation changes only
296
+ *
297
+ * Optional during transition period, will become required after backlog update.
298
+ */
299
+ exposure: z.enum(WU_EXPOSURE_VALUES, {
300
+ error: `Invalid exposure value. Valid values: ${WU_EXPOSURE_VALUES.join(", ")}`
301
+ }).optional(),
302
+ /**
303
+ * WU-1998: User journey description for user-facing WUs
304
+ *
305
+ * Recommended for exposure: 'ui' and 'api'.
306
+ * Describes the end-user interaction flow affected by this WU.
307
+ */
308
+ user_journey: z.string().optional(),
309
+ /**
310
+ * WU-1998: Related UI WUs for backend/API changes
311
+ *
312
+ * For WUs with exposure: 'api', this field lists UI WUs that consume the API.
313
+ * Ensures backend features have corresponding UI coverage.
314
+ * Each entry must match WU-XXX format.
315
+ */
316
+ ui_pairing_wus: normalizedStringArray.pipe(z.array(z.string().regex(/^WU-\d+$/, WU_ID_FORMAT_MESSAGE))).optional(),
317
+ /**
318
+ * WU-2022: Navigation path for UI-exposed features
319
+ *
320
+ * For WUs with exposure: 'ui', specifies the route where the feature is accessible.
321
+ * Used by wu:done to verify that UI features are actually navigable.
322
+ * Prevents "orphaned code" where features exist but users cannot access them.
323
+ *
324
+ * Example: '/dashboard', '/settings/preferences', '/space'
325
+ */
326
+ navigation_path: z.string().optional(),
327
+ // === End Exposure System Fields ===
328
+ // === Sizing Estimate Fields (WU-2141) ===
329
+ /**
330
+ * WU-2141: Optional sizing estimate metadata.
331
+ *
332
+ * Records expected WU complexity for tooling-backed sizing enforcement.
333
+ * Absent for historical WUs (backward compatible).
334
+ *
335
+ * Fields:
336
+ * - estimated_files: Expected number of files to modify
337
+ * - estimated_tool_calls: Expected tool call count
338
+ * - strategy: Execution strategy from wu-sizing-guide.md
339
+ * - exception_type: Override type when thresholds intentionally exceeded
340
+ * - exception_reason: Justification for the exception (required with exception_type)
341
+ */
342
+ sizing_estimate: z.object({
343
+ estimated_files: z.number().int().min(0),
344
+ estimated_tool_calls: z.number().int().min(0),
345
+ strategy: z.enum([
346
+ "single-session",
347
+ "checkpoint-resume",
348
+ "orchestrator-worker",
349
+ "decomposition"
350
+ ]),
351
+ exception_type: z.enum(["docs-only", "shallow-multi-file"]).optional(),
352
+ exception_reason: z.string().optional()
353
+ }).refine((data) => {
354
+ if (data.exception_type !== void 0) {
355
+ return data.exception_reason !== void 0 && data.exception_reason.trim().length > 0;
356
+ }
357
+ return true;
358
+ }, {
359
+ message: "sizing_estimate.exception_reason is required and must be non-empty when exception_type is set",
360
+ path: ["exception_reason"]
361
+ }).optional(),
362
+ // === End Sizing Estimate Fields ===
363
+ // === Agent-First Approval Fields (WU-2079 → WU-2080) ===
364
+ /**
365
+ * WU-2080: Escalation triggers detected for this WU
366
+ *
367
+ * Agent-first model: agents auto-approve by default.
368
+ * Human escalation only when these triggers are detected:
369
+ * - sensitive_data: Changes to sensitive data handling
370
+ * - security_p0: P0 security incident or vulnerability
371
+ * - budget: Budget/resource allocation above threshold
372
+ * - cross_lane_arch: Cross-lane architectural decision
373
+ *
374
+ * Empty array = no escalation needed, agent proceeds autonomously.
375
+ */
376
+ escalation_triggers: z.array(z.enum(["sensitive_data", "security_p0", "budget", "cross_lane_arch"])).optional().default([]),
377
+ /**
378
+ * WU-2080: Human escalation required flag
379
+ *
380
+ * Auto-set to true when escalation_triggers is non-empty.
381
+ * When true, wu:done requires human confirmation before completion.
382
+ */
383
+ requires_human_escalation: z.boolean().optional().default(false),
384
+ /**
385
+ * WU-2080: Email(s) of approvers who signed off
386
+ *
387
+ * Auto-populated with claiming agent at wu:claim.
388
+ * Additional human approvers added when escalation is resolved.
389
+ */
390
+ approved_by: z.array(z.string().email()).optional(),
391
+ /**
392
+ * WU-2080: Timestamp when approval was granted
393
+ *
394
+ * Auto-set at wu:claim for agent auto-approval.
395
+ * Updated when human escalation is resolved.
396
+ */
397
+ approved_at: z.string().optional().transform((val) => normalizeISODateTime(val)),
398
+ /**
399
+ * WU-2080: Human who resolved escalation (if UnsafeAny)
400
+ *
401
+ * Only set when requires_human_escalation was true and resolved.
402
+ */
403
+ escalation_resolved_by: z.string().email().optional(),
404
+ /**
405
+ * WU-2080: Timestamp when human resolved escalation
406
+ */
407
+ escalation_resolved_at: z.string().optional().transform((val) => normalizeISODateTime(val)),
408
+ // Legacy fields (deprecated, kept for backwards compatibility)
409
+ /** @deprecated Use escalation_triggers instead */
410
+ requires_cso_approval: z.boolean().optional().default(false),
411
+ /** @deprecated Use escalation_triggers instead */
412
+ requires_cto_approval: z.boolean().optional().default(false),
413
+ /** @deprecated Use escalation_triggers instead */
414
+ requires_design_approval: z.boolean().optional().default(false)
415
+ // === End Agent-First Approval Fields ===
416
+ };
417
+ var BaseWUSchema = z.object({
418
+ ...sharedFields,
419
+ description: baseDescriptionField,
420
+ acceptance: baseAcceptanceField
421
+ });
422
+ var ReadyWUSchema = BaseWUSchema;
423
+ var WUSchema = z.object({
424
+ ...sharedFields,
425
+ description: strictDescriptionField,
426
+ acceptance: strictAcceptanceField
427
+ });
428
+ function validateWU(data) {
429
+ return WUSchema.safeParse(data);
430
+ }
431
+ function validateReadyWU(data) {
432
+ return ReadyWUSchema.safeParse(data);
433
+ }
434
+ function validateDoneWU(wu) {
435
+ const errors = [];
436
+ if (wu.type !== "documentation" && wu.type !== "process") {
437
+ if (!wu.code_paths || wu.code_paths.length === 0) {
438
+ errors.push("Code paths required for non-documentation WUs");
439
+ }
440
+ }
441
+ return {
442
+ valid: errors.length === 0,
443
+ errors
444
+ };
445
+ }
446
+ function validateApprovalGates(wu) {
447
+ const errors = [];
448
+ const warnings = [];
449
+ const triggers = wu.escalation_triggers || [];
450
+ const requiresEscalation = wu.requires_human_escalation || triggers.length > 0;
451
+ if (requiresEscalation) {
452
+ const resolved = wu.escalation_resolved_by && wu.escalation_resolved_at;
453
+ if (!resolved) {
454
+ errors.push(`Human escalation required for: ${triggers.join(", ")}
455
+ To resolve: pnpm wu:escalate --resolve --id ${wu.id}`);
456
+ }
457
+ }
458
+ if (wu.requires_cso_approval || wu.requires_cto_approval || wu.requires_design_approval) {
459
+ warnings.push("Using deprecated requires_X_approval fields. Migrate to escalation_triggers model.");
460
+ }
461
+ return {
462
+ valid: errors.length === 0,
463
+ errors,
464
+ warnings
465
+ };
466
+ }
467
+ function detectEscalationTriggers(wu) {
468
+ const triggers = [];
469
+ const lane = (wu.lane || "").toLowerCase();
470
+ const codePaths = wu.code_paths || [];
471
+ const sensitivePatterns = ["pii", "user-data", "auth", "credentials"];
472
+ const touchesSensitive = codePaths.some((p) => sensitivePatterns.some((pat) => p.toLowerCase().includes(pat)));
473
+ if (touchesSensitive || lane.includes("pii")) {
474
+ triggers.push("sensitive_data");
475
+ }
476
+ if (wu.priority === "P0" && lane.includes("security")) {
477
+ triggers.push("security_p0");
478
+ }
479
+ return triggers;
480
+ }
481
+ function generateAutoApproval(wu, agentEmail) {
482
+ const triggers = detectEscalationTriggers(wu);
483
+ const now = (/* @__PURE__ */ new Date()).toISOString();
484
+ return {
485
+ approved_by: [agentEmail],
486
+ approved_at: now,
487
+ escalation_triggers: triggers,
488
+ requires_human_escalation: triggers.length > 0
489
+ };
490
+ }
491
+ function validateAndNormalizeWUYAML(data) {
492
+ const result = WUSchema.safeParse(data);
493
+ if (!result.success) {
494
+ const errors = result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`);
495
+ return {
496
+ valid: false,
497
+ normalized: null,
498
+ errors,
499
+ wasNormalized: false
500
+ };
501
+ }
502
+ const normalized = result.data;
503
+ const original = typeof data === "object" && data !== null ? data : {};
504
+ const wasNormalized = detectNormalizationChanges(original, normalized);
505
+ return {
506
+ valid: true,
507
+ normalized,
508
+ errors: [],
509
+ wasNormalized
510
+ };
511
+ }
512
+ function validateWUCompleteness(wu) {
513
+ const warnings = [];
514
+ const status = wu.status ?? "";
515
+ const isTerminal = WU_STATUS_GROUPS.TERMINAL.includes(status);
516
+ if (isTerminal) {
517
+ return { warnings };
518
+ }
519
+ const type = wu.type || "feature";
520
+ const requiresContext = ["feature", "bug", "refactor"].includes(type);
521
+ if (!requiresContext) {
522
+ return { warnings };
523
+ }
524
+ if (!wu.notes || wu.notes.trim().length === 0) {
525
+ warnings.push(`${wu.id}: Missing 'notes' field. Add implementation context, deployment instructions, or plan links.`);
526
+ }
527
+ const hasManualTests = wu.tests?.manual && wu.tests.manual.length > 0;
528
+ if (!hasManualTests) {
529
+ warnings.push(`${wu.id}: Missing 'tests.manual' field. Add manual verification steps for acceptance criteria.`);
530
+ }
531
+ if (type === "feature") {
532
+ const specRefs = wu.spec_refs;
533
+ const hasSpecRefs = !!specRefs && (specRefs.length ?? 0) > 0;
534
+ if (!hasSpecRefs) {
535
+ const plansDirHint = `${createWuPaths().PLANS_DIR().replace(/\/+$/, "")}/`;
536
+ warnings.push(`${wu.id}: Missing 'spec_refs' field. Link to plan file (${plansDirHint}, lumenflow://plans/, or ~/.lumenflow/plans/) for traceability.`);
537
+ }
538
+ }
539
+ return { warnings };
540
+ }
541
+ function detectNormalizationChanges(original, normalized) {
542
+ if (original.description !== normalized.description) {
543
+ return true;
544
+ }
545
+ const origPaths = Array.isArray(original.code_paths) ? original.code_paths : [];
546
+ const normPaths = Array.isArray(normalized.code_paths) ? normalized.code_paths : [];
547
+ if (origPaths.length !== normPaths.length) {
548
+ return true;
549
+ }
550
+ for (let i = 0; i < origPaths.length; i++) {
551
+ if (origPaths[i] !== normPaths[i]) {
552
+ return true;
553
+ }
554
+ }
555
+ if (Array.isArray(original.acceptance) && Array.isArray(normalized.acceptance)) {
556
+ if (original.acceptance.length !== normalized.acceptance.length) {
557
+ return true;
558
+ }
559
+ for (let i = 0; i < original.acceptance.length; i++) {
560
+ if (original.acceptance[i] !== normalized.acceptance[i]) {
561
+ return true;
562
+ }
563
+ }
564
+ }
565
+ return false;
566
+ }
567
+
568
+ // ../core/dist/wu-yaml.js
569
+ import { existsSync, readFileSync, writeFileSync, promises as fs } from "fs";
570
+ import { parse, stringify } from "yaml";
571
+ var YAML_SCALAR_TYPES = Object.freeze({
572
+ /** Unquoted string (when safe) */
573
+ PLAIN: "PLAIN",
574
+ /** Single-quoted string */
575
+ QUOTE_SINGLE: "QUOTE_SINGLE",
576
+ /** Double-quoted string */
577
+ QUOTE_DOUBLE: "QUOTE_DOUBLE",
578
+ /** Block literal (|) */
579
+ BLOCK_LITERAL: "BLOCK_LITERAL",
580
+ /** Block folded (>) */
581
+ BLOCK_FOLDED: "BLOCK_FOLDED"
582
+ });
583
+ var YAML_LINE_WIDTH = 100;
584
+ var YAML_STRINGIFY_OPTIONS = Object.freeze({
585
+ lineWidth: YAML_LINE_WIDTH,
586
+ singleQuote: true,
587
+ defaultKeyType: YAML_SCALAR_TYPES.PLAIN
588
+ });
589
+ function parseWUYaml(text, sourcePath) {
590
+ try {
591
+ return parse(text);
592
+ } catch (e) {
593
+ const msg = getErrorMessage(e);
594
+ throw createError(ErrorCodes.YAML_PARSE_ERROR, `Failed to parse YAML ${sourcePath}: ${msg}`, {
595
+ path: sourcePath,
596
+ originalError: msg
597
+ });
598
+ }
599
+ }
600
+ function validateWUId(doc, expectedId, wuPath) {
601
+ const parsed = doc;
602
+ if (!parsed || parsed.id !== expectedId) {
603
+ throw createError(ErrorCodes.WU_NOT_FOUND, `WU YAML id mismatch. Expected ${expectedId}, found ${parsed && parsed.id}`, { path: wuPath, expectedId, foundId: parsed && parsed.id });
604
+ }
605
+ return parsed;
606
+ }
607
+ function readWU(wuPath, expectedId) {
608
+ if (!existsSync(wuPath)) {
609
+ throw createError(ErrorCodes.FILE_NOT_FOUND, `WU file not found: ${wuPath}`, {
610
+ path: wuPath,
611
+ expectedId
612
+ });
613
+ }
614
+ const text = readFileSync(wuPath, { encoding: "utf-8" });
615
+ const doc = parseWUYaml(text, wuPath);
616
+ return validateWUId(doc, expectedId, wuPath);
617
+ }
618
+ async function readWUAsync(wuPath, expectedId) {
619
+ try {
620
+ const text = await fs.readFile(wuPath, { encoding: "utf-8" });
621
+ const doc = parseWUYaml(text, wuPath);
622
+ return validateWUId(doc, expectedId, wuPath);
623
+ } catch (err) {
624
+ const errObj = err;
625
+ if (errObj.code === "ENOENT") {
626
+ throw createError(ErrorCodes.FILE_NOT_FOUND, `WU file not found: ${wuPath}`, {
627
+ path: wuPath,
628
+ expectedId
629
+ });
630
+ }
631
+ throw err;
632
+ }
633
+ }
634
+ function parseYAML(text) {
635
+ return parse(text);
636
+ }
637
+ function stringifyYAML(doc, options = {}) {
638
+ return stringify(doc, { ...YAML_STRINGIFY_OPTIONS, ...options });
639
+ }
640
+ function readWURaw(yamlPath) {
641
+ if (!existsSync(yamlPath)) {
642
+ throw createError(ErrorCodes.FILE_NOT_FOUND, `YAML file not found: ${yamlPath}`, {
643
+ path: yamlPath
644
+ });
645
+ }
646
+ const text = readFileSync(yamlPath, { encoding: "utf-8" });
647
+ return parseWUYaml(text, yamlPath);
648
+ }
649
+ function writeWU(wuPath, doc) {
650
+ BaseWUSchema.parse(doc);
651
+ const out = stringify(doc, YAML_STRINGIFY_OPTIONS);
652
+ writeFileSync(wuPath, out, { encoding: "utf-8" });
653
+ }
654
+ function appendNote(doc, note) {
655
+ if (!note)
656
+ return;
657
+ const existing = doc.notes;
658
+ if (existing === void 0 || existing === null || existing === "") {
659
+ doc.notes = note;
660
+ } else if (Array.isArray(existing)) {
661
+ const joined = existing.filter(Boolean).join(STRING_LITERALS.NEWLINE).trimEnd();
662
+ doc.notes = joined ? `${joined}${STRING_LITERALS.NEWLINE}${note}` : note;
663
+ } else if (typeof existing === "string") {
664
+ const trimmed = existing.trimEnd();
665
+ doc.notes = trimmed ? `${trimmed}${STRING_LITERALS.NEWLINE}${note}` : note;
666
+ } else {
667
+ doc.notes = note;
668
+ }
669
+ }
670
+
671
+ export {
672
+ todayISO,
673
+ normalizeToDateString,
674
+ PLACEHOLDER_SENTINEL,
675
+ BaseWUSchema,
676
+ validateWU,
677
+ validateReadyWU,
678
+ validateDoneWU,
679
+ validateApprovalGates,
680
+ generateAutoApproval,
681
+ validateAndNormalizeWUYAML,
682
+ validateWUCompleteness,
683
+ readWU,
684
+ readWUAsync,
685
+ parseYAML,
686
+ stringifyYAML,
687
+ readWURaw,
688
+ writeWU,
689
+ appendNote
690
+ };