frontier-mcp 0.1.0

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 (115) hide show
  1. package/README.md +104 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +10 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/domain.d.ts +230 -0
  7. package/dist/domain.d.ts.map +1 -0
  8. package/dist/domain.js +23 -0
  9. package/dist/domain.js.map +1 -0
  10. package/dist/edges.d.ts +35 -0
  11. package/dist/edges.d.ts.map +1 -0
  12. package/dist/edges.js +61 -0
  13. package/dist/edges.js.map +1 -0
  14. package/dist/frontier.d.ts +12 -0
  15. package/dist/frontier.d.ts.map +1 -0
  16. package/dist/frontier.js +34 -0
  17. package/dist/frontier.js.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/server.d.ts +29 -0
  23. package/dist/server.d.ts.map +1 -0
  24. package/dist/server.js +210 -0
  25. package/dist/server.js.map +1 -0
  26. package/dist/storage/driver.d.ts +131 -0
  27. package/dist/storage/driver.d.ts.map +1 -0
  28. package/dist/storage/driver.js +43 -0
  29. package/dist/storage/driver.js.map +1 -0
  30. package/dist/storage/markdown/create.d.ts +52 -0
  31. package/dist/storage/markdown/create.d.ts.map +1 -0
  32. package/dist/storage/markdown/create.js +298 -0
  33. package/dist/storage/markdown/create.js.map +1 -0
  34. package/dist/storage/markdown/driver.d.ts +7 -0
  35. package/dist/storage/markdown/driver.d.ts.map +1 -0
  36. package/dist/storage/markdown/driver.js +492 -0
  37. package/dist/storage/markdown/driver.js.map +1 -0
  38. package/dist/storage/markdown/frontmatter.d.ts +17 -0
  39. package/dist/storage/markdown/frontmatter.d.ts.map +1 -0
  40. package/dist/storage/markdown/frontmatter.js +36 -0
  41. package/dist/storage/markdown/frontmatter.js.map +1 -0
  42. package/dist/storage/markdown/header-doc.d.ts +56 -0
  43. package/dist/storage/markdown/header-doc.d.ts.map +1 -0
  44. package/dist/storage/markdown/header-doc.js +287 -0
  45. package/dist/storage/markdown/header-doc.js.map +1 -0
  46. package/dist/storage/markdown/legacy.d.ts +16 -0
  47. package/dist/storage/markdown/legacy.d.ts.map +1 -0
  48. package/dist/storage/markdown/legacy.js +152 -0
  49. package/dist/storage/markdown/legacy.js.map +1 -0
  50. package/dist/storage/markdown/migrate.d.ts +27 -0
  51. package/dist/storage/markdown/migrate.d.ts.map +1 -0
  52. package/dist/storage/markdown/migrate.js +158 -0
  53. package/dist/storage/markdown/migrate.js.map +1 -0
  54. package/dist/storage/markdown/serialize.d.ts +37 -0
  55. package/dist/storage/markdown/serialize.d.ts.map +1 -0
  56. package/dist/storage/markdown/serialize.js +321 -0
  57. package/dist/storage/markdown/serialize.js.map +1 -0
  58. package/dist/storage/markdown/ticket.d.ts +12 -0
  59. package/dist/storage/markdown/ticket.d.ts.map +1 -0
  60. package/dist/storage/markdown/ticket.js +97 -0
  61. package/dist/storage/markdown/ticket.js.map +1 -0
  62. package/dist/storage/markdown/write.d.ts +42 -0
  63. package/dist/storage/markdown/write.d.ts.map +1 -0
  64. package/dist/storage/markdown/write.js +134 -0
  65. package/dist/storage/markdown/write.js.map +1 -0
  66. package/dist/tools/create-tickets.d.ts +63 -0
  67. package/dist/tools/create-tickets.d.ts.map +1 -0
  68. package/dist/tools/create-tickets.js +161 -0
  69. package/dist/tools/create-tickets.js.map +1 -0
  70. package/dist/tools/edit-map.d.ts +29 -0
  71. package/dist/tools/edit-map.d.ts.map +1 -0
  72. package/dist/tools/edit-map.js +81 -0
  73. package/dist/tools/edit-map.js.map +1 -0
  74. package/dist/tools/get-board.d.ts +29 -0
  75. package/dist/tools/get-board.d.ts.map +1 -0
  76. package/dist/tools/get-board.js +190 -0
  77. package/dist/tools/get-board.js.map +1 -0
  78. package/dist/tools/get-tickets.d.ts +14 -0
  79. package/dist/tools/get-tickets.d.ts.map +1 -0
  80. package/dist/tools/get-tickets.js +56 -0
  81. package/dist/tools/get-tickets.js.map +1 -0
  82. package/dist/tools/list-efforts.d.ts +16 -0
  83. package/dist/tools/list-efforts.d.ts.map +1 -0
  84. package/dist/tools/list-efforts.js +38 -0
  85. package/dist/tools/list-efforts.js.map +1 -0
  86. package/dist/tools/migrate-effort.d.ts +11 -0
  87. package/dist/tools/migrate-effort.d.ts.map +1 -0
  88. package/dist/tools/migrate-effort.js +54 -0
  89. package/dist/tools/migrate-effort.js.map +1 -0
  90. package/dist/tools/spec.d.ts +12 -0
  91. package/dist/tools/spec.d.ts.map +1 -0
  92. package/dist/tools/spec.js +27 -0
  93. package/dist/tools/spec.js.map +1 -0
  94. package/dist/tools/update-ticket.d.ts +62 -0
  95. package/dist/tools/update-ticket.d.ts.map +1 -0
  96. package/dist/tools/update-ticket.js +164 -0
  97. package/dist/tools/update-ticket.js.map +1 -0
  98. package/dist/tracker-doc.d.ts +10 -0
  99. package/dist/tracker-doc.d.ts.map +1 -0
  100. package/dist/tracker-doc.js +16 -0
  101. package/dist/tracker-doc.js.map +1 -0
  102. package/dist/workspace-index.d.ts +39 -0
  103. package/dist/workspace-index.d.ts.map +1 -0
  104. package/dist/workspace-index.js +83 -0
  105. package/dist/workspace-index.js.map +1 -0
  106. package/dist/workspace-watcher.d.ts +24 -0
  107. package/dist/workspace-watcher.d.ts.map +1 -0
  108. package/dist/workspace-watcher.js +87 -0
  109. package/dist/workspace-watcher.js.map +1 -0
  110. package/dist/workspace.d.ts +23 -0
  111. package/dist/workspace.d.ts.map +1 -0
  112. package/dist/workspace.js +70 -0
  113. package/dist/workspace.js.map +1 -0
  114. package/docs/agents/issue-tracker.md +141 -0
  115. package/package.json +56 -0
@@ -0,0 +1,287 @@
1
+ import { splitFrontmatter } from './frontmatter.js';
2
+ import { NoSuchFogPatch } from '../driver.js';
3
+ /**
4
+ * The Map's typed sections live under `##` headings. Only this module knows
5
+ * that — the driver interface carries the values, never the section names, so
6
+ * a SQLite driver can hold them as columns.
7
+ */
8
+ const NEXT_HEADING = /^##[ \t]/m;
9
+ export const SECTION_HEADINGS = {
10
+ destination: 'Destination',
11
+ notes: 'Notes',
12
+ decisions: 'Decisions so far',
13
+ fog: 'Not yet specified',
14
+ outOfScope: 'Out of scope',
15
+ };
16
+ /** Markers stating that their contents are overwritten — ADR 0002. */
17
+ export const GENERATED_OPEN = '<!-- GENERATED: overwritten on every mutation through the server. Do not hand-edit. -->';
18
+ export const GENERATED_CLOSE = '<!-- /GENERATED -->';
19
+ const GENERATED_BLOCK = /<!--[ \t]*GENERATED\b[^>]*-->\r?\n?([\s\S]*?)<!--[ \t]*\/GENERATED[ \t]*-->/i;
20
+ const BULLET = /^[ \t]*[-*][ \t]+(.+?)[ \t]*$/;
21
+ /**
22
+ * The opening paragraph of a Spec — the first prose under its title. A Spec has
23
+ * no Destination section, so this is the orienting text a Spec-only Effort has,
24
+ * and a Board that opens with nothing leaves every Ticket line uninterpretable.
25
+ */
26
+ export function readSpecOpening(contents) {
27
+ const { body } = splitFrontmatter(contents);
28
+ return body
29
+ .split(/\n{2,}/)
30
+ .map(block => block.trim())
31
+ .find(block => block !== '' && !block.startsWith('#') && !isFieldLine(block));
32
+ }
33
+ /**
34
+ * Typed sections a caller can read without paying for the whole Map body —
35
+ * Decisions-so-far is deliberately absent; it is never read from the file.
36
+ */
37
+ export function readMapDocument(contents, revision) {
38
+ return {
39
+ destination: readSection(contents, SECTION_HEADINGS.destination),
40
+ notes: readSection(contents, SECTION_HEADINGS.notes),
41
+ notYetSpecified: bulletsOutsideGenerated(readSectionRaw(contents, SECTION_HEADINGS.fog)),
42
+ outOfScope: bulletsOutsideGenerated(readSectionRaw(contents, SECTION_HEADINGS.outOfScope)),
43
+ revision,
44
+ };
45
+ }
46
+ /**
47
+ * Apply a section edit and rewrite the derived blocks. Content outside the
48
+ * GENERATED markers is left alone; the blocks themselves are replaced whole.
49
+ * When Decisions has no markers, its whole body is replaced by a GENERATED
50
+ * block (unfenced stale prose must not remain). Out of scope without markers
51
+ * keeps unfenced bullets as hand-owned and installs the GENERATED block under
52
+ * them. Missing headings are not invented unless
53
+ * {@link ApplyMapEditOptions.ensureDerivedSections} is set.
54
+ */
55
+ export function applyMapEdit(contents, edit, decisions, dropped, options = {}) {
56
+ let next = contents;
57
+ if (edit.destination !== undefined) {
58
+ next = setSection(next, SECTION_HEADINGS.destination, edit.destination.trim());
59
+ }
60
+ if (edit.notes !== undefined) {
61
+ next = setSection(next, SECTION_HEADINGS.notes, edit.notes.trim());
62
+ }
63
+ if (edit.addFog !== undefined) {
64
+ next = appendBullet(next, SECTION_HEADINGS.fog, edit.addFog.trim());
65
+ }
66
+ if (edit.graduateFog !== undefined) {
67
+ next = removeBullet(next, SECTION_HEADINGS.fog, edit.graduateFog.trim());
68
+ }
69
+ if (edit.ruleOut !== undefined) {
70
+ next = appendBullet(next, SECTION_HEADINGS.outOfScope, edit.ruleOut.trim());
71
+ }
72
+ if (options.ensureDerivedSections) {
73
+ next = ensureDerivedHeading(next, SECTION_HEADINGS.decisions);
74
+ next = ensureDerivedHeading(next, SECTION_HEADINGS.outOfScope);
75
+ }
76
+ // Decisions is wholly derived: unfenced prose is stale cache, not hand-owned.
77
+ next = replaceGenerated(next, SECTION_HEADINGS.decisions, renderPointers(decisions), {
78
+ wipeUnfenced: true,
79
+ });
80
+ // Out of scope mixes hand-ruled bullets (outside) with dropped Tickets (inside).
81
+ next = replaceGenerated(next, SECTION_HEADINGS.outOfScope, renderPointers(dropped));
82
+ return next;
83
+ }
84
+ /** A brand-new Map file, with empty generated blocks ready for the first write. */
85
+ export function scaffoldMap(edit) {
86
+ const destination = edit.destination?.trim() ?? '';
87
+ const notes = edit.notes?.trim() ?? '';
88
+ const fog = edit.addFog !== undefined ? `- ${edit.addFog.trim()}` : '';
89
+ const ruled = edit.ruleOut !== undefined ? `- ${edit.ruleOut.trim()}` : '';
90
+ if (edit.graduateFog !== undefined)
91
+ throw new NoSuchFogPatch(edit.graduateFog);
92
+ return [
93
+ '---',
94
+ 'header: map',
95
+ '---',
96
+ '',
97
+ '# Map',
98
+ '',
99
+ `## ${SECTION_HEADINGS.destination}`,
100
+ '',
101
+ destination,
102
+ '',
103
+ `## ${SECTION_HEADINGS.notes}`,
104
+ '',
105
+ notes,
106
+ '',
107
+ `## ${SECTION_HEADINGS.decisions}`,
108
+ '',
109
+ GENERATED_OPEN,
110
+ GENERATED_CLOSE,
111
+ '',
112
+ `## ${SECTION_HEADINGS.fog}`,
113
+ '',
114
+ fog,
115
+ '',
116
+ `## ${SECTION_HEADINGS.outOfScope}`,
117
+ '',
118
+ ruled,
119
+ '',
120
+ GENERATED_OPEN,
121
+ GENERATED_CLOSE,
122
+ '',
123
+ ]
124
+ .join('\n')
125
+ .replace(/\n{3,}/g, '\n\n');
126
+ }
127
+ export function ensureTrailingNewline(text) {
128
+ return text.endsWith('\n') ? text : `${text}\n`;
129
+ }
130
+ function renderPointers(pointers) {
131
+ if (pointers.length === 0)
132
+ return '';
133
+ return pointers
134
+ .map(pointer => {
135
+ const head = `- [${pointer.id} — ${pointer.title}](${pointer.link})`;
136
+ return pointer.gist === '' ? head : `${head} — ${pointer.gist}`;
137
+ })
138
+ .join('\n');
139
+ }
140
+ export function readSection(contents, heading) {
141
+ const raw = readSectionRaw(contents, heading);
142
+ if (raw === undefined)
143
+ return undefined;
144
+ const trimmed = stripGenerated(raw).trim();
145
+ return trimmed === '' ? undefined : trimmed;
146
+ }
147
+ function readSectionRaw(contents, heading) {
148
+ const { body } = splitFrontmatter(contents);
149
+ const located = locateSection(body, heading);
150
+ if (located === undefined)
151
+ return undefined;
152
+ return body.slice(located.start, located.end);
153
+ }
154
+ function setSection(contents, heading, value) {
155
+ const { hasFence, raw, body } = splitFrontmatter(contents);
156
+ const located = locateSection(body, heading);
157
+ const sectionBody = value === '' ? '\n\n' : `\n\n${value}\n\n`;
158
+ let nextBody;
159
+ if (located === undefined) {
160
+ nextBody = ensureTrailingNewline(body) + `\n## ${heading}\n${sectionBody}`;
161
+ }
162
+ else {
163
+ const { start, end } = located;
164
+ // Keep any GENERATED block that lives in this section (Out of scope).
165
+ const existing = body.slice(start, end);
166
+ const generated = existing.match(GENERATED_BLOCK)?.[0];
167
+ const replacement = generated === undefined
168
+ ? sectionBody
169
+ : `${sectionBody}\n${generated.endsWith('\n') ? generated : `${generated}\n`}`;
170
+ nextBody = body.slice(0, start) + replacement + body.slice(end);
171
+ }
172
+ return reassemble(hasFence, raw, nextBody);
173
+ }
174
+ function appendBullet(contents, heading, text) {
175
+ const raw = readSectionRaw(contents, heading);
176
+ if (raw === undefined) {
177
+ return setSection(contents, heading, `- ${text}`);
178
+ }
179
+ const outside = stripGenerated(raw).replace(/\s+$/, '');
180
+ const bullet = `- ${text}`;
181
+ const combined = outside === '' ? bullet : `${outside}\n${bullet}`;
182
+ return setSection(contents, heading, combined.trim());
183
+ }
184
+ function removeBullet(contents, heading, text) {
185
+ const raw = readSectionRaw(contents, heading);
186
+ if (raw === undefined)
187
+ throw new NoSuchFogPatch(text);
188
+ const lines = stripGenerated(raw).split('\n');
189
+ const index = lines.findIndex(line => {
190
+ const match = BULLET.exec(line);
191
+ return match?.[1] === text;
192
+ });
193
+ if (index < 0)
194
+ throw new NoSuchFogPatch(text);
195
+ const next = [...lines.slice(0, index), ...lines.slice(index + 1)]
196
+ .join('\n')
197
+ .replace(/^\n+/, '')
198
+ .replace(/\n+$/, '');
199
+ return setSection(contents, heading, next);
200
+ }
201
+ /**
202
+ * Append a server-owned heading with an empty GENERATED block when the Map
203
+ * never grew one. Only the resolve/drop refresh path asks for this — typed
204
+ * section edits must not reshape an unrelated Map.
205
+ */
206
+ function ensureDerivedHeading(contents, heading) {
207
+ if (readSectionRaw(contents, heading) !== undefined)
208
+ return contents;
209
+ const block = `${GENERATED_OPEN}\n${GENERATED_CLOSE}\n`;
210
+ const { hasFence, raw, body } = splitFrontmatter(contents);
211
+ const addition = `\n## ${heading}\n\n${block}`;
212
+ return reassemble(hasFence, raw, ensureTrailingNewline(body) + addition);
213
+ }
214
+ function replaceGenerated(contents, heading, inner, options = {}) {
215
+ const raw = readSectionRaw(contents, heading);
216
+ // Content outside the markers is never touched — inventing a missing section
217
+ // would rewrite the Map shape on an unrelated edit.
218
+ if (raw === undefined)
219
+ return contents;
220
+ const block = inner === ''
221
+ ? `${GENERATED_OPEN}\n${GENERATED_CLOSE}\n`
222
+ : `${GENERATED_OPEN}\n${inner}\n${GENERATED_CLOSE}\n`;
223
+ const { hasFence, raw: fence, body } = splitFrontmatter(contents);
224
+ const { start, end } = locateSection(body, heading);
225
+ const section = body.slice(start, end);
226
+ let replacement;
227
+ if (GENERATED_BLOCK.test(section)) {
228
+ replacement = section.replace(GENERATED_BLOCK, block.trimEnd());
229
+ if (!replacement.endsWith('\n'))
230
+ replacement += '\n';
231
+ }
232
+ else if (options.wipeUnfenced) {
233
+ // No markers: the whole section body is the derived cache the server owns.
234
+ // Replacing (not appending) clears hand-typed stale Decisions that would
235
+ // otherwise lie next to a fresh GENERATED block — ADR 0002.
236
+ replacement = `\n${block}`;
237
+ }
238
+ else {
239
+ // Mixed sections (Out of scope): keep unfenced bullets as hand-owned and
240
+ // install the GENERATED dropped-Ticket block beneath them.
241
+ const outside = section.replace(/\s+$/, '');
242
+ replacement = outside === '' ? `\n${block}` : `${outside}\n\n${block}`;
243
+ }
244
+ return reassemble(hasFence, fence, body.slice(0, start) + replacement + body.slice(end));
245
+ }
246
+ /** Byte range of a section's body inside `body` (after the heading line). */
247
+ function locateSection(body, heading) {
248
+ const pattern = new RegExp(`^##[ \\t]+${escapeRegExp(heading)}[ \\t]*$`, 'im');
249
+ const match = pattern.exec(body);
250
+ if (match === null)
251
+ return undefined;
252
+ const start = match.index + match[0].length;
253
+ const after = body.slice(start);
254
+ const next = NEXT_HEADING.exec(after);
255
+ const end = next === null ? body.length : start + next.index;
256
+ return { start, end };
257
+ }
258
+ function bulletsOutsideGenerated(raw) {
259
+ if (raw === undefined)
260
+ return [];
261
+ return stripGenerated(raw)
262
+ .split('\n')
263
+ .map(line => BULLET.exec(line)?.[1])
264
+ .filter((item) => item !== undefined);
265
+ }
266
+ function stripGenerated(raw) {
267
+ return raw.replace(GENERATED_BLOCK, '');
268
+ }
269
+ function reassemble(hasFence, raw, body) {
270
+ // Preserve leading newlines in the body; only guarantee a trailing newline.
271
+ const next = ensureTrailingNewline(body);
272
+ if (!hasFence || raw === undefined)
273
+ return next;
274
+ return `---\n${raw}\n---\n\n${next.replace(/^\n+/, '')}`;
275
+ }
276
+ function escapeRegExp(value) {
277
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
278
+ }
279
+ /**
280
+ * A single `Key: value` line — `Status: ready-for-agent` and the like. These sit
281
+ * under the title in several of these documents and say nothing about where the
282
+ * Effort is going, which is the whole job of the text a Board opens with.
283
+ */
284
+ function isFieldLine(block) {
285
+ return !block.includes('\n') && /^[A-Za-z][\w -]{0,24}:[ \t]*\S+$/.test(block);
286
+ }
287
+ //# sourceMappingURL=header-doc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header-doc.js","sourceRoot":"","sources":["../../../src/storage/markdown/header-doc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Kind, Status } from '../../domain.ts';
2
+ export interface LegacyTicket {
3
+ readonly id: string | undefined;
4
+ readonly title: string;
5
+ readonly kind: Kind;
6
+ readonly status: Status;
7
+ readonly triage: string | undefined;
8
+ readonly blockedBy: readonly string[];
9
+ /** The raw `Status:` text when it did not map to a known Status. */
10
+ readonly unrecognizedStatus: string | undefined;
11
+ /** Sub-slice references (`T38.1`) coarsened to the Ticket that exists. */
12
+ readonly collapsedRefs: readonly string[];
13
+ readonly type: string | undefined;
14
+ }
15
+ export declare function parseLegacyBody(contents: string): LegacyTicket;
16
+ //# sourceMappingURL=legacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy.d.ts","sourceRoot":"","sources":["../../../src/storage/markdown/legacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAkFpD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,oEAAoE;IACpE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,CAmB9D"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * The best-effort parser for Ticket files that predate the schema. Every field
3
+ * it produces is an inference, which is why a Legacy Ticket is flagged: an
4
+ * agent needs to know which values to distrust.
5
+ *
6
+ * The shapes here are the ones the real repos actually contain, not the ones a
7
+ * schema would have liked. Reads never write, so a file parsed badly is only
8
+ * ever reported badly — it is never corrected on disk.
9
+ */
10
+ /** `# T30 — Grammy group boot`, or `# 01 — Is the fix a latent bug?` with no id. */
11
+ const HEADING = /^#[ \t]+(.+?)[ \t]*$/m;
12
+ const HEADING_ID = /^(T\d+)[ \t]*[—–-][ \t]*(.*)$/;
13
+ const HEADING_ORDER = /^\d+[ \t]*[—–-][ \t]*(.*)$/;
14
+ /**
15
+ * `Status: resolved`, `**Status:** done`, or ``Status: resolved — **`0.5.0`
16
+ * published**``. Bold is tolerated everywhere, because these files mark up
17
+ * their field labels inconsistently and a bold label is still a label.
18
+ */
19
+ const STATUS_LINE = /^\**Status\**:\**[ \t]*(.+?)[ \t]*$/im;
20
+ const TYPE_LINE = /^\**Type\**:\**[ \t]*(.+?)[ \t]*$/im;
21
+ /** `**Depends on:** T31 (...)` and `Blocked by: 01, 02`, bold or bare. */
22
+ const EDGE_LINE = /^\**(?:Depends on|Blocked by)\**:\**[ \t]*(.+?)[ \t]*$/gim;
23
+ /** A withdrawn Edge, struck through rather than deleted. */
24
+ const STRIKETHROUGH = /~~[\s\S]*?~~/g;
25
+ /**
26
+ * Whole markdown links, label and target both, so a path never contributes a
27
+ * false id. Verified against every Edge line in the fixtures: real
28
+ * references are always written outside their link, so nothing is lost — and a
29
+ * label like `[../T99/issues/01.md]` would otherwise mint an Edge to T99.
30
+ */
31
+ const MARKDOWN_LINK = /\[[^\]]*\]\([^)]*\)/g;
32
+ /** `T31`, and `T38.1` — a sub-slice of T38, which is the Ticket that exists. */
33
+ const TICKET_REF = /\bT(\d+)(\.\d+)?\b/g;
34
+ /**
35
+ * A bare `01` in `Blocked by: 01, 02` — this Effort's sort orders, written
36
+ * before ids existed. Anything touching a `.` is excluded so a version string
37
+ * like `0.5.0` never becomes an Edge.
38
+ */
39
+ const BARE_ORDER = /(?:^|[\s,;])(\d{1,3})(?![.\d])(?=[\s,;]|$)/g;
40
+ /**
41
+ * Status words seen in the wild, mapped to the four the schema has. `done` and
42
+ * `fixed` are resolved by another name. `superseded` and `deferred` are closed
43
+ * rather than open, which is the safe direction: a Ticket wrongly left off the
44
+ * Frontier is a missed opportunity, one wrongly put on it is wasted work.
45
+ */
46
+ const STATUS_WORDS = new Map([
47
+ ['open', 'open'],
48
+ ['claimed', 'claimed'],
49
+ ['resolved', 'resolved'],
50
+ ['done', 'resolved'],
51
+ ['fixed', 'resolved'],
52
+ ['dropped', 'dropped'],
53
+ ['superseded', 'dropped'],
54
+ ['deferred', 'dropped'],
55
+ ]);
56
+ /**
57
+ * Triage roles found in a `Status:` line, where they do not belong. These are
58
+ * checked first: a triage role says nothing about lifecycle position, so
59
+ * finding one means the Status field never held a Status at all.
60
+ *
61
+ * `wontfix` is here and deliberately not in {@link STATUS_WORDS} — it is a
62
+ * Triage role, not a Status, and mapping it to `dropped` would make it
63
+ * contagious, turning every dependent into a broken Edge.
64
+ */
65
+ const TRIAGE_WORDS = new Set([
66
+ 'needs-triage',
67
+ 'needs-info',
68
+ 'ready-for-agent',
69
+ 'ready-for-human',
70
+ 'wontfix',
71
+ ]);
72
+ export function parseLegacyBody(contents) {
73
+ const { id, title } = readHeading(contents);
74
+ const { status, triage, unrecognized } = readStatus(STATUS_LINE.exec(contents)?.[1]);
75
+ const { edges, collapsed } = readEdges(contents);
76
+ const type = TYPE_LINE.exec(contents)?.[1];
77
+ return {
78
+ id,
79
+ title,
80
+ // A `Type:` line is wayfinder's decision vocabulary; its presence is what
81
+ // marks the Ticket as a question rather than a slice of work.
82
+ kind: type === undefined ? 'build' : 'decision',
83
+ type,
84
+ status,
85
+ triage,
86
+ blockedBy: edges,
87
+ unrecognizedStatus: unrecognized,
88
+ collapsedRefs: collapsed,
89
+ };
90
+ }
91
+ function readHeading(contents) {
92
+ const heading = HEADING.exec(contents)?.[1]?.trim();
93
+ if (heading === undefined || heading === '')
94
+ return { id: undefined, title: '(untitled)' };
95
+ const withId = HEADING_ID.exec(heading);
96
+ if (withId?.[1] !== undefined)
97
+ return { id: withId[1], title: (withId[2] ?? '').trim() };
98
+ // `# 01 — Title`: the number is this Effort's sort order, never an id. A
99
+ // per-Effort `01` is not unique repo-wide, so minting one is migration's job.
100
+ const withOrder = HEADING_ORDER.exec(heading);
101
+ if (withOrder?.[1] !== undefined)
102
+ return { id: undefined, title: withOrder[1].trim() };
103
+ return { id: undefined, title: heading };
104
+ }
105
+ function readStatus(raw) {
106
+ if (raw === undefined)
107
+ return { status: 'open', triage: undefined, unrecognized: undefined };
108
+ // `resolved — 2026-08-04` and `deferred to 0.6.0` both carry their meaning in
109
+ // the first word; everything after it is a note for a human.
110
+ const word = (raw.split(/[—–]| - /)[0] ?? '').trim().toLowerCase();
111
+ const first = (word.split(/\s+/)[0] ?? '').replace(/[.,]$/, '');
112
+ // A Triage role is checked first, and leaves the lifecycle position alone —
113
+ // it never described one.
114
+ if (TRIAGE_WORDS.has(first))
115
+ return { status: 'open', triage: first, unrecognized: undefined };
116
+ const status = STATUS_WORDS.get(first);
117
+ if (status !== undefined)
118
+ return { status, triage: undefined, unrecognized: undefined };
119
+ return { status: 'open', triage: undefined, unrecognized: raw };
120
+ }
121
+ /**
122
+ * Edges out of prose. Struck-through text is dropped because a withdrawn Edge
123
+ * is not an Edge, and whole markdown links are stripped so a relative path
124
+ * never contributes an id of its own.
125
+ *
126
+ * Bare numbers (`Blocked by: 01, 02`) are kept verbatim. They are this Effort's
127
+ * sort orders, not ids, and are reported as unresolvable rather than guessed at
128
+ * — resolving them would invent a repo-wide identity the file never had.
129
+ */
130
+ function readEdges(contents) {
131
+ const edges = new Set();
132
+ const collapsed = new Set();
133
+ for (const line of contents.matchAll(EDGE_LINE)) {
134
+ const declared = (line[1] ?? '').replace(STRIKETHROUGH, '').replace(MARKDOWN_LINK, '');
135
+ if (/^\s*(?:—|–|-|none)\s*$/i.test(declared))
136
+ continue;
137
+ for (const ref of declared.matchAll(TICKET_REF)) {
138
+ const id = `T${ref[1] ?? ''}`;
139
+ // `T38.1` names a slice of T38, and T38 is the Ticket that exists — but
140
+ // the reader is told, because the Edge on the Board is then coarser than
141
+ // the one the file declared.
142
+ if (ref[2] !== undefined)
143
+ collapsed.add(`${id}${ref[2]}`);
144
+ edges.add(id);
145
+ }
146
+ for (const bare of declared.matchAll(BARE_ORDER))
147
+ edges.add(bare[1] ?? '');
148
+ }
149
+ edges.delete('');
150
+ return { edges: [...edges], collapsed: [...collapsed] };
151
+ }
152
+ //# sourceMappingURL=legacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy.js","sourceRoot":"","sources":["../../../src/storage/markdown/legacy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { MigrationReport, Ticket } from '../../domain.ts';
2
+ import { type Rescan } from './create.ts';
3
+ export interface TicketFileEntry {
4
+ readonly filename: string;
5
+ readonly ticket: Ticket;
6
+ }
7
+ export interface MigrateFilesRequest {
8
+ readonly scratch: string;
9
+ readonly effort: string;
10
+ readonly issues: string;
11
+ readonly entries: readonly TicketFileEntry[];
12
+ readonly allTickets: readonly Ticket[];
13
+ readonly preview: boolean;
14
+ readonly rename: boolean;
15
+ readonly rescan: Rescan;
16
+ readonly readContents: (filename: string) => Promise<string>;
17
+ }
18
+ /**
19
+ * Normalize Legacy Tickets in one Effort, mint ids for those without, and
20
+ * optionally rename. Preview plans mint ids from the scan and writes nothing.
21
+ *
22
+ * Bare sort-order Edges (`Blocked by: 01`) are rewritten to the id the target
23
+ * Ticket carries after this call — preserved or newly minted — so migration
24
+ * does not leave a Board full of unresolvable `01` references.
25
+ */
26
+ export declare function migrateEffortFiles(request: MigrateFilesRequest): Promise<MigrationReport>;
27
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/storage/markdown/migrate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAChF,OAAO,EAAqD,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAI7F,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9D;AASD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CA0B/F"}