@deskwork/core 0.10.2 → 0.11.1

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 (73) hide show
  1. package/dist/calendar/parse.d.ts +24 -0
  2. package/dist/calendar/parse.d.ts.map +1 -0
  3. package/dist/calendar/parse.js +64 -0
  4. package/dist/calendar/parse.js.map +1 -0
  5. package/dist/calendar/render.d.ts +3 -0
  6. package/dist/calendar/render.d.ts.map +1 -0
  7. package/dist/calendar/render.js +46 -0
  8. package/dist/calendar/render.js.map +1 -0
  9. package/dist/doctor/migrate.d.ts +11 -0
  10. package/dist/doctor/migrate.d.ts.map +1 -0
  11. package/dist/doctor/migrate.js +101 -0
  12. package/dist/doctor/migrate.js.map +1 -0
  13. package/dist/doctor/repair.d.ts +9 -0
  14. package/dist/doctor/repair.d.ts.map +1 -0
  15. package/dist/doctor/repair.js +35 -0
  16. package/dist/doctor/repair.js.map +1 -0
  17. package/dist/doctor/validate.d.ts +11 -0
  18. package/dist/doctor/validate.d.ts.map +1 -0
  19. package/dist/doctor/validate.js +405 -0
  20. package/dist/doctor/validate.js.map +1 -0
  21. package/dist/iterate/iterate.d.ts +13 -0
  22. package/dist/iterate/iterate.d.ts.map +1 -0
  23. package/dist/iterate/iterate.js +69 -0
  24. package/dist/iterate/iterate.js.map +1 -0
  25. package/dist/journal/append.d.ts +3 -0
  26. package/dist/journal/append.d.ts.map +1 -0
  27. package/dist/journal/append.js +18 -0
  28. package/dist/journal/append.js.map +1 -0
  29. package/dist/journal/index.d.ts +3 -0
  30. package/dist/journal/index.d.ts.map +1 -0
  31. package/dist/journal/index.js +3 -0
  32. package/dist/journal/index.js.map +1 -0
  33. package/dist/journal/read.d.ts +9 -0
  34. package/dist/journal/read.d.ts.map +1 -0
  35. package/dist/journal/read.js +41 -0
  36. package/dist/journal/read.js.map +1 -0
  37. package/dist/schema/annotation.d.ts +49 -0
  38. package/dist/schema/annotation.d.ts.map +1 -0
  39. package/dist/schema/annotation.js +13 -0
  40. package/dist/schema/annotation.js.map +1 -0
  41. package/dist/schema/entry.d.ts +57 -0
  42. package/dist/schema/entry.d.ts.map +1 -0
  43. package/dist/schema/entry.js +47 -0
  44. package/dist/schema/entry.js.map +1 -0
  45. package/dist/schema/journal-events.d.ts +275 -0
  46. package/dist/schema/journal-events.d.ts.map +1 -0
  47. package/dist/schema/journal-events.js +60 -0
  48. package/dist/schema/journal-events.js.map +1 -0
  49. package/dist/sidecar/index.d.ts +6 -0
  50. package/dist/sidecar/index.d.ts.map +1 -0
  51. package/dist/sidecar/index.js +6 -0
  52. package/dist/sidecar/index.js.map +1 -0
  53. package/dist/sidecar/lookup.d.ts +2 -0
  54. package/dist/sidecar/lookup.d.ts.map +1 -0
  55. package/dist/sidecar/lookup.js +30 -0
  56. package/dist/sidecar/lookup.js.map +1 -0
  57. package/dist/sidecar/paths.d.ts +3 -0
  58. package/dist/sidecar/paths.d.ts.map +1 -0
  59. package/dist/sidecar/paths.js +8 -0
  60. package/dist/sidecar/paths.js.map +1 -0
  61. package/dist/sidecar/read-all.d.ts +14 -0
  62. package/dist/sidecar/read-all.d.ts.map +1 -0
  63. package/dist/sidecar/read-all.js +49 -0
  64. package/dist/sidecar/read-all.js.map +1 -0
  65. package/dist/sidecar/read.d.ts +3 -0
  66. package/dist/sidecar/read.d.ts.map +1 -0
  67. package/dist/sidecar/read.js +30 -0
  68. package/dist/sidecar/read.js.map +1 -0
  69. package/dist/sidecar/write.d.ts +3 -0
  70. package/dist/sidecar/write.d.ts.map +1 -0
  71. package/dist/sidecar/write.js +16 -0
  72. package/dist/sidecar/write.js.map +1 -0
  73. package/package.json +27 -2
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Migration-only parser: extracts per-row entries from a legacy `calendar.md`
3
+ * so the doctor migration class can build per-entry sidecars.
4
+ *
5
+ * This module is independent of the legacy parser at `packages/core/src/calendar.ts`
6
+ * (which uses a legacy Stage type with `Review` / `Paused`). The new entry-centric
7
+ * Stage type lives in `@/schema/entry`. Legacy section names are mapped to new
8
+ * stages via `LEGACY_STAGE_MAP` (e.g. `Paused -> Blocked`, `Review -> dropped`).
9
+ *
10
+ * Not used at steady-state runtime — only invoked by the doctor migration that
11
+ * converts a legacy calendar.md into one sidecar per entry.
12
+ */
13
+ import type { Stage } from '../schema/entry.ts';
14
+ export interface MigrationSourceEntry {
15
+ currentStage: Stage;
16
+ uuid: string;
17
+ slug: string;
18
+ title: string;
19
+ description: string;
20
+ keywords: string[];
21
+ source: string;
22
+ }
23
+ export declare function extractEntriesForMigration(md: string): MigrationSourceEntry[];
24
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/calendar/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,KAAK,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAmCD,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAyB7E"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Migration-only parser: extracts per-row entries from a legacy `calendar.md`
3
+ * so the doctor migration class can build per-entry sidecars.
4
+ *
5
+ * This module is independent of the legacy parser at `packages/core/src/calendar.ts`
6
+ * (which uses a legacy Stage type with `Review` / `Paused`). The new entry-centric
7
+ * Stage type lives in `@/schema/entry`. Legacy section names are mapped to new
8
+ * stages via `LEGACY_STAGE_MAP` (e.g. `Paused -> Blocked`, `Review -> dropped`).
9
+ *
10
+ * Not used at steady-state runtime — only invoked by the doctor migration that
11
+ * converts a legacy calendar.md into one sidecar per entry.
12
+ */
13
+ const LEGACY_STAGE_MAP = {
14
+ Ideas: 'Ideas',
15
+ Planned: 'Planned',
16
+ Outlining: 'Outlining',
17
+ Drafting: 'Drafting',
18
+ Final: 'Final',
19
+ Published: 'Published',
20
+ Blocked: 'Blocked',
21
+ Paused: 'Blocked', // migration mapping: legacy Paused -> new Blocked
22
+ Cancelled: 'Cancelled',
23
+ Review: null, // dropped: review is a state, not a stage in the new model
24
+ Distribution: null, // not a stage: shortform distribution is a separate model
25
+ };
26
+ function splitSections(md) {
27
+ const sectionRe = /^## (\w+)\s*$/gm;
28
+ const matches = [...md.matchAll(sectionRe)];
29
+ const sections = [];
30
+ for (const [i, current] of matches.entries()) {
31
+ const startOfHeader = current.index ?? 0;
32
+ const start = startOfHeader + current[0].length;
33
+ const next = matches[i + 1];
34
+ const end = next ? (next.index ?? md.length) : md.length;
35
+ sections.push({ name: current[1], body: md.slice(start, end) });
36
+ }
37
+ return sections;
38
+ }
39
+ export function extractEntriesForMigration(md) {
40
+ const sections = splitSections(md);
41
+ const entries = [];
42
+ for (const { name, body } of sections) {
43
+ const stage = LEGACY_STAGE_MAP[name];
44
+ if (!stage)
45
+ continue;
46
+ // Match the canonical six-column legacy row: UUID | Slug | Title | Description | Keywords | Source
47
+ // The row regex pins on a UUID in the first column to avoid matching the header
48
+ // and separator rows of the table (which start with `| UUID |` and `|------|`).
49
+ const rowRe = /^\|\s*([0-9a-f-]{36})\s*\|\s*([^|]+?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|/gm;
50
+ for (const m of body.matchAll(rowRe)) {
51
+ entries.push({
52
+ currentStage: stage,
53
+ uuid: m[1],
54
+ slug: m[2].trim(),
55
+ title: m[3].trim(),
56
+ description: m[4].trim(),
57
+ keywords: m[5].split(',').map((s) => s.trim()).filter(Boolean),
58
+ source: m[6].trim(),
59
+ });
60
+ }
61
+ }
62
+ return entries;
63
+ }
64
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/calendar/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAcH,MAAM,gBAAgB,GAAiC;IACrD,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS,EAAW,kDAAkD;IAC9E,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,IAAI,EAAiB,2DAA2D;IACxF,YAAY,EAAE,IAAI,EAAW,0DAA0D;CACxF,CAAC;AAOF,SAAS,aAAa,CAAC,EAAU;IAC/B,MAAM,SAAS,GAAG,iBAAiB,CAAC;IACpC,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAChD,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC;QACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,EAAU;IACnD,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IACnC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,mGAAmG;QACnG,gFAAgF;QAChF,gFAAgF;QAChF,MAAM,KAAK,GAAG,8GAA8G,CAAC;QAC7H,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC;gBACX,YAAY,EAAE,KAAK;gBACnB,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACV,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBACjB,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBAClB,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;gBAC9D,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Entry } from '../schema/entry.ts';
2
+ export declare function renderCalendar(entries: Entry[]): string;
3
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/calendar/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,oBAAoB,CAAC;AAwCvD,wBAAgB,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAUvD"}
@@ -0,0 +1,46 @@
1
+ const STAGE_ORDER = [
2
+ 'Ideas', 'Planned', 'Outlining', 'Drafting', 'Final', 'Published', 'Blocked', 'Cancelled',
3
+ ];
4
+ const HEADER = '# Editorial Calendar\n\n';
5
+ const TABLE_HEADER = '| UUID | Slug | Title | Description | Keywords | Source | Updated |\n|------|------|------|------|------|------|------|\n';
6
+ const EMPTY = '*No entries.*\n\n';
7
+ function escapePipe(s) {
8
+ return s.replace(/\|/g, '\\|');
9
+ }
10
+ function renderRow(e) {
11
+ return `| ${e.uuid} | ${escapePipe(e.slug)} | ${escapePipe(e.title)} | ${escapePipe(e.description ?? '')} | ${escapePipe(e.keywords.join(', '))} | ${escapePipe(e.source)} | ${e.updatedAt} |`;
12
+ }
13
+ function bucketize(entries) {
14
+ const byStage = new Map();
15
+ for (const stage of STAGE_ORDER)
16
+ byStage.set(stage, []);
17
+ for (const e of entries) {
18
+ const bucket = byStage.get(e.currentStage);
19
+ if (bucket)
20
+ bucket.push(e);
21
+ }
22
+ return byStage;
23
+ }
24
+ function renderStageSection(stage, bucket) {
25
+ let section = `## ${stage}\n\n`;
26
+ if (bucket.length === 0) {
27
+ section += EMPTY;
28
+ return section;
29
+ }
30
+ section += TABLE_HEADER;
31
+ for (const e of bucket)
32
+ section += renderRow(e) + '\n';
33
+ section += '\n';
34
+ return section;
35
+ }
36
+ export function renderCalendar(entries) {
37
+ const byStage = bucketize(entries);
38
+ let md = HEADER;
39
+ for (const stage of STAGE_ORDER) {
40
+ const bucket = byStage.get(stage) ?? [];
41
+ md += renderStageSection(stage, bucket);
42
+ }
43
+ md += `## Distribution\n\n*reserved for shortform DistributionRecords — separate model*\n`;
44
+ return md;
45
+ }
46
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/calendar/render.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAqB;IACpC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW;CACjF,CAAC;AAEX,MAAM,MAAM,GAAG,0BAA0B,CAAC;AAC1C,MAAM,YAAY,GAAG,2HAA2H,CAAC;AACjJ,MAAM,KAAK,GAAG,mBAAmB,CAAC;AAElC,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,CAAQ;IACzB,OAAO,KAAK,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,IAAI,CAAC;AACjM,CAAC;AAED,SAAS,SAAS,CAAC,OAAgB;IACjC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,WAAW;QAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACxD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC3C,IAAI,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAY,EAAE,MAAwB;IAChE,IAAI,OAAO,GAAG,MAAM,KAAK,MAAM,CAAC;IAChC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,KAAK,CAAC;QACjB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,YAAY,CAAC;IACxB,KAAK,MAAM,CAAC,IAAI,MAAM;QAAE,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACvD,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAgB;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IAEnC,IAAI,EAAE,GAAG,MAAM,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACxC,EAAE,IAAI,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,CAAC;IACD,EAAE,IAAI,oFAAoF,CAAC;IAC3F,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,11 @@
1
+ interface MigrateOptions {
2
+ dryRun: boolean;
3
+ }
4
+ interface MigrateResult {
5
+ entriesMigrated: number;
6
+ unmigratable: string[];
7
+ }
8
+ export declare function detectLegacySchema(projectRoot: string): Promise<boolean>;
9
+ export declare function migrateCalendar(projectRoot: string, opts: MigrateOptions): Promise<MigrateResult>;
10
+ export {};
11
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/doctor/migrate.ts"],"names":[],"mappings":"AAUA,UAAU,cAAc;IACtB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,aAAa;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAc9E;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,aAAa,CAAC,CAqDxB"}
@@ -0,0 +1,101 @@
1
+ import { readFile, writeFile, access } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { extractEntriesForMigration } from "../calendar/parse.js";
4
+ import { writeSidecar } from "../sidecar/write.js";
5
+ import { renderCalendar } from "../calendar/render.js";
6
+ import { appendJournalEvent } from "../journal/append.js";
7
+ import { readJournalEvents } from "../journal/read.js";
8
+ export async function detectLegacySchema(projectRoot) {
9
+ // Legacy: calendar.md has Paused/Review sections, OR no .deskwork/entries directory.
10
+ try {
11
+ const md = await readFile(join(projectRoot, '.deskwork', 'calendar.md'), 'utf8');
12
+ if (/^## Paused\b/m.test(md) || /^## Review\b/m.test(md))
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ try {
19
+ await access(join(projectRoot, '.deskwork', 'entries'));
20
+ return false;
21
+ }
22
+ catch {
23
+ return true;
24
+ }
25
+ }
26
+ export async function migrateCalendar(projectRoot, opts) {
27
+ const md = await readFile(join(projectRoot, '.deskwork', 'calendar.md'), 'utf8');
28
+ const sources = extractEntriesForMigration(md);
29
+ const sidecars = [];
30
+ const unmigratable = [];
31
+ for (const src of sources) {
32
+ // Build iteration history from journal — best-effort.
33
+ const events = await readJournalEvents(projectRoot, { entryId: src.uuid });
34
+ const iterationByStage = countIterationsByStage(events);
35
+ const earliest = events[0]?.at ?? new Date().toISOString();
36
+ const latest = events[events.length - 1]?.at ?? new Date().toISOString();
37
+ const priorStage = src.currentStage === 'Blocked' || src.currentStage === 'Cancelled'
38
+ ? inferPriorStageFromJournal(events)
39
+ : undefined;
40
+ const reviewState = latestReviewStateFromJournal(events);
41
+ const description = src.description !== '' ? src.description : undefined;
42
+ // Build entry with conditional spread to satisfy exactOptionalPropertyTypes.
43
+ const entry = {
44
+ uuid: src.uuid,
45
+ slug: src.slug,
46
+ title: src.title,
47
+ keywords: src.keywords,
48
+ source: src.source,
49
+ currentStage: src.currentStage,
50
+ iterationByStage,
51
+ createdAt: earliest,
52
+ updatedAt: latest,
53
+ ...(description !== undefined && { description }),
54
+ ...(priorStage !== undefined && { priorStage }),
55
+ ...(reviewState !== undefined && { reviewState }),
56
+ };
57
+ sidecars.push(entry);
58
+ }
59
+ if (!opts.dryRun) {
60
+ for (const e of sidecars) {
61
+ await writeSidecar(projectRoot, e);
62
+ await appendJournalEvent(projectRoot, {
63
+ kind: 'entry-created',
64
+ at: new Date().toISOString(),
65
+ entryId: e.uuid,
66
+ entry: e,
67
+ });
68
+ }
69
+ const newMd = renderCalendar(sidecars);
70
+ await writeFile(join(projectRoot, '.deskwork', 'calendar.md'), newMd);
71
+ }
72
+ return { entriesMigrated: sidecars.length, unmigratable };
73
+ }
74
+ function countIterationsByStage(events) {
75
+ const counts = {};
76
+ for (const e of events) {
77
+ if (e.kind === 'iteration') {
78
+ const stage = e.stage;
79
+ counts[stage] = (counts[stage] ?? 0) + 1;
80
+ }
81
+ }
82
+ return counts;
83
+ }
84
+ function inferPriorStageFromJournal(events) {
85
+ for (let i = events.length - 1; i >= 0; i--) {
86
+ const e = events[i];
87
+ if (e && e.kind === 'stage-transition' && e.to !== 'Blocked' && e.to !== 'Cancelled') {
88
+ return e.from;
89
+ }
90
+ }
91
+ return 'Drafting'; // safe default when no transition history is available
92
+ }
93
+ function latestReviewStateFromJournal(events) {
94
+ for (let i = events.length - 1; i >= 0; i--) {
95
+ const e = events[i];
96
+ if (e && e.kind === 'review-state-change' && e.to)
97
+ return e.to;
98
+ }
99
+ return undefined;
100
+ }
101
+ //# sourceMappingURL=migrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/doctor/migrate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface RepairOptions {
2
+ destructive: boolean;
3
+ }
4
+ export interface RepairResult {
5
+ applied: string[];
6
+ pendingDestructive: string[];
7
+ }
8
+ export declare function repairAll(projectRoot: string, opts: RepairOptions): Promise<RepairResult>;
9
+ //# sourceMappingURL=repair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.d.ts","sourceRoot":"","sources":["../../src/doctor/repair.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAyB/F"}
@@ -0,0 +1,35 @@
1
+ import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join, dirname } from 'node:path';
3
+ import { sidecarsDir } from "../sidecar/paths.js";
4
+ import { EntrySchema } from "../schema/entry.js";
5
+ import { renderCalendar } from "../calendar/render.js";
6
+ export async function repairAll(projectRoot, opts) {
7
+ void opts;
8
+ const result = { applied: [], pendingDestructive: [] };
9
+ // Regenerate calendar.md from sidecars
10
+ const dir = sidecarsDir(projectRoot);
11
+ let names = [];
12
+ try {
13
+ names = await readdir(dir);
14
+ }
15
+ catch {
16
+ return result;
17
+ }
18
+ const entries = [];
19
+ for (const name of names.filter(n => n.endsWith('.json'))) {
20
+ const raw = await readFile(join(dir, name), 'utf8');
21
+ try {
22
+ const parsed = EntrySchema.safeParse(JSON.parse(raw));
23
+ if (parsed.success)
24
+ entries.push(parsed.data);
25
+ }
26
+ catch { /* skip malformed */ }
27
+ }
28
+ const md = renderCalendar(entries);
29
+ const calendarPath = join(projectRoot, '.deskwork', 'calendar.md');
30
+ await mkdir(dirname(calendarPath), { recursive: true });
31
+ await writeFile(calendarPath, md);
32
+ result.applied.push('calendar-regenerated');
33
+ return result;
34
+ }
35
+ //# sourceMappingURL=repair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repair.js","sourceRoot":"","sources":["../../src/doctor/repair.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAc,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAWvD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,WAAmB,EAAE,IAAmB;IACtE,KAAK,IAAI,CAAC;IACV,MAAM,MAAM,GAAiB,EAAE,OAAO,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC;IAErE,uCAAuC;IACvC,MAAM,GAAG,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IACrC,IAAI,KAAK,GAAa,EAAE,CAAC;IACzB,IAAI,CAAC;QAAC,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,MAAM,CAAC;IAAC,CAAC;IAE5D,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACtD,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;IACnE,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,MAAM,SAAS,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAClC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IAE5C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ export interface ValidationFailure {
2
+ category: 'schema' | 'calendar-sidecar' | 'frontmatter-sidecar' | 'journal-sidecar' | 'iteration-history' | 'file-presence' | 'stage-invariants' | 'cross-entry' | 'migration';
3
+ message: string;
4
+ entryId?: string;
5
+ path?: string;
6
+ }
7
+ export interface ValidationResult {
8
+ failures: ValidationFailure[];
9
+ }
10
+ export declare function validateAll(projectRoot: string): Promise<ValidationResult>;
11
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/doctor/validate.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EACJ,QAAQ,GACR,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,GACnB,eAAe,GACf,kBAAkB,GAClB,aAAa,GACb,WAAW,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AA8YD,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAWhF"}