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.
- package/README.md +104 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +10 -0
- package/dist/bin.js.map +1 -0
- package/dist/domain.d.ts +230 -0
- package/dist/domain.d.ts.map +1 -0
- package/dist/domain.js +23 -0
- package/dist/domain.js.map +1 -0
- package/dist/edges.d.ts +35 -0
- package/dist/edges.d.ts.map +1 -0
- package/dist/edges.js +61 -0
- package/dist/edges.js.map +1 -0
- package/dist/frontier.d.ts +12 -0
- package/dist/frontier.d.ts.map +1 -0
- package/dist/frontier.js +34 -0
- package/dist/frontier.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +29 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +210 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/driver.d.ts +131 -0
- package/dist/storage/driver.d.ts.map +1 -0
- package/dist/storage/driver.js +43 -0
- package/dist/storage/driver.js.map +1 -0
- package/dist/storage/markdown/create.d.ts +52 -0
- package/dist/storage/markdown/create.d.ts.map +1 -0
- package/dist/storage/markdown/create.js +298 -0
- package/dist/storage/markdown/create.js.map +1 -0
- package/dist/storage/markdown/driver.d.ts +7 -0
- package/dist/storage/markdown/driver.d.ts.map +1 -0
- package/dist/storage/markdown/driver.js +492 -0
- package/dist/storage/markdown/driver.js.map +1 -0
- package/dist/storage/markdown/frontmatter.d.ts +17 -0
- package/dist/storage/markdown/frontmatter.d.ts.map +1 -0
- package/dist/storage/markdown/frontmatter.js +36 -0
- package/dist/storage/markdown/frontmatter.js.map +1 -0
- package/dist/storage/markdown/header-doc.d.ts +56 -0
- package/dist/storage/markdown/header-doc.d.ts.map +1 -0
- package/dist/storage/markdown/header-doc.js +287 -0
- package/dist/storage/markdown/header-doc.js.map +1 -0
- package/dist/storage/markdown/legacy.d.ts +16 -0
- package/dist/storage/markdown/legacy.d.ts.map +1 -0
- package/dist/storage/markdown/legacy.js +152 -0
- package/dist/storage/markdown/legacy.js.map +1 -0
- package/dist/storage/markdown/migrate.d.ts +27 -0
- package/dist/storage/markdown/migrate.d.ts.map +1 -0
- package/dist/storage/markdown/migrate.js +158 -0
- package/dist/storage/markdown/migrate.js.map +1 -0
- package/dist/storage/markdown/serialize.d.ts +37 -0
- package/dist/storage/markdown/serialize.d.ts.map +1 -0
- package/dist/storage/markdown/serialize.js +321 -0
- package/dist/storage/markdown/serialize.js.map +1 -0
- package/dist/storage/markdown/ticket.d.ts +12 -0
- package/dist/storage/markdown/ticket.d.ts.map +1 -0
- package/dist/storage/markdown/ticket.js +97 -0
- package/dist/storage/markdown/ticket.js.map +1 -0
- package/dist/storage/markdown/write.d.ts +42 -0
- package/dist/storage/markdown/write.d.ts.map +1 -0
- package/dist/storage/markdown/write.js +134 -0
- package/dist/storage/markdown/write.js.map +1 -0
- package/dist/tools/create-tickets.d.ts +63 -0
- package/dist/tools/create-tickets.d.ts.map +1 -0
- package/dist/tools/create-tickets.js +161 -0
- package/dist/tools/create-tickets.js.map +1 -0
- package/dist/tools/edit-map.d.ts +29 -0
- package/dist/tools/edit-map.d.ts.map +1 -0
- package/dist/tools/edit-map.js +81 -0
- package/dist/tools/edit-map.js.map +1 -0
- package/dist/tools/get-board.d.ts +29 -0
- package/dist/tools/get-board.d.ts.map +1 -0
- package/dist/tools/get-board.js +190 -0
- package/dist/tools/get-board.js.map +1 -0
- package/dist/tools/get-tickets.d.ts +14 -0
- package/dist/tools/get-tickets.d.ts.map +1 -0
- package/dist/tools/get-tickets.js +56 -0
- package/dist/tools/get-tickets.js.map +1 -0
- package/dist/tools/list-efforts.d.ts +16 -0
- package/dist/tools/list-efforts.d.ts.map +1 -0
- package/dist/tools/list-efforts.js +38 -0
- package/dist/tools/list-efforts.js.map +1 -0
- package/dist/tools/migrate-effort.d.ts +11 -0
- package/dist/tools/migrate-effort.d.ts.map +1 -0
- package/dist/tools/migrate-effort.js +54 -0
- package/dist/tools/migrate-effort.js.map +1 -0
- package/dist/tools/spec.d.ts +12 -0
- package/dist/tools/spec.d.ts.map +1 -0
- package/dist/tools/spec.js +27 -0
- package/dist/tools/spec.js.map +1 -0
- package/dist/tools/update-ticket.d.ts +62 -0
- package/dist/tools/update-ticket.d.ts.map +1 -0
- package/dist/tools/update-ticket.js +164 -0
- package/dist/tools/update-ticket.js.map +1 -0
- package/dist/tracker-doc.d.ts +10 -0
- package/dist/tracker-doc.d.ts.map +1 -0
- package/dist/tracker-doc.js +16 -0
- package/dist/tracker-doc.js.map +1 -0
- package/dist/workspace-index.d.ts +39 -0
- package/dist/workspace-index.d.ts.map +1 -0
- package/dist/workspace-index.js +83 -0
- package/dist/workspace-index.js.map +1 -0
- package/dist/workspace-watcher.d.ts +24 -0
- package/dist/workspace-watcher.d.ts.map +1 -0
- package/dist/workspace-watcher.js +87 -0
- package/dist/workspace-watcher.js.map +1 -0
- package/dist/workspace.d.ts +23 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +70 -0
- package/dist/workspace.js.map +1 -0
- package/docs/agents/issue-tracker.md +141 -0
- package/package.json +56 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { rename as renameFile, unlink } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { peekMintedIds, ticketFilename, withIdReservations } from './create.js';
|
|
4
|
+
import { normalizeTicket } from './serialize.js';
|
|
5
|
+
import { stage, unstage, writeAtomically } from './write.js';
|
|
6
|
+
/**
|
|
7
|
+
* Normalize Legacy Tickets in one Effort, mint ids for those without, and
|
|
8
|
+
* optionally rename. Preview plans mint ids from the scan and writes nothing.
|
|
9
|
+
*
|
|
10
|
+
* Bare sort-order Edges (`Blocked by: 01`) are rewritten to the id the target
|
|
11
|
+
* Ticket carries after this call — preserved or newly minted — so migration
|
|
12
|
+
* does not leave a Board full of unresolvable `01` references.
|
|
13
|
+
*/
|
|
14
|
+
export async function migrateEffortFiles(request) {
|
|
15
|
+
const { effort, entries, preview, rename } = request;
|
|
16
|
+
const candidates = entries.filter(entry => entry.ticket.legacy || entry.ticket.id === undefined);
|
|
17
|
+
if (candidates.length === 0) {
|
|
18
|
+
return { effort, preview, renamed: rename, changes: [] };
|
|
19
|
+
}
|
|
20
|
+
const needMint = candidates.filter(entry => entry.ticket.id === undefined).length;
|
|
21
|
+
const used = idsOf(request.allTickets);
|
|
22
|
+
if (preview) {
|
|
23
|
+
const plan = planChanges(candidates, peekMintedIds(used, needMint), entries, rename);
|
|
24
|
+
return { effort, preview: true, renamed: rename, changes: plan.map(file => file.change) };
|
|
25
|
+
}
|
|
26
|
+
return withIdReservations(request.scratch, needMint, request.rescan, async (minted) => {
|
|
27
|
+
const plan = planChanges(candidates, minted, entries, rename);
|
|
28
|
+
await applyPlan(request, plan);
|
|
29
|
+
return {
|
|
30
|
+
effort,
|
|
31
|
+
preview: false,
|
|
32
|
+
renamed: rename,
|
|
33
|
+
changes: plan.map(file => file.change),
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function planChanges(candidates, minted, entries, rename) {
|
|
38
|
+
let mintAt = 0;
|
|
39
|
+
const idByOrder = new Map();
|
|
40
|
+
// Every Ticket in the Effort contributes to Edge remapping — a blocker that
|
|
41
|
+
// already had an id must still be findable by its sort order.
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (entry.ticket.id !== undefined)
|
|
44
|
+
idByOrder.set(entry.ticket.order, entry.ticket.id);
|
|
45
|
+
}
|
|
46
|
+
const assigned = new Map();
|
|
47
|
+
for (const entry of candidates) {
|
|
48
|
+
if (entry.ticket.id !== undefined) {
|
|
49
|
+
assigned.set(entry.ticket.handle, entry.ticket.id);
|
|
50
|
+
idByOrder.set(entry.ticket.order, entry.ticket.id);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const id = minted[mintAt++];
|
|
54
|
+
if (id === undefined)
|
|
55
|
+
throw new Error('Reserved fewer ids than Tickets needing one.');
|
|
56
|
+
assigned.set(entry.ticket.handle, id);
|
|
57
|
+
idByOrder.set(entry.ticket.order, id);
|
|
58
|
+
}
|
|
59
|
+
return candidates.map(entry => {
|
|
60
|
+
const id = assigned.get(entry.ticket.handle);
|
|
61
|
+
if (id === undefined)
|
|
62
|
+
throw new Error(`No id planned for ${entry.ticket.handle}.`);
|
|
63
|
+
const to = rename ? ticketFilename(entry.ticket.order, id, entry.ticket.title) : entry.filename;
|
|
64
|
+
const blockedBy = remapEdges(entry.ticket.blockedBy, idByOrder);
|
|
65
|
+
return {
|
|
66
|
+
from: entry.filename,
|
|
67
|
+
to,
|
|
68
|
+
defaults: {
|
|
69
|
+
id,
|
|
70
|
+
title: entry.ticket.title,
|
|
71
|
+
kind: entry.ticket.kind,
|
|
72
|
+
type: entry.ticket.type,
|
|
73
|
+
status: entry.ticket.status,
|
|
74
|
+
triage: entry.ticket.triage,
|
|
75
|
+
blockedBy,
|
|
76
|
+
},
|
|
77
|
+
change: {
|
|
78
|
+
beforeHandle: entry.ticket.handle,
|
|
79
|
+
id,
|
|
80
|
+
title: entry.ticket.title,
|
|
81
|
+
minted: entry.ticket.id === undefined,
|
|
82
|
+
normalized: entry.ticket.legacy,
|
|
83
|
+
fromName: to === entry.filename ? undefined : entry.filename,
|
|
84
|
+
toName: to === entry.filename ? undefined : to,
|
|
85
|
+
blockedBy,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async function applyPlan(request, plan) {
|
|
91
|
+
const targets = new Set(plan.map(file => file.to));
|
|
92
|
+
const sources = new Set(plan.map(file => file.from));
|
|
93
|
+
for (const file of plan) {
|
|
94
|
+
if (file.to !== file.from && !sources.has(file.to)) {
|
|
95
|
+
// A name that already exists and is not being vacated would destroy a Ticket.
|
|
96
|
+
const clash = request.entries.some(entry => entry.filename === file.to);
|
|
97
|
+
if (clash) {
|
|
98
|
+
throw new Error(`Cannot rename to ${file.to}: that name is already taken in ${request.effort}.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Detect two Tickets collapsing onto one target name.
|
|
103
|
+
if (targets.size !== plan.length) {
|
|
104
|
+
throw new Error(`Rename would collide on a filename inside Effort '${request.effort}'.`);
|
|
105
|
+
}
|
|
106
|
+
const prepared = await Promise.all(plan.map(async (file) => ({
|
|
107
|
+
...file,
|
|
108
|
+
contents: normalizeTicket(await request.readContents(file.from), file.defaults),
|
|
109
|
+
})));
|
|
110
|
+
// Same-name updates are per-file atomic. Renames stage every new file first,
|
|
111
|
+
// land them, then remove the vacated names — so a failure mid-batch never
|
|
112
|
+
// deletes a source whose replacement did not land.
|
|
113
|
+
const inPlace = prepared.filter(file => file.from === file.to);
|
|
114
|
+
const moved = prepared.filter(file => file.from !== file.to);
|
|
115
|
+
await Promise.all(inPlace.map(file => writeAtomically(join(request.issues, file.to), file.contents)));
|
|
116
|
+
if (moved.length === 0)
|
|
117
|
+
return;
|
|
118
|
+
const staged = await Promise.allSettled(moved.map(async (file) => ({
|
|
119
|
+
temporary: await stage(join(request.issues, file.to), file.contents),
|
|
120
|
+
target: join(request.issues, file.to),
|
|
121
|
+
from: join(request.issues, file.from),
|
|
122
|
+
})));
|
|
123
|
+
const ready = staged.filter(entry => entry.status === 'fulfilled').map(entry => entry.value);
|
|
124
|
+
const failed = staged.find(entry => entry.status === 'rejected');
|
|
125
|
+
if (failed !== undefined) {
|
|
126
|
+
await Promise.allSettled(ready.map(entry => unstage(entry.temporary)));
|
|
127
|
+
throw failed.reason;
|
|
128
|
+
}
|
|
129
|
+
const landed = await Promise.allSettled(ready.map(async (entry) => {
|
|
130
|
+
await renameFile(entry.temporary, entry.target);
|
|
131
|
+
return entry;
|
|
132
|
+
}));
|
|
133
|
+
const written = landed.filter(entry => entry.status === 'fulfilled').map(entry => entry.value);
|
|
134
|
+
const lost = landed.find(entry => entry.status === 'rejected');
|
|
135
|
+
if (lost !== undefined) {
|
|
136
|
+
await Promise.allSettled([
|
|
137
|
+
...written.map(entry => unlink(entry.target).catch(() => { })),
|
|
138
|
+
...ready.map(entry => unstage(entry.temporary)),
|
|
139
|
+
]);
|
|
140
|
+
throw lost.reason;
|
|
141
|
+
}
|
|
142
|
+
await Promise.all(written.map(entry => unlink(entry.from)));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A bare sort-order Edge becomes the id of the Ticket at that order. Real ids
|
|
146
|
+
* and anything else pass through unchanged — dangling Edges stay Board warnings.
|
|
147
|
+
*/
|
|
148
|
+
function remapEdges(edges, idByOrder) {
|
|
149
|
+
return edges.map(edge => {
|
|
150
|
+
if (!/^\d+$/.test(edge))
|
|
151
|
+
return edge;
|
|
152
|
+
return idByOrder.get(Number(edge)) ?? edge;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function idsOf(tickets) {
|
|
156
|
+
return new Set(tickets.map(entry => entry.id).filter(id => id !== undefined));
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/storage/markdown/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TicketEdit } from '../../domain.ts';
|
|
2
|
+
export interface Defaults {
|
|
3
|
+
readonly id: string | undefined;
|
|
4
|
+
readonly title: string;
|
|
5
|
+
readonly kind: string;
|
|
6
|
+
readonly type: string | undefined;
|
|
7
|
+
readonly status: string;
|
|
8
|
+
readonly triage: string | undefined;
|
|
9
|
+
readonly blockedBy: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a Ticket file to schema frontmatter. A Legacy file with no fence
|
|
13
|
+
* gets a full block from {@link defaults}; a file that already has frontmatter
|
|
14
|
+
* keeps key order and only gains a missing id plus an updated Edge list (so a
|
|
15
|
+
* bare sort-order Edge can become the id migration just minted). Body prose is
|
|
16
|
+
* kept verbatim either way.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalizeTicket(contents: string, defaults: Defaults): string;
|
|
19
|
+
/**
|
|
20
|
+
* Apply an edit to a Ticket file, returning the new contents.
|
|
21
|
+
*
|
|
22
|
+
* Per ADR 0003 the frontmatter is mutated as a parsed YAML document rather than
|
|
23
|
+
* rebuilt from an object, so comments, key order and quoting style survive on
|
|
24
|
+
* every field the edit did not name. The body is a string this only ever
|
|
25
|
+
* appends to or replaces one known section of.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyEdit(contents: string, edit: TicketEdit, defaults: Defaults): string;
|
|
28
|
+
/**
|
|
29
|
+
* A Ticket file as first written. Everything is a field this driver is creating,
|
|
30
|
+
* so the whole block is in schema order — the one case where that is allowed,
|
|
31
|
+
* since there is no author's ordering to preserve.
|
|
32
|
+
*
|
|
33
|
+
* The body is the caller's prose, stored verbatim. A draft with none produces a
|
|
34
|
+
* Ticket with none rather than a heading nobody asked for.
|
|
35
|
+
*/
|
|
36
|
+
export declare function serializeNew(fields: Defaults, body: string | undefined): string;
|
|
37
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../../src/storage/markdown/serialize.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA4BlD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAiB5E;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAsBxF;AA0LD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAK/E"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { Document, isSeq, parseDocument } from 'yaml';
|
|
2
|
+
import { splitFrontmatter } from './frontmatter.js';
|
|
3
|
+
/**
|
|
4
|
+
* Field order for frontmatter this driver *creates*. An existing file is never
|
|
5
|
+
* reordered — a new field is appended, because moving keys a write did not name
|
|
6
|
+
* is exactly the reformatting ADR 0003 forbids.
|
|
7
|
+
*/
|
|
8
|
+
const FIELD_ORDER = [
|
|
9
|
+
'id',
|
|
10
|
+
'title',
|
|
11
|
+
'kind',
|
|
12
|
+
'type',
|
|
13
|
+
'status',
|
|
14
|
+
'triage',
|
|
15
|
+
'blocked_by',
|
|
16
|
+
'claimed_by',
|
|
17
|
+
'claimed_at',
|
|
18
|
+
'answer_gist',
|
|
19
|
+
'dropped_reason',
|
|
20
|
+
];
|
|
21
|
+
/** The body sections a write may touch. Everything else is opaque prose. */
|
|
22
|
+
const ANSWER_HEADING = '## Answer';
|
|
23
|
+
const COMMENTS_HEADING = '## Comments';
|
|
24
|
+
/** Any `##` heading, so replacing the answer stops at the next section. */
|
|
25
|
+
const NEXT_SECTION = /^##[ \t]/m;
|
|
26
|
+
/**
|
|
27
|
+
* Normalize a Ticket file to schema frontmatter. A Legacy file with no fence
|
|
28
|
+
* gets a full block from {@link defaults}; a file that already has frontmatter
|
|
29
|
+
* keeps key order and only gains a missing id plus an updated Edge list (so a
|
|
30
|
+
* bare sort-order Edge can become the id migration just minted). Body prose is
|
|
31
|
+
* kept verbatim either way.
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeTicket(contents, defaults) {
|
|
34
|
+
const split = splitFrontmatter(contents);
|
|
35
|
+
if (!split.hasFence)
|
|
36
|
+
return applyEdit(contents, {}, defaults);
|
|
37
|
+
const document = existingDocument(split.raw);
|
|
38
|
+
if (document === undefined)
|
|
39
|
+
return applyEdit(contents, {}, defaults);
|
|
40
|
+
if (defaults.id !== undefined) {
|
|
41
|
+
const existing = document.get('id');
|
|
42
|
+
if (existing === undefined || existing === null || String(existing).trim() === '') {
|
|
43
|
+
document.set('id', defaults.id);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
setEdges(document, defaults.blockedBy);
|
|
47
|
+
const body = split.body.replace(/^\s+/, '').replace(/\n+$/, '');
|
|
48
|
+
return `---\n${render(document)}\n---\n\n${body}\n`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Apply an edit to a Ticket file, returning the new contents.
|
|
52
|
+
*
|
|
53
|
+
* Per ADR 0003 the frontmatter is mutated as a parsed YAML document rather than
|
|
54
|
+
* rebuilt from an object, so comments, key order and quoting style survive on
|
|
55
|
+
* every field the edit did not name. The body is a string this only ever
|
|
56
|
+
* appends to or replaces one known section of.
|
|
57
|
+
*/
|
|
58
|
+
export function applyEdit(contents, edit, defaults) {
|
|
59
|
+
const split = splitFrontmatter(contents);
|
|
60
|
+
const document = existingDocument(split.hasFence ? split.raw : undefined) ?? schemaFor(defaults);
|
|
61
|
+
for (const [field, value] of fieldsOf(edit)) {
|
|
62
|
+
if (value === null)
|
|
63
|
+
document.delete(field);
|
|
64
|
+
else
|
|
65
|
+
document.set(field, value);
|
|
66
|
+
}
|
|
67
|
+
if (edit.blockedBy !== undefined)
|
|
68
|
+
setEdges(document, edit.blockedBy);
|
|
69
|
+
const frontmatter = render(document);
|
|
70
|
+
// Only the outer edges are trimmed, and only newlines at the end — trailing
|
|
71
|
+
// spaces on the last line of a comment are part of what the author wrote.
|
|
72
|
+
let body = split.body.replace(/^\s+/, '').replace(/\n+$/, '');
|
|
73
|
+
body = withTicks(body, edit.tick);
|
|
74
|
+
// The answer goes in before the comment log so the log stays at the bottom,
|
|
75
|
+
// which is where the skills append and where a reader looks for it.
|
|
76
|
+
body = withAnswer(body, edit.answer);
|
|
77
|
+
body = withComment(body, edit.comment);
|
|
78
|
+
return `---\n${frontmatter}\n---\n\n${body}\n`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The Edge list, replaced whole rather than merged — a caller changing Edges
|
|
82
|
+
* knows which ones it wants, and a merge would leave no way to remove one.
|
|
83
|
+
*
|
|
84
|
+
* The existing collection style survives: a Ticket written `blocked_by: [T3]`
|
|
85
|
+
* must not come back as a block list just because its Edges changed. That is the
|
|
86
|
+
* same rule as the rest of ADR 0003, applied to the one field being named.
|
|
87
|
+
*/
|
|
88
|
+
function setEdges(document, blockedBy) {
|
|
89
|
+
const existing = document.get('blocked_by', true);
|
|
90
|
+
const flow = isSeq(existing) ? existing.flow === true : true;
|
|
91
|
+
document.set('blocked_by', document.createNode([...blockedBy], { flow }));
|
|
92
|
+
}
|
|
93
|
+
/** Where a `##` section ends: the next `##` heading, or the end of the body. */
|
|
94
|
+
function endOfSection(body, from) {
|
|
95
|
+
const after = body.slice(from);
|
|
96
|
+
const next = NEXT_SECTION.exec(after);
|
|
97
|
+
return next === null ? body.length : from + next.index;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Tick acceptance criteria by their text. Only the checkbox marker changes —
|
|
101
|
+
* the line keeps its own indentation and wording, and every other line of the
|
|
102
|
+
* body is untouched, so progress is visible without rewriting the body around
|
|
103
|
+
* it. Wrapped criteria match both as `get_tickets` returns them and re-joined
|
|
104
|
+
* onto one line; comparison collapses whitespace so neither form needs special
|
|
105
|
+
* casing.
|
|
106
|
+
*/
|
|
107
|
+
function withTicks(body, tick) {
|
|
108
|
+
if (tick === undefined || tick.length === 0)
|
|
109
|
+
return body;
|
|
110
|
+
const lines = body.split('\n');
|
|
111
|
+
const criteria = findCriteria(lines);
|
|
112
|
+
// Resolve every reference before changing anything, so an ambiguous or
|
|
113
|
+
// unmatched one leaves the file exactly as it was.
|
|
114
|
+
const targets = tick.map(asked => ({ asked, line: resolveCriterion(asked, criteria) }));
|
|
115
|
+
const ticked = [...lines];
|
|
116
|
+
for (const { line } of targets) {
|
|
117
|
+
const box = CHECKBOX.exec(ticked[line] ?? '');
|
|
118
|
+
if (box !== null)
|
|
119
|
+
ticked[line] = `${box[1]}x${box[3]}${box[4]}`;
|
|
120
|
+
}
|
|
121
|
+
return ticked.join('\n');
|
|
122
|
+
}
|
|
123
|
+
const CHECKBOX = /^(\s*[-*]\s*\[)( |x|X)(\]\s*)(.*)$/;
|
|
124
|
+
/** A fenced code block, where a `- [ ]` is illustration rather than a criterion. */
|
|
125
|
+
const FENCE_LINE = /^\s*(?:```|~~~)/;
|
|
126
|
+
/**
|
|
127
|
+
* Collapse runs of whitespace (including newlines and continuation indent) so
|
|
128
|
+
* a wrapped criterion matches both the `get_tickets` form and a single-line
|
|
129
|
+
* rejoin of the same words.
|
|
130
|
+
*/
|
|
131
|
+
function collapseCriterionText(text) {
|
|
132
|
+
return text.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A continuation of the criterion above: indented prose that is not itself a
|
|
136
|
+
* checkbox, heading, or fence. Blank lines end the wrap — house style never
|
|
137
|
+
* leaves a hole inside a criterion.
|
|
138
|
+
*/
|
|
139
|
+
function isCriterionContinuation(line) {
|
|
140
|
+
if (line.trim() === '')
|
|
141
|
+
return false;
|
|
142
|
+
if (CHECKBOX.test(line))
|
|
143
|
+
return false;
|
|
144
|
+
if (FENCE_LINE.test(line))
|
|
145
|
+
return false;
|
|
146
|
+
if (/^#{1,6}\s/.test(line))
|
|
147
|
+
return false;
|
|
148
|
+
return /^\s+\S/.test(line);
|
|
149
|
+
}
|
|
150
|
+
/** Checkbox lines outside any code fence — the ones that are really criteria. */
|
|
151
|
+
function findCriteria(lines) {
|
|
152
|
+
const criteria = [];
|
|
153
|
+
let fenced = false;
|
|
154
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
155
|
+
const line = lines[index] ?? '';
|
|
156
|
+
if (FENCE_LINE.test(line)) {
|
|
157
|
+
fenced = !fenced;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (fenced)
|
|
161
|
+
continue;
|
|
162
|
+
const box = CHECKBOX.exec(line);
|
|
163
|
+
if (box === null)
|
|
164
|
+
continue;
|
|
165
|
+
const checkboxLine = index;
|
|
166
|
+
const parts = [box[4] ?? ''];
|
|
167
|
+
while (index + 1 < lines.length && isCriterionContinuation(lines[index + 1] ?? '')) {
|
|
168
|
+
index += 1;
|
|
169
|
+
parts.push(lines[index] ?? '');
|
|
170
|
+
}
|
|
171
|
+
criteria.push({ line: checkboxLine, text: collapseCriterionText(parts.join('\n')) });
|
|
172
|
+
}
|
|
173
|
+
return criteria;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Exact text wins. Otherwise a substring is accepted only when it names exactly
|
|
177
|
+
* one criterion — a reference that fits several is a question, not an
|
|
178
|
+
* instruction, and ticking all of them would be answering it on the caller's
|
|
179
|
+
* behalf.
|
|
180
|
+
*/
|
|
181
|
+
function resolveCriterion(asked, criteria) {
|
|
182
|
+
const target = collapseCriterionText(asked);
|
|
183
|
+
const exact = criteria.filter(criterion => criterion.text === target);
|
|
184
|
+
if (exact.length === 1)
|
|
185
|
+
return exact[0]?.line ?? -1;
|
|
186
|
+
if (exact.length > 1)
|
|
187
|
+
throw new Error(`"${asked}" matches ${String(exact.length)} criteria.`);
|
|
188
|
+
const partial = criteria.filter(criterion => criterion.text.includes(target));
|
|
189
|
+
if (partial.length === 1)
|
|
190
|
+
return partial[0]?.line ?? -1;
|
|
191
|
+
if (partial.length > 1) {
|
|
192
|
+
throw new Error(`"${asked}" matches ${String(partial.length)} criteria — name one exactly: ` +
|
|
193
|
+
partial.map(criterion => `"${criterion.text}"`).join(', '));
|
|
194
|
+
}
|
|
195
|
+
throw new Error(`No acceptance criterion matches "${asked}".`);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Append to the comment log, creating its heading the first time. The comment
|
|
199
|
+
* is stored exactly as written — nothing is prepended, appended, or reformatted
|
|
200
|
+
* around it, including `/triage`'s mandatory disclaimer, which is the skill's
|
|
201
|
+
* own to write and not ours to inject.
|
|
202
|
+
*/
|
|
203
|
+
function withComment(body, comment) {
|
|
204
|
+
if (comment === undefined)
|
|
205
|
+
return body;
|
|
206
|
+
const heading = /^##[ \t]+Comments[ \t]*$/m.exec(body);
|
|
207
|
+
if (heading === null)
|
|
208
|
+
return `${body}\n\n${COMMENTS_HEADING}\n\n${comment}`;
|
|
209
|
+
// At the end of the Comments section, not the end of the body — a Ticket
|
|
210
|
+
// whose log is followed by another section would otherwise take the comment
|
|
211
|
+
// into whichever section happens to come last.
|
|
212
|
+
const end = endOfSection(body, heading.index + heading[0].length);
|
|
213
|
+
const before = body.slice(0, end).replace(/\n+$/, '');
|
|
214
|
+
const rest = body.slice(end);
|
|
215
|
+
return rest === '' ? `${before}\n\n${comment}` : `${before}\n\n${comment}\n\n${rest}`;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The existing frontmatter, or undefined when there is none to preserve. A fence
|
|
219
|
+
* whose YAML does not parse cannot be round-tripped — `yaml` refuses to
|
|
220
|
+
* stringify a document carrying errors — so it is rebuilt from the schema
|
|
221
|
+
* instead, which is what "strict on write" means for a file this broken.
|
|
222
|
+
*/
|
|
223
|
+
function existingDocument(raw) {
|
|
224
|
+
if (raw === undefined)
|
|
225
|
+
return undefined;
|
|
226
|
+
const parsed = parseDocument(raw);
|
|
227
|
+
return parsed.errors.length > 0 ? undefined : parsed;
|
|
228
|
+
}
|
|
229
|
+
function fieldsOf(edit) {
|
|
230
|
+
const pairs = [];
|
|
231
|
+
if (edit.status !== undefined)
|
|
232
|
+
pairs.push(['status', edit.status]);
|
|
233
|
+
if (edit.triage !== undefined)
|
|
234
|
+
pairs.push(['triage', edit.triage]);
|
|
235
|
+
if (edit.claimedBy !== undefined)
|
|
236
|
+
pairs.push(['claimed_by', edit.claimedBy]);
|
|
237
|
+
if (edit.claimedAt !== undefined)
|
|
238
|
+
pairs.push(['claimed_at', edit.claimedAt]);
|
|
239
|
+
if (edit.answerGist !== undefined)
|
|
240
|
+
pairs.push(['answer_gist', edit.answerGist]);
|
|
241
|
+
if (edit.droppedReason !== undefined)
|
|
242
|
+
pairs.push(['dropped_reason', edit.droppedReason]);
|
|
243
|
+
return pairs;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* A Ticket file as first written. Everything is a field this driver is creating,
|
|
247
|
+
* so the whole block is in schema order — the one case where that is allowed,
|
|
248
|
+
* since there is no author's ordering to preserve.
|
|
249
|
+
*
|
|
250
|
+
* The body is the caller's prose, stored verbatim. A draft with none produces a
|
|
251
|
+
* Ticket with none rather than a heading nobody asked for.
|
|
252
|
+
*/
|
|
253
|
+
export function serializeNew(fields, body) {
|
|
254
|
+
const frontmatter = render(schemaFor(fields));
|
|
255
|
+
const prose = body?.trim() ?? '';
|
|
256
|
+
return prose === '' ? `---\n${frontmatter}\n---\n` : `---\n${frontmatter}\n---\n\n${prose}\n`;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* `lineWidth: 0` stops long values being re-wrapped, and without
|
|
260
|
+
* `flowCollectionPadding: false` an untouched `[a, b]` comes back `[ a, b ]` —
|
|
261
|
+
* both would be reformatting a field the edit never named.
|
|
262
|
+
*/
|
|
263
|
+
function render(document) {
|
|
264
|
+
return document.toString({ lineWidth: 0, flowCollectionPadding: false }).trimEnd();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A Legacy file has no frontmatter, so a write is where it gets one — the
|
|
268
|
+
* "strict on write" half of the contract: the tracker converts itself as it is
|
|
269
|
+
* worked rather than in one risky pass. Only a created block gets to be in the
|
|
270
|
+
* schema's field order.
|
|
271
|
+
*/
|
|
272
|
+
function schemaFor(defaults) {
|
|
273
|
+
const document = new Document({});
|
|
274
|
+
const values = {
|
|
275
|
+
...(defaults.id === undefined ? {} : { id: defaults.id }),
|
|
276
|
+
title: defaults.title,
|
|
277
|
+
kind: defaults.kind,
|
|
278
|
+
...(defaults.type === undefined ? {} : { type: defaults.type }),
|
|
279
|
+
status: defaults.status,
|
|
280
|
+
...(defaults.triage === undefined ? {} : { triage: defaults.triage }),
|
|
281
|
+
// Flow, because that is how every Ticket in these repos is written by hand
|
|
282
|
+
// and a normalized file should not stand out from the ones beside it.
|
|
283
|
+
blocked_by: document.createNode([...defaults.blockedBy], { flow: true }),
|
|
284
|
+
};
|
|
285
|
+
for (const field of FIELD_ORDER) {
|
|
286
|
+
const value = values[field];
|
|
287
|
+
if (value !== undefined)
|
|
288
|
+
document.set(field, value);
|
|
289
|
+
}
|
|
290
|
+
return document;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* The answer replaces the Answer section and nothing else. Slicing to the end of
|
|
294
|
+
* the body would take the comment log with it — a Ticket body is opaque apart
|
|
295
|
+
* from the three places the schema says are edited.
|
|
296
|
+
*/
|
|
297
|
+
function withAnswer(body, answer) {
|
|
298
|
+
if (answer === undefined)
|
|
299
|
+
return body;
|
|
300
|
+
const section = `${ANSWER_HEADING}\n\n${answer.trim()}`;
|
|
301
|
+
const start = findAnswerHeading(body);
|
|
302
|
+
if (start === -1) {
|
|
303
|
+
// No Answer section yet. It goes above the comment log if there is one, so
|
|
304
|
+
// the log stays at the bottom where it is appended and read.
|
|
305
|
+
const comments = /^##[ \t]+Comments[ \t]*$/m.exec(body);
|
|
306
|
+
if (comments === null)
|
|
307
|
+
return `${body}\n\n${section}`;
|
|
308
|
+
const head = body.slice(0, comments.index).replace(/\n+$/, '');
|
|
309
|
+
return `${head}\n\n${section}\n\n${body.slice(comments.index)}`;
|
|
310
|
+
}
|
|
311
|
+
const end = endOfSection(body, start + ANSWER_HEADING.length);
|
|
312
|
+
const head = body.slice(0, start).replace(/\n+$/, '');
|
|
313
|
+
const rest = body.slice(end).replace(/^\n+/, '');
|
|
314
|
+
return rest === '' ? `${head}\n\n${section}` : `${head}\n\n${section}\n\n${rest}`;
|
|
315
|
+
}
|
|
316
|
+
/** `## Answer` at the start of a line, and not the start of `## Answerable`. */
|
|
317
|
+
function findAnswerHeading(body) {
|
|
318
|
+
const match = /^##[ \t]+Answer[ \t]*$/m.exec(body);
|
|
319
|
+
return match?.index ?? -1;
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=serialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../../src/storage/markdown/serialize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Ticket } from '../../domain.ts';
|
|
2
|
+
export interface TicketFile {
|
|
3
|
+
readonly filename: string;
|
|
4
|
+
readonly contents: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parse one Ticket file. Schema-conformant files read from their frontmatter;
|
|
8
|
+
* everything else falls through to the Legacy parser and is flagged, because a
|
|
9
|
+
* server that is useless until its repo is migrated never gets installed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseTicket(effort: string, file: TicketFile, fallbackOrder: number, revision: string): Ticket;
|
|
12
|
+
//# sourceMappingURL=ticket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ticket.d.ts","sourceRoot":"","sources":["../../../src/storage/markdown/ticket.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAQ5D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CA6BR"}
|