@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,154 @@
1
+ /**
2
+ * Rule: orphan-frontmatter-id.
3
+ *
4
+ * Audit: every entry in the content index whose id has no matching
5
+ * calendar entry. The file is bound to "something", but the calendar
6
+ * doesn't know about it.
7
+ *
8
+ * Repair: there are three plausible operator intents — (a) add a
9
+ * calendar row for the file, (b) clear the orphan id from the file
10
+ * (un-bind), or (c) leave it alone. Without a way to gather the
11
+ * intent, the rule reports findings and presents a prompt; with
12
+ * `--yes`, the safest action is "do nothing" — auto-creating
13
+ * calendar rows or auto-deleting frontmatter is destructive.
14
+ */
15
+ import { readFileSync, writeFileSync } from 'node:fs';
16
+ import { relative } from 'node:path';
17
+ import { parseFrontmatter, removeFrontmatterPaths } from "../../frontmatter.js";
18
+ const RULE_ID = 'orphan-frontmatter-id';
19
+ /**
20
+ * Clear the `deskwork.id` field from a markdown file's frontmatter.
21
+ * Returns true when the field was present and cleared; false when there
22
+ * was nothing to clear. Issue #38: scoped to the namespaced key — top-
23
+ * level `id:` belongs to the operator and is left alone.
24
+ *
25
+ * Uses the round-trip-preserving emitter so untouched keys keep their
26
+ * exact bytes (quoting, comments, ordering). Empty `deskwork:` blocks
27
+ * are pruned via `removeFrontmatterPaths`.
28
+ */
29
+ function clearFrontmatterId(absPath) {
30
+ const raw = readFileSync(absPath, 'utf-8');
31
+ const { data } = parseFrontmatter(raw);
32
+ const block = data.deskwork;
33
+ if (block === undefined || block === null)
34
+ return false;
35
+ if (typeof block !== 'object' || Array.isArray(block))
36
+ return false;
37
+ const blockObj = block;
38
+ if (!('id' in blockObj))
39
+ return false;
40
+ const updated = removeFrontmatterPaths(raw, [['deskwork', 'id']]);
41
+ if (updated === raw)
42
+ return false;
43
+ writeFileSync(absPath, updated, 'utf-8');
44
+ return true;
45
+ }
46
+ const rule = {
47
+ id: RULE_ID,
48
+ label: 'Files with frontmatter ids that are not in the calendar',
49
+ async audit(ctx) {
50
+ const findings = [];
51
+ const calendarIds = new Set();
52
+ for (const e of ctx.calendar.entries) {
53
+ if (e.id)
54
+ calendarIds.add(e.id);
55
+ }
56
+ for (const [id, absPath] of ctx.index.byId) {
57
+ if (calendarIds.has(id))
58
+ continue;
59
+ findings.push({
60
+ ruleId: RULE_ID,
61
+ site: ctx.site,
62
+ severity: 'warning',
63
+ message: `File ${relative(ctx.projectRoot, absPath)} carries id ${id}, which is not in the calendar`,
64
+ details: { absolutePath: absPath, entryId: id },
65
+ });
66
+ }
67
+ return findings;
68
+ },
69
+ async plan(_ctx, finding) {
70
+ const absPath = String(finding.details.absolutePath ?? '');
71
+ const entryId = String(finding.details.entryId ?? '');
72
+ return {
73
+ kind: 'prompt',
74
+ finding,
75
+ question: `File ${absPath} has id ${entryId} but no calendar entry matches. Pick an action:`,
76
+ choices: [
77
+ {
78
+ id: 'none',
79
+ label: 'leave as-is (default; review manually)',
80
+ payload: { action: 'none' },
81
+ },
82
+ {
83
+ id: 'clear-id',
84
+ label: `clear the id from ${absPath} (un-bind the file)`,
85
+ payload: { action: 'clear-id', absolutePath: absPath },
86
+ },
87
+ ],
88
+ };
89
+ },
90
+ async apply(ctx, plan) {
91
+ if (plan.kind !== 'apply') {
92
+ return {
93
+ finding: plan.finding,
94
+ applied: false,
95
+ message: 'plan is not directly appliable; runner should resolve prompt first',
96
+ skipReason: 'apply-failed',
97
+ };
98
+ }
99
+ const action = String(plan.payload.action ?? '');
100
+ if (action === 'none') {
101
+ return {
102
+ finding: plan.finding,
103
+ applied: false,
104
+ message: 'left file unchanged per operator choice',
105
+ skipReason: 'no-action-needed',
106
+ };
107
+ }
108
+ if (action === 'clear-id') {
109
+ const absPath = String(plan.payload.absolutePath ?? '');
110
+ if (!absPath) {
111
+ return {
112
+ finding: plan.finding,
113
+ applied: false,
114
+ message: 'clear-id apply payload missing absolutePath',
115
+ skipReason: 'apply-failed',
116
+ };
117
+ }
118
+ try {
119
+ const changed = clearFrontmatterId(absPath);
120
+ if (!changed) {
121
+ return {
122
+ finding: plan.finding,
123
+ applied: false,
124
+ message: `no id field in ${relative(ctx.projectRoot, absPath)} to clear`,
125
+ skipReason: 'no-action-needed',
126
+ };
127
+ }
128
+ }
129
+ catch (err) {
130
+ const reason = err instanceof Error ? err.message : String(err);
131
+ return {
132
+ finding: plan.finding,
133
+ applied: false,
134
+ message: `failed to clear frontmatter id: ${reason}`,
135
+ skipReason: 'apply-failed',
136
+ };
137
+ }
138
+ return {
139
+ finding: plan.finding,
140
+ applied: true,
141
+ message: `cleared id from ${relative(ctx.projectRoot, absPath)}`,
142
+ details: { absolutePath: absPath },
143
+ };
144
+ }
145
+ return {
146
+ finding: plan.finding,
147
+ applied: false,
148
+ message: `unknown apply action: ${action}`,
149
+ skipReason: 'apply-failed',
150
+ };
151
+ },
152
+ };
153
+ export default rule;
154
+ //# sourceMappingURL=orphan-frontmatter-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orphan-frontmatter-id.js","sourceRoot":"","sources":["../../../src/doctor/rules/orphan-frontmatter-id.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Rule: schema-rejected.
3
+ *
4
+ * The host's content collection schema may reject the `id` frontmatter
5
+ * field that deskwork relies on for the calendar/file binding. We
6
+ * detect this at *write time* in other code paths (scaffolder, the
7
+ * other rules' apply()), not by an active probe — running an actual
8
+ * Astro build inside doctor would be slow and project-specific.
9
+ *
10
+ * This rule's audit always returns empty for that reason. The
11
+ * `printSchemaPatchInstructions` helper is the user-facing surface;
12
+ * other rules (and the CLI command) call it when they observe an
13
+ * actual schema rejection. Phase 19b-followup may add an active
14
+ * probe (write a tmpfile to contentDir, attempt astro check, etc.)
15
+ * once the integration cost is justified.
16
+ */
17
+ import type { DoctorRule } from '../types.ts';
18
+ declare const rule: DoctorRule;
19
+ export default rule;
20
+ //# sourceMappingURL=schema-rejected.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rejected.d.ts","sourceRoot":"","sources":["../../../src/doctor/rules/schema-rejected.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAEV,UAAU,EAIX,MAAM,aAAa,CAAC;AAIrB,QAAA,MAAM,IAAI,EAAE,UA2BX,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Rule: schema-rejected.
3
+ *
4
+ * The host's content collection schema may reject the `id` frontmatter
5
+ * field that deskwork relies on for the calendar/file binding. We
6
+ * detect this at *write time* in other code paths (scaffolder, the
7
+ * other rules' apply()), not by an active probe — running an actual
8
+ * Astro build inside doctor would be slow and project-specific.
9
+ *
10
+ * This rule's audit always returns empty for that reason. The
11
+ * `printSchemaPatchInstructions` helper is the user-facing surface;
12
+ * other rules (and the CLI command) call it when they observe an
13
+ * actual schema rejection. Phase 19b-followup may add an active
14
+ * probe (write a tmpfile to contentDir, attempt astro check, etc.)
15
+ * once the integration cost is justified.
16
+ */
17
+ import { printSchemaPatchInstructions } from "../schema-patch.js";
18
+ const RULE_ID = 'schema-rejected';
19
+ const rule = {
20
+ id: RULE_ID,
21
+ label: "Host's content schema rejects the `id` frontmatter field",
22
+ async audit(_ctx) {
23
+ // Passive — see file header. Other code paths surface schema-patch
24
+ // instructions when they observe an actual rejection.
25
+ return [];
26
+ },
27
+ async plan(_ctx, finding) {
28
+ return {
29
+ kind: 'report-only',
30
+ finding,
31
+ reason: printSchemaPatchInstructions(),
32
+ };
33
+ },
34
+ async apply(_ctx, plan) {
35
+ return {
36
+ finding: plan.finding,
37
+ applied: false,
38
+ message: 'schema-rejected has no automatic repair — operator must patch the host content schema',
39
+ skipReason: 'schema-rejected',
40
+ };
41
+ },
42
+ };
43
+ export default rule;
44
+ //# sourceMappingURL=schema-rejected.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-rejected.js","sourceRoot":"","sources":["../../../src/doctor/rules/schema-rejected.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AASlE,MAAM,OAAO,GAAG,iBAAiB,CAAC;AAElC,MAAM,IAAI,GAAe;IACvB,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,0DAA0D;IAEjE,KAAK,CAAC,KAAK,CAAC,IAAmB;QAC7B,mEAAmE;QACnE,sDAAsD;QACtD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAmB,EAAE,OAAgB;QAC9C,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,OAAO;YACP,MAAM,EAAE,4BAA4B,EAAE;SACvC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAmB,EAAE,IAAgB;QAC/C,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,KAAK;YACd,OAAO,EACL,uFAAuF;YACzF,UAAU,EAAE,iBAAiB;SAC9B,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Rule: slug-collision.
3
+ *
4
+ * Audit: two or more calendar entries share the same slug. With UUID
5
+ * identity this is no longer a hard error (joins go through id), but
6
+ * it's still a bug — the host renderer maps URLs by slug, so two
7
+ * entries claiming the same slug produces duplicate or hidden public
8
+ * URLs.
9
+ *
10
+ * Repair: rename one slug. Doctor doesn't pick which one — that's an
11
+ * editorial decision (which entry "owns" the public URL). The rule
12
+ * reports findings; with no interactive UI it returns `report-only`.
13
+ * `--yes` mode skips.
14
+ */
15
+ import type { DoctorRule } from '../types.ts';
16
+ declare const rule: DoctorRule;
17
+ export default rule;
18
+ //# sourceMappingURL=slug-collision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-collision.d.ts","sourceRoot":"","sources":["../../../src/doctor/rules/slug-collision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAEV,UAAU,EAIX,MAAM,aAAa,CAAC;AAyBrB,QAAA,MAAM,IAAI,EAAE,UAiCX,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Rule: slug-collision.
3
+ *
4
+ * Audit: two or more calendar entries share the same slug. With UUID
5
+ * identity this is no longer a hard error (joins go through id), but
6
+ * it's still a bug — the host renderer maps URLs by slug, so two
7
+ * entries claiming the same slug produces duplicate or hidden public
8
+ * URLs.
9
+ *
10
+ * Repair: rename one slug. Doctor doesn't pick which one — that's an
11
+ * editorial decision (which entry "owns" the public URL). The rule
12
+ * reports findings; with no interactive UI it returns `report-only`.
13
+ * `--yes` mode skips.
14
+ */
15
+ const RULE_ID = 'slug-collision';
16
+ function findCollisions(ctx) {
17
+ const bySlug = new Map();
18
+ for (const e of ctx.calendar.entries) {
19
+ if (!e.slug)
20
+ continue;
21
+ const list = bySlug.get(e.slug);
22
+ if (list)
23
+ list.push(e.id ?? '');
24
+ else
25
+ bySlug.set(e.slug, [e.id ?? '']);
26
+ }
27
+ const out = [];
28
+ for (const [slug, ids] of bySlug) {
29
+ if (ids.length > 1)
30
+ out.push({ slug, entryIds: ids });
31
+ }
32
+ return out;
33
+ }
34
+ const rule = {
35
+ id: RULE_ID,
36
+ label: 'Duplicate slugs in the calendar',
37
+ async audit(ctx) {
38
+ return findCollisions(ctx).map((g) => ({
39
+ ruleId: RULE_ID,
40
+ site: ctx.site,
41
+ severity: 'error',
42
+ message: `slug "${g.slug}" is shared by ${g.entryIds.length} calendar entries`,
43
+ details: { slug: g.slug, entryIds: g.entryIds },
44
+ }));
45
+ },
46
+ async plan(_ctx, finding) {
47
+ return {
48
+ kind: 'report-only',
49
+ finding,
50
+ reason: 'pick which entry owns the slug and rename the others via `deskwork rename-slug` ' +
51
+ '(or hand-edit the calendar). Doctor refuses to choose automatically — slug is ' +
52
+ 'host-public-URL, an editorial decision.',
53
+ };
54
+ },
55
+ async apply(_ctx, plan) {
56
+ return {
57
+ finding: plan.finding,
58
+ applied: false,
59
+ message: 'slug-collision has no automatic repair (operator must rename)',
60
+ skipReason: 'editorial-decision',
61
+ };
62
+ },
63
+ };
64
+ export default rule;
65
+ //# sourceMappingURL=slug-collision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slug-collision.js","sourceRoot":"","sources":["../../../src/doctor/rules/slug-collision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,MAAM,OAAO,GAAG,gBAAgB,CAAC;AAQjC,SAAS,cAAc,CAAC,GAAkB;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,CAAC,CAAC,IAAI;YAAE,SAAS;QACtB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;;YAC3B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAe;IACvB,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,iCAAiC;IAExC,KAAK,CAAC,KAAK,CAAC,GAAkB;QAC5B,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrC,MAAM,EAAE,OAAO;YACf,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,QAAQ,CAAC,MAAM,mBAAmB;YAC9E,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE;SAChD,CAAC,CAAC,CAAC;IACN,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAmB,EAAE,OAAgB;QAC9C,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,OAAO;YACP,MAAM,EACJ,kFAAkF;gBAClF,gFAAgF;gBAChF,yCAAyC;SAC5C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,IAAmB,EAAE,IAAgB;QAC/C,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,+DAA+D;YACxE,UAAU,EAAE,oBAAoB;SACjC,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Rule: workflow-stale.
3
+ *
4
+ * Audit: a `DraftWorkflowItem` whose `(site, slug)` no longer resolves
5
+ * to a calendar entry on the workflow's site. Two failure modes:
6
+ * - The entry was deleted from the calendar (truly stale).
7
+ * - The entry's slug was renamed and the workflow predates the rename.
8
+ *
9
+ * Detecting the slug-rename case requires `entryId` on the workflow —
10
+ * not yet present on legacy records. For now the rule reports only
11
+ * "no entry found by site+slug" findings and ignores the rename case.
12
+ *
13
+ * Repair: clear the stale workflow record from the pipeline journal.
14
+ * The history journal is append-only and stays untouched (provenance).
15
+ * `--yes` applies; interactive prompts before deletion.
16
+ */
17
+ import type { DoctorRule } from '../types.ts';
18
+ declare const rule: DoctorRule;
19
+ export default rule;
20
+ //# sourceMappingURL=workflow-stale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-stale.d.ts","sourceRoot":"","sources":["../../../src/doctor/rules/workflow-stale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,EAEV,UAAU,EAIX,MAAM,aAAa,CAAC;AA6CrB,QAAA,MAAM,IAAI,EAAE,UA+EX,CAAC;AAEF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Rule: workflow-stale.
3
+ *
4
+ * Audit: a `DraftWorkflowItem` whose `(site, slug)` no longer resolves
5
+ * to a calendar entry on the workflow's site. Two failure modes:
6
+ * - The entry was deleted from the calendar (truly stale).
7
+ * - The entry's slug was renamed and the workflow predates the rename.
8
+ *
9
+ * Detecting the slug-rename case requires `entryId` on the workflow —
10
+ * not yet present on legacy records. For now the rule reports only
11
+ * "no entry found by site+slug" findings and ignores the rename case.
12
+ *
13
+ * Repair: clear the stale workflow record from the pipeline journal.
14
+ * The history journal is append-only and stays untouched (provenance).
15
+ * `--yes` applies; interactive prompts before deletion.
16
+ */
17
+ import { unlinkSync, readdirSync } from 'node:fs';
18
+ import { join } from 'node:path';
19
+ import { pipelinePath } from "../../review/pipeline.js";
20
+ const RULE_ID = 'workflow-stale';
21
+ function isStale(workflow, ctx) {
22
+ if (workflow.site !== ctx.site)
23
+ return false;
24
+ if (workflow.state === 'applied' || workflow.state === 'cancelled') {
25
+ return false;
26
+ }
27
+ // Prefer entryId match when both sides have it. If the workflow has
28
+ // an entryId and the calendar doesn't carry that id, the workflow is
29
+ // stale regardless of slug. If it doesn't, fall back to slug match.
30
+ if (workflow.entryId) {
31
+ return !ctx.calendar.entries.some((e) => e.id === workflow.entryId);
32
+ }
33
+ return !ctx.calendar.entries.some((e) => e.slug === workflow.slug);
34
+ }
35
+ /**
36
+ * Find the pipeline-journal file backing a workflow id. Files are
37
+ * named `<normalizedTimestamp>-<id>.json`; we suffix-match on
38
+ * `-<id>.json`.
39
+ */
40
+ function findWorkflowFile(projectRoot, config, workflowId) {
41
+ const dir = pipelinePath(projectRoot, config);
42
+ let names;
43
+ try {
44
+ names = readdirSync(dir);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ const suffix = `-${workflowId}.json`;
50
+ for (const name of names) {
51
+ if (name.endsWith(suffix))
52
+ return join(dir, name);
53
+ }
54
+ return null;
55
+ }
56
+ const rule = {
57
+ id: RULE_ID,
58
+ label: 'Workflow records that no longer match a calendar entry',
59
+ async audit(ctx) {
60
+ const findings = [];
61
+ for (const w of ctx.workflows) {
62
+ if (!isStale(w, ctx))
63
+ continue;
64
+ findings.push({
65
+ ruleId: RULE_ID,
66
+ site: ctx.site,
67
+ severity: 'warning',
68
+ message: `Workflow ${w.id} (slug "${w.slug}", state ${w.state}) has no matching calendar entry`,
69
+ details: {
70
+ workflowId: w.id,
71
+ slug: w.slug,
72
+ state: w.state,
73
+ entryId: w.entryId ?? null,
74
+ },
75
+ });
76
+ }
77
+ return findings;
78
+ },
79
+ async plan(_ctx, finding) {
80
+ const workflowId = String(finding.details.workflowId ?? '');
81
+ return {
82
+ kind: 'apply',
83
+ finding,
84
+ summary: `delete pipeline journal entry for workflow ${workflowId} (history journal preserved)`,
85
+ payload: { workflowId },
86
+ };
87
+ },
88
+ async apply(ctx, plan) {
89
+ if (plan.kind !== 'apply') {
90
+ return {
91
+ finding: plan.finding,
92
+ applied: false,
93
+ message: 'plan is not directly appliable; runner should resolve prompt first',
94
+ skipReason: 'apply-failed',
95
+ };
96
+ }
97
+ const workflowId = String(plan.payload.workflowId ?? '');
98
+ if (!workflowId) {
99
+ return {
100
+ finding: plan.finding,
101
+ applied: false,
102
+ message: 'apply payload missing workflowId',
103
+ skipReason: 'apply-failed',
104
+ };
105
+ }
106
+ const file = findWorkflowFile(ctx.projectRoot, ctx.config, workflowId);
107
+ if (!file) {
108
+ return {
109
+ finding: plan.finding,
110
+ applied: false,
111
+ message: `no pipeline file found for workflow ${workflowId}`,
112
+ skipReason: 'apply-failed',
113
+ };
114
+ }
115
+ try {
116
+ unlinkSync(file);
117
+ }
118
+ catch (err) {
119
+ const reason = err instanceof Error ? err.message : String(err);
120
+ return {
121
+ finding: plan.finding,
122
+ applied: false,
123
+ message: `failed to delete ${file}: ${reason}`,
124
+ skipReason: 'apply-failed',
125
+ };
126
+ }
127
+ return {
128
+ finding: plan.finding,
129
+ applied: true,
130
+ message: `deleted pipeline entry for workflow ${workflowId}`,
131
+ details: { file, workflowId },
132
+ };
133
+ },
134
+ };
135
+ export default rule;
136
+ //# sourceMappingURL=workflow-stale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-stale.js","sourceRoot":"","sources":["../../../src/doctor/rules/workflow-stale.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Doctor runner — orchestrates rule execution.
3
+ *
4
+ * The runner owns the per-site setup (read calendar, build content
5
+ * index, scope workflows) and walks the registered rules in a stable
6
+ * order. `runAudit` only calls `audit()`; `runRepair` chains audit →
7
+ * plan → (interaction) → apply.
8
+ *
9
+ * Sibling-relative imports per the project convention.
10
+ */
11
+ import type { DeskworkConfig } from '../config.ts';
12
+ import type { DoctorInteraction, DoctorReport, DoctorRule } from './types.ts';
13
+ /**
14
+ * Registry of all rules in the order they run. The order matters: we
15
+ * detect calendar-uuid-missing first (to flush UUIDs), then run the
16
+ * frontmatter-id rules (which depend on UUIDs being persisted on
17
+ * disk to be useful in long-lived data).
18
+ *
19
+ * `legacy-top-level-id-migration` (Issue #38) runs BEFORE
20
+ * `missing-frontmatter-id` so that v0.7.0/v0.7.1-shaped files migrate
21
+ * to the namespaced form first; on the same run, the
22
+ * missing-frontmatter-id rule then sees the migrated files as bound
23
+ * (via `deskwork.id`) and doesn't re-report them.
24
+ */
25
+ export declare const RULES: ReadonlyArray<DoctorRule>;
26
+ /**
27
+ * Resolve a CSV/comma-separated `--fix=` argument to rule ids.
28
+ *
29
+ * Returns the full list of built-in rule ids for `''` and `'all'`.
30
+ * Unknown built-in id strings are rejected (exit 2 in the CLI).
31
+ *
32
+ * Project rules registered via `<projectRoot>/.deskwork/doctor/*.ts`
33
+ * (Phase 23f) are selected by passing `'all'`; the runner picks them
34
+ * up from the merged rule list. Selecting an individual project rule
35
+ * by id via `--fix=<id>` is not yet supported — file an issue if the
36
+ * usage emerges.
37
+ */
38
+ export declare function parseFixArgument(arg: string): string[];
39
+ export interface DoctorRunOptions {
40
+ projectRoot: string;
41
+ config: DeskworkConfig;
42
+ /** Restrict to one site; undefined = run for every site in config. */
43
+ site?: string;
44
+ /** Restrict the rule set; undefined = all rules. */
45
+ ruleIds?: string[];
46
+ }
47
+ /**
48
+ * Audit: collect findings without mutating the world. Returns a fully-
49
+ * built report with empty `repairs`. Suitable for pre-commit hooks
50
+ * that just want a non-zero exit code on any finding.
51
+ */
52
+ export declare function runAudit(opts: DoctorRunOptions, interaction: DoctorInteraction): Promise<DoctorReport>;
53
+ /**
54
+ * Repair: run audit → plan → (consult interaction) → apply. Returns
55
+ * the audit findings AND the repair results so callers can render
56
+ * a single report covering both phases.
57
+ *
58
+ * For `prompt` plans the runner consults `interaction.pickChoice` to
59
+ * resolve to an apply payload; for `apply` plans it consults
60
+ * `interaction.confirmApply`. `report-only` plans never apply.
61
+ */
62
+ export declare function runRepair(opts: DoctorRunOptions, interaction: DoctorInteraction): Promise<DoctorReport>;
63
+ /**
64
+ * Pre-built interaction: always confirm `apply` plans, skip `prompt`
65
+ * plans (no way to choose without a UI). Used by `--yes` mode.
66
+ *
67
+ * Exposed for the CLI command to construct.
68
+ */
69
+ export declare const yesInteraction: DoctorInteraction;
70
+ /**
71
+ * Pre-built interaction: never apply anything. Used to dry-run a
72
+ * repair pipeline (prompt resolution + apply both no-op).
73
+ */
74
+ export declare const declineInteraction: DoctorInteraction;
75
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/doctor/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAUnD,OAAO,KAAK,EAEV,iBAAiB,EACjB,YAAY,EACZ,UAAU,EAIX,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,EAAE,aAAa,CAAC,UAAU,CAS3C,CAAC;AAMF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBtD;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAwED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC,YAAY,CAAC,CAavB;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,gBAAgB,EACtB,WAAW,EAAE,iBAAiB,GAC7B,OAAO,CAAC,YAAY,CAAC,CAoBvB;AAmGD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,iBAU5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,iBAOhC,CAAC"}