trellis-hgl-core 0.6.0-beta.18

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 (202) hide show
  1. package/LICENSE +235 -0
  2. package/dist/channel/api/assert.d.ts +3 -0
  3. package/dist/channel/api/assert.d.ts.map +1 -0
  4. package/dist/channel/api/assert.js +11 -0
  5. package/dist/channel/api/assert.js.map +1 -0
  6. package/dist/channel/api/context.d.ts +21 -0
  7. package/dist/channel/api/context.d.ts.map +1 -0
  8. package/dist/channel/api/context.js +99 -0
  9. package/dist/channel/api/context.js.map +1 -0
  10. package/dist/channel/api/create.d.ts +9 -0
  11. package/dist/channel/api/create.d.ts.map +1 -0
  12. package/dist/channel/api/create.js +104 -0
  13. package/dist/channel/api/create.js.map +1 -0
  14. package/dist/channel/api/inbox.d.ts +51 -0
  15. package/dist/channel/api/inbox.d.ts.map +1 -0
  16. package/dist/channel/api/inbox.js +176 -0
  17. package/dist/channel/api/inbox.js.map +1 -0
  18. package/dist/channel/api/interrupt.d.ts +31 -0
  19. package/dist/channel/api/interrupt.d.ts.map +1 -0
  20. package/dist/channel/api/interrupt.js +102 -0
  21. package/dist/channel/api/interrupt.js.map +1 -0
  22. package/dist/channel/api/post-thread.d.ts +14 -0
  23. package/dist/channel/api/post-thread.d.ts.map +1 -0
  24. package/dist/channel/api/post-thread.js +106 -0
  25. package/dist/channel/api/post-thread.js.map +1 -0
  26. package/dist/channel/api/read.d.ts +17 -0
  27. package/dist/channel/api/read.d.ts.map +1 -0
  28. package/dist/channel/api/read.js +44 -0
  29. package/dist/channel/api/read.js.map +1 -0
  30. package/dist/channel/api/resolve.d.ts +21 -0
  31. package/dist/channel/api/resolve.d.ts.map +1 -0
  32. package/dist/channel/api/resolve.js +28 -0
  33. package/dist/channel/api/resolve.js.map +1 -0
  34. package/dist/channel/api/runtime.d.ts +70 -0
  35. package/dist/channel/api/runtime.d.ts.map +1 -0
  36. package/dist/channel/api/runtime.js +11 -0
  37. package/dist/channel/api/runtime.js.map +1 -0
  38. package/dist/channel/api/send.d.ts +4 -0
  39. package/dist/channel/api/send.d.ts.map +1 -0
  40. package/dist/channel/api/send.js +44 -0
  41. package/dist/channel/api/send.js.map +1 -0
  42. package/dist/channel/api/spawn.d.ts +13 -0
  43. package/dist/channel/api/spawn.d.ts.map +1 -0
  44. package/dist/channel/api/spawn.js +54 -0
  45. package/dist/channel/api/spawn.js.map +1 -0
  46. package/dist/channel/api/title.d.ts +5 -0
  47. package/dist/channel/api/title.d.ts.map +1 -0
  48. package/dist/channel/api/title.js +40 -0
  49. package/dist/channel/api/title.js.map +1 -0
  50. package/dist/channel/api/types.d.ts +64 -0
  51. package/dist/channel/api/types.d.ts.map +1 -0
  52. package/dist/channel/api/types.js +2 -0
  53. package/dist/channel/api/types.js.map +1 -0
  54. package/dist/channel/api/watch-channels.d.ts +40 -0
  55. package/dist/channel/api/watch-channels.d.ts.map +1 -0
  56. package/dist/channel/api/watch-channels.js +143 -0
  57. package/dist/channel/api/watch-channels.js.map +1 -0
  58. package/dist/channel/api/watch.d.ts +11 -0
  59. package/dist/channel/api/watch.d.ts.map +1 -0
  60. package/dist/channel/api/watch.js +17 -0
  61. package/dist/channel/api/watch.js.map +1 -0
  62. package/dist/channel/api/workers.d.ts +69 -0
  63. package/dist/channel/api/workers.d.ts.map +1 -0
  64. package/dist/channel/api/workers.js +145 -0
  65. package/dist/channel/api/workers.js.map +1 -0
  66. package/dist/channel/index.d.ts +38 -0
  67. package/dist/channel/index.d.ts.map +1 -0
  68. package/dist/channel/index.js +23 -0
  69. package/dist/channel/index.js.map +1 -0
  70. package/dist/channel/internal/store/channel-metadata.d.ts +23 -0
  71. package/dist/channel/internal/store/channel-metadata.d.ts.map +1 -0
  72. package/dist/channel/internal/store/channel-metadata.js +94 -0
  73. package/dist/channel/internal/store/channel-metadata.js.map +1 -0
  74. package/dist/channel/internal/store/delivery.d.ts +27 -0
  75. package/dist/channel/internal/store/delivery.d.ts.map +1 -0
  76. package/dist/channel/internal/store/delivery.js +37 -0
  77. package/dist/channel/internal/store/delivery.js.map +1 -0
  78. package/dist/channel/internal/store/events.d.ts +203 -0
  79. package/dist/channel/internal/store/events.d.ts.map +1 -0
  80. package/dist/channel/internal/store/events.js +185 -0
  81. package/dist/channel/internal/store/events.js.map +1 -0
  82. package/dist/channel/internal/store/filter.d.ts +22 -0
  83. package/dist/channel/internal/store/filter.d.ts.map +1 -0
  84. package/dist/channel/internal/store/filter.js +78 -0
  85. package/dist/channel/internal/store/filter.js.map +1 -0
  86. package/dist/channel/internal/store/inbox.d.ts +17 -0
  87. package/dist/channel/internal/store/inbox.d.ts.map +1 -0
  88. package/dist/channel/internal/store/inbox.js +30 -0
  89. package/dist/channel/internal/store/inbox.js.map +1 -0
  90. package/dist/channel/internal/store/lock.d.ts +17 -0
  91. package/dist/channel/internal/store/lock.d.ts.map +1 -0
  92. package/dist/channel/internal/store/lock.js +88 -0
  93. package/dist/channel/internal/store/lock.js.map +1 -0
  94. package/dist/channel/internal/store/paths.d.ts +43 -0
  95. package/dist/channel/internal/store/paths.d.ts.map +1 -0
  96. package/dist/channel/internal/store/paths.js +233 -0
  97. package/dist/channel/internal/store/paths.js.map +1 -0
  98. package/dist/channel/internal/store/schema.d.ts +77 -0
  99. package/dist/channel/internal/store/schema.d.ts.map +1 -0
  100. package/dist/channel/internal/store/schema.js +127 -0
  101. package/dist/channel/internal/store/schema.js.map +1 -0
  102. package/dist/channel/internal/store/seq.d.ts +12 -0
  103. package/dist/channel/internal/store/seq.d.ts.map +1 -0
  104. package/dist/channel/internal/store/seq.js +133 -0
  105. package/dist/channel/internal/store/seq.js.map +1 -0
  106. package/dist/channel/internal/store/thread-state.d.ts +37 -0
  107. package/dist/channel/internal/store/thread-state.d.ts.map +1 -0
  108. package/dist/channel/internal/store/thread-state.js +206 -0
  109. package/dist/channel/internal/store/thread-state.js.map +1 -0
  110. package/dist/channel/internal/store/watch.d.ts +10 -0
  111. package/dist/channel/internal/store/watch.d.ts.map +1 -0
  112. package/dist/channel/internal/store/watch.js +122 -0
  113. package/dist/channel/internal/store/watch.js.map +1 -0
  114. package/dist/channel/internal/store/worker-state.d.ts +49 -0
  115. package/dist/channel/internal/store/worker-state.d.ts.map +1 -0
  116. package/dist/channel/internal/store/worker-state.js +207 -0
  117. package/dist/channel/internal/store/worker-state.js.map +1 -0
  118. package/dist/index.d.ts +3 -0
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +7 -0
  121. package/dist/index.js.map +1 -0
  122. package/dist/mem/adapters/claude.d.ts +22 -0
  123. package/dist/mem/adapters/claude.d.ts.map +1 -0
  124. package/dist/mem/adapters/claude.js +252 -0
  125. package/dist/mem/adapters/claude.js.map +1 -0
  126. package/dist/mem/adapters/codex.d.ts +35 -0
  127. package/dist/mem/adapters/codex.d.ts.map +1 -0
  128. package/dist/mem/adapters/codex.js +222 -0
  129. package/dist/mem/adapters/codex.js.map +1 -0
  130. package/dist/mem/adapters/opencode.d.ts +19 -0
  131. package/dist/mem/adapters/opencode.d.ts.map +1 -0
  132. package/dist/mem/adapters/opencode.js +25 -0
  133. package/dist/mem/adapters/opencode.js.map +1 -0
  134. package/dist/mem/context.d.ts +23 -0
  135. package/dist/mem/context.d.ts.map +1 -0
  136. package/dist/mem/context.js +118 -0
  137. package/dist/mem/context.js.map +1 -0
  138. package/dist/mem/dialogue.d.ts +17 -0
  139. package/dist/mem/dialogue.d.ts.map +1 -0
  140. package/dist/mem/dialogue.js +51 -0
  141. package/dist/mem/dialogue.js.map +1 -0
  142. package/dist/mem/filter.d.ts +31 -0
  143. package/dist/mem/filter.d.ts.map +1 -0
  144. package/dist/mem/filter.js +70 -0
  145. package/dist/mem/filter.js.map +1 -0
  146. package/dist/mem/index.d.ts +18 -0
  147. package/dist/mem/index.d.ts.map +1 -0
  148. package/dist/mem/index.js +17 -0
  149. package/dist/mem/index.js.map +1 -0
  150. package/dist/mem/internal/jsonl.d.ts +35 -0
  151. package/dist/mem/internal/jsonl.d.ts.map +1 -0
  152. package/dist/mem/internal/jsonl.js +125 -0
  153. package/dist/mem/internal/jsonl.js.map +1 -0
  154. package/dist/mem/internal/paths.d.ts +18 -0
  155. package/dist/mem/internal/paths.d.ts.map +1 -0
  156. package/dist/mem/internal/paths.js +51 -0
  157. package/dist/mem/internal/paths.js.map +1 -0
  158. package/dist/mem/phase.d.ts +45 -0
  159. package/dist/mem/phase.d.ts.map +1 -0
  160. package/dist/mem/phase.js +220 -0
  161. package/dist/mem/phase.js.map +1 -0
  162. package/dist/mem/projects.d.ts +13 -0
  163. package/dist/mem/projects.d.ts.map +1 -0
  164. package/dist/mem/projects.js +37 -0
  165. package/dist/mem/projects.js.map +1 -0
  166. package/dist/mem/search.d.ts +32 -0
  167. package/dist/mem/search.d.ts.map +1 -0
  168. package/dist/mem/search.js +125 -0
  169. package/dist/mem/search.js.map +1 -0
  170. package/dist/mem/sessions.d.ts +37 -0
  171. package/dist/mem/sessions.d.ts.map +1 -0
  172. package/dist/mem/sessions.js +270 -0
  173. package/dist/mem/sessions.js.map +1 -0
  174. package/dist/mem/types.d.ts +176 -0
  175. package/dist/mem/types.d.ts.map +1 -0
  176. package/dist/mem/types.js +10 -0
  177. package/dist/mem/types.js.map +1 -0
  178. package/dist/task/index.d.ts +9 -0
  179. package/dist/task/index.d.ts.map +1 -0
  180. package/dist/task/index.js +9 -0
  181. package/dist/task/index.js.map +1 -0
  182. package/dist/task/paths.d.ts +37 -0
  183. package/dist/task/paths.d.ts.map +1 -0
  184. package/dist/task/paths.js +49 -0
  185. package/dist/task/paths.js.map +1 -0
  186. package/dist/task/phase.d.ts +27 -0
  187. package/dist/task/phase.d.ts.map +1 -0
  188. package/dist/task/phase.js +24 -0
  189. package/dist/task/phase.js.map +1 -0
  190. package/dist/task/records.d.ts +39 -0
  191. package/dist/task/records.d.ts.map +1 -0
  192. package/dist/task/records.js +89 -0
  193. package/dist/task/records.js.map +1 -0
  194. package/dist/task/schema.d.ts +77 -0
  195. package/dist/task/schema.d.ts.map +1 -0
  196. package/dist/task/schema.js +220 -0
  197. package/dist/task/schema.js.map +1 -0
  198. package/dist/testing/index.d.ts +2 -0
  199. package/dist/testing/index.d.ts.map +1 -0
  200. package/dist/testing/index.js +4 -0
  201. package/dist/testing/index.js.map +1 -0
  202. package/package.json +78 -0
@@ -0,0 +1,9 @@
1
+ export type { TrellisTaskRecord, TaskRecordField, } from "./schema.js";
2
+ export { TASK_RECORD_FIELD_ORDER, emptyTaskRecord, taskRecordSchema, } from "./schema.js";
3
+ export type { LoadTaskRecordOptions, WriteTaskRecordOptions, } from "./records.js";
4
+ export { loadTaskRecord, writeTaskRecord, } from "./records.js";
5
+ export type { TaskDirParts } from "./paths.js";
6
+ export { validateTaskDirName, isValidTaskDirName } from "./paths.js";
7
+ export type { TrellisTaskPhase } from "./phase.js";
8
+ export { inferTaskPhase } from "./phase.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAKA,YAAY,EACV,iBAAiB,EACjB,eAAe,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ // Public task API surface — canonical task record shape, factory,
2
+ // schema, I/O helpers, directory validation, and phase inference.
3
+ //
4
+ // Task API is intentionally independent from the channel API.
5
+ export { TASK_RECORD_FIELD_ORDER, emptyTaskRecord, taskRecordSchema, } from "./schema.js";
6
+ export { loadTaskRecord, writeTaskRecord, } from "./records.js";
7
+ export { validateTaskDirName, isValidTaskDirName } from "./paths.js";
8
+ export { inferTaskPhase } from "./phase.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/task/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,8DAA8D;AAO9D,OAAO,EACL,uBAAuB,EACvB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAOrB,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Task directory naming.
3
+ *
4
+ * User-created task dirs follow the `MM-DD-slug` pattern produced by
5
+ * `.trellis/scripts/common/task_store.py::cmd_create`:
6
+ *
7
+ * <tasks-dir>/05-13-trellis-core-sdk-package/
8
+ *
9
+ * Trellis also creates system onboarding tasks during `trellis init` using a
10
+ * `00-slug` prefix, such as `00-bootstrap-guidelines` and `00-join-new-developer`.
11
+ *
12
+ * `MM` is the two-digit month, `DD` is the two-digit day, and `slug` is
13
+ * a lower-kebab-case identifier composed of `[a-z0-9-]+` characters.
14
+ */
15
+ export interface TaskDirParts {
16
+ /**
17
+ * The directory prefix. Dated tasks use `MM-DD`; Trellis system onboarding
18
+ * tasks use `00`.
19
+ */
20
+ prefix: string;
21
+ /** Two-digit month for dated tasks, or `null` for `00-*` system tasks. */
22
+ month: string | null;
23
+ /** Two-digit day for dated tasks, or `null` for `00-*` system tasks. */
24
+ day: string | null;
25
+ slug: string;
26
+ }
27
+ /**
28
+ * Validate a task directory base name (no slashes). Returns the parsed
29
+ * components when valid, or `null` when the name does not match a canonical
30
+ * task dir shape.
31
+ *
32
+ * Throws `TypeError` if `name` is not a string — guards downstream code
33
+ * from accidentally validating `Buffer`, `Path`, or other inputs.
34
+ */
35
+ export declare function validateTaskDirName(name: string): TaskDirParts | null;
36
+ export declare function isValidTaskDirName(name: string): boolean;
37
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/task/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,wEAAwE;IACxE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAqBrE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Task directory naming.
3
+ *
4
+ * User-created task dirs follow the `MM-DD-slug` pattern produced by
5
+ * `.trellis/scripts/common/task_store.py::cmd_create`:
6
+ *
7
+ * <tasks-dir>/05-13-trellis-core-sdk-package/
8
+ *
9
+ * Trellis also creates system onboarding tasks during `trellis init` using a
10
+ * `00-slug` prefix, such as `00-bootstrap-guidelines` and `00-join-new-developer`.
11
+ *
12
+ * `MM` is the two-digit month, `DD` is the two-digit day, and `slug` is
13
+ * a lower-kebab-case identifier composed of `[a-z0-9-]+` characters.
14
+ */
15
+ const DATED_TASK_DIR_RE = /^(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])-([a-z0-9]+(?:-[a-z0-9]+)*)$/;
16
+ const SYSTEM_TASK_DIR_RE = /^00-(bootstrap-guidelines|join-[a-z0-9]+(?:-[a-z0-9]+)*)$/;
17
+ /**
18
+ * Validate a task directory base name (no slashes). Returns the parsed
19
+ * components when valid, or `null` when the name does not match a canonical
20
+ * task dir shape.
21
+ *
22
+ * Throws `TypeError` if `name` is not a string — guards downstream code
23
+ * from accidentally validating `Buffer`, `Path`, or other inputs.
24
+ */
25
+ export function validateTaskDirName(name) {
26
+ if (typeof name !== "string") {
27
+ throw new TypeError("task directory name must be a string");
28
+ }
29
+ const dated = DATED_TASK_DIR_RE.exec(name);
30
+ if (dated) {
31
+ const [, month, day, slug] = dated;
32
+ if (month === undefined || day === undefined || slug === undefined) {
33
+ return null;
34
+ }
35
+ return { prefix: `${month}-${day}`, month, day, slug };
36
+ }
37
+ const system = SYSTEM_TASK_DIR_RE.exec(name);
38
+ if (system) {
39
+ const [, slug] = system;
40
+ if (slug === undefined)
41
+ return null;
42
+ return { prefix: "00", month: null, day: null, slug };
43
+ }
44
+ return null;
45
+ }
46
+ export function isValidTaskDirName(name) {
47
+ return validateTaskDirName(name) !== null;
48
+ }
49
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/task/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,iBAAiB,GACrB,oEAAoE,CAAC;AACvE,MAAM,kBAAkB,GACtB,2DAA2D,CAAC;AAe9D;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QACnC,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACnE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACzD,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC;QACxB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,mBAAmB,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAC5C,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { TrellisTaskRecord } from "./schema.js";
2
+ /**
3
+ * Coarse-grained Trellis task phase derived from task status.
4
+ *
5
+ * Phase is a projection of {@link TrellisTaskRecord.status} only. There is
6
+ * no separate `current_phase` field stored on disk — `inferTaskPhase`
7
+ * exists so consumers can render the workflow phase without depending on
8
+ * `.trellis/workflow.md` parsing.
9
+ *
10
+ * Mapping:
11
+ *
12
+ * status | phase
13
+ * --------------------|-----------
14
+ * planning | plan
15
+ * in_progress | implement
16
+ * review | review
17
+ * completed | done | completed
18
+ * <anything else> | unknown
19
+ */
20
+ export type TrellisTaskPhase = "plan" | "implement" | "review" | "completed" | "unknown";
21
+ /**
22
+ * Infer the phase of a task from either its parsed record or its raw
23
+ * status string. Accepts a record so callers that already have one don't
24
+ * need to re-pluck `status` first.
25
+ */
26
+ export declare function inferTaskPhase(recordOrStatus: TrellisTaskRecord | string | null | undefined): TrellisTaskPhase;
27
+ //# sourceMappingURL=phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.d.ts","sourceRoot":"","sources":["../../src/task/phase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,WAAW,GACX,QAAQ,GACR,WAAW,GACX,SAAS,CAAC;AAEd;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,cAAc,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAC5D,gBAAgB,CAkBlB"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Infer the phase of a task from either its parsed record or its raw
3
+ * status string. Accepts a record so callers that already have one don't
4
+ * need to re-pluck `status` first.
5
+ */
6
+ export function inferTaskPhase(recordOrStatus) {
7
+ const status = typeof recordOrStatus === "string"
8
+ ? recordOrStatus
9
+ : recordOrStatus?.status;
10
+ switch (status) {
11
+ case "planning":
12
+ return "plan";
13
+ case "in_progress":
14
+ return "implement";
15
+ case "review":
16
+ return "review";
17
+ case "completed":
18
+ case "done":
19
+ return "completed";
20
+ default:
21
+ return "unknown";
22
+ }
23
+ }
24
+ //# sourceMappingURL=phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phase.js","sourceRoot":"","sources":["../../src/task/phase.ts"],"names":[],"mappings":"AA2BA;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,cAA6D;IAE7D,MAAM,MAAM,GACV,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,cAAc,EAAE,MAAM,CAAC;IAC7B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,UAAU;YACb,OAAO,MAAM,CAAC;QAChB,KAAK,aAAa;YAChB,OAAO,WAAW,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB,KAAK,WAAW,CAAC;QACjB,KAAK,MAAM;YACT,OAAO,WAAW,CAAC;QACrB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { emptyTaskRecord, type TrellisTaskRecord } from "./schema.js";
2
+ export interface LoadTaskRecordOptions {
3
+ /** Absolute or repo-relative directory containing `task.json`. */
4
+ taskDir: string;
5
+ /** Optional repo root used to resolve relative `taskDir` values. */
6
+ cwd?: string;
7
+ }
8
+ export interface WriteTaskRecordOptions {
9
+ /** Absolute or repo-relative directory containing `task.json`. */
10
+ taskDir: string;
11
+ /** Canonical record to persist. Unknown fields on disk are preserved. */
12
+ record: TrellisTaskRecord;
13
+ /** Optional repo root used to resolve relative `taskDir` values. */
14
+ cwd?: string;
15
+ }
16
+ /**
17
+ * Read a task.json file and return a canonicalized record.
18
+ *
19
+ * Unknown fields on disk that are not part of the canonical 24-field
20
+ * shape are NOT returned — `loadTaskRecord` is the structured public API.
21
+ * To preserve unknown fields across a load/write cycle, callers should
22
+ * use {@link writeTaskRecord}, which merges canonical updates on top of
23
+ * the on-disk JSON object instead of overwriting it.
24
+ */
25
+ export declare function loadTaskRecord(options: LoadTaskRecordOptions): TrellisTaskRecord;
26
+ /**
27
+ * Write a task.json file with canonical field ordering. Unknown fields
28
+ * already present on disk are preserved verbatim — only the canonical
29
+ * fields are overwritten by `record`. Field order: canonical fields
30
+ * first (in `TASK_RECORD_FIELD_ORDER`), then any preserved unknown
31
+ * fields in their original insertion order. If an existing `task.json` is
32
+ * present but cannot be parsed as a JSON object, the write is rejected instead
33
+ * of silently replacing potentially recoverable local data.
34
+ *
35
+ * The directory containing `task.json` is created if it does not exist.
36
+ */
37
+ export declare function writeTaskRecord(options: WriteTaskRecordOptions): void;
38
+ export { emptyTaskRecord };
39
+ //# sourceMappingURL=records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/task/records.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,eAAe,EAGf,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,EAAE,iBAAiB,CAAC;IAC1B,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,iBAAiB,CAYnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAsBrE;AAoCD,OAAO,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,89 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { TASK_RECORD_FIELD_ORDER, emptyTaskRecord, isPlainObject, taskRecordSchema, } from "./schema.js";
4
+ const TASK_JSON_BASENAME = "task.json";
5
+ /**
6
+ * Read a task.json file and return a canonicalized record.
7
+ *
8
+ * Unknown fields on disk that are not part of the canonical 24-field
9
+ * shape are NOT returned — `loadTaskRecord` is the structured public API.
10
+ * To preserve unknown fields across a load/write cycle, callers should
11
+ * use {@link writeTaskRecord}, which merges canonical updates on top of
12
+ * the on-disk JSON object instead of overwriting it.
13
+ */
14
+ export function loadTaskRecord(options) {
15
+ const file = resolveTaskJsonPath(options.taskDir, options.cwd);
16
+ const raw = fs.readFileSync(file, "utf-8");
17
+ let parsed;
18
+ try {
19
+ parsed = JSON.parse(raw);
20
+ }
21
+ catch (err) {
22
+ throw new Error(`Failed to parse ${file}: ${err instanceof Error ? err.message : err}`);
23
+ }
24
+ return taskRecordSchema.parse(parsed);
25
+ }
26
+ /**
27
+ * Write a task.json file with canonical field ordering. Unknown fields
28
+ * already present on disk are preserved verbatim — only the canonical
29
+ * fields are overwritten by `record`. Field order: canonical fields
30
+ * first (in `TASK_RECORD_FIELD_ORDER`), then any preserved unknown
31
+ * fields in their original insertion order. If an existing `task.json` is
32
+ * present but cannot be parsed as a JSON object, the write is rejected instead
33
+ * of silently replacing potentially recoverable local data.
34
+ *
35
+ * The directory containing `task.json` is created if it does not exist.
36
+ */
37
+ export function writeTaskRecord(options) {
38
+ const record = taskRecordSchema.parse(options.record);
39
+ const file = resolveTaskJsonPath(options.taskDir, options.cwd);
40
+ fs.mkdirSync(path.dirname(file), { recursive: true });
41
+ const existing = readExistingObject(file);
42
+ const out = {};
43
+ const recordBag = record;
44
+ for (const field of TASK_RECORD_FIELD_ORDER) {
45
+ out[field] = recordBag[field];
46
+ }
47
+ if (existing) {
48
+ for (const key of Object.keys(existing)) {
49
+ if (!(key in out)) {
50
+ out[key] = existing[key];
51
+ }
52
+ }
53
+ }
54
+ const json = JSON.stringify(out, null, 2) + "\n";
55
+ fs.writeFileSync(file, json, "utf-8");
56
+ }
57
+ function readExistingObject(file) {
58
+ let raw;
59
+ try {
60
+ raw = fs.readFileSync(file, "utf-8");
61
+ }
62
+ catch (err) {
63
+ if (err.code === "ENOENT")
64
+ return null;
65
+ throw err;
66
+ }
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(raw);
70
+ }
71
+ catch (err) {
72
+ throw new Error(`Refusing to overwrite corrupt ${file}: ${err instanceof Error ? err.message : String(err)}`);
73
+ }
74
+ if (!isPlainObject(parsed)) {
75
+ throw new Error(`Refusing to overwrite non-object task record at ${file}`);
76
+ }
77
+ return parsed;
78
+ }
79
+ function resolveTaskJsonPath(taskDir, cwd) {
80
+ if (path.isAbsolute(taskDir)) {
81
+ return path.join(taskDir, TASK_JSON_BASENAME);
82
+ }
83
+ const base = cwd ?? process.cwd();
84
+ return path.join(path.resolve(base, taskDir), TASK_JSON_BASENAME);
85
+ }
86
+ // Re-exported so callers can build a starter record without hitting the
87
+ // schema module directly.
88
+ export { emptyTaskRecord };
89
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/task/records.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Canonical task.json shape — single source of truth for Trellis tasks.
3
+ *
4
+ * The runtime Python writer is `.trellis/scripts/common/task_store.py`
5
+ * (`cmd_create`). The 24-field shape and field order below mirror that
6
+ * writer exactly so every TS and Python entry point produces structurally
7
+ * identical task.json files.
8
+ *
9
+ * Downstream consumers (CLI bootstrap, migration tooling, external Node
10
+ * services) should depend on this type instead of redefining their own
11
+ * task.json shape.
12
+ */
13
+ export interface TrellisTaskRecord {
14
+ id: string;
15
+ name: string;
16
+ title: string;
17
+ description: string;
18
+ status: string;
19
+ dev_type: string | null;
20
+ scope: string | null;
21
+ package: string | null;
22
+ priority: string;
23
+ creator: string;
24
+ assignee: string;
25
+ createdAt: string;
26
+ completedAt: string | null;
27
+ branch: string | null;
28
+ base_branch: string | null;
29
+ worktree_path: string | null;
30
+ commit: string | null;
31
+ pr_url: string | null;
32
+ subtasks: string[];
33
+ children: string[];
34
+ parent: string | null;
35
+ relatedFiles: string[];
36
+ notes: string;
37
+ meta: Record<string, unknown>;
38
+ }
39
+ /**
40
+ * Canonical task field order — matches `task_store.py::cmd_create`. Used
41
+ * by `writeTaskRecord` so the on-disk JSON layout is deterministic.
42
+ */
43
+ export declare const TASK_RECORD_FIELD_ORDER: readonly ["id", "name", "title", "description", "status", "dev_type", "scope", "package", "priority", "creator", "assignee", "createdAt", "completedAt", "branch", "base_branch", "worktree_path", "commit", "pr_url", "subtasks", "children", "parent", "relatedFiles", "notes", "meta"];
44
+ export type TaskRecordField = (typeof TASK_RECORD_FIELD_ORDER)[number];
45
+ /**
46
+ * Lightweight runtime schema for {@link TrellisTaskRecord}. Zero-dep on
47
+ * purpose — `taskRecordSchema.parse(input)` returns a canonicalized
48
+ * record, throwing on shape violations; `taskRecordSchema.safeParse`
49
+ * returns a result discriminated by `success`.
50
+ *
51
+ * All canonical fields are required; older partial records are rejected rather
52
+ * than backfilled with defaults. Unknown fields on the input are intentionally
53
+ * omitted from this structured output. `writeTaskRecord` preserves unknown
54
+ * fields already present on disk by merging canonical updates over the existing
55
+ * JSON object.
56
+ */
57
+ export declare const taskRecordSchema: {
58
+ readonly parse: (input: unknown) => TrellisTaskRecord;
59
+ readonly safeParse: (input: unknown) => {
60
+ success: true;
61
+ data: TrellisTaskRecord;
62
+ } | {
63
+ success: false;
64
+ error: Error;
65
+ };
66
+ };
67
+ /**
68
+ * Produce a fully-populated canonical-shape {@link TrellisTaskRecord}.
69
+ *
70
+ * All 24 fields are present in canonical order. `overrides` shallow-merges
71
+ * over the defaults — callers supply per-task values (id, name, title,
72
+ * assignee, createdAt, etc.) and leave null-default fields untouched
73
+ * unless they have a real value.
74
+ */
75
+ export declare function emptyTaskRecord(overrides?: Partial<TrellisTaskRecord>): TrellisTaskRecord;
76
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
77
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/task/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,2RAyBqB,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC;AAkCvE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB;4BACd,OAAO,KAAG,iBAAiB;gCAI/B,OAAO,KAEZ;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,iBAAiB,CAAA;KAAE,GAC1C;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE;CAU5B,CAAC;AAwDX;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,GACzC,iBAAiB,CA0CnB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAO9E"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Canonical task field order — matches `task_store.py::cmd_create`. Used
3
+ * by `writeTaskRecord` so the on-disk JSON layout is deterministic.
4
+ */
5
+ export const TASK_RECORD_FIELD_ORDER = [
6
+ "id",
7
+ "name",
8
+ "title",
9
+ "description",
10
+ "status",
11
+ "dev_type",
12
+ "scope",
13
+ "package",
14
+ "priority",
15
+ "creator",
16
+ "assignee",
17
+ "createdAt",
18
+ "completedAt",
19
+ "branch",
20
+ "base_branch",
21
+ "worktree_path",
22
+ "commit",
23
+ "pr_url",
24
+ "subtasks",
25
+ "children",
26
+ "parent",
27
+ "relatedFiles",
28
+ "notes",
29
+ "meta",
30
+ ];
31
+ const STRING_FIELDS = new Set([
32
+ "id",
33
+ "name",
34
+ "title",
35
+ "description",
36
+ "status",
37
+ "priority",
38
+ "creator",
39
+ "assignee",
40
+ "createdAt",
41
+ "notes",
42
+ ]);
43
+ const NULLABLE_STRING_FIELDS = new Set([
44
+ "dev_type",
45
+ "scope",
46
+ "package",
47
+ "completedAt",
48
+ "branch",
49
+ "base_branch",
50
+ "worktree_path",
51
+ "commit",
52
+ "pr_url",
53
+ "parent",
54
+ ]);
55
+ const STRING_ARRAY_FIELDS = new Set([
56
+ "subtasks",
57
+ "children",
58
+ "relatedFiles",
59
+ ]);
60
+ /**
61
+ * Lightweight runtime schema for {@link TrellisTaskRecord}. Zero-dep on
62
+ * purpose — `taskRecordSchema.parse(input)` returns a canonicalized
63
+ * record, throwing on shape violations; `taskRecordSchema.safeParse`
64
+ * returns a result discriminated by `success`.
65
+ *
66
+ * All canonical fields are required; older partial records are rejected rather
67
+ * than backfilled with defaults. Unknown fields on the input are intentionally
68
+ * omitted from this structured output. `writeTaskRecord` preserves unknown
69
+ * fields already present on disk by merging canonical updates over the existing
70
+ * JSON object.
71
+ */
72
+ export const taskRecordSchema = {
73
+ parse(input) {
74
+ return parseTaskRecord(input);
75
+ },
76
+ safeParse(input) {
77
+ try {
78
+ return { success: true, data: parseTaskRecord(input) };
79
+ }
80
+ catch (err) {
81
+ return {
82
+ success: false,
83
+ error: err instanceof Error ? err : new Error(String(err)),
84
+ };
85
+ }
86
+ },
87
+ };
88
+ function parseTaskRecord(input) {
89
+ if (!isPlainObject(input)) {
90
+ throw new Error("task record must be a JSON object");
91
+ }
92
+ const out = emptyTaskRecord();
93
+ for (const field of TASK_RECORD_FIELD_ORDER) {
94
+ if (!(field in input)) {
95
+ throw new Error(`task.${field} is required`);
96
+ }
97
+ const value = input[field];
98
+ assignField(out, field, value);
99
+ }
100
+ return out;
101
+ }
102
+ function assignField(record, field, value) {
103
+ const bag = record;
104
+ if (STRING_FIELDS.has(field)) {
105
+ if (typeof value !== "string") {
106
+ throw new Error(`task.${field} must be a string`);
107
+ }
108
+ bag[field] = value;
109
+ return;
110
+ }
111
+ if (NULLABLE_STRING_FIELDS.has(field)) {
112
+ if (value !== null && typeof value !== "string") {
113
+ throw new Error(`task.${field} must be a string or null`);
114
+ }
115
+ bag[field] = value;
116
+ return;
117
+ }
118
+ if (STRING_ARRAY_FIELDS.has(field)) {
119
+ if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
120
+ throw new Error(`task.${field} must be an array of strings`);
121
+ }
122
+ bag[field] = [...value];
123
+ return;
124
+ }
125
+ if (field === "meta") {
126
+ if (!isPlainObject(value)) {
127
+ throw new Error("task.meta must be a JSON object");
128
+ }
129
+ record.meta = cloneJsonObject(value, "task.meta");
130
+ return;
131
+ }
132
+ // Should be unreachable given the field sets cover every canonical field.
133
+ /* c8 ignore next */
134
+ throw new Error(`unknown canonical task field: ${field}`);
135
+ }
136
+ /**
137
+ * Produce a fully-populated canonical-shape {@link TrellisTaskRecord}.
138
+ *
139
+ * All 24 fields are present in canonical order. `overrides` shallow-merges
140
+ * over the defaults — callers supply per-task values (id, name, title,
141
+ * assignee, createdAt, etc.) and leave null-default fields untouched
142
+ * unless they have a real value.
143
+ */
144
+ export function emptyTaskRecord(overrides = {}) {
145
+ const today = new Date().toISOString().split("T")[0] ?? "";
146
+ const base = {
147
+ id: "",
148
+ name: "",
149
+ title: "",
150
+ description: "",
151
+ status: "planning",
152
+ dev_type: null,
153
+ scope: null,
154
+ package: null,
155
+ priority: "P2",
156
+ creator: "",
157
+ assignee: "",
158
+ createdAt: today,
159
+ completedAt: null,
160
+ branch: null,
161
+ base_branch: null,
162
+ worktree_path: null,
163
+ commit: null,
164
+ pr_url: null,
165
+ subtasks: [],
166
+ children: [],
167
+ parent: null,
168
+ relatedFiles: [],
169
+ notes: "",
170
+ meta: {},
171
+ };
172
+ const record = { ...base, ...overrides };
173
+ if (overrides.subtasks !== undefined) {
174
+ record.subtasks = [...overrides.subtasks];
175
+ }
176
+ if (overrides.children !== undefined) {
177
+ record.children = [...overrides.children];
178
+ }
179
+ if (overrides.relatedFiles !== undefined) {
180
+ record.relatedFiles = [...overrides.relatedFiles];
181
+ }
182
+ if (overrides.meta !== undefined) {
183
+ record.meta = cloneJsonObject(overrides.meta, "task.meta");
184
+ }
185
+ return record;
186
+ }
187
+ export function isPlainObject(value) {
188
+ return (typeof value === "object" &&
189
+ value !== null &&
190
+ !Array.isArray(value) &&
191
+ Object.getPrototypeOf(value) === Object.prototype);
192
+ }
193
+ function cloneJsonObject(value, path) {
194
+ const out = {};
195
+ for (const [key, child] of Object.entries(value)) {
196
+ out[key] = cloneJsonValue(child, `${path}.${key}`);
197
+ }
198
+ return out;
199
+ }
200
+ function cloneJsonValue(value, path) {
201
+ if (value === null ||
202
+ typeof value === "string" ||
203
+ typeof value === "boolean") {
204
+ return value;
205
+ }
206
+ if (typeof value === "number") {
207
+ if (!Number.isFinite(value)) {
208
+ throw new Error(`${path} must be a finite JSON number`);
209
+ }
210
+ return value;
211
+ }
212
+ if (Array.isArray(value)) {
213
+ return value.map((item, index) => cloneJsonValue(item, `${path}[${index}]`));
214
+ }
215
+ if (isPlainObject(value)) {
216
+ return cloneJsonObject(value, path);
217
+ }
218
+ throw new Error(`${path} must contain only JSON values`);
219
+ }
220
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/task/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ // Testing helpers placeholder. Exposes nothing in P0; reserved for the
2
+ // public testing surface in later phases.
3
+ export {};
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,0CAA0C"}