@deskwork/core 0.9.5

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 (188) hide show
  1. package/dist/body-state.d.ts +27 -0
  2. package/dist/body-state.d.ts.map +1 -0
  3. package/dist/body-state.js +62 -0
  4. package/dist/body-state.js.map +1 -0
  5. package/dist/calendar-mutations.d.ts +124 -0
  6. package/dist/calendar-mutations.d.ts.map +1 -0
  7. package/dist/calendar-mutations.js +305 -0
  8. package/dist/calendar-mutations.js.map +1 -0
  9. package/dist/calendar.d.ts +54 -0
  10. package/dist/calendar.d.ts.map +1 -0
  11. package/dist/calendar.js +430 -0
  12. package/dist/calendar.js.map +1 -0
  13. package/dist/cli.d.ts +38 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +72 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +91 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +216 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/content-index.d.ts +74 -0
  22. package/dist/content-index.d.ts.map +1 -0
  23. package/dist/content-index.js +205 -0
  24. package/dist/content-index.js.map +1 -0
  25. package/dist/content-tree-fs-walk.d.ts +54 -0
  26. package/dist/content-tree-fs-walk.d.ts.map +1 -0
  27. package/dist/content-tree-fs-walk.js +112 -0
  28. package/dist/content-tree-fs-walk.js.map +1 -0
  29. package/dist/content-tree-helpers.d.ts +52 -0
  30. package/dist/content-tree-helpers.d.ts.map +1 -0
  31. package/dist/content-tree-helpers.js +116 -0
  32. package/dist/content-tree-helpers.js.map +1 -0
  33. package/dist/content-tree-types.d.ts +175 -0
  34. package/dist/content-tree-types.d.ts.map +1 -0
  35. package/dist/content-tree-types.js +10 -0
  36. package/dist/content-tree-types.js.map +1 -0
  37. package/dist/content-tree.d.ts +93 -0
  38. package/dist/content-tree.d.ts.map +1 -0
  39. package/dist/content-tree.js +385 -0
  40. package/dist/content-tree.js.map +1 -0
  41. package/dist/doctor/index.d.ts +11 -0
  42. package/dist/doctor/index.d.ts.map +1 -0
  43. package/dist/doctor/index.js +10 -0
  44. package/dist/doctor/index.js.map +1 -0
  45. package/dist/doctor/project-rules.d.ts +59 -0
  46. package/dist/doctor/project-rules.d.ts.map +1 -0
  47. package/dist/doctor/project-rules.js +143 -0
  48. package/dist/doctor/project-rules.js.map +1 -0
  49. package/dist/doctor/rules/calendar-uuid-missing.d.ts +19 -0
  50. package/dist/doctor/rules/calendar-uuid-missing.d.ts.map +1 -0
  51. package/dist/doctor/rules/calendar-uuid-missing.js +176 -0
  52. package/dist/doctor/rules/calendar-uuid-missing.js.map +1 -0
  53. package/dist/doctor/rules/duplicate-id.d.ts +27 -0
  54. package/dist/doctor/rules/duplicate-id.d.ts.map +1 -0
  55. package/dist/doctor/rules/duplicate-id.js +157 -0
  56. package/dist/doctor/rules/duplicate-id.js.map +1 -0
  57. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts +40 -0
  58. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -0
  59. package/dist/doctor/rules/legacy-top-level-id-migration.js +232 -0
  60. package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -0
  61. package/dist/doctor/rules/missing-frontmatter-id.d.ts +45 -0
  62. package/dist/doctor/rules/missing-frontmatter-id.d.ts.map +1 -0
  63. package/dist/doctor/rules/missing-frontmatter-id.js +283 -0
  64. package/dist/doctor/rules/missing-frontmatter-id.js.map +1 -0
  65. package/dist/doctor/rules/orphan-frontmatter-id.d.ts +18 -0
  66. package/dist/doctor/rules/orphan-frontmatter-id.d.ts.map +1 -0
  67. package/dist/doctor/rules/orphan-frontmatter-id.js +154 -0
  68. package/dist/doctor/rules/orphan-frontmatter-id.js.map +1 -0
  69. package/dist/doctor/rules/schema-rejected.d.ts +20 -0
  70. package/dist/doctor/rules/schema-rejected.d.ts.map +1 -0
  71. package/dist/doctor/rules/schema-rejected.js +44 -0
  72. package/dist/doctor/rules/schema-rejected.js.map +1 -0
  73. package/dist/doctor/rules/slug-collision.d.ts +18 -0
  74. package/dist/doctor/rules/slug-collision.d.ts.map +1 -0
  75. package/dist/doctor/rules/slug-collision.js +65 -0
  76. package/dist/doctor/rules/slug-collision.js.map +1 -0
  77. package/dist/doctor/rules/workflow-stale.d.ts +20 -0
  78. package/dist/doctor/rules/workflow-stale.d.ts.map +1 -0
  79. package/dist/doctor/rules/workflow-stale.js +136 -0
  80. package/dist/doctor/rules/workflow-stale.js.map +1 -0
  81. package/dist/doctor/runner.d.ts +75 -0
  82. package/dist/doctor/runner.d.ts.map +1 -0
  83. package/dist/doctor/runner.js +289 -0
  84. package/dist/doctor/runner.js.map +1 -0
  85. package/dist/doctor/schema-patch.d.ts +21 -0
  86. package/dist/doctor/schema-patch.d.ts.map +1 -0
  87. package/dist/doctor/schema-patch.js +92 -0
  88. package/dist/doctor/schema-patch.js.map +1 -0
  89. package/dist/doctor/types.d.ts +185 -0
  90. package/dist/doctor/types.d.ts.map +1 -0
  91. package/dist/doctor/types.js +13 -0
  92. package/dist/doctor/types.js.map +1 -0
  93. package/dist/frontmatter.d.ts +103 -0
  94. package/dist/frontmatter.d.ts.map +1 -0
  95. package/dist/frontmatter.js +306 -0
  96. package/dist/frontmatter.js.map +1 -0
  97. package/dist/index.d.ts +27 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +27 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/ingest-derive.d.ts +79 -0
  102. package/dist/ingest-derive.d.ts.map +1 -0
  103. package/dist/ingest-derive.js +299 -0
  104. package/dist/ingest-derive.js.map +1 -0
  105. package/dist/ingest-paths.d.ts +37 -0
  106. package/dist/ingest-paths.d.ts.map +1 -0
  107. package/dist/ingest-paths.js +176 -0
  108. package/dist/ingest-paths.js.map +1 -0
  109. package/dist/ingest.d.ts +162 -0
  110. package/dist/ingest.d.ts.map +1 -0
  111. package/dist/ingest.js +269 -0
  112. package/dist/ingest.js.map +1 -0
  113. package/dist/journal.d.ts +49 -0
  114. package/dist/journal.d.ts.map +1 -0
  115. package/dist/journal.js +113 -0
  116. package/dist/journal.js.map +1 -0
  117. package/dist/outline-split.d.ts +38 -0
  118. package/dist/outline-split.d.ts.map +1 -0
  119. package/dist/outline-split.js +84 -0
  120. package/dist/outline-split.js.map +1 -0
  121. package/dist/overrides.d.ts +83 -0
  122. package/dist/overrides.d.ts.map +1 -0
  123. package/dist/overrides.js +88 -0
  124. package/dist/overrides.js.map +1 -0
  125. package/dist/paths.d.ts +183 -0
  126. package/dist/paths.d.ts.map +1 -0
  127. package/dist/paths.js +266 -0
  128. package/dist/paths.js.map +1 -0
  129. package/dist/remark-image-figure.mjs +77 -0
  130. package/dist/remark-strip-first-h1.mjs +26 -0
  131. package/dist/remark-strip-outline.mjs +44 -0
  132. package/dist/rename-slug.d.ts +49 -0
  133. package/dist/rename-slug.d.ts.map +1 -0
  134. package/dist/rename-slug.js +161 -0
  135. package/dist/rename-slug.js.map +1 -0
  136. package/dist/review/handlers.d.ts +55 -0
  137. package/dist/review/handlers.d.ts.map +1 -0
  138. package/dist/review/handlers.js +307 -0
  139. package/dist/review/handlers.js.map +1 -0
  140. package/dist/review/index.d.ts +14 -0
  141. package/dist/review/index.d.ts.map +1 -0
  142. package/dist/review/index.js +13 -0
  143. package/dist/review/index.js.map +1 -0
  144. package/dist/review/journal-mappers.d.ts +35 -0
  145. package/dist/review/journal-mappers.d.ts.map +1 -0
  146. package/dist/review/journal-mappers.js +48 -0
  147. package/dist/review/journal-mappers.js.map +1 -0
  148. package/dist/review/pipeline.d.ts +79 -0
  149. package/dist/review/pipeline.d.ts.map +1 -0
  150. package/dist/review/pipeline.js +234 -0
  151. package/dist/review/pipeline.js.map +1 -0
  152. package/dist/review/render.d.ts +27 -0
  153. package/dist/review/render.d.ts.map +1 -0
  154. package/dist/review/render.js +42 -0
  155. package/dist/review/render.js.map +1 -0
  156. package/dist/review/report.d.ts +50 -0
  157. package/dist/review/report.d.ts.map +1 -0
  158. package/dist/review/report.js +164 -0
  159. package/dist/review/report.js.map +1 -0
  160. package/dist/review/result.d.ts +12 -0
  161. package/dist/review/result.d.ts.map +1 -0
  162. package/dist/review/result.js +12 -0
  163. package/dist/review/result.js.map +1 -0
  164. package/dist/review/start-handlers.d.ts +62 -0
  165. package/dist/review/start-handlers.d.ts.map +1 -0
  166. package/dist/review/start-handlers.js +223 -0
  167. package/dist/review/start-handlers.js.map +1 -0
  168. package/dist/review/types.d.ts +169 -0
  169. package/dist/review/types.d.ts.map +1 -0
  170. package/dist/review/types.js +26 -0
  171. package/dist/review/types.js.map +1 -0
  172. package/dist/review/workflow-paths.d.ts +68 -0
  173. package/dist/review/workflow-paths.d.ts.map +1 -0
  174. package/dist/review/workflow-paths.js +112 -0
  175. package/dist/review/workflow-paths.js.map +1 -0
  176. package/dist/scaffold.d.ts +67 -0
  177. package/dist/scaffold.d.ts.map +1 -0
  178. package/dist/scaffold.js +122 -0
  179. package/dist/scaffold.js.map +1 -0
  180. package/dist/scrapbook.d.ts +229 -0
  181. package/dist/scrapbook.d.ts.map +1 -0
  182. package/dist/scrapbook.js +500 -0
  183. package/dist/scrapbook.js.map +1 -0
  184. package/dist/types.d.ts +197 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +120 -0
  187. package/dist/types.js.map +1 -0
  188. package/package.json +160 -0
@@ -0,0 +1,84 @@
1
+ /*
2
+ * outline-split.ts — shared utility for separating the `## Outline`
3
+ * section from the rest of a dispatch markdown document.
4
+ *
5
+ * The outline lives in the source file (SSOT: the markdown file IS
6
+ * the article, including its outline). But during body editing the
7
+ * operator doesn't want the outline cluttering the source view —
8
+ * it's reference material, not editing material, once the
9
+ * Outlining phase is approved. The editor splits on mount, presents
10
+ * only the body for editing, and rejoins on save so the disk file
11
+ * is never lossy.
12
+ *
13
+ * Shape:
14
+ * splitOutline(md) → { outline, body, present }
15
+ * joinOutline(outline, body) → md
16
+ *
17
+ * The split round-trips losslessly: `joinOutline(...splitOutline(x))`
18
+ * equals the original document whenever `splitOutline(x).present`
19
+ * is true. When no outline is present, `outline` is empty and join
20
+ * returns just the body.
21
+ */
22
+ /**
23
+ * Detect and separate the `## Outline` section. The section runs
24
+ * from its `## Outline` heading through the next `## ` heading
25
+ * (non-inclusive) or end of file. Anything before the outline is
26
+ * prepended to `body`; anything after is appended. Rejoin via
27
+ * `joinOutline`.
28
+ *
29
+ * This mirrors the line-wise logic in `scripts/lib/editorial/
30
+ * body-state.ts` (kept separate so the browser-side bundle
31
+ * doesn't have to drag in the server's fs/path imports).
32
+ */
33
+ export function splitOutline(md) {
34
+ const lines = md.split('\n');
35
+ const startIdx = lines.findIndex((line) => /^##[ \t]+Outline\b/.test(line));
36
+ if (startIdx < 0) {
37
+ return { outline: '', body: md, present: false, startLine: -1, endLine: -1 };
38
+ }
39
+ let endIdx = lines.length;
40
+ for (let i = startIdx + 1; i < lines.length; i++) {
41
+ if (/^##[ \t]+/.test(lines[i])) {
42
+ endIdx = i;
43
+ break;
44
+ }
45
+ }
46
+ const outline = lines.slice(startIdx, endIdx).join('\n');
47
+ const body = [...lines.slice(0, startIdx), ...lines.slice(endIdx)].join('\n');
48
+ return { outline, body, present: true, startLine: startIdx, endLine: endIdx };
49
+ }
50
+ /**
51
+ * Rejoin an outline section with body content. If `outline` is
52
+ * empty, returns `body` unchanged. Otherwise splices the outline
53
+ * back at the same structural position it was extracted from —
54
+ * which for the scaffold shape (frontmatter + H1 + outline + body
55
+ * sections) is right after the H1.
56
+ *
57
+ * Strategy: find the first `## ` heading in `body` and insert the
58
+ * outline immediately before it, separated by a blank line on each
59
+ * side. If the body has no H2 (e.g., pre-drafting, still just
60
+ * frontmatter + H1), append outline at the end.
61
+ */
62
+ export function joinOutline(outline, body) {
63
+ if (!outline)
64
+ return body;
65
+ const outlineTrimmed = outline.replace(/\n+$/, '');
66
+ const bodyLines = body.split('\n');
67
+ const firstH2 = bodyLines.findIndex((line) => /^##[ \t]+/.test(line));
68
+ if (firstH2 < 0) {
69
+ // No H2 in body — append outline to the end. Preserves the
70
+ // scaffold shape where only frontmatter + H1 exist yet.
71
+ const trailingNewline = body.endsWith('\n') ? '' : '\n';
72
+ return `${body}${trailingNewline}\n${outlineTrimmed}\n`;
73
+ }
74
+ const before = bodyLines.slice(0, firstH2);
75
+ const after = bodyLines.slice(firstH2);
76
+ // Ensure exactly one blank line separates outline from the
77
+ // following H2, and that the outline ends without trailing
78
+ // newlines (we control the separator).
79
+ while (before.length > 0 && before[before.length - 1] === '') {
80
+ before.pop();
81
+ }
82
+ return [...before, '', outlineTrimmed, '', ...after].join('\n');
83
+ }
84
+ //# sourceMappingURL=outline-split.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outline-split.js","sourceRoot":"","sources":["../src/outline-split.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAeH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;IAC/E,CAAC;IACD,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,CAAC,CAAC;YACX,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,IAAY;IACvD,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,2DAA2D;QAC3D,wDAAwD;QACxD,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACxD,OAAO,GAAG,IAAI,GAAG,eAAe,KAAK,cAAc,IAAI,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,2DAA2D;IAC3D,2DAA2D;IAC3D,uCAAuC;IACvC,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC7D,MAAM,CAAC,GAAG,EAAE,CAAC;IACf,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Override resolver — phase 23f.
3
+ *
4
+ * The customization layer lets operators drop a file into
5
+ * `<projectRoot>/.deskwork/<category>/<name>` and have the plugin pick
6
+ * it up at runtime instead of the built-in default. Three categories
7
+ * exist today: `templates` (studio page renderers), `prompts`
8
+ * (reserved — no defaults yet), and `doctor` (runner rules).
9
+ *
10
+ * This module is the lookup contract. Each method takes a base name
11
+ * (no extension) and returns the absolute path of an override file if
12
+ * one exists, or `null` otherwise. Callers decide what to do with the
13
+ * returned path:
14
+ * - studio page renderers `await import(path)` and call the module's
15
+ * `default` export with the same arguments the built-in renderer
16
+ * expected;
17
+ * - the doctor runner discovers project rules via the directory
18
+ * listing path (see `doctor/runner.ts`); the resolver method here
19
+ * is for one-off basename-collision lookups.
20
+ *
21
+ * Discovery is sync and uses `existsSync` — there is no caching. The
22
+ * project root is captured at construction time, so the resolver is
23
+ * cheap to create per-request if needed.
24
+ *
25
+ * Filename convention: callers pass names without extension. The
26
+ * resolver checks for `.ts` files first (the documented contract); if
27
+ * that doesn't exist it returns `null`. We only support TypeScript
28
+ * source — operators are expected to author overrides as TS modules
29
+ * the plugin's runtime tsx loader can execute.
30
+ *
31
+ * Override module contract for `templates`:
32
+ * ```ts
33
+ * import type { StudioContext } from '@deskwork/studio/routes/api';
34
+ * export default function (ctx: StudioContext, ...args): string;
35
+ * ```
36
+ * The exact `args` shape mirrors whatever the built-in renderer
37
+ * expects (see each `pages/<name>.ts`). The override's return type
38
+ * must match too (`string` HTML for sync renderers, `Promise<string>`
39
+ * for async ones).
40
+ *
41
+ * Override module contract for `doctor`:
42
+ * ```ts
43
+ * import type { DoctorRule } from '@deskwork/core/doctor';
44
+ * const rule: DoctorRule = { ... };
45
+ * export default rule;
46
+ * ```
47
+ * The rule's `id` is what the runner uses for fix-by-id lookups; if
48
+ * the basename matches a built-in rule basename, the project rule
49
+ * wins (see `doctor/runner.ts` for the merge logic).
50
+ *
51
+ * `prompts` is reserved — no default sources exist yet, so the
52
+ * resolver just returns paths if they happen to be present. Future
53
+ * deskwork features will pin a default-source mapping for this
54
+ * category.
55
+ */
56
+ export type OverrideCategory = 'templates' | 'prompts' | 'doctor';
57
+ export interface OverrideResolver {
58
+ /** Look up a `templates/<name>.ts` override. */
59
+ template(name: string): string | null;
60
+ /** Look up a `prompts/<name>.ts` override. */
61
+ prompt(name: string): string | null;
62
+ /** Look up a `doctor/<name>.ts` override. */
63
+ doctorRule(name: string): string | null;
64
+ /**
65
+ * Absolute path of the `.deskwork/<category>` directory the resolver
66
+ * watches. Useful for callers that want to enumerate every override
67
+ * (the doctor runner does this to merge project rules with built-in
68
+ * rules). Returns the directory path even when it doesn't exist —
69
+ * the caller checks `existsSync` if it cares.
70
+ */
71
+ categoryDir(category: OverrideCategory): string;
72
+ }
73
+ /**
74
+ * Build a resolver scoped to `projectRoot`. The factory captures the
75
+ * project root once; subsequent lookups join `<projectRoot>/.deskwork/`
76
+ * with the category and basename.
77
+ *
78
+ * Sync by design — every studio request needs to consult the resolver
79
+ * before deciding which renderer to dispatch, and an `await` on the
80
+ * fast-path would cost a microtask hop on every request.
81
+ */
82
+ export declare function createOverrideResolver(projectRoot: string): OverrideResolver;
83
+ //# sourceMappingURL=overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAKH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,8CAA8C;IAC9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,6CAA6C;IAC7C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACxC;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAAC;CACjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAsB5E"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Override resolver — phase 23f.
3
+ *
4
+ * The customization layer lets operators drop a file into
5
+ * `<projectRoot>/.deskwork/<category>/<name>` and have the plugin pick
6
+ * it up at runtime instead of the built-in default. Three categories
7
+ * exist today: `templates` (studio page renderers), `prompts`
8
+ * (reserved — no defaults yet), and `doctor` (runner rules).
9
+ *
10
+ * This module is the lookup contract. Each method takes a base name
11
+ * (no extension) and returns the absolute path of an override file if
12
+ * one exists, or `null` otherwise. Callers decide what to do with the
13
+ * returned path:
14
+ * - studio page renderers `await import(path)` and call the module's
15
+ * `default` export with the same arguments the built-in renderer
16
+ * expected;
17
+ * - the doctor runner discovers project rules via the directory
18
+ * listing path (see `doctor/runner.ts`); the resolver method here
19
+ * is for one-off basename-collision lookups.
20
+ *
21
+ * Discovery is sync and uses `existsSync` — there is no caching. The
22
+ * project root is captured at construction time, so the resolver is
23
+ * cheap to create per-request if needed.
24
+ *
25
+ * Filename convention: callers pass names without extension. The
26
+ * resolver checks for `.ts` files first (the documented contract); if
27
+ * that doesn't exist it returns `null`. We only support TypeScript
28
+ * source — operators are expected to author overrides as TS modules
29
+ * the plugin's runtime tsx loader can execute.
30
+ *
31
+ * Override module contract for `templates`:
32
+ * ```ts
33
+ * import type { StudioContext } from '@deskwork/studio/routes/api';
34
+ * export default function (ctx: StudioContext, ...args): string;
35
+ * ```
36
+ * The exact `args` shape mirrors whatever the built-in renderer
37
+ * expects (see each `pages/<name>.ts`). The override's return type
38
+ * must match too (`string` HTML for sync renderers, `Promise<string>`
39
+ * for async ones).
40
+ *
41
+ * Override module contract for `doctor`:
42
+ * ```ts
43
+ * import type { DoctorRule } from '@deskwork/core/doctor';
44
+ * const rule: DoctorRule = { ... };
45
+ * export default rule;
46
+ * ```
47
+ * The rule's `id` is what the runner uses for fix-by-id lookups; if
48
+ * the basename matches a built-in rule basename, the project rule
49
+ * wins (see `doctor/runner.ts` for the merge logic).
50
+ *
51
+ * `prompts` is reserved — no default sources exist yet, so the
52
+ * resolver just returns paths if they happen to be present. Future
53
+ * deskwork features will pin a default-source mapping for this
54
+ * category.
55
+ */
56
+ import { existsSync } from 'node:fs';
57
+ import { join } from 'node:path';
58
+ /**
59
+ * Build a resolver scoped to `projectRoot`. The factory captures the
60
+ * project root once; subsequent lookups join `<projectRoot>/.deskwork/`
61
+ * with the category and basename.
62
+ *
63
+ * Sync by design — every studio request needs to consult the resolver
64
+ * before deciding which renderer to dispatch, and an `await` on the
65
+ * fast-path would cost a microtask hop on every request.
66
+ */
67
+ export function createOverrideResolver(projectRoot) {
68
+ const root = join(projectRoot, '.deskwork');
69
+ function lookup(category, name) {
70
+ const path = join(root, category, `${name}.ts`);
71
+ return existsSync(path) ? path : null;
72
+ }
73
+ return {
74
+ template(name) {
75
+ return lookup('templates', name);
76
+ },
77
+ prompt(name) {
78
+ return lookup('prompts', name);
79
+ },
80
+ doctorRule(name) {
81
+ return lookup('doctor', name);
82
+ },
83
+ categoryDir(category) {
84
+ return join(root, category);
85
+ },
86
+ };
87
+ }
88
+ //# sourceMappingURL=overrides.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.js","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAqBjC;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAE5C,SAAS,MAAM,CAAC,QAA0B,EAAE,IAAY;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;QAChD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAED,OAAO;QACL,QAAQ,CAAC,IAAY;YACnB,OAAO,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,CAAC,IAAY;YACjB,OAAO,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QACD,UAAU,CAAC,IAAY;YACrB,OAAO,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;QACD,WAAW,CAAC,QAA0B;YACpC,OAAO,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Path and site resolution against a DeskworkConfig.
3
+ *
4
+ * Every skill that touches disk goes through these helpers so that hardcoded
5
+ * paths — and assumptions about which sites exist — stay out of skill logic.
6
+ *
7
+ * Phase 19c — entry-to-file resolution precedence
8
+ * -----------------------------------------------
9
+ * For locating the markdown file backing a calendar entry, the canonical
10
+ * order is:
11
+ *
12
+ * 1. **Content index** — when an entry id is known, scan
13
+ * `<contentDir>/` for a markdown file whose frontmatter `id:`
14
+ * matches. This is refactor-proof: the binding moves with the file
15
+ * because the id lives inside the file.
16
+ * 2. **Slug-template fallback** — when the index has no record (entry
17
+ * not bound to frontmatter yet, e.g. pre-doctor state), fall back
18
+ * to the site's `blogFilenameTemplate` keyed by slug. This
19
+ * preserves audiocontrol-shaped flat-blog behavior unchanged.
20
+ *
21
+ * `findEntryFile` implements this precedence directly. `resolveBlogFilePath`
22
+ * remains the legacy slug-template-only entry point used by callers that
23
+ * don't have an entry id available (scaffold for new files, doctor's
24
+ * candidate-search by template, the legacy publish path). New code with
25
+ * access to a calendar entry should prefer `findEntryFile`.
26
+ */
27
+ import type { DeskworkConfig } from './config.ts';
28
+ import type { ContentIndex } from './content-index.ts';
29
+ import type { Platform } from './types.ts';
30
+ /**
31
+ * Resolve a user-supplied site argument to a configured site slug.
32
+ *
33
+ * An empty / null / undefined value falls back to `config.defaultSite`.
34
+ * An unknown value throws with the list of configured sites.
35
+ */
36
+ export declare function resolveSite(config: DeskworkConfig, site: string | null | undefined): string;
37
+ /** Absolute path to the site's editorial calendar file. */
38
+ export declare function resolveCalendarPath(projectRoot: string, config: DeskworkConfig, site?: string | null): string;
39
+ /** Absolute path to the site's channels file, or undefined when the site declares none. */
40
+ export declare function resolveChannelsPath(projectRoot: string, config: DeskworkConfig, site?: string | null): string | undefined;
41
+ /** Absolute path to the site's blog content directory. */
42
+ export declare function resolveContentDir(projectRoot: string, config: DeskworkConfig, site?: string | null): string;
43
+ /**
44
+ * Bare public hostname for the site (no protocol). Returns `undefined` for
45
+ * collections that aren't published as a website (no `host` configured).
46
+ * Callers needing a non-undefined value for display should fall back to the
47
+ * site slug; callers needing a real URL should throw if undefined.
48
+ */
49
+ export declare function resolveSiteHost(config: DeskworkConfig, site?: string | null): string | undefined;
50
+ /**
51
+ * Canonical public base URL for the site, with trailing slash. Throws when
52
+ * the collection has no `host` configured (i.e. is not published as a
53
+ * website) — callers that need a URL must guarantee the collection is a
54
+ * website-rendered one before calling.
55
+ */
56
+ export declare function resolveSiteBaseUrl(config: DeskworkConfig, site?: string | null): string;
57
+ /**
58
+ * Absolute path to the blog post markdown for a given slug.
59
+ *
60
+ * Resolution order (first match wins):
61
+ * 1. Explicit `filePath` argument — joined with the site's `contentDir`.
62
+ * Used by the scaffolder when an explicit layout (`index` /
63
+ * `readme` / `flat`) was requested.
64
+ * 2. The site's configured `blogFilenameTemplate` (default
65
+ * `{slug}/index.md`). Audiocontrol-shaped flat blogs hit this path.
66
+ *
67
+ * Slug-only API for callers that don't have an entry id available
68
+ * (scaffold for not-yet-existent files, doctor's candidate search,
69
+ * legacy publish/iterate paths). Callers that already hold a calendar
70
+ * entry should prefer `findEntryFile` — it consults the content index
71
+ * first, falling back to this template-driven path only when no
72
+ * frontmatter binding exists yet.
73
+ */
74
+ export declare function resolveBlogFilePath(projectRoot: string, config: DeskworkConfig, site: string | null | undefined, slug: string, filePath?: string): string;
75
+ /**
76
+ * Absolute path to the per-post directory for a slug —
77
+ * `<contentDir>/<slug>/`. Used by features that co-locate per-post
78
+ * artifacts (scrapbook, feature images) regardless of whether the
79
+ * blog markdown lives at `<slug>/index.md` or as a flat `<slug>.md`.
80
+ *
81
+ * The directory is not guaranteed to exist. Callers that need it
82
+ * created should `mkdirSync({ recursive: true })`.
83
+ */
84
+ export declare function resolveBlogPostDir(projectRoot: string, config: DeskworkConfig, site: string | null | undefined, slug: string): string;
85
+ /**
86
+ * Resolve a calendar entry's file location via the content index.
87
+ *
88
+ * Refactor-proof: the binding is whatever file currently has matching
89
+ * frontmatter `id:`. When the index has no record (entry's file hasn't
90
+ * been bound yet — pre-doctor state) AND `legacyEntryForFallback` is
91
+ * supplied, falls back to the site's `blogFilenameTemplate` to preserve
92
+ * legacy behavior. Without that fallback hint, returns `undefined` so
93
+ * callers can decide how to surface the missing binding.
94
+ *
95
+ * Note: the returned path is what the index says — its existence on
96
+ * disk is implied (the index only records files it walked). The
97
+ * template fallback path may NOT exist on disk; callers that need
98
+ * existence guarantees should `existsSync` the result.
99
+ *
100
+ * @param projectRoot Absolute path to the deskwork project root.
101
+ * @param config Loaded deskwork config.
102
+ * @param site Site slug (or null/undefined for the default site).
103
+ * @param entryId Calendar entry's stable UUID.
104
+ * @param index Pre-built content index. When omitted, this function
105
+ * builds one. The studio passes a per-request memoized
106
+ * index; the CLI typically lets it build per call.
107
+ * @param legacyEntryForFallback When supplied, allows the slug-template
108
+ * fallback for entries that haven't been bound to
109
+ * frontmatter yet. Pass `{slug}` to opt in.
110
+ * @returns absolute path, or undefined if neither index nor template resolves.
111
+ */
112
+ export declare function findEntryFile(projectRoot: string, config: DeskworkConfig, site: string | null | undefined, entryId: string, index?: ContentIndex, legacyEntryForFallback?: {
113
+ slug: string;
114
+ }): string | undefined;
115
+ /**
116
+ * Resolve the markdown file backing a calendar entry, preferring the
117
+ * UUID frontmatter binding (refactor-proof) and falling back to the
118
+ * site's slug-template only when no binding exists.
119
+ *
120
+ * Equivalent to the studio's `resolveLongformFilePath` but exposed as a
121
+ * top-level helper from `paths.ts` so CLI commands can use it without
122
+ * pulling in `review/` infrastructure. Always returns an absolute path
123
+ * (the slug-template fallback is unconditional); callers should
124
+ * `existsSync` if they need an existence guarantee.
125
+ *
126
+ * Precedence:
127
+ * 1. Content index — when `entryId` is supplied (and non-empty), look
128
+ * up the file whose frontmatter `deskwork.id:` matches. Refactor-
129
+ * proof: the binding follows the file regardless of slug rename or
130
+ * directory relocation.
131
+ * 2. Slug-template fallback — when the index has no record (entry's
132
+ * file isn't bound to frontmatter yet, e.g. pre-doctor / pre-ingest
133
+ * state) or no `entryId` was supplied, fall back to
134
+ * `resolveBlogFilePath(slug)`.
135
+ *
136
+ * @param projectRoot Absolute path to the deskwork project root.
137
+ * @param config Loaded deskwork config.
138
+ * @param site Site slug (or null/undefined for the default site).
139
+ * @param slug Calendar entry slug — used both as the legacy fallback
140
+ * template input and as a hint for the slug-template fallback.
141
+ * @param entryId Calendar entry's stable UUID. When omitted or empty,
142
+ * resolution falls straight through to the slug template.
143
+ * @param index Pre-built content index. When omitted, this function
144
+ * builds one. Pass the per-request memoized index when
145
+ * calling from the studio; let the CLI build per call.
146
+ */
147
+ export declare function resolveEntryFilePath(projectRoot: string, config: DeskworkConfig, site: string | null | undefined, slug: string, entryId?: string, index?: ContentIndex): string;
148
+ /**
149
+ * Resolve the markdown file path for a shortform draft.
150
+ *
151
+ * <contentDir>/<entry-dir>/scrapbook/shortform/<platform>[-<channel>].md
152
+ *
153
+ * Platform is the lowercase Platform value. Channel (if present) is appended
154
+ * as `-<channel>`. Channel must validate against the kebab-case regex —
155
+ * deskwork stores channels as kebab-case strings throughout.
156
+ *
157
+ * The entry directory is resolved through `findEntryFile` (id-driven,
158
+ * refactor-proof) with slug-template fallback for legacy entries created
159
+ * pre-doctor. The slug-template fallback is intentional migration logic so
160
+ * pre-bind entries keep working.
161
+ *
162
+ * Forward-compatibility: every reference to the shortform file location
163
+ * goes through this function. Phase 20 (sandbox migration) redirects this
164
+ * single function; everything downstream (handlers, CLI, studio) works
165
+ * unchanged.
166
+ *
167
+ * @param projectRoot Absolute path to the deskwork project root.
168
+ * @param config Loaded deskwork config.
169
+ * @param site Site slug (or null/undefined for the default site).
170
+ * @param entry Calendar entry — `id` preferred, `slug` used both as the
171
+ * legacy fallback and to identify the entry directory.
172
+ * @param platform Which distribution platform.
173
+ * @param channel Optional sub-channel (e.g. `synthdiy` for r/synthdiy).
174
+ * Must be kebab-case.
175
+ * @param index Optional pre-built content index (per-request memoization).
176
+ * @returns absolute file path, or undefined when neither the index nor the
177
+ * slug-template fallback resolves the entry's directory.
178
+ */
179
+ export declare function resolveShortformFilePath(projectRoot: string, config: DeskworkConfig, site: string, entry: {
180
+ id?: string;
181
+ slug: string;
182
+ }, platform: Platform, channel?: string, index?: ContentIndex): string | undefined;
183
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}