@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,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editorial review pipeline — workflow + version API.
|
|
3
|
+
*
|
|
4
|
+
* Ported from audiocontrol.org's scripts/lib/editorial-review/pipeline.ts.
|
|
5
|
+
* Storage: two journal directories under `<journalDir>/` — a `pipeline/`
|
|
6
|
+
* dir holding one file per workflow (latest state) and a `history/` dir
|
|
7
|
+
* holding one file per event (versions, annotations, state transitions).
|
|
8
|
+
*
|
|
9
|
+
* The default `journalDir` is `.deskwork/review-journal`. Host projects
|
|
10
|
+
* migrating from a prior layout can override via the top-level
|
|
11
|
+
* `reviewJournalDir` config field (e.g. audiocontrol points it at
|
|
12
|
+
* `journal/editorial` to read existing data).
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { appendJournal, readJournal } from "../journal.js";
|
|
17
|
+
import { envelopeFor, unwrap, } from "./journal-mappers.js";
|
|
18
|
+
import { isValidTransition, } from "./types.js";
|
|
19
|
+
const DEFAULT_JOURNAL_DIR = '.deskwork/review-journal';
|
|
20
|
+
const PIPELINE_SUBDIR = 'pipeline';
|
|
21
|
+
const HISTORY_SUBDIR = 'history';
|
|
22
|
+
/** Absolute path to the review journal root for a project. */
|
|
23
|
+
export function reviewJournalRoot(projectRoot, config) {
|
|
24
|
+
return join(projectRoot, config.reviewJournalDir ?? DEFAULT_JOURNAL_DIR);
|
|
25
|
+
}
|
|
26
|
+
/** Path to the pipeline journal (one file per workflow). */
|
|
27
|
+
export function pipelinePath(projectRoot, config) {
|
|
28
|
+
return join(reviewJournalRoot(projectRoot, config), PIPELINE_SUBDIR);
|
|
29
|
+
}
|
|
30
|
+
/** Path to the history journal (one file per event). */
|
|
31
|
+
export function historyPath(projectRoot, config) {
|
|
32
|
+
return join(reviewJournalRoot(projectRoot, config), HISTORY_SUBDIR);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Read every workflow (one record per id by construction — state transitions
|
|
36
|
+
* overwrite the existing file in place).
|
|
37
|
+
*/
|
|
38
|
+
export function readWorkflows(projectRoot, config) {
|
|
39
|
+
return readJournal(pipelinePath(projectRoot, config), {
|
|
40
|
+
timestampField: 'createdAt',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Read the full history log, oldest first. */
|
|
44
|
+
export function readHistory(projectRoot, config) {
|
|
45
|
+
const envelopes = readJournal(historyPath(projectRoot, config));
|
|
46
|
+
return envelopes.map(unwrap);
|
|
47
|
+
}
|
|
48
|
+
export function readWorkflow(projectRoot, config, id) {
|
|
49
|
+
return readWorkflows(projectRoot, config).find((w) => w.id === id) ?? null;
|
|
50
|
+
}
|
|
51
|
+
function writeWorkflow(projectRoot, config, workflow) {
|
|
52
|
+
appendJournal(pipelinePath(projectRoot, config), workflow, {
|
|
53
|
+
idField: 'id',
|
|
54
|
+
timestampField: 'createdAt',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function writeHistory(projectRoot, config, entry) {
|
|
58
|
+
appendJournal(historyPath(projectRoot, config), envelopeFor(entry), {
|
|
59
|
+
idField: 'id',
|
|
60
|
+
timestampField: 'timestamp',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function matchesKey(w, k) {
|
|
64
|
+
// Prefer entryId when both sides have it — stable identity survives
|
|
65
|
+
// slug renames. Fall back to (site, slug) otherwise so legacy
|
|
66
|
+
// workflows remain matchable.
|
|
67
|
+
const idMatch = k.entryId && w.entryId
|
|
68
|
+
? w.entryId === k.entryId
|
|
69
|
+
: w.site === k.site && w.slug === k.slug;
|
|
70
|
+
return (idMatch &&
|
|
71
|
+
w.contentKind === k.contentKind &&
|
|
72
|
+
(w.platform ?? null) === (k.platform ?? null) &&
|
|
73
|
+
(w.channel ?? null) === (k.channel ?? null));
|
|
74
|
+
}
|
|
75
|
+
function findOpenByKey(projectRoot, config, params) {
|
|
76
|
+
return (readWorkflows(projectRoot, config).find((w) => matchesKey(w, params) &&
|
|
77
|
+
w.state !== 'applied' &&
|
|
78
|
+
w.state !== 'cancelled') ?? null);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Create a new workflow plus its v1. Idempotent on the natural key
|
|
82
|
+
* (site, slug, contentKind, platform?, channel?) — if a non-terminal
|
|
83
|
+
* workflow already exists for that tuple, returns it unchanged.
|
|
84
|
+
*/
|
|
85
|
+
export function createWorkflow(projectRoot, config, params) {
|
|
86
|
+
const existing = findOpenByKey(projectRoot, config, params);
|
|
87
|
+
if (existing)
|
|
88
|
+
return existing;
|
|
89
|
+
const now = new Date().toISOString();
|
|
90
|
+
const item = {
|
|
91
|
+
id: randomUUID(),
|
|
92
|
+
site: params.site,
|
|
93
|
+
slug: params.slug,
|
|
94
|
+
contentKind: params.contentKind,
|
|
95
|
+
state: 'open',
|
|
96
|
+
currentVersion: 1,
|
|
97
|
+
createdAt: now,
|
|
98
|
+
updatedAt: now,
|
|
99
|
+
};
|
|
100
|
+
if (params.entryId !== undefined)
|
|
101
|
+
item.entryId = params.entryId;
|
|
102
|
+
if (params.platform !== undefined)
|
|
103
|
+
item.platform = params.platform;
|
|
104
|
+
if (params.channel !== undefined)
|
|
105
|
+
item.channel = params.channel;
|
|
106
|
+
writeWorkflow(projectRoot, config, item);
|
|
107
|
+
writeHistory(projectRoot, config, {
|
|
108
|
+
kind: 'workflow-created',
|
|
109
|
+
at: now,
|
|
110
|
+
workflow: item,
|
|
111
|
+
});
|
|
112
|
+
const v1 = {
|
|
113
|
+
version: 1,
|
|
114
|
+
markdown: params.initialMarkdown,
|
|
115
|
+
createdAt: now,
|
|
116
|
+
originatedBy: params.initialOriginatedBy ?? 'agent',
|
|
117
|
+
};
|
|
118
|
+
writeHistory(projectRoot, config, {
|
|
119
|
+
kind: 'version',
|
|
120
|
+
at: now,
|
|
121
|
+
workflowId: item.id,
|
|
122
|
+
version: v1,
|
|
123
|
+
});
|
|
124
|
+
return item;
|
|
125
|
+
}
|
|
126
|
+
/** List workflows in non-terminal states; optionally scoped to a site. */
|
|
127
|
+
export function listOpen(projectRoot, config, site) {
|
|
128
|
+
return readWorkflows(projectRoot, config).filter((w) => w.state !== 'applied' &&
|
|
129
|
+
w.state !== 'cancelled' &&
|
|
130
|
+
(!site || w.site === site));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Transition a workflow to a new state. Validates against VALID_TRANSITIONS
|
|
134
|
+
* and appends a history event. The workflow's file is overwritten in place.
|
|
135
|
+
*/
|
|
136
|
+
export function transitionState(projectRoot, config, workflowId, to) {
|
|
137
|
+
const current = readWorkflow(projectRoot, config, workflowId);
|
|
138
|
+
if (!current)
|
|
139
|
+
throw new Error(`Unknown workflow: ${workflowId}`);
|
|
140
|
+
if (!isValidTransition(current.state, to)) {
|
|
141
|
+
throw new Error(`Invalid transition for workflow ${workflowId}: ${current.state} → ${to}`);
|
|
142
|
+
}
|
|
143
|
+
const now = new Date().toISOString();
|
|
144
|
+
const updated = { ...current, state: to, updatedAt: now };
|
|
145
|
+
writeWorkflow(projectRoot, config, updated);
|
|
146
|
+
writeHistory(projectRoot, config, {
|
|
147
|
+
kind: 'workflow-state',
|
|
148
|
+
at: now,
|
|
149
|
+
workflowId,
|
|
150
|
+
from: current.state,
|
|
151
|
+
to,
|
|
152
|
+
});
|
|
153
|
+
return updated;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Append a new version. Increments currentVersion on the workflow.
|
|
157
|
+
* Does not transition state — callers combine with transitionState().
|
|
158
|
+
*/
|
|
159
|
+
export function appendVersion(projectRoot, config, workflowId, markdown, originatedBy) {
|
|
160
|
+
const current = readWorkflow(projectRoot, config, workflowId);
|
|
161
|
+
if (!current)
|
|
162
|
+
throw new Error(`Unknown workflow: ${workflowId}`);
|
|
163
|
+
const now = new Date().toISOString();
|
|
164
|
+
const version = {
|
|
165
|
+
version: current.currentVersion + 1,
|
|
166
|
+
markdown,
|
|
167
|
+
createdAt: now,
|
|
168
|
+
originatedBy,
|
|
169
|
+
};
|
|
170
|
+
writeHistory(projectRoot, config, {
|
|
171
|
+
kind: 'version',
|
|
172
|
+
at: now,
|
|
173
|
+
workflowId,
|
|
174
|
+
version,
|
|
175
|
+
});
|
|
176
|
+
const updated = {
|
|
177
|
+
...current,
|
|
178
|
+
currentVersion: version.version,
|
|
179
|
+
updatedAt: now,
|
|
180
|
+
};
|
|
181
|
+
writeWorkflow(projectRoot, config, updated);
|
|
182
|
+
return version;
|
|
183
|
+
}
|
|
184
|
+
/** Append an annotation to history. Does not transition state. */
|
|
185
|
+
export function appendAnnotation(projectRoot, config, annotation) {
|
|
186
|
+
writeHistory(projectRoot, config, {
|
|
187
|
+
kind: 'annotation',
|
|
188
|
+
at: annotation.createdAt,
|
|
189
|
+
annotation,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
export function readVersions(projectRoot, config, workflowId) {
|
|
193
|
+
const versions = [];
|
|
194
|
+
for (const entry of readHistory(projectRoot, config)) {
|
|
195
|
+
if (entry.kind === 'version' && entry.workflowId === workflowId) {
|
|
196
|
+
versions.push(entry.version);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return versions.sort((a, b) => a.version - b.version);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Annotations for a workflow, optionally filtered to a specific version.
|
|
203
|
+
* comment/approve/reject match by `version`; edit matches by `beforeVersion`.
|
|
204
|
+
*/
|
|
205
|
+
export function readAnnotations(projectRoot, config, workflowId, version) {
|
|
206
|
+
const anns = [];
|
|
207
|
+
for (const entry of readHistory(projectRoot, config)) {
|
|
208
|
+
if (entry.kind !== 'annotation')
|
|
209
|
+
continue;
|
|
210
|
+
const a = entry.annotation;
|
|
211
|
+
if (a.workflowId !== workflowId)
|
|
212
|
+
continue;
|
|
213
|
+
if (version === undefined) {
|
|
214
|
+
anns.push(a);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const matchesVersion = (a.type === 'comment' && a.version === version) ||
|
|
218
|
+
(a.type === 'approve' && a.version === version) ||
|
|
219
|
+
(a.type === 'reject' && a.version === version) ||
|
|
220
|
+
(a.type === 'edit' && a.beforeVersion === version);
|
|
221
|
+
if (matchesVersion)
|
|
222
|
+
anns.push(a);
|
|
223
|
+
}
|
|
224
|
+
return anns;
|
|
225
|
+
}
|
|
226
|
+
/** Mint an annotation with a server-assigned id and timestamp. */
|
|
227
|
+
export function mintAnnotation(partial) {
|
|
228
|
+
return {
|
|
229
|
+
...partial,
|
|
230
|
+
id: randomUUID(),
|
|
231
|
+
createdAt: new Date().toISOString(),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../../src/review/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a draft's markdown body as HTML for the studio review surface.
|
|
3
|
+
*
|
|
4
|
+
* Frontmatter parsing reuses the main lib/frontmatter module so the
|
|
5
|
+
* shape stays consistent across deskwork. Markdown → HTML uses unified
|
|
6
|
+
* + remark + rehype; the studio renders the HTML into the same
|
|
7
|
+
* BlogLayout-equivalent that the published site would use.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the public Astro pipeline: the body's leading `# Title` is
|
|
10
|
+
* stripped (BlogLayout / review shell renders title from frontmatter,
|
|
11
|
+
* the body repeat is a print-magazine convention that reads as
|
|
12
|
+
* throat-clearing on the web) and standalone images are wrapped in
|
|
13
|
+
* `<figure><figcaption>`. Outline-strip is NOT added here on purpose —
|
|
14
|
+
* the review surface needs the outline visible for annotate-and-iterate
|
|
15
|
+
* work.
|
|
16
|
+
*/
|
|
17
|
+
export interface ParsedDraft {
|
|
18
|
+
/** Frontmatter values. Values are whatever YAML parses them to. */
|
|
19
|
+
frontmatter: Record<string, unknown>;
|
|
20
|
+
/** Everything after the closing `---`. */
|
|
21
|
+
body: string;
|
|
22
|
+
}
|
|
23
|
+
/** Split a draft into its frontmatter and body. */
|
|
24
|
+
export declare function parseDraftFrontmatter(markdown: string): ParsedDraft;
|
|
25
|
+
/** Render a markdown string as HTML. */
|
|
26
|
+
export declare function renderMarkdownToHtml(markdown: string): Promise<string>;
|
|
27
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAYH,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAGnE;AAED,wCAAwC;AACxC,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5E"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render a draft's markdown body as HTML for the studio review surface.
|
|
3
|
+
*
|
|
4
|
+
* Frontmatter parsing reuses the main lib/frontmatter module so the
|
|
5
|
+
* shape stays consistent across deskwork. Markdown → HTML uses unified
|
|
6
|
+
* + remark + rehype; the studio renders the HTML into the same
|
|
7
|
+
* BlogLayout-equivalent that the published site would use.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the public Astro pipeline: the body's leading `# Title` is
|
|
10
|
+
* stripped (BlogLayout / review shell renders title from frontmatter,
|
|
11
|
+
* the body repeat is a print-magazine convention that reads as
|
|
12
|
+
* throat-clearing on the web) and standalone images are wrapped in
|
|
13
|
+
* `<figure><figcaption>`. Outline-strip is NOT added here on purpose —
|
|
14
|
+
* the review surface needs the outline visible for annotate-and-iterate
|
|
15
|
+
* work.
|
|
16
|
+
*/
|
|
17
|
+
import { unified } from 'unified';
|
|
18
|
+
import remarkParse from 'remark-parse';
|
|
19
|
+
import remarkRehype from 'remark-rehype';
|
|
20
|
+
import rehypeStringify from 'rehype-stringify';
|
|
21
|
+
import { parseFrontmatter } from "../frontmatter.js";
|
|
22
|
+
// @ts-expect-error — JS module without a .d.ts; the plugin is plain mdast traversal.
|
|
23
|
+
import remarkImageFigure from '../remark-image-figure.mjs';
|
|
24
|
+
// @ts-expect-error — JS module without a .d.ts; the plugin is plain mdast traversal.
|
|
25
|
+
import remarkStripFirstH1 from '../remark-strip-first-h1.mjs';
|
|
26
|
+
/** Split a draft into its frontmatter and body. */
|
|
27
|
+
export function parseDraftFrontmatter(markdown) {
|
|
28
|
+
const { data, body } = parseFrontmatter(markdown);
|
|
29
|
+
return { frontmatter: data, body };
|
|
30
|
+
}
|
|
31
|
+
/** Render a markdown string as HTML. */
|
|
32
|
+
export async function renderMarkdownToHtml(markdown) {
|
|
33
|
+
const result = await unified()
|
|
34
|
+
.use(remarkParse)
|
|
35
|
+
.use(remarkStripFirstH1)
|
|
36
|
+
.use(remarkImageFigure)
|
|
37
|
+
.use(remarkRehype)
|
|
38
|
+
.use(rehypeStringify)
|
|
39
|
+
.process(markdown);
|
|
40
|
+
return String(result);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,qFAAqF;AACrF,OAAO,iBAAiB,MAAM,4BAA4B,CAAC;AAC3D,qFAAqF;AACrF,OAAO,kBAAkB,MAAM,8BAA8B,CAAC;AAS9D,mDAAmD;AACnD,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IACzD,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE;SAC3B,GAAG,CAAC,WAAW,CAAC;SAChB,GAAG,CAAC,kBAAkB,CAAC;SACvB,GAAG,CAAC,iBAAiB,CAAC;SACtB,GAAG,CAAC,YAAY,CAAC;SACjB,GAAG,CAAC,eAAe,CAAC;SACpB,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice-drift feedback signal — aggregate comment annotations across
|
|
3
|
+
* completed review cycles to surface which voice-skill principles are
|
|
4
|
+
* catching the most operator corrections. The signal surfaces; revising
|
|
5
|
+
* voice skills themselves stays human-driven.
|
|
6
|
+
*
|
|
7
|
+
* Ported from audiocontrol.org's scripts/lib/editorial-review/report.ts.
|
|
8
|
+
* Takes `DeskworkConfig` instead of an implicit rootDir-only contract.
|
|
9
|
+
*/
|
|
10
|
+
import type { DeskworkConfig } from '../config.ts';
|
|
11
|
+
import type { AnnotationCategory } from './types.ts';
|
|
12
|
+
export interface ReportOptions {
|
|
13
|
+
/** Include only workflows that have reached a terminal state. Default true. */
|
|
14
|
+
terminalOnly?: boolean;
|
|
15
|
+
/** Optional site filter. */
|
|
16
|
+
site?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface CategoryCounts {
|
|
19
|
+
voiceDrift: number;
|
|
20
|
+
missingReceipt: number;
|
|
21
|
+
tutorialFraming: number;
|
|
22
|
+
saasVocabulary: number;
|
|
23
|
+
fakeAuthority: number;
|
|
24
|
+
structural: number;
|
|
25
|
+
other: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ReportBreakdown {
|
|
28
|
+
approvedCount: number;
|
|
29
|
+
cancelledCount: number;
|
|
30
|
+
totalComments: number;
|
|
31
|
+
commentsByCategory: CategoryCounts;
|
|
32
|
+
rejectCount: number;
|
|
33
|
+
}
|
|
34
|
+
export interface ReviewReport {
|
|
35
|
+
all: ReportBreakdown;
|
|
36
|
+
bySite: Record<string, ReportBreakdown>;
|
|
37
|
+
topCategories: Array<{
|
|
38
|
+
category: AnnotationCategory;
|
|
39
|
+
count: number;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a voice-drift report from the pipeline + history. Counts only
|
|
44
|
+
* workflows that reached a terminal state by default — in-flight
|
|
45
|
+
* workflows don't represent settled signal yet.
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildReport(projectRoot: string, config: DeskworkConfig, opts?: ReportOptions): ReviewReport;
|
|
48
|
+
/** Render a report as plain text for the review-report skill. */
|
|
49
|
+
export declare function renderReport(report: ReviewReport): string;
|
|
50
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/review/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAsB,MAAM,YAAY,CAAC;AAGzE,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,cAAc,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,eAAe,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACxC,aAAa,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,kBAAkB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AA2DD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,aAAkB,GACvB,YAAY,CAyDd;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAgCzD"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice-drift feedback signal — aggregate comment annotations across
|
|
3
|
+
* completed review cycles to surface which voice-skill principles are
|
|
4
|
+
* catching the most operator corrections. The signal surfaces; revising
|
|
5
|
+
* voice skills themselves stays human-driven.
|
|
6
|
+
*
|
|
7
|
+
* Ported from audiocontrol.org's scripts/lib/editorial-review/report.ts.
|
|
8
|
+
* Takes `DeskworkConfig` instead of an implicit rootDir-only contract.
|
|
9
|
+
*/
|
|
10
|
+
import { readHistory, readWorkflows } from "./pipeline.js";
|
|
11
|
+
const CATEGORY_KEYS = [
|
|
12
|
+
'voice-drift',
|
|
13
|
+
'missing-receipt',
|
|
14
|
+
'tutorial-framing',
|
|
15
|
+
'saas-vocabulary',
|
|
16
|
+
'fake-authority',
|
|
17
|
+
'structural',
|
|
18
|
+
'other',
|
|
19
|
+
];
|
|
20
|
+
function emptyCounts() {
|
|
21
|
+
return {
|
|
22
|
+
voiceDrift: 0,
|
|
23
|
+
missingReceipt: 0,
|
|
24
|
+
tutorialFraming: 0,
|
|
25
|
+
saasVocabulary: 0,
|
|
26
|
+
fakeAuthority: 0,
|
|
27
|
+
structural: 0,
|
|
28
|
+
other: 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function emptyBreakdown() {
|
|
32
|
+
return {
|
|
33
|
+
approvedCount: 0,
|
|
34
|
+
cancelledCount: 0,
|
|
35
|
+
totalComments: 0,
|
|
36
|
+
commentsByCategory: emptyCounts(),
|
|
37
|
+
rejectCount: 0,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function bump(counts, category) {
|
|
41
|
+
const key = category ?? 'other';
|
|
42
|
+
switch (key) {
|
|
43
|
+
case 'voice-drift':
|
|
44
|
+
counts.voiceDrift++;
|
|
45
|
+
break;
|
|
46
|
+
case 'missing-receipt':
|
|
47
|
+
counts.missingReceipt++;
|
|
48
|
+
break;
|
|
49
|
+
case 'tutorial-framing':
|
|
50
|
+
counts.tutorialFraming++;
|
|
51
|
+
break;
|
|
52
|
+
case 'saas-vocabulary':
|
|
53
|
+
counts.saasVocabulary++;
|
|
54
|
+
break;
|
|
55
|
+
case 'fake-authority':
|
|
56
|
+
counts.fakeAuthority++;
|
|
57
|
+
break;
|
|
58
|
+
case 'structural':
|
|
59
|
+
counts.structural++;
|
|
60
|
+
break;
|
|
61
|
+
default: counts.other++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function categoryValue(counts, cat) {
|
|
65
|
+
switch (cat) {
|
|
66
|
+
case 'voice-drift': return counts.voiceDrift;
|
|
67
|
+
case 'missing-receipt': return counts.missingReceipt;
|
|
68
|
+
case 'tutorial-framing': return counts.tutorialFraming;
|
|
69
|
+
case 'saas-vocabulary': return counts.saasVocabulary;
|
|
70
|
+
case 'fake-authority': return counts.fakeAuthority;
|
|
71
|
+
case 'structural': return counts.structural;
|
|
72
|
+
case 'other': return counts.other;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build a voice-drift report from the pipeline + history. Counts only
|
|
77
|
+
* workflows that reached a terminal state by default — in-flight
|
|
78
|
+
* workflows don't represent settled signal yet.
|
|
79
|
+
*/
|
|
80
|
+
export function buildReport(projectRoot, config, opts = {}) {
|
|
81
|
+
const { terminalOnly = true, site } = opts;
|
|
82
|
+
const workflows = readWorkflows(projectRoot, config).filter((w) => !site || w.site === site);
|
|
83
|
+
const terminalStates = ['applied', 'cancelled'];
|
|
84
|
+
const scoped = terminalOnly
|
|
85
|
+
? workflows.filter((w) => terminalStates.includes(w.state))
|
|
86
|
+
: workflows;
|
|
87
|
+
const workflowIds = new Set(scoped.map((w) => w.id));
|
|
88
|
+
const all = emptyBreakdown();
|
|
89
|
+
const bySite = {};
|
|
90
|
+
for (const w of scoped) {
|
|
91
|
+
if (w.state === 'applied')
|
|
92
|
+
all.approvedCount++;
|
|
93
|
+
if (w.state === 'cancelled')
|
|
94
|
+
all.cancelledCount++;
|
|
95
|
+
const b = bySite[w.site] ?? emptyBreakdown();
|
|
96
|
+
if (w.state === 'applied')
|
|
97
|
+
b.approvedCount++;
|
|
98
|
+
if (w.state === 'cancelled')
|
|
99
|
+
b.cancelledCount++;
|
|
100
|
+
bySite[w.site] = b;
|
|
101
|
+
}
|
|
102
|
+
const workflowSiteById = new Map(scoped.map((w) => [w.id, w.site]));
|
|
103
|
+
for (const entry of readHistory(projectRoot, config)) {
|
|
104
|
+
if (entry.kind !== 'annotation')
|
|
105
|
+
continue;
|
|
106
|
+
const ann = entry.annotation;
|
|
107
|
+
if (!workflowIds.has(ann.workflowId))
|
|
108
|
+
continue;
|
|
109
|
+
const s = workflowSiteById.get(ann.workflowId);
|
|
110
|
+
if (ann.type === 'comment') {
|
|
111
|
+
all.totalComments++;
|
|
112
|
+
bump(all.commentsByCategory, ann.category);
|
|
113
|
+
if (s) {
|
|
114
|
+
const b = bySite[s] ?? emptyBreakdown();
|
|
115
|
+
b.totalComments++;
|
|
116
|
+
bump(b.commentsByCategory, ann.category);
|
|
117
|
+
bySite[s] = b;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else if (ann.type === 'reject') {
|
|
121
|
+
all.rejectCount++;
|
|
122
|
+
if (s) {
|
|
123
|
+
const b = bySite[s] ?? emptyBreakdown();
|
|
124
|
+
b.rejectCount++;
|
|
125
|
+
bySite[s] = b;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const topCategories = CATEGORY_KEYS.map((cat) => ({
|
|
130
|
+
category: cat,
|
|
131
|
+
count: categoryValue(all.commentsByCategory, cat),
|
|
132
|
+
})).sort((a, b) => b.count - a.count);
|
|
133
|
+
return { all, bySite, topCategories };
|
|
134
|
+
}
|
|
135
|
+
/** Render a report as plain text for the review-report skill. */
|
|
136
|
+
export function renderReport(report) {
|
|
137
|
+
const lines = [];
|
|
138
|
+
lines.push('Editorial review — voice-drift signal');
|
|
139
|
+
lines.push('');
|
|
140
|
+
lines.push(`Scope: ${report.all.approvedCount} approved, ${report.all.cancelledCount} cancelled (${report.all.rejectCount} reject annotations recorded)`);
|
|
141
|
+
lines.push(`Total comments: ${report.all.totalComments}`);
|
|
142
|
+
lines.push('');
|
|
143
|
+
lines.push('Categories (most → least frequent):');
|
|
144
|
+
for (const { category, count } of report.topCategories) {
|
|
145
|
+
lines.push(` ${category.padEnd(18)} ${count}`);
|
|
146
|
+
}
|
|
147
|
+
const siteKeys = Object.keys(report.bySite).sort();
|
|
148
|
+
if (siteKeys.length > 1) {
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push('Per-site breakdown:');
|
|
151
|
+
for (const s of siteKeys) {
|
|
152
|
+
const b = report.bySite[s];
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push(` ${s}: ${b.approvedCount} approved, ${b.cancelledCount} cancelled, ${b.totalComments} comments, ${b.rejectCount} rejects`);
|
|
155
|
+
for (const cat of CATEGORY_KEYS) {
|
|
156
|
+
const n = categoryValue(b.commentsByCategory, cat);
|
|
157
|
+
if (n > 0)
|
|
158
|
+
lines.push(` ${cat.padEnd(18)} ${n}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return lines.join('\n');
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/review/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common HTTP-shape result type for review handlers. Pulled out so the
|
|
3
|
+
* handler modules (`handlers.ts`, `start-handlers.ts`) can share it
|
|
4
|
+
* without one importing the other.
|
|
5
|
+
*/
|
|
6
|
+
export interface HandlerResult {
|
|
7
|
+
status: number;
|
|
8
|
+
body: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function err(status: number, message: string): HandlerResult;
|
|
11
|
+
export declare function ok(body: unknown): HandlerResult;
|
|
12
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/review/result.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAElE;AAED,wBAAgB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,CAE/C"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common HTTP-shape result type for review handlers. Pulled out so the
|
|
3
|
+
* handler modules (`handlers.ts`, `start-handlers.ts`) can share it
|
|
4
|
+
* without one importing the other.
|
|
5
|
+
*/
|
|
6
|
+
export function err(status, message) {
|
|
7
|
+
return { status, body: { error: message } };
|
|
8
|
+
}
|
|
9
|
+
export function ok(body) {
|
|
10
|
+
return { status: 200, body };
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/review/result.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,OAAe;IACjD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,EAAE,CAAC,IAAa;IAC9B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-creation handlers (longform, shortform). Pulled out of
|
|
3
|
+
* `handlers.ts` to keep that file under the 500-line guideline.
|
|
4
|
+
*
|
|
5
|
+
* Both handlers follow the same shape:
|
|
6
|
+
* 1. Validate input (site/slug/platform — same SLUG_RE the rest of
|
|
7
|
+
* deskwork uses).
|
|
8
|
+
* 2. Resolve the calendar entry (idem-key for the workflow tuple).
|
|
9
|
+
* 3. Resolve the on-disk markdown file path. Longform requires the file
|
|
10
|
+
* to already exist (scaffolded by /deskwork:outline). Shortform
|
|
11
|
+
* scaffolds the file in place when missing — frontmatter carries
|
|
12
|
+
* the deskwork-namespaced binding (`deskwork: { id, platform,
|
|
13
|
+
* channel? }`), body starts as `initialMarkdown ?? ''`.
|
|
14
|
+
* 4. Call `createWorkflow` (idempotent on the tuple).
|
|
15
|
+
*
|
|
16
|
+
* Phase 21a: shortform IS the same shape as longform — file on disk is
|
|
17
|
+
* SSOT, journal stores snapshots, the studio's review surface renders
|
|
18
|
+
* both kinds identically.
|
|
19
|
+
*/
|
|
20
|
+
import type { DeskworkConfig } from '../config.ts';
|
|
21
|
+
import { type Platform } from '../types.ts';
|
|
22
|
+
import { type HandlerResult } from './result.ts';
|
|
23
|
+
/**
|
|
24
|
+
* Enqueue a longform draft review from the studio dashboard. Reads the
|
|
25
|
+
* blog post markdown from disk (honoring blogFilenameTemplate) and calls
|
|
26
|
+
* createWorkflow. Idempotent on (entryId | (site, slug), 'longform').
|
|
27
|
+
*/
|
|
28
|
+
export declare function handleStartLongform(projectRoot: string, config: DeskworkConfig, body: unknown): HandlerResult;
|
|
29
|
+
/**
|
|
30
|
+
* Enqueue a shortform draft review. Mirrors `handleStartLongform`'s shape:
|
|
31
|
+
* resolve the calendar entry by slug, compute the shortform file path,
|
|
32
|
+
* scaffold the file (frontmatter `deskwork: { id, platform, channel? }` +
|
|
33
|
+
* body) when missing, then `createWorkflow` with `contentKind: 'shortform'`
|
|
34
|
+
* and v1 mirroring the file body.
|
|
35
|
+
*
|
|
36
|
+
* Idempotent: if a workflow already exists for the
|
|
37
|
+
* (entryId|site+slug, contentKind, platform, channel) tuple,
|
|
38
|
+
* `createWorkflow` returns it unchanged. If the file already exists on
|
|
39
|
+
* disk (operator resuming), creation is skipped and the body is read.
|
|
40
|
+
*
|
|
41
|
+
* Lifecycle decoupling: shortform is independent of the longform
|
|
42
|
+
* outline → drafting → published path. If the entry has no body file
|
|
43
|
+
* yet (Ideas / Planned / Outlining without scaffold), this handler
|
|
44
|
+
* still works — it creates the entry directory + scrapbook subdirs
|
|
45
|
+
* before writing the shortform file.
|
|
46
|
+
*/
|
|
47
|
+
export declare function handleStartShortform(projectRoot: string, config: DeskworkConfig, body: unknown): HandlerResult;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the on-disk markdown file path for any kind of workflow.
|
|
50
|
+
* Dispatches on `contentKind`. Returns `undefined` only for shortform when
|
|
51
|
+
* the entry directory cannot be derived (no entry id and no body file
|
|
52
|
+
* scaffolded yet).
|
|
53
|
+
*/
|
|
54
|
+
export declare function workflowFilePath(projectRoot: string, config: DeskworkConfig, workflow: {
|
|
55
|
+
site: string;
|
|
56
|
+
slug: string;
|
|
57
|
+
contentKind: 'longform' | 'shortform' | 'outline';
|
|
58
|
+
entryId?: string;
|
|
59
|
+
platform?: Platform;
|
|
60
|
+
channel?: string;
|
|
61
|
+
}): string | undefined;
|
|
62
|
+
//# sourceMappingURL=start-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-handlers.d.ts","sourceRoot":"","sources":["../../src/review/start-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQxD,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAW1D;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAoDf;AAiBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAsHf;AAMD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE;IACR,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GACA,MAAM,GAAG,SAAS,CAsBpB"}
|