@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.
- package/dist/body-state.d.ts +27 -0
- package/dist/body-state.d.ts.map +1 -0
- package/dist/body-state.js +62 -0
- package/dist/body-state.js.map +1 -0
- package/dist/calendar-mutations.d.ts +124 -0
- package/dist/calendar-mutations.d.ts.map +1 -0
- package/dist/calendar-mutations.js +305 -0
- package/dist/calendar-mutations.js.map +1 -0
- package/dist/calendar.d.ts +54 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +430 -0
- package/dist/calendar.js.map +1 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +72 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +91 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +216 -0
- package/dist/config.js.map +1 -0
- package/dist/content-index.d.ts +74 -0
- package/dist/content-index.d.ts.map +1 -0
- package/dist/content-index.js +205 -0
- package/dist/content-index.js.map +1 -0
- package/dist/content-tree-fs-walk.d.ts +54 -0
- package/dist/content-tree-fs-walk.d.ts.map +1 -0
- package/dist/content-tree-fs-walk.js +112 -0
- package/dist/content-tree-fs-walk.js.map +1 -0
- package/dist/content-tree-helpers.d.ts +52 -0
- package/dist/content-tree-helpers.d.ts.map +1 -0
- package/dist/content-tree-helpers.js +116 -0
- package/dist/content-tree-helpers.js.map +1 -0
- package/dist/content-tree-types.d.ts +175 -0
- package/dist/content-tree-types.d.ts.map +1 -0
- package/dist/content-tree-types.js +10 -0
- package/dist/content-tree-types.js.map +1 -0
- package/dist/content-tree.d.ts +93 -0
- package/dist/content-tree.d.ts.map +1 -0
- package/dist/content-tree.js +385 -0
- package/dist/content-tree.js.map +1 -0
- package/dist/doctor/index.d.ts +11 -0
- package/dist/doctor/index.d.ts.map +1 -0
- package/dist/doctor/index.js +10 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/project-rules.d.ts +59 -0
- package/dist/doctor/project-rules.d.ts.map +1 -0
- package/dist/doctor/project-rules.js +143 -0
- package/dist/doctor/project-rules.js.map +1 -0
- package/dist/doctor/rules/calendar-uuid-missing.d.ts +19 -0
- package/dist/doctor/rules/calendar-uuid-missing.d.ts.map +1 -0
- package/dist/doctor/rules/calendar-uuid-missing.js +176 -0
- package/dist/doctor/rules/calendar-uuid-missing.js.map +1 -0
- package/dist/doctor/rules/duplicate-id.d.ts +27 -0
- package/dist/doctor/rules/duplicate-id.d.ts.map +1 -0
- package/dist/doctor/rules/duplicate-id.js +157 -0
- package/dist/doctor/rules/duplicate-id.js.map +1 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.d.ts +40 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.js +232 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -0
- package/dist/doctor/rules/missing-frontmatter-id.d.ts +45 -0
- package/dist/doctor/rules/missing-frontmatter-id.d.ts.map +1 -0
- package/dist/doctor/rules/missing-frontmatter-id.js +283 -0
- package/dist/doctor/rules/missing-frontmatter-id.js.map +1 -0
- package/dist/doctor/rules/orphan-frontmatter-id.d.ts +18 -0
- package/dist/doctor/rules/orphan-frontmatter-id.d.ts.map +1 -0
- package/dist/doctor/rules/orphan-frontmatter-id.js +154 -0
- package/dist/doctor/rules/orphan-frontmatter-id.js.map +1 -0
- package/dist/doctor/rules/schema-rejected.d.ts +20 -0
- package/dist/doctor/rules/schema-rejected.d.ts.map +1 -0
- package/dist/doctor/rules/schema-rejected.js +44 -0
- package/dist/doctor/rules/schema-rejected.js.map +1 -0
- package/dist/doctor/rules/slug-collision.d.ts +18 -0
- package/dist/doctor/rules/slug-collision.d.ts.map +1 -0
- package/dist/doctor/rules/slug-collision.js +65 -0
- package/dist/doctor/rules/slug-collision.js.map +1 -0
- package/dist/doctor/rules/workflow-stale.d.ts +20 -0
- package/dist/doctor/rules/workflow-stale.d.ts.map +1 -0
- package/dist/doctor/rules/workflow-stale.js +136 -0
- package/dist/doctor/rules/workflow-stale.js.map +1 -0
- package/dist/doctor/runner.d.ts +75 -0
- package/dist/doctor/runner.d.ts.map +1 -0
- package/dist/doctor/runner.js +289 -0
- package/dist/doctor/runner.js.map +1 -0
- package/dist/doctor/schema-patch.d.ts +21 -0
- package/dist/doctor/schema-patch.d.ts.map +1 -0
- package/dist/doctor/schema-patch.js +92 -0
- package/dist/doctor/schema-patch.js.map +1 -0
- package/dist/doctor/types.d.ts +185 -0
- package/dist/doctor/types.d.ts.map +1 -0
- package/dist/doctor/types.js +13 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/frontmatter.d.ts +103 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +306 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest-derive.d.ts +79 -0
- package/dist/ingest-derive.d.ts.map +1 -0
- package/dist/ingest-derive.js +299 -0
- package/dist/ingest-derive.js.map +1 -0
- package/dist/ingest-paths.d.ts +37 -0
- package/dist/ingest-paths.d.ts.map +1 -0
- package/dist/ingest-paths.js +176 -0
- package/dist/ingest-paths.js.map +1 -0
- package/dist/ingest.d.ts +162 -0
- package/dist/ingest.d.ts.map +1 -0
- package/dist/ingest.js +269 -0
- package/dist/ingest.js.map +1 -0
- package/dist/journal.d.ts +49 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +113 -0
- package/dist/journal.js.map +1 -0
- package/dist/outline-split.d.ts +38 -0
- package/dist/outline-split.d.ts.map +1 -0
- package/dist/outline-split.js +84 -0
- package/dist/outline-split.js.map +1 -0
- package/dist/overrides.d.ts +83 -0
- package/dist/overrides.d.ts.map +1 -0
- package/dist/overrides.js +88 -0
- package/dist/overrides.js.map +1 -0
- package/dist/paths.d.ts +183 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +266 -0
- package/dist/paths.js.map +1 -0
- package/dist/remark-image-figure.mjs +77 -0
- package/dist/remark-strip-first-h1.mjs +26 -0
- package/dist/remark-strip-outline.mjs +44 -0
- package/dist/rename-slug.d.ts +49 -0
- package/dist/rename-slug.d.ts.map +1 -0
- package/dist/rename-slug.js +161 -0
- package/dist/rename-slug.js.map +1 -0
- package/dist/review/handlers.d.ts +55 -0
- package/dist/review/handlers.d.ts.map +1 -0
- package/dist/review/handlers.js +307 -0
- package/dist/review/handlers.js.map +1 -0
- package/dist/review/index.d.ts +14 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +13 -0
- package/dist/review/index.js.map +1 -0
- package/dist/review/journal-mappers.d.ts +35 -0
- package/dist/review/journal-mappers.d.ts.map +1 -0
- package/dist/review/journal-mappers.js +48 -0
- package/dist/review/journal-mappers.js.map +1 -0
- package/dist/review/pipeline.d.ts +79 -0
- package/dist/review/pipeline.d.ts.map +1 -0
- package/dist/review/pipeline.js +234 -0
- package/dist/review/pipeline.js.map +1 -0
- package/dist/review/render.d.ts +27 -0
- package/dist/review/render.d.ts.map +1 -0
- package/dist/review/render.js +42 -0
- package/dist/review/render.js.map +1 -0
- package/dist/review/report.d.ts +50 -0
- package/dist/review/report.d.ts.map +1 -0
- package/dist/review/report.js +164 -0
- package/dist/review/report.js.map +1 -0
- package/dist/review/result.d.ts +12 -0
- package/dist/review/result.d.ts.map +1 -0
- package/dist/review/result.js +12 -0
- package/dist/review/result.js.map +1 -0
- package/dist/review/start-handlers.d.ts +62 -0
- package/dist/review/start-handlers.d.ts.map +1 -0
- package/dist/review/start-handlers.js +223 -0
- package/dist/review/start-handlers.js.map +1 -0
- package/dist/review/types.d.ts +169 -0
- package/dist/review/types.d.ts.map +1 -0
- package/dist/review/types.js +26 -0
- package/dist/review/types.js.map +1 -0
- package/dist/review/workflow-paths.d.ts +68 -0
- package/dist/review/workflow-paths.d.ts.map +1 -0
- package/dist/review/workflow-paths.js +112 -0
- package/dist/review/workflow-paths.js.map +1 -0
- package/dist/scaffold.d.ts +67 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +122 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/scrapbook.d.ts +229 -0
- package/dist/scrapbook.d.ts.map +1 -0
- package/dist/scrapbook.js +500 -0
- package/dist/scrapbook.js.map +1 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +120 -0
- package/dist/types.js.map +1 -0
- package/package.json +160 -0
|
@@ -0,0 +1,289 @@
|
|
|
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 { readCalendar } from "../calendar.js";
|
|
12
|
+
import { buildContentIndex } from "../content-index.js";
|
|
13
|
+
import { resolveCalendarPath } from "../paths.js";
|
|
14
|
+
import { readWorkflows } from "../review/pipeline.js";
|
|
15
|
+
import missingFrontmatterId from "./rules/missing-frontmatter-id.js";
|
|
16
|
+
import orphanFrontmatterId from "./rules/orphan-frontmatter-id.js";
|
|
17
|
+
import duplicateId from "./rules/duplicate-id.js";
|
|
18
|
+
import slugCollision from "./rules/slug-collision.js";
|
|
19
|
+
import schemaRejected from "./rules/schema-rejected.js";
|
|
20
|
+
import workflowStale from "./rules/workflow-stale.js";
|
|
21
|
+
import calendarUuidMissing from "./rules/calendar-uuid-missing.js";
|
|
22
|
+
import legacyTopLevelIdMigration from "./rules/legacy-top-level-id-migration.js";
|
|
23
|
+
import { loadProjectRules, mergeRules } from "./project-rules.js";
|
|
24
|
+
/**
|
|
25
|
+
* Registry of all rules in the order they run. The order matters: we
|
|
26
|
+
* detect calendar-uuid-missing first (to flush UUIDs), then run the
|
|
27
|
+
* frontmatter-id rules (which depend on UUIDs being persisted on
|
|
28
|
+
* disk to be useful in long-lived data).
|
|
29
|
+
*
|
|
30
|
+
* `legacy-top-level-id-migration` (Issue #38) runs BEFORE
|
|
31
|
+
* `missing-frontmatter-id` so that v0.7.0/v0.7.1-shaped files migrate
|
|
32
|
+
* to the namespaced form first; on the same run, the
|
|
33
|
+
* missing-frontmatter-id rule then sees the migrated files as bound
|
|
34
|
+
* (via `deskwork.id`) and doesn't re-report them.
|
|
35
|
+
*/
|
|
36
|
+
export const RULES = [
|
|
37
|
+
calendarUuidMissing,
|
|
38
|
+
legacyTopLevelIdMigration,
|
|
39
|
+
missingFrontmatterId,
|
|
40
|
+
orphanFrontmatterId,
|
|
41
|
+
duplicateId,
|
|
42
|
+
slugCollision,
|
|
43
|
+
workflowStale,
|
|
44
|
+
schemaRejected,
|
|
45
|
+
];
|
|
46
|
+
const RULE_BY_ID = new Map(RULES.map((r) => [r.id, r]));
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a CSV/comma-separated `--fix=` argument to rule ids.
|
|
49
|
+
*
|
|
50
|
+
* Returns the full list of built-in rule ids for `''` and `'all'`.
|
|
51
|
+
* Unknown built-in id strings are rejected (exit 2 in the CLI).
|
|
52
|
+
*
|
|
53
|
+
* Project rules registered via `<projectRoot>/.deskwork/doctor/*.ts`
|
|
54
|
+
* (Phase 23f) are selected by passing `'all'`; the runner picks them
|
|
55
|
+
* up from the merged rule list. Selecting an individual project rule
|
|
56
|
+
* by id via `--fix=<id>` is not yet supported — file an issue if the
|
|
57
|
+
* usage emerges.
|
|
58
|
+
*/
|
|
59
|
+
export function parseFixArgument(arg) {
|
|
60
|
+
const trimmed = arg.trim();
|
|
61
|
+
if (trimmed === '' || trimmed === 'all') {
|
|
62
|
+
return RULES.map((r) => r.id);
|
|
63
|
+
}
|
|
64
|
+
const ids = trimmed
|
|
65
|
+
.split(',')
|
|
66
|
+
.map((s) => s.trim())
|
|
67
|
+
.filter((s) => s.length > 0);
|
|
68
|
+
for (const id of ids) {
|
|
69
|
+
if (!RULE_BY_ID.has(id)) {
|
|
70
|
+
throw new Error(`Unknown doctor rule: "${id}". Known: ${RULES.map((r) => r.id).join(', ')}, all`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return ids;
|
|
74
|
+
}
|
|
75
|
+
/** Build the per-site context once for a run. */
|
|
76
|
+
function buildContext(opts, site, interaction) {
|
|
77
|
+
const calendarPath = resolveCalendarPath(opts.projectRoot, opts.config, site);
|
|
78
|
+
const calendar = readCalendar(calendarPath);
|
|
79
|
+
const index = buildContentIndex(opts.projectRoot, opts.config, site);
|
|
80
|
+
const allWorkflows = readWorkflows(opts.projectRoot, opts.config);
|
|
81
|
+
const workflows = allWorkflows.filter((w) => w.site === site);
|
|
82
|
+
return {
|
|
83
|
+
projectRoot: opts.projectRoot,
|
|
84
|
+
config: opts.config,
|
|
85
|
+
site,
|
|
86
|
+
calendar,
|
|
87
|
+
index,
|
|
88
|
+
workflows,
|
|
89
|
+
interaction,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function selectSites(opts) {
|
|
93
|
+
if (opts.site !== undefined) {
|
|
94
|
+
if (!(opts.site in opts.config.sites)) {
|
|
95
|
+
throw new Error(`Unknown site "${opts.site}". Configured sites: ${Object.keys(opts.config.sites).join(', ')}`);
|
|
96
|
+
}
|
|
97
|
+
return [opts.site];
|
|
98
|
+
}
|
|
99
|
+
return Object.keys(opts.config.sites);
|
|
100
|
+
}
|
|
101
|
+
function selectRules(available, ruleIds) {
|
|
102
|
+
if (ruleIds === undefined)
|
|
103
|
+
return [...available];
|
|
104
|
+
const byId = new Map(available.map((r) => [r.id, r]));
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const id of ruleIds) {
|
|
107
|
+
const rule = byId.get(id);
|
|
108
|
+
if (!rule) {
|
|
109
|
+
throw new Error(`Unknown doctor rule: "${id}". Known: ${available.map((r) => r.id).join(', ')}, all`);
|
|
110
|
+
}
|
|
111
|
+
out.push(rule);
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Phase 23f: build the effective rule set for an audit or repair run.
|
|
117
|
+
* Built-in rules merged with project rules from
|
|
118
|
+
* `<projectRoot>/.deskwork/doctor/*.ts`. Project rules with a basename
|
|
119
|
+
* matching a built-in's basename REPLACE that built-in (override
|
|
120
|
+
* semantics); new basenames append.
|
|
121
|
+
*
|
|
122
|
+
* Loaded once per run — not per finding — so disk i/o for
|
|
123
|
+
* `readdirSync` + N dynamic imports happens at the start of an audit.
|
|
124
|
+
*/
|
|
125
|
+
async function buildEffectiveRules(projectRoot) {
|
|
126
|
+
const projectRules = await loadProjectRules(projectRoot);
|
|
127
|
+
return mergeRules(RULES, projectRules);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Audit: collect findings without mutating the world. Returns a fully-
|
|
131
|
+
* built report with empty `repairs`. Suitable for pre-commit hooks
|
|
132
|
+
* that just want a non-zero exit code on any finding.
|
|
133
|
+
*/
|
|
134
|
+
export async function runAudit(opts, interaction) {
|
|
135
|
+
const sites = selectSites(opts);
|
|
136
|
+
const available = await buildEffectiveRules(opts.projectRoot);
|
|
137
|
+
const rules = selectRules(available, opts.ruleIds);
|
|
138
|
+
const findings = [];
|
|
139
|
+
for (const site of sites) {
|
|
140
|
+
const ctx = buildContext(opts, site, interaction);
|
|
141
|
+
for (const rule of rules) {
|
|
142
|
+
const out = await rule.audit(ctx);
|
|
143
|
+
findings.push(...out);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { findings, repairs: [], sites };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Repair: run audit → plan → (consult interaction) → apply. Returns
|
|
150
|
+
* the audit findings AND the repair results so callers can render
|
|
151
|
+
* a single report covering both phases.
|
|
152
|
+
*
|
|
153
|
+
* For `prompt` plans the runner consults `interaction.pickChoice` to
|
|
154
|
+
* resolve to an apply payload; for `apply` plans it consults
|
|
155
|
+
* `interaction.confirmApply`. `report-only` plans never apply.
|
|
156
|
+
*/
|
|
157
|
+
export async function runRepair(opts, interaction) {
|
|
158
|
+
const sites = selectSites(opts);
|
|
159
|
+
const available = await buildEffectiveRules(opts.projectRoot);
|
|
160
|
+
const rules = selectRules(available, opts.ruleIds);
|
|
161
|
+
const findings = [];
|
|
162
|
+
const repairs = [];
|
|
163
|
+
for (const site of sites) {
|
|
164
|
+
const ctx = buildContext(opts, site, interaction);
|
|
165
|
+
for (const rule of rules) {
|
|
166
|
+
const ruleFindings = await rule.audit(ctx);
|
|
167
|
+
findings.push(...ruleFindings);
|
|
168
|
+
for (const finding of ruleFindings) {
|
|
169
|
+
const plan = await rule.plan(ctx, finding);
|
|
170
|
+
const result = await resolveAndApply(rule, ctx, plan, interaction);
|
|
171
|
+
repairs.push(result);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { findings, repairs, sites };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Resolve a `prompt` plan via the interaction adapter and apply it.
|
|
179
|
+
* `apply` plans go through `confirmApply` first; `report-only` plans
|
|
180
|
+
* record a non-applied result.
|
|
181
|
+
*/
|
|
182
|
+
async function resolveAndApply(rule, ctx, plan, interaction) {
|
|
183
|
+
if (plan.kind === 'report-only') {
|
|
184
|
+
// `report-only` is the rule's signal that the finding can't be
|
|
185
|
+
// auto-repaired in this run. The granularity (prerequisite vs
|
|
186
|
+
// editorial-decision vs schema-rejected) is rule-specific; the
|
|
187
|
+
// rule sets `skipReason` via the report-only finding's details
|
|
188
|
+
// — see `reportOnlySkipReason()` below.
|
|
189
|
+
return {
|
|
190
|
+
finding: plan.finding,
|
|
191
|
+
applied: false,
|
|
192
|
+
message: plan.reason,
|
|
193
|
+
skipReason: reportOnlySkipReason(rule, plan.finding),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (plan.kind === 'prompt') {
|
|
197
|
+
const choiceId = await interaction.pickChoice(plan);
|
|
198
|
+
if (choiceId === undefined) {
|
|
199
|
+
return {
|
|
200
|
+
finding: plan.finding,
|
|
201
|
+
applied: false,
|
|
202
|
+
message: 'skipped (operator declined or --yes mode encountered ambiguity)',
|
|
203
|
+
skipReason: 'ambiguous',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const choice = plan.choices.find((c) => c.id === choiceId);
|
|
207
|
+
if (!choice) {
|
|
208
|
+
return {
|
|
209
|
+
finding: plan.finding,
|
|
210
|
+
applied: false,
|
|
211
|
+
message: `unknown choice id: ${choiceId}`,
|
|
212
|
+
skipReason: 'apply-failed',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const applyPlan = {
|
|
216
|
+
kind: 'apply',
|
|
217
|
+
finding: plan.finding,
|
|
218
|
+
summary: choice.label,
|
|
219
|
+
payload: choice.payload,
|
|
220
|
+
};
|
|
221
|
+
return rule.apply(ctx, applyPlan);
|
|
222
|
+
}
|
|
223
|
+
// apply
|
|
224
|
+
const ok = await interaction.confirmApply(plan);
|
|
225
|
+
if (!ok) {
|
|
226
|
+
return {
|
|
227
|
+
finding: plan.finding,
|
|
228
|
+
applied: false,
|
|
229
|
+
message: 'skipped (operator declined)',
|
|
230
|
+
skipReason: 'operator-declined',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return rule.apply(ctx, plan);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Map a rule's `report-only` plan to a `SkipReason`. The mapping is by
|
|
237
|
+
* rule id because the existing rules don't carry an explicit skip-
|
|
238
|
+
* reason field on their `report-only` plans — adding one to every
|
|
239
|
+
* rule would be churn for a 1:1 relationship that's already implicit
|
|
240
|
+
* in the rule's purpose.
|
|
241
|
+
*/
|
|
242
|
+
function reportOnlySkipReason(rule, _finding) {
|
|
243
|
+
switch (rule.id) {
|
|
244
|
+
case 'missing-frontmatter-id':
|
|
245
|
+
// Always "no candidate file found" — the operator hasn't
|
|
246
|
+
// scaffolded the body file yet (run /deskwork:outline).
|
|
247
|
+
return 'prerequisite-missing';
|
|
248
|
+
case 'slug-collision':
|
|
249
|
+
// Editorial: which slug "owns" the public URL.
|
|
250
|
+
return 'editorial-decision';
|
|
251
|
+
case 'schema-rejected':
|
|
252
|
+
// Patch instructions only; nothing to apply automatically.
|
|
253
|
+
return 'schema-rejected';
|
|
254
|
+
default:
|
|
255
|
+
// Conservative fallback — treat unfamiliar rules as needing
|
|
256
|
+
// operator follow-up.
|
|
257
|
+
return 'editorial-decision';
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Pre-built interaction: always confirm `apply` plans, skip `prompt`
|
|
262
|
+
* plans (no way to choose without a UI). Used by `--yes` mode.
|
|
263
|
+
*
|
|
264
|
+
* Exposed for the CLI command to construct.
|
|
265
|
+
*/
|
|
266
|
+
export const yesInteraction = {
|
|
267
|
+
async pickChoice(_plan) {
|
|
268
|
+
// `--yes` skips ambiguous cases by design — the workplan calls
|
|
269
|
+
// out missing-frontmatter-id with multiple candidates as the
|
|
270
|
+
// canonical example.
|
|
271
|
+
return undefined;
|
|
272
|
+
},
|
|
273
|
+
async confirmApply(_plan) {
|
|
274
|
+
return true;
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* Pre-built interaction: never apply anything. Used to dry-run a
|
|
279
|
+
* repair pipeline (prompt resolution + apply both no-op).
|
|
280
|
+
*/
|
|
281
|
+
export const declineInteraction = {
|
|
282
|
+
async pickChoice(_plan) {
|
|
283
|
+
return undefined;
|
|
284
|
+
},
|
|
285
|
+
async confirmApply(_plan) {
|
|
286
|
+
return false;
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/doctor/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-patch instructions for host content collections that reject
|
|
3
|
+
* the deskwork frontmatter binding.
|
|
4
|
+
*
|
|
5
|
+
* Astro's content collection schemas are strict by default — a
|
|
6
|
+
* `z.object({ ... })` schema without `.passthrough()` (or an explicit
|
|
7
|
+
* entry for the deskwork namespace) refuses files whose frontmatter
|
|
8
|
+
* carries unknown keys. Phase 19 introduced the binding key in
|
|
9
|
+
* frontmatter; v0.7.2 (Issue #38) moved it under a `deskwork:`
|
|
10
|
+
* namespace so deskwork doesn't claim the global top-level keyspace.
|
|
11
|
+
*
|
|
12
|
+
* The `schema-rejected` doctor rule and the scaffolder both surface
|
|
13
|
+
* this text when an actual schema rejection is observed at write time.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Return the operator-facing schema-patch instructions. The optional
|
|
17
|
+
* `collection` argument is reserved for future per-collection scoping
|
|
18
|
+
* (the current implementation returns the same text regardless).
|
|
19
|
+
*/
|
|
20
|
+
export declare function printSchemaPatchInstructions(collection?: string): string;
|
|
21
|
+
//# sourceMappingURL=schema-patch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-patch.d.ts","sourceRoot":"","sources":["../../src/doctor/schema-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqEH;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAKxE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-patch instructions for host content collections that reject
|
|
3
|
+
* the deskwork frontmatter binding.
|
|
4
|
+
*
|
|
5
|
+
* Astro's content collection schemas are strict by default — a
|
|
6
|
+
* `z.object({ ... })` schema without `.passthrough()` (or an explicit
|
|
7
|
+
* entry for the deskwork namespace) refuses files whose frontmatter
|
|
8
|
+
* carries unknown keys. Phase 19 introduced the binding key in
|
|
9
|
+
* frontmatter; v0.7.2 (Issue #38) moved it under a `deskwork:`
|
|
10
|
+
* namespace so deskwork doesn't claim the global top-level keyspace.
|
|
11
|
+
*
|
|
12
|
+
* The `schema-rejected` doctor rule and the scaffolder both surface
|
|
13
|
+
* this text when an actual schema rejection is observed at write time.
|
|
14
|
+
*/
|
|
15
|
+
const TEMPLATE = `# Host content schema must permit the \`deskwork\` namespace in frontmatter
|
|
16
|
+
|
|
17
|
+
Deskwork binds calendar entries to markdown files via a UUID written
|
|
18
|
+
under a \`deskwork:\` mapping in frontmatter (\`deskwork.id\`, UUID v4).
|
|
19
|
+
For Astro projects with strict content collection schemas, this means
|
|
20
|
+
the site's \`src/content/config.ts\` must allow the namespace to pass
|
|
21
|
+
through.
|
|
22
|
+
|
|
23
|
+
Note: top-level \`id:\` is NOT what to add — that field belongs to the
|
|
24
|
+
operator's keyspace. Deskwork no longer claims it.
|
|
25
|
+
|
|
26
|
+
Pick one of the following patches and apply it to your collection
|
|
27
|
+
schema. Re-run the failing command after the patch.
|
|
28
|
+
|
|
29
|
+
## Option 1 — explicit \`deskwork\` namespace (recommended)
|
|
30
|
+
|
|
31
|
+
\`\`\`ts
|
|
32
|
+
import { defineCollection, z } from 'astro:content';
|
|
33
|
+
|
|
34
|
+
const blog = defineCollection({
|
|
35
|
+
type: 'content',
|
|
36
|
+
schema: z.object({
|
|
37
|
+
deskwork: z.object({ id: z.string().uuid() }).passthrough().optional(),
|
|
38
|
+
title: z.string(),
|
|
39
|
+
description: z.string().optional(),
|
|
40
|
+
// ...your existing fields
|
|
41
|
+
}),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const collections = { blog };
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
The \`deskwork\` block is optional so legacy files without it keep
|
|
48
|
+
validating; deskwork sets it on every new scaffold and via
|
|
49
|
+
\`deskwork doctor --fix=missing-frontmatter-id\` /
|
|
50
|
+
\`--fix=legacy-top-level-id-migration\`. The inner \`.passthrough()\`
|
|
51
|
+
leaves room for additional deskwork-scoped fields without forcing a
|
|
52
|
+
schema change every release.
|
|
53
|
+
|
|
54
|
+
## Option 2 — passthrough unknown keys at the top level
|
|
55
|
+
|
|
56
|
+
\`\`\`ts
|
|
57
|
+
const blog = defineCollection({
|
|
58
|
+
type: 'content',
|
|
59
|
+
schema: z.object({
|
|
60
|
+
title: z.string(),
|
|
61
|
+
// ...your existing fields
|
|
62
|
+
}).passthrough(),
|
|
63
|
+
});
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
Less precise but a smaller diff. Top-level \`.passthrough()\` accepts
|
|
67
|
+
any extra keys — including the entire \`deskwork:\` namespace — without
|
|
68
|
+
complaint. Use this when the schema is wide and you don't want to
|
|
69
|
+
enumerate every deskwork-internal field.
|
|
70
|
+
|
|
71
|
+
## Hugo / Jekyll / Eleventy / plain markdown
|
|
72
|
+
|
|
73
|
+
These engines don't validate frontmatter against a schema; the
|
|
74
|
+
\`deskwork:\` mapping already passes through untouched. No patch
|
|
75
|
+
needed.
|
|
76
|
+
|
|
77
|
+
After patching, re-run the original deskwork command. The
|
|
78
|
+
\`schema-rejected\` rule's findings will clear on the next
|
|
79
|
+
\`deskwork doctor\` audit.
|
|
80
|
+
`;
|
|
81
|
+
/**
|
|
82
|
+
* Return the operator-facing schema-patch instructions. The optional
|
|
83
|
+
* `collection` argument is reserved for future per-collection scoping
|
|
84
|
+
* (the current implementation returns the same text regardless).
|
|
85
|
+
*/
|
|
86
|
+
export function printSchemaPatchInstructions(collection) {
|
|
87
|
+
if (collection !== undefined && collection.length > 0) {
|
|
88
|
+
return `${TEMPLATE}\n(Reported for collection: ${collection})\n`;
|
|
89
|
+
}
|
|
90
|
+
return TEMPLATE;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=schema-patch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-patch.js","sourceRoot":"","sources":["../../src/doctor/schema-patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEhB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAAC,UAAmB;IAC9D,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,OAAO,GAAG,QAAQ,+BAA+B,UAAU,KAAK,CAAC;IACnE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor — type definitions.
|
|
3
|
+
*
|
|
4
|
+
* `deskwork doctor` walks calendar + content tree + workflow store and
|
|
5
|
+
* produces structured reports. Each rule audits, optionally proposes a
|
|
6
|
+
* repair plan, and (with operator consent) applies the plan. The runner
|
|
7
|
+
* orchestrates rule execution; this module owns the data shapes.
|
|
8
|
+
*
|
|
9
|
+
* Sibling-relative imports per the project convention — `@/` doesn't
|
|
10
|
+
* resolve under tsx at runtime in this package's `src/`, only in tests.
|
|
11
|
+
*/
|
|
12
|
+
import type { DeskworkConfig } from '../config.ts';
|
|
13
|
+
import type { EditorialCalendar } from '../types.ts';
|
|
14
|
+
import type { ContentIndex } from '../content-index.ts';
|
|
15
|
+
import type { DraftWorkflowItem } from '../review/types.ts';
|
|
16
|
+
/** Severity for a finding — used only to color text-mode output. */
|
|
17
|
+
export type FindingSeverity = 'error' | 'warning' | 'info';
|
|
18
|
+
/**
|
|
19
|
+
* A single audit finding produced by a rule. The `details` map is rule-
|
|
20
|
+
* specific and meant for both human display and for `plan()` to reuse
|
|
21
|
+
* without re-walking the world.
|
|
22
|
+
*/
|
|
23
|
+
export interface Finding {
|
|
24
|
+
/** Stable rule id (matches `DoctorRule.id`). */
|
|
25
|
+
ruleId: string;
|
|
26
|
+
/** Site slug the finding belongs to (multi-site projects). */
|
|
27
|
+
site: string;
|
|
28
|
+
/** Severity bucket. */
|
|
29
|
+
severity: FindingSeverity;
|
|
30
|
+
/** Short human-readable label, suitable for one-line text output. */
|
|
31
|
+
message: string;
|
|
32
|
+
/** Rule-defined payload (entry id, file paths, etc.). */
|
|
33
|
+
details: Readonly<Record<string, unknown>>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What a rule would do if applied. `kind` discriminates: rules either
|
|
37
|
+
* have a concrete repair (`apply`), need operator input (`prompt`), or
|
|
38
|
+
* can only be reported on (`report-only`).
|
|
39
|
+
*
|
|
40
|
+
* The runner uses `kind` to decide whether to ask the operator (in
|
|
41
|
+
* interactive mode) or skip with a clear message (in `--yes` mode).
|
|
42
|
+
*/
|
|
43
|
+
export type RepairPlan = {
|
|
44
|
+
kind: 'apply';
|
|
45
|
+
/** The finding this plan addresses. */
|
|
46
|
+
finding: Finding;
|
|
47
|
+
/** One-line summary the runner shows before applying. */
|
|
48
|
+
summary: string;
|
|
49
|
+
/** Rule-defined payload — passed verbatim to `apply()`. */
|
|
50
|
+
payload: Readonly<Record<string, unknown>>;
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'prompt';
|
|
53
|
+
finding: Finding;
|
|
54
|
+
/** Operator-facing question. */
|
|
55
|
+
question: string;
|
|
56
|
+
/** Possible answers — first is the default. */
|
|
57
|
+
choices: ReadonlyArray<RepairChoice>;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'report-only';
|
|
60
|
+
finding: Finding;
|
|
61
|
+
/** Why no repair is offered (e.g. "operator must decide manually"). */
|
|
62
|
+
reason: string;
|
|
63
|
+
};
|
|
64
|
+
/** A single choice the operator can pick when a plan is `prompt`. */
|
|
65
|
+
export interface RepairChoice {
|
|
66
|
+
/** Stable id — what the runner records when the operator picks this. */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Operator-facing label. */
|
|
69
|
+
label: string;
|
|
70
|
+
/** Rule-defined payload to pass to `apply()` if chosen. */
|
|
71
|
+
payload: Readonly<Record<string, unknown>>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Reason a `RepairResult` was not applied — the granularity matters for
|
|
75
|
+
* the exit-code logic in `--fix` mode (Issue #44, Phase 22).
|
|
76
|
+
*
|
|
77
|
+
* - `prerequisite-missing`: the rule could not run because something
|
|
78
|
+
* outside doctor's scope hasn't happened yet (e.g. the entry has no
|
|
79
|
+
* body file because `outline` hasn't been called). Operator action
|
|
80
|
+
* is required, but it's not a doctor failure — `--fix` should still
|
|
81
|
+
* exit 0 once every applicable rule has been applied.
|
|
82
|
+
* - `ambiguous`: the rule has multiple valid resolutions and refuses
|
|
83
|
+
* to pick one without operator input. `--yes` mode skips these; the
|
|
84
|
+
* operator has to re-run interactively. `--fix` exits non-zero.
|
|
85
|
+
* - `editorial-decision`: the rule has a single resolution path but
|
|
86
|
+
* that path requires a human judgment call (e.g. picking which slug
|
|
87
|
+
* "owns" a public URL when two collide). `--fix` exits non-zero.
|
|
88
|
+
* - `schema-rejected`: the host's content-collection schema refused
|
|
89
|
+
* the write. `--fix` exits non-zero — the operator must patch the
|
|
90
|
+
* schema before retrying.
|
|
91
|
+
* - `operator-declined`: interactive mode and the operator said no.
|
|
92
|
+
* `--fix` exits non-zero (the operator chose not to proceed).
|
|
93
|
+
* - `apply-failed`: the rule's `apply()` raised on disk. `--fix` exits
|
|
94
|
+
* non-zero — a real failure to track down.
|
|
95
|
+
* - `no-action-needed`: the rule's plan resolved to a no-op (e.g. the
|
|
96
|
+
* operator chose "leave as-is" in an orphan-id prompt). `--fix`
|
|
97
|
+
* treats this as a successful skip.
|
|
98
|
+
*/
|
|
99
|
+
export type SkipReason = 'prerequisite-missing' | 'ambiguous' | 'editorial-decision' | 'schema-rejected' | 'operator-declined' | 'apply-failed' | 'no-action-needed';
|
|
100
|
+
/** Outcome of applying a repair plan. */
|
|
101
|
+
export interface RepairResult {
|
|
102
|
+
finding: Finding;
|
|
103
|
+
/** True when the repair landed on disk. */
|
|
104
|
+
applied: boolean;
|
|
105
|
+
/** Human-readable summary of what happened (or why it was skipped). */
|
|
106
|
+
message: string;
|
|
107
|
+
/**
|
|
108
|
+
* When `applied` is false, why. Used by the CLI command to decide
|
|
109
|
+
* whether the skip is a real follow-up (exit non-zero) or a no-op
|
|
110
|
+
* waiting on operator action outside doctor's scope (exit zero).
|
|
111
|
+
* Optional for backward compatibility — older rule implementations
|
|
112
|
+
* may not set it; the CLI defaults to "treat as a real follow-up".
|
|
113
|
+
*/
|
|
114
|
+
skipReason?: SkipReason;
|
|
115
|
+
/** Optional rule-defined details — e.g. paths written. */
|
|
116
|
+
details?: Readonly<Record<string, unknown>>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Operator interaction adapter. Rules don't depend on the runner's UI;
|
|
120
|
+
* they declare prompts in their `plan()` and the runner provides an
|
|
121
|
+
* interaction implementation (interactive readline, `--yes` auto-pick,
|
|
122
|
+
* or a test stub).
|
|
123
|
+
*/
|
|
124
|
+
export interface DoctorInteraction {
|
|
125
|
+
/**
|
|
126
|
+
* Resolve a `prompt` plan to a single choice id. Returns `undefined`
|
|
127
|
+
* to skip without applying — `--yes` mode does this for ambiguous
|
|
128
|
+
* cases; interactive mode does it when the operator declines.
|
|
129
|
+
*/
|
|
130
|
+
pickChoice(plan: Extract<RepairPlan, {
|
|
131
|
+
kind: 'prompt';
|
|
132
|
+
}>): Promise<string | undefined>;
|
|
133
|
+
/**
|
|
134
|
+
* Confirm an `apply` plan. Returns `true` to apply, `false` to skip.
|
|
135
|
+
* `--yes` mode always returns `true`; interactive mode shows the
|
|
136
|
+
* summary and asks.
|
|
137
|
+
*/
|
|
138
|
+
confirmApply(plan: Extract<RepairPlan, {
|
|
139
|
+
kind: 'apply';
|
|
140
|
+
}>): Promise<boolean>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Carrier of every per-site input a rule needs. Rules receive this and
|
|
144
|
+
* return findings without re-walking the calendar or re-reading config.
|
|
145
|
+
*/
|
|
146
|
+
export interface DoctorContext {
|
|
147
|
+
projectRoot: string;
|
|
148
|
+
config: DeskworkConfig;
|
|
149
|
+
/** Site slug the runner is currently auditing. */
|
|
150
|
+
site: string;
|
|
151
|
+
/** Calendar for `site` — already parsed. */
|
|
152
|
+
calendar: EditorialCalendar;
|
|
153
|
+
/** Content index for `site` — already built. */
|
|
154
|
+
index: ContentIndex;
|
|
155
|
+
/** Workflows from the review store, scoped to `site`. */
|
|
156
|
+
workflows: ReadonlyArray<DraftWorkflowItem>;
|
|
157
|
+
/** Operator interaction — used by the runner during repair. */
|
|
158
|
+
interaction: DoctorInteraction;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The contract every rule implements.
|
|
162
|
+
*
|
|
163
|
+
* `audit` is a pure read of the world. `plan` decides what (if anything)
|
|
164
|
+
* the rule would do for a finding. `apply` executes the plan. The runner
|
|
165
|
+
* decides whether to chain these — audit-only mode stops after `audit`.
|
|
166
|
+
*/
|
|
167
|
+
export interface DoctorRule {
|
|
168
|
+
/** Stable identifier — appears in `--fix=<id>`. Use kebab-case. */
|
|
169
|
+
readonly id: string;
|
|
170
|
+
/** One-line operator-facing label. */
|
|
171
|
+
readonly label: string;
|
|
172
|
+
audit(ctx: DoctorContext): Promise<Finding[]>;
|
|
173
|
+
plan(ctx: DoctorContext, finding: Finding): Promise<RepairPlan>;
|
|
174
|
+
apply(ctx: DoctorContext, plan: RepairPlan): Promise<RepairResult>;
|
|
175
|
+
}
|
|
176
|
+
/** Aggregate report returned by the runner. */
|
|
177
|
+
export interface DoctorReport {
|
|
178
|
+
/** Per-site findings, in rule iteration order. */
|
|
179
|
+
findings: Finding[];
|
|
180
|
+
/** Per-site repair results — empty in audit-only mode. */
|
|
181
|
+
repairs: RepairResult[];
|
|
182
|
+
/** Sites the runner exercised, in iteration order. */
|
|
183
|
+
sites: string[];
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/doctor/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor — type definitions.
|
|
3
|
+
*
|
|
4
|
+
* `deskwork doctor` walks calendar + content tree + workflow store and
|
|
5
|
+
* produces structured reports. Each rule audits, optionally proposes a
|
|
6
|
+
* repair plan, and (with operator consent) applies the plan. The runner
|
|
7
|
+
* orchestrates rule execution; this module owns the data shapes.
|
|
8
|
+
*
|
|
9
|
+
* Sibling-relative imports per the project convention — `@/` doesn't
|
|
10
|
+
* resolve under tsx at runtime in this package's `src/`, only in tests.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/doctor/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|