@rasensio/aidlc 1.66.0 → 1.67.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 (40) hide show
  1. package/dist/commands/roadmap.d.ts +20 -3
  2. package/dist/commands/roadmap.d.ts.map +1 -1
  3. package/dist/commands/roadmap.js +60 -15
  4. package/dist/commands/roadmap.js.map +1 -1
  5. package/dist/doctor/migrations/index.d.ts.map +1 -1
  6. package/dist/doctor/migrations/index.js +5 -0
  7. package/dist/doctor/migrations/index.js.map +1 -1
  8. package/dist/doctor/migrations/roadmap-mermaid-graphs.d.ts +22 -0
  9. package/dist/doctor/migrations/roadmap-mermaid-graphs.d.ts.map +1 -0
  10. package/dist/doctor/migrations/roadmap-mermaid-graphs.js +74 -0
  11. package/dist/doctor/migrations/roadmap-mermaid-graphs.js.map +1 -0
  12. package/dist/roadmap/graph.d.ts +73 -28
  13. package/dist/roadmap/graph.d.ts.map +1 -1
  14. package/dist/roadmap/graph.js +184 -125
  15. package/dist/roadmap/graph.js.map +1 -1
  16. package/dist/roadmap/next.d.ts +5 -0
  17. package/dist/roadmap/next.d.ts.map +1 -1
  18. package/dist/roadmap/next.js +7 -1
  19. package/dist/roadmap/next.js.map +1 -1
  20. package/dist/roadmap/open-browser.d.ts +34 -0
  21. package/dist/roadmap/open-browser.d.ts.map +1 -0
  22. package/dist/roadmap/open-browser.js +40 -0
  23. package/dist/roadmap/open-browser.js.map +1 -0
  24. package/dist/roadmap/paths.d.ts +5 -1
  25. package/dist/roadmap/paths.d.ts.map +1 -1
  26. package/dist/roadmap/paths.js +5 -9
  27. package/dist/roadmap/paths.js.map +1 -1
  28. package/dist/roadmap/view-assets.d.ts +18 -0
  29. package/dist/roadmap/view-assets.d.ts.map +1 -0
  30. package/dist/roadmap/view-assets.js +301 -0
  31. package/dist/roadmap/view-assets.js.map +1 -0
  32. package/dist/roadmap/view-html.d.ts +37 -0
  33. package/dist/roadmap/view-html.d.ts.map +1 -0
  34. package/dist/roadmap/view-html.js +317 -0
  35. package/dist/roadmap/view-html.js.map +1 -0
  36. package/dist/roadmap/view-model.d.ts +93 -0
  37. package/dist/roadmap/view-model.d.ts.map +1 -0
  38. package/dist/roadmap/view-model.js +194 -0
  39. package/dist/roadmap/view-model.js.map +1 -0
  40. package/package.json +2 -2
@@ -0,0 +1,317 @@
1
+ /**
2
+ * The roadmap page as one HTML string (html-roadmap-view).
3
+ *
4
+ * Pure: a `RoadmapView` in, bytes out. The only time-dependent text is the generated-at
5
+ * line, so two pages built from the same items differ in that line alone (AC-10) — ages
6
+ * are left to the page script for exactly that reason.
7
+ *
8
+ * Every item-derived string goes through `escapeHtml`, in text and in double-quoted
9
+ * attributes alike (AC-12); there is no second escaping scheme anywhere on the page.
10
+ * That is why DOM ids are positions (`card-i3`) rather than item ids, and why each side
11
+ * panel is a `<template>` of escaped HTML rather than JSON inside a script, which would
12
+ * need its own escaper for `</script>`. The Content-Security-Policy allows the one style
13
+ * block and the one script by hash (AC-13), so a string that escaped escaping would still
14
+ * not run.
15
+ *
16
+ * @module
17
+ */
18
+ import { createHash } from 'node:crypto';
19
+ import { NODE_H, NODE_W } from './graph.js';
20
+ import { ROADMAP_STATUSES } from './paths.js';
21
+ import { VIEW_SCRIPT, VIEW_STYLE } from './view-assets.js';
22
+ /** Done cards shown before "show all" (AC-18). */
23
+ export const DONE_VISIBLE = 20;
24
+ /** Map labels are cut to this many code points; the full title is in the node's `<title>`. */
25
+ const LABEL_MAX = 34;
26
+ const COLUMN_NAME = {
27
+ inbox: 'Inbox',
28
+ backlog: 'Backlog',
29
+ 'in-progress': 'In progress',
30
+ done: 'Done',
31
+ hold: 'Hold',
32
+ };
33
+ const STATUS_TEXT = {
34
+ inbox: 'inbox',
35
+ backlog: 'backlog',
36
+ 'in-progress': 'in progress',
37
+ done: 'done',
38
+ hold: 'hold',
39
+ };
40
+ const ENTITIES = {
41
+ '&': '&amp;',
42
+ '<': '&lt;',
43
+ '>': '&gt;',
44
+ '"': '&quot;',
45
+ "'": '&#39;',
46
+ '`': '&#96;',
47
+ };
48
+ /** Escape text for HTML text and double-quoted attribute values alike. */
49
+ export function escapeHtml(text) {
50
+ return text.replace(/[&<>"'`]/g, (c) => ENTITIES[c]);
51
+ }
52
+ /** A CSP source expression for `text`. */
53
+ export function cspHash(text) {
54
+ return `'sha256-${createHash('sha256').update(text, 'utf8').digest('base64')}'`;
55
+ }
56
+ /** The policy the page carries: nothing loads, and only this page's own style and script run. */
57
+ export function contentSecurityPolicy() {
58
+ return `default-src 'none'; style-src ${cspHash(VIEW_STYLE)}; script-src ${cspHash(VIEW_SCRIPT)}; base-uri 'none'; form-action 'none'`;
59
+ }
60
+ /** `**bold**` and `` `code` `` inside one line; every other character is escaped. Emits no links. */
61
+ function inline(md) {
62
+ return md
63
+ .split(/(\*\*[^*\n]+\*\*|`[^`\n]+`)/)
64
+ .map((part) => {
65
+ if (/^\*\*[^*\n]+\*\*$/.test(part))
66
+ return `<strong>${escapeHtml(part.slice(2, -2))}</strong>`;
67
+ if (/^`[^`\n]+`$/.test(part))
68
+ return `<code>${escapeHtml(part.slice(1, -1))}</code>`;
69
+ return escapeHtml(part);
70
+ })
71
+ .join('');
72
+ }
73
+ /**
74
+ * The executive summary as HTML: paragraphs and `- ` lists, with bold and code spans.
75
+ *
76
+ * Tokenised from the raw markdown and escaped piece by piece, so markup in an item is text
77
+ * on the page. Deliberately not a markdown library: the summaries are six bullets, and a
78
+ * library would be the first dependency the page grows.
79
+ */
80
+ export function renderSummary(markdown) {
81
+ const blocks = [];
82
+ let para = [];
83
+ let list = [];
84
+ const flushPara = () => {
85
+ if (para.length > 0)
86
+ blocks.push(`<p>${inline(para.join(' '))}</p>`);
87
+ para = [];
88
+ };
89
+ const flushList = () => {
90
+ if (list.length > 0)
91
+ blocks.push(`<ul>${list.map((li) => `<li>${inline(li)}</li>`).join('')}</ul>`);
92
+ list = [];
93
+ };
94
+ for (const raw of markdown.split('\n')) {
95
+ const line = raw.trim();
96
+ const bullet = /^[-*]\s+(.*)$/.exec(line);
97
+ if (line === '') {
98
+ flushPara();
99
+ flushList();
100
+ }
101
+ else if (bullet) {
102
+ flushPara();
103
+ list.push(bullet[1]);
104
+ }
105
+ else if (list.length > 0 && /^\s/.test(raw)) {
106
+ // A wrapped bullet: an indented line continues the item above it.
107
+ list[list.length - 1] += ` ${line}`;
108
+ }
109
+ else {
110
+ flushList();
111
+ para.push(line);
112
+ }
113
+ }
114
+ flushPara();
115
+ flushList();
116
+ return blocks.join('');
117
+ }
118
+ function truncate(text) {
119
+ const points = Array.from(text);
120
+ return points.length <= LABEL_MAX ? text : `${points.slice(0, LABEL_MAX - 1).join('')}…`;
121
+ }
122
+ function link(href, text) {
123
+ return href === null ? null : `<a href="${escapeHtml(href)}">${escapeHtml(text)}</a>`;
124
+ }
125
+ function itemLinks(v) {
126
+ const parts = [
127
+ link(v.links.item, 'item file'),
128
+ link(v.links.instance, `instance ${v.instanceName ?? ''}`.trim()),
129
+ link(v.links.retrospective, 'retrospective'),
130
+ link(v.links.pulls, 'pull requests'),
131
+ ].filter((p) => p !== null);
132
+ return parts.length === 0 ? '' : `<p class="links">${parts.join(' · ')}</p>`;
133
+ }
134
+ function badges(v) {
135
+ return [
136
+ v.ready ? '<span class="badge ready">ready</span>' : '',
137
+ v.blocked ? '<span class="badge blocked">blocked</span>' : '',
138
+ ].join('');
139
+ }
140
+ function holdLine(v) {
141
+ if (v.hold === null)
142
+ return '';
143
+ return `<p class="hold"><strong>${v.hold.kind}</strong> — ${escapeHtml(v.hold.reason ?? 'no reason recorded')}</p>`;
144
+ }
145
+ function card(v, capped) {
146
+ const classes = ['card', `s-${v.status}`, v.ready ? 'ready' : '', v.blocked ? 'blocked' : '', capped ? 'capped' : '']
147
+ .filter((c) => c !== '')
148
+ .join(' ');
149
+ const rank = v.rank === null ? '' : `<span class="rank">#${v.rank}</span> `;
150
+ const age = v.createdAt === null ? '' : `<time class="age" datetime="${v.createdAt}">created ${v.createdAt}</time>`;
151
+ const marks = badges(v) + age;
152
+ return [
153
+ `<li class="${classes}" id="card-${v.ref}" data-ref="${v.ref}" data-search="${escapeHtml(`${v.title} ${v.id ?? v.key}`.toLowerCase())}">`,
154
+ `<p class="title"><button type="button" class="open" aria-haspopup="dialog">${rank}${escapeHtml(v.title)}</button></p>`,
155
+ marks === '' ? '' : `<p class="marks">${marks}</p>`,
156
+ holdLine(v),
157
+ itemLinks(v),
158
+ '</li>',
159
+ ].join('');
160
+ }
161
+ function column(view, byRef, status) {
162
+ const refs = view.columns[status];
163
+ const cards = refs.map((ref, n) => card(byRef.get(ref), status === 'done' && n >= DONE_VISIBLE)).join('\n');
164
+ const more = status === 'done' && refs.length > DONE_VISIBLE
165
+ ? `<button type="button" class="show-all" aria-expanded="false" data-more="Show all (${refs.length})" data-less="Show the latest ${DONE_VISIBLE}">Show all (${refs.length})</button>`
166
+ : '';
167
+ return [
168
+ `<section class="col s-${status}" aria-labelledby="col-${status}">`,
169
+ `<h3 id="col-${status}">${COLUMN_NAME[status]} <span class="count">${refs.length}</span></h3>`,
170
+ `<ol class="cards">${cards === '' ? '' : `\n${cards}\n`}</ol>`,
171
+ more,
172
+ '</section>',
173
+ ].join('\n');
174
+ }
175
+ function labelOf(view, byRef, ref) {
176
+ const item = byRef.get(ref);
177
+ if (item !== undefined) {
178
+ return { title: item.title, statusText: STATUS_TEXT[item.status], cls: `s-${item.status}`, blocked: item.blocked };
179
+ }
180
+ const missing = view.missing.find((m) => m.ref === ref);
181
+ return { title: `missing: ${missing.id}`, statusText: 'no item has this id', cls: 'missing', blocked: false };
182
+ }
183
+ function svgNode(l, ref, x, y, chain, sub) {
184
+ const sub2 = l.blocked ? `${l.statusText} · blocked` : l.statusText;
185
+ return (`<g class="node ${l.cls}" id="node-${ref}" data-ref="${ref}" data-x="${x}" data-y="${y}" data-cx="${NODE_W / 2}" data-cy="${NODE_H / 2}" ` +
186
+ `data-chain="${chain.join(' ')}" data-sub="${sub.join(' ')}" transform="translate(${x} ${y})" tabindex="0" role="button" ` +
187
+ `aria-label="${escapeHtml(`${l.title} — ${sub2}`)}">` +
188
+ `<title>${escapeHtml(l.title)}</title><rect width="${NODE_W}" height="${NODE_H}" rx="6"/>` +
189
+ `<text class="label" x="10" y="20">${escapeHtml(truncate(l.title))}</text><text class="sub" x="10" y="38">${escapeHtml(sub2)}</text></g>`);
190
+ }
191
+ function mapSection(view, byRef) {
192
+ const { layout } = view;
193
+ const at = new Map(layout.nodes.map((n) => [n.ref, n]));
194
+ const edges = layout.edges.map((e) => {
195
+ const a = at.get(e.from);
196
+ const b = at.get(e.to);
197
+ const [x1, y1, x2, y2] = [a.x + NODE_W, a.y + NODE_H / 2, b.x, b.y + NODE_H / 2];
198
+ const mx = (x1 + x2) / 2;
199
+ return `<path class="edge${e.dashed ? ' dashed' : ''}" data-from="${e.from}" data-to="${e.to}" d="M${x1} ${y1} C${mx} ${y1} ${mx} ${y2} ${x2} ${y2}"/>`;
200
+ });
201
+ const nodes = layout.nodes.map((n) => svgNode(labelOf(view, byRef, n.ref), n.ref, n.x, n.y, n.chain, n.subtree));
202
+ const toggles = layout.nodes
203
+ .filter((n) => n.subtree.length > 0)
204
+ .map((n) => `<g class="toggle" data-toggle="${n.ref}" transform="translate(${n.x + NODE_W} ${n.y + NODE_H / 2})" tabindex="0" role="button" aria-expanded="true" ` +
205
+ `aria-label="${escapeHtml(`Collapse or expand what sits under ${labelOf(view, byRef, n.ref).title}`)}"><circle r="10"/><text y="5">−</text></g>`);
206
+ const unlinked = layout.unlinked.map((u) => svgNode(labelOf(view, byRef, u.ref), u.ref, u.x, u.y, [], []));
207
+ const legend = ROADMAP_STATUSES.map((s) => `<span class="sw s-${s}"></span>${STATUS_TEXT[s]}`).join('') + '<span class="sw missing"></span>missing id';
208
+ const unlinkedButton = layout.unlinked.length === 0
209
+ ? ''
210
+ : ` <button type="button" id="unlinked-toggle" aria-pressed="false" data-count="${layout.unlinked.length}">Show unlinked items (${layout.unlinked.length})</button>`;
211
+ const empty = layout.nodes.length === 0 ? '<p class="empty">No dependencies yet: no item depends on another.</p>' : '';
212
+ return [
213
+ '<section class="map-section" aria-labelledby="map-h">',
214
+ '<h2 id="map-h">Dependency map</h2>',
215
+ empty,
216
+ `<p class="map-controls"><button type="button" id="fit">Fit to screen</button>${unlinkedButton}</p>`,
217
+ `<p class="legend">Colour is status:${legend}. Dashed links are further dependencies; a blocked item needs something not yet done.</p>`,
218
+ '<div class="map-wrap">',
219
+ `<svg id="map" role="group" aria-label="Dependency map: drag to pan, scroll or pinch to zoom" data-linked-w="${layout.linkedSize.width}" data-linked-h="${layout.linkedSize.height}" data-full-w="${layout.fullSize.width}" data-full-h="${layout.fullSize.height}">`,
220
+ '<g id="viewport">',
221
+ `<g class="edges">${edges.join('')}</g>`,
222
+ `<g class="forest">${nodes.join('\n')}</g>`,
223
+ `<g class="toggles">${toggles.join('')}</g>`,
224
+ `<g class="unlinked off">${unlinked.join('\n')}</g>`,
225
+ '</g>',
226
+ '</svg>',
227
+ '</div>',
228
+ '</section>',
229
+ ]
230
+ .filter((l) => l !== '')
231
+ .join('\n');
232
+ }
233
+ function refList(view, byRef, refs) {
234
+ if (refs.length === 0)
235
+ return '<p>Nothing.</p>';
236
+ const items = refs.map((ref) => {
237
+ const l = labelOf(view, byRef, ref);
238
+ const target = byRef.has(ref) ? `card-${ref}` : `node-${ref}`;
239
+ return `<li><a href="#${target}" data-goto="${ref}">${escapeHtml(l.title)}</a> (${escapeHtml(l.statusText)})</li>`;
240
+ });
241
+ return `<ul>${items.join('')}</ul>`;
242
+ }
243
+ function detail(view, byRef, v) {
244
+ const rank = v.rank === null ? '' : ` · rank #${v.rank}`;
245
+ return [
246
+ `<template id="detail-${v.ref}">`,
247
+ `<h2 id="panel-title">${escapeHtml(v.title)}</h2>`,
248
+ `<p class="meta">${STATUS_TEXT[v.status]}${rank} · <code>${escapeHtml(v.id ?? v.filename)}</code></p>`,
249
+ badges(v) === '' ? '' : `<p>${badges(v)}</p>`,
250
+ holdLine(v),
251
+ '<h3>Executive summary</h3>',
252
+ v.summary === null ? '<p>No executive summary.</p>' : renderSummary(v.summary),
253
+ '<h3>Needs</h3>',
254
+ refList(view, byRef, v.depRefs),
255
+ '<h3>Needed by</h3>',
256
+ refList(view, byRef, v.dependentRefs),
257
+ itemLinks(v),
258
+ '</template>',
259
+ ].join('');
260
+ }
261
+ function missingDetail(view, byRef, m) {
262
+ return [
263
+ `<template id="detail-${m.ref}">`,
264
+ `<h2 id="panel-title">missing: ${escapeHtml(m.id)}</h2>`,
265
+ '<p>No item carries this id, so everything that depends on it is blocked. Check <code>depends_on</code> for a typo.</p>',
266
+ '<h3>Needed by</h3>',
267
+ refList(view, byRef, m.dependentRefs),
268
+ '</template>',
269
+ ].join('');
270
+ }
271
+ /** "2026-10-06 22:19 UTC" — minutes are enough to tell two pages apart. */
272
+ function humanTime(iso) {
273
+ return `${iso.slice(0, 10)} ${iso.slice(11, 16)} UTC`;
274
+ }
275
+ /** Render the page. Pure. */
276
+ export function renderView(view) {
277
+ const byRef = new Map(view.items.map((v) => [v.ref, v]));
278
+ return [
279
+ '<!doctype html>',
280
+ '<html lang="en">',
281
+ '<head>',
282
+ '<meta charset="utf-8">',
283
+ `<meta http-equiv="Content-Security-Policy" content="${contentSecurityPolicy()}">`,
284
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
285
+ '<title>Roadmap</title>',
286
+ `<style>${VIEW_STYLE}</style>`,
287
+ '</head>',
288
+ '<body>',
289
+ '<header class="top">',
290
+ '<h1>Roadmap</h1>',
291
+ `<p class="generated">Generated <time datetime="${view.generatedAt}">${humanTime(view.generatedAt)}</time> by <code>aidlc roadmap view</code></p>`,
292
+ '<p class="search"><label for="q">Search titles and ids</label> <input id="q" type="search" autocomplete="off"></p>',
293
+ '<p class="search-status" id="search-status" aria-live="polite"></p>',
294
+ '</header>',
295
+ '<main>',
296
+ '<section aria-labelledby="board-h">',
297
+ '<h2 id="board-h">Board</h2>',
298
+ '<div class="board">',
299
+ ...ROADMAP_STATUSES.map((s) => column(view, byRef, s)),
300
+ '</div>',
301
+ '</section>',
302
+ mapSection(view, byRef),
303
+ '</main>',
304
+ '<aside id="panel" class="panel" role="dialog" aria-modal="false" aria-labelledby="panel-title" hidden>',
305
+ '<button type="button" class="close" id="panel-close" aria-label="Close the panel">×</button>',
306
+ '<div id="panel-body"></div>',
307
+ '</aside>',
308
+ ...view.items.map((v) => detail(view, byRef, v)),
309
+ ...view.missing.map((m) => missingDetail(view, byRef, m)),
310
+ '<footer><p>Cost per item is reported by <code>aidlc metrics</code>; this page shows none.</p></footer>',
311
+ `<script>${VIEW_SCRIPT}</script>`,
312
+ '</body>',
313
+ '</html>',
314
+ '',
315
+ ].join('\n');
316
+ }
317
+ //# sourceMappingURL=view-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-html.js","sourceRoot":"","sources":["../../src/roadmap/view-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * What `aidlc roadmap view` shows, before any HTML (html-roadmap-view).
3
+ *
4
+ * `readView` is the only function here that touches the disk or git: it lists the primary
5
+ * checkout's items, asks `pickNextAt` for the ready set, and resolves each instance's
6
+ * folder, branch and the `origin` remote. `buildView` turns that into columns, marks and
7
+ * links and is pure, so every board criterion is a test over in-memory items.
8
+ *
9
+ * Every link is relative to the directory the page is written to, so the page works from
10
+ * `file://` wherever it lands, and nothing in it points off the page except GitHub
11
+ * (AC-11). A link that cannot be made relative — another drive on Windows — is omitted
12
+ * rather than written as an absolute `file://` URL.
13
+ *
14
+ * @module
15
+ */
16
+ import { type MapLayout } from './graph.js';
17
+ import { type RoadmapItem, type RoadmapStatus } from './paths.js';
18
+ import type { RepoCoords } from './sync/body.js';
19
+ export interface HoldNote {
20
+ kind: 'parked' | 'declined' | 'on hold';
21
+ reason: string | null;
22
+ }
23
+ export interface ItemLinks {
24
+ item: string | null;
25
+ instance: string | null;
26
+ retrospective: string | null;
27
+ pulls: string | null;
28
+ }
29
+ export interface ViewItem {
30
+ /** `i<n>`: position in the global order, and the suffix of every DOM id for it. */
31
+ ref: string;
32
+ key: string;
33
+ id: string | null;
34
+ filename: string;
35
+ status: RoadmapStatus;
36
+ title: string;
37
+ /** Backlog rank, or null when unranked, malformed, or not in backlog. */
38
+ rank: number | null;
39
+ createdAt: string | null;
40
+ /** Date of the last history entry. */
41
+ finishedAt: string | null;
42
+ depRefs: string[];
43
+ dependentRefs: string[];
44
+ blocked: boolean;
45
+ ready: boolean;
46
+ hold: HoldNote | null;
47
+ summary: string | null;
48
+ instanceName: string | null;
49
+ links: ItemLinks;
50
+ }
51
+ export interface RoadmapView {
52
+ items: ViewItem[];
53
+ columns: Record<RoadmapStatus, string[]>;
54
+ missing: {
55
+ ref: string;
56
+ id: string;
57
+ dependentRefs: string[];
58
+ }[];
59
+ layout: MapLayout;
60
+ generatedAt: string;
61
+ }
62
+ export interface ViewSources {
63
+ /** Directory the page is written to; every file link is relative to it. */
64
+ outDir: string;
65
+ /** `<primary checkout>/.aidlc/state`. */
66
+ stateRoot: string;
67
+ repo: RepoCoords | null;
68
+ branchOf(instance: string): string | null;
69
+ /** Backlog file names `roadmap next` would offer. */
70
+ ready: ReadonlySet<string>;
71
+ }
72
+ export declare function holdNote(reason: unknown): HoldNote;
73
+ /** A relative, percent-encoded href from `fromDir` to `target`, or null when none exists. */
74
+ export declare function relativeHref(fromDir: string, target: string): string | null;
75
+ /** GitHub's pull-request search for one head branch (AC-22). A branch link would 404 once merged. */
76
+ export declare function pullsUrl(repo: RepoCoords, branch: string): string;
77
+ /**
78
+ * `owner`/`name` from an `origin` URL, or null when it is not a GitHub repository (AC-23).
79
+ * Accepts the https, scp-like ssh and `ssh://` forms, with or without `.git`.
80
+ */
81
+ export declare function parseGitHubRemote(url: string): RepoCoords | null;
82
+ /** Build the view. Pure: the same items and sources give the same view. */
83
+ export declare function buildView(items: readonly RoadmapItem[], sources: ViewSources, now: Date): RoadmapView;
84
+ /** `branch` from an instance's `instance.yaml`, or null. */
85
+ export declare function instanceBranch(stateRoot: string, instance: string): string | null;
86
+ /** The `origin` remote as GitHub coordinates, or null — no remote, not GitHub, or no git. */
87
+ export declare function originCoords(cwd: string): RepoCoords | null;
88
+ /**
89
+ * Read the primary checkout's roadmap — the items `roadmap next` reads, from whichever
90
+ * worktree this runs in (AC-8) — and build the view for a page written to `outFile`.
91
+ */
92
+ export declare function readView(cwd: string, outFile: string, now: Date): RoadmapView;
93
+ //# sourceMappingURL=view-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-model.d.ts","sourceRoot":"","sources":["../../src/roadmap/view-model.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * What `aidlc roadmap view` shows, before any HTML (html-roadmap-view).
3
+ *
4
+ * `readView` is the only function here that touches the disk or git: it lists the primary
5
+ * checkout's items, asks `pickNextAt` for the ready set, and resolves each instance's
6
+ * folder, branch and the `origin` remote. `buildView` turns that into columns, marks and
7
+ * links and is pure, so every board criterion is a test over in-memory items.
8
+ *
9
+ * Every link is relative to the directory the page is written to, so the page works from
10
+ * `file://` wherever it lands, and nothing in it points off the page except GitHub
11
+ * (AC-11). A link that cannot be made relative — another drive on Windows — is omitted
12
+ * rather than written as an absolute `file://` URL.
13
+ *
14
+ * @module
15
+ */
16
+ import { existsSync, readFileSync } from 'node:fs';
17
+ import { execFileSync } from 'node:child_process';
18
+ import { dirname, isAbsolute, join, relative, sep } from 'node:path';
19
+ import { parse as parseYaml } from 'yaml';
20
+ import { openRegistry } from '../concurrency/registry.js';
21
+ import { isSafeSegment } from '../core/paths.js';
22
+ import { layoutMap } from './graph.js';
23
+ import { compareCandidates, parsePriority, pickNextAt } from './next.js';
24
+ import { frontmatter, itemBody, listStatusAt, primaryCheckout, roadmapRoot, ROADMAP_STATUSES, } from './paths.js';
25
+ import { summarySection } from './summary.js';
26
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
27
+ /** A `YYYY-MM-DD` date from YAML, which may have parsed it as a string or a Date. */
28
+ function dateOf(raw) {
29
+ if (raw instanceof Date && !Number.isNaN(raw.getTime()))
30
+ return raw.toISOString().slice(0, 10);
31
+ return typeof raw === 'string' && DATE_RE.test(raw) ? raw : null;
32
+ }
33
+ function lastHistoryEntry(data) {
34
+ const history = data.history;
35
+ if (!Array.isArray(history) || history.length === 0)
36
+ return null;
37
+ const last = history[history.length - 1];
38
+ return typeof last === 'object' && last !== null ? last : null;
39
+ }
40
+ /** The `parked — ` / `declined — ` prefix the park and triage flows write (AC-19). */
41
+ const HOLD_PREFIX_RE = /^(parked|declined)\b\s*(?:[—–-]\s*)?/;
42
+ export function holdNote(reason) {
43
+ if (typeof reason !== 'string' || reason.trim() === '')
44
+ return { kind: 'on hold', reason: null };
45
+ const match = HOLD_PREFIX_RE.exec(reason);
46
+ if (!match)
47
+ return { kind: 'on hold', reason };
48
+ const rest = reason.slice(match[0].length).trim();
49
+ return { kind: match[1], reason: rest === '' ? null : rest };
50
+ }
51
+ /** A relative, percent-encoded href from `fromDir` to `target`, or null when none exists. */
52
+ export function relativeHref(fromDir, target) {
53
+ const rel = relative(fromDir, target);
54
+ if (rel === '' || isAbsolute(rel))
55
+ return null;
56
+ return rel.split(sep).map(encodeURIComponent).join('/');
57
+ }
58
+ /** GitHub's pull-request search for one head branch (AC-22). A branch link would 404 once merged. */
59
+ export function pullsUrl(repo, branch) {
60
+ return `https://github.com/${repo.owner}/${repo.name}/pulls?q=is%3Apr+head%3A${encodeURIComponent(branch)}`;
61
+ }
62
+ const OWNER_OR_NAME_RE = /^[A-Za-z0-9_.-]+$/;
63
+ /**
64
+ * `owner`/`name` from an `origin` URL, or null when it is not a GitHub repository (AC-23).
65
+ * Accepts the https, scp-like ssh and `ssh://` forms, with or without `.git`.
66
+ */
67
+ export function parseGitHubRemote(url) {
68
+ const m = /^https:\/\/(?:[^@/]+@)?github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(url.trim()) ??
69
+ /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/.exec(url.trim()) ??
70
+ /^ssh:\/\/git@github\.com(?::\d+)?\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(url.trim());
71
+ if (!m)
72
+ return null;
73
+ const [, owner, name] = m;
74
+ if (!OWNER_OR_NAME_RE.test(owner) || !OWNER_OR_NAME_RE.test(name) || name === '.' || name === '..')
75
+ return null;
76
+ return { owner, name };
77
+ }
78
+ /** Build the view. Pure: the same items and sources give the same view. */
79
+ export function buildView(items, sources, now) {
80
+ const doneIds = new Set(items.filter((i) => i.status === 'done' && i.id !== null).map((i) => i.id));
81
+ const parsed = items.map((item, n) => {
82
+ const data = frontmatter(item.raw)?.data ?? {};
83
+ const deps = Array.isArray(data.depends_on)
84
+ ? data.depends_on.filter((d) => typeof d === 'string')
85
+ : [];
86
+ return { item, ref: `i${n}`, key: item.id ?? item.filename.replace(/\.md$/, ''), data, deps };
87
+ });
88
+ const layout = layoutMap(parsed.map((p) => ({ ref: p.ref, key: p.key, deps: p.deps })));
89
+ const viewItems = parsed.map(({ item, ref, key, data, deps }) => {
90
+ const priority = parsePriority(data.priority);
91
+ const last = lastHistoryEntry(data);
92
+ let instanceName = null;
93
+ const links = { item: relativeHref(sources.outDir, item.file), instance: null, retrospective: null, pulls: null };
94
+ // `promoted_to` is tracked YAML; it becomes a path only once it is one safe segment.
95
+ if (item.promotedTo !== null && isSafeSegment(item.promotedTo)) {
96
+ instanceName = item.promotedTo;
97
+ const dir = join(sources.stateRoot, instanceName);
98
+ if (existsSync(dir)) {
99
+ const href = relativeHref(sources.outDir, dir);
100
+ links.instance = href === null ? null : `${href}/`;
101
+ const retro = join(dir, 'retrospective.md');
102
+ if (existsSync(retro))
103
+ links.retrospective = relativeHref(sources.outDir, retro);
104
+ }
105
+ const branch = sources.branchOf(instanceName);
106
+ if (branch !== null && sources.repo !== null)
107
+ links.pulls = pullsUrl(sources.repo, branch);
108
+ }
109
+ const summary = summarySection(itemBody(item.raw))?.trim() ?? '';
110
+ return {
111
+ ref,
112
+ key,
113
+ id: item.id,
114
+ filename: item.filename,
115
+ status: item.status,
116
+ title: item.title ?? item.filename,
117
+ rank: item.status === 'backlog' && typeof priority === 'number' ? priority : null,
118
+ createdAt: dateOf(data.created_at),
119
+ finishedAt: last === null ? null : dateOf(last.at),
120
+ depRefs: layout.links[ref].needs,
121
+ dependentRefs: layout.links[ref].neededBy,
122
+ // The rule `pickNext` uses: a dependency counts only once an item carrying its id is done (AC-26).
123
+ blocked: deps.some((d) => !doneIds.has(d)),
124
+ ready: item.status === 'backlog' && sources.ready.has(item.filename),
125
+ hold: item.status === 'hold' ? holdNote(last?.reason) : null,
126
+ summary: summary === '' ? null : summary,
127
+ instanceName,
128
+ links,
129
+ };
130
+ });
131
+ const columns = Object.fromEntries(ROADMAP_STATUSES.map((s) => [s, []]));
132
+ for (const v of viewItems)
133
+ columns[v.status].push(v);
134
+ // Same comparator `roadmap next` sorts with; a malformed rank sorts as unranked (AC-16).
135
+ columns.backlog.sort((a, b) => compareCandidates({ file: a.filename, id: a.id, title: a.title, priority: a.rank }, { file: b.filename, id: b.id, title: b.title, priority: b.rank }));
136
+ // Most recently finished first; an item with no dated history sinks to the end (AC-18).
137
+ columns.done.sort((a, b) => {
138
+ if (a.finishedAt !== b.finishedAt) {
139
+ if (a.finishedAt === null)
140
+ return 1;
141
+ if (b.finishedAt === null)
142
+ return -1;
143
+ return a.finishedAt < b.finishedAt ? 1 : -1;
144
+ }
145
+ return a.filename < b.filename ? 1 : a.filename > b.filename ? -1 : 0;
146
+ });
147
+ return {
148
+ items: viewItems,
149
+ columns: Object.fromEntries(ROADMAP_STATUSES.map((s) => [s, columns[s].map((v) => v.ref)])),
150
+ missing: layout.missing.map((m) => ({ ...m, dependentRefs: layout.links[m.ref].neededBy })),
151
+ layout,
152
+ generatedAt: now.toISOString(),
153
+ };
154
+ }
155
+ /** `branch` from an instance's `instance.yaml`, or null. */
156
+ export function instanceBranch(stateRoot, instance) {
157
+ try {
158
+ const data = parseYaml(readFileSync(join(stateRoot, instance, 'instance.yaml'), 'utf8'));
159
+ const branch = typeof data === 'object' && data !== null ? data.branch : null;
160
+ return typeof branch === 'string' && branch.length > 0 ? branch : null;
161
+ }
162
+ catch {
163
+ return null;
164
+ }
165
+ }
166
+ /** The `origin` remote as GitHub coordinates, or null — no remote, not GitHub, or no git. */
167
+ export function originCoords(cwd) {
168
+ try {
169
+ const url = execFileSync('git', ['remote', 'get-url', 'origin'], {
170
+ cwd,
171
+ encoding: 'utf8',
172
+ stdio: ['ignore', 'pipe', 'ignore'],
173
+ });
174
+ return parseGitHubRemote(url);
175
+ }
176
+ catch {
177
+ return null;
178
+ }
179
+ }
180
+ /**
181
+ * Read the primary checkout's roadmap — the items `roadmap next` reads, from whichever
182
+ * worktree this runs in (AC-8) — and build the view for a page written to `outFile`.
183
+ */
184
+ export function readView(cwd, outFile, now) {
185
+ const root = roadmapRoot(cwd);
186
+ const primary = primaryCheckout(cwd);
187
+ const items = ROADMAP_STATUSES.flatMap((s) => listStatusAt(root, s));
188
+ const ready = new Set(pickNextAt(root).ready.map((c) => c.file));
189
+ const stateRoot = join(primary, '.aidlc', 'state');
190
+ const registry = openRegistry(primary);
191
+ const branchOf = (instance) => instanceBranch(stateRoot, instance) ?? registry?.lookup(instance)?.branch ?? null;
192
+ return buildView(items, { outDir: dirname(outFile), stateRoot, repo: originCoords(primary), branchOf, ready }, now);
193
+ }
194
+ //# sourceMappingURL=view-model.js.map