@rasensio/aidlc 1.19.0 → 1.21.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 (96) hide show
  1. package/dist/change/cross-check.d.ts +28 -0
  2. package/dist/change/cross-check.d.ts.map +1 -0
  3. package/dist/change/cross-check.js +53 -0
  4. package/dist/change/cross-check.js.map +1 -0
  5. package/dist/change/impact.d.ts +57 -0
  6. package/dist/change/impact.d.ts.map +1 -0
  7. package/dist/change/impact.js +288 -0
  8. package/dist/change/impact.js.map +1 -0
  9. package/dist/change/index.d.ts +12 -0
  10. package/dist/change/index.d.ts.map +1 -0
  11. package/dist/change/index.js +11 -0
  12. package/dist/change/index.js.map +1 -0
  13. package/dist/change/parser.d.ts +52 -0
  14. package/dist/change/parser.d.ts.map +1 -0
  15. package/dist/change/parser.js +339 -0
  16. package/dist/change/parser.js.map +1 -0
  17. package/dist/change/reopen.d.ts +80 -0
  18. package/dist/change/reopen.d.ts.map +1 -0
  19. package/dist/change/reopen.js +227 -0
  20. package/dist/change/reopen.js.map +1 -0
  21. package/dist/change/types.d.ts +144 -0
  22. package/dist/change/types.d.ts.map +1 -0
  23. package/dist/change/types.js +8 -0
  24. package/dist/change/types.js.map +1 -0
  25. package/dist/cli.d.ts.map +1 -1
  26. package/dist/cli.js +4 -0
  27. package/dist/cli.js.map +1 -1
  28. package/dist/commands/amend.d.ts +22 -0
  29. package/dist/commands/amend.d.ts.map +1 -0
  30. package/dist/commands/amend.js +355 -0
  31. package/dist/commands/amend.js.map +1 -0
  32. package/dist/commands/docs.d.ts.map +1 -1
  33. package/dist/commands/docs.js +6 -0
  34. package/dist/commands/docs.js.map +1 -1
  35. package/dist/commands/roadmap.d.ts +66 -0
  36. package/dist/commands/roadmap.d.ts.map +1 -0
  37. package/dist/commands/roadmap.js +188 -0
  38. package/dist/commands/roadmap.js.map +1 -0
  39. package/dist/core/lifecycle.d.ts +3 -0
  40. package/dist/core/lifecycle.d.ts.map +1 -1
  41. package/dist/core/lifecycle.js +13 -2
  42. package/dist/core/lifecycle.js.map +1 -1
  43. package/dist/core/types.d.ts +37 -2
  44. package/dist/core/types.d.ts.map +1 -1
  45. package/dist/menu/roster.d.ts.map +1 -1
  46. package/dist/menu/roster.js +8 -0
  47. package/dist/menu/roster.js.map +1 -1
  48. package/dist/roadmap/sync/body.d.ts +37 -0
  49. package/dist/roadmap/sync/body.d.ts.map +1 -0
  50. package/dist/roadmap/sync/body.js +52 -0
  51. package/dist/roadmap/sync/body.js.map +1 -0
  52. package/dist/roadmap/sync/collect.d.ts +45 -0
  53. package/dist/roadmap/sync/collect.d.ts.map +1 -0
  54. package/dist/roadmap/sync/collect.js +93 -0
  55. package/dist/roadmap/sync/collect.js.map +1 -0
  56. package/dist/roadmap/sync/config.d.ts +95 -0
  57. package/dist/roadmap/sync/config.d.ts.map +1 -0
  58. package/dist/roadmap/sync/config.js +236 -0
  59. package/dist/roadmap/sync/config.js.map +1 -0
  60. package/dist/roadmap/sync/discover.d.ts +56 -0
  61. package/dist/roadmap/sync/discover.d.ts.map +1 -0
  62. package/dist/roadmap/sync/discover.js +126 -0
  63. package/dist/roadmap/sync/discover.js.map +1 -0
  64. package/dist/roadmap/sync/execute.d.ts +41 -0
  65. package/dist/roadmap/sync/execute.d.ts.map +1 -0
  66. package/dist/roadmap/sync/execute.js +164 -0
  67. package/dist/roadmap/sync/execute.js.map +1 -0
  68. package/dist/roadmap/sync/gh.d.ts +64 -0
  69. package/dist/roadmap/sync/gh.d.ts.map +1 -0
  70. package/dist/roadmap/sync/gh.js +86 -0
  71. package/dist/roadmap/sync/gh.js.map +1 -0
  72. package/dist/roadmap/sync/import.d.ts +73 -0
  73. package/dist/roadmap/sync/import.d.ts.map +1 -0
  74. package/dist/roadmap/sync/import.js +269 -0
  75. package/dist/roadmap/sync/import.js.map +1 -0
  76. package/dist/roadmap/sync/labels.d.ts +57 -0
  77. package/dist/roadmap/sync/labels.d.ts.map +1 -0
  78. package/dist/roadmap/sync/labels.js +80 -0
  79. package/dist/roadmap/sync/labels.js.map +1 -0
  80. package/dist/roadmap/sync/marker.d.ts +38 -0
  81. package/dist/roadmap/sync/marker.d.ts.map +1 -0
  82. package/dist/roadmap/sync/marker.js +58 -0
  83. package/dist/roadmap/sync/marker.js.map +1 -0
  84. package/dist/roadmap/sync/plan.d.ts +40 -0
  85. package/dist/roadmap/sync/plan.d.ts.map +1 -0
  86. package/dist/roadmap/sync/plan.js +272 -0
  87. package/dist/roadmap/sync/plan.js.map +1 -0
  88. package/dist/roadmap/sync/render.d.ts +28 -0
  89. package/dist/roadmap/sync/render.d.ts.map +1 -0
  90. package/dist/roadmap/sync/render.js +110 -0
  91. package/dist/roadmap/sync/render.js.map +1 -0
  92. package/dist/roadmap/sync/types.d.ts +111 -0
  93. package/dist/roadmap/sync/types.d.ts.map +1 -0
  94. package/dist/roadmap/sync/types.js +43 -0
  95. package/dist/roadmap/sync/types.js.map +1 -0
  96. package/package.json +2 -2
@@ -0,0 +1,269 @@
1
+ /**
2
+ * One-shot import: a GitHub issue seeds a roadmap item.
3
+ *
4
+ * Import, not sync. Nothing reconciles afterwards, nothing is written back to the issue,
5
+ * and running it twice over the same issue is reported rather than duplicated.
6
+ *
7
+ * The `title` is **always** double-quoted, with no judgement call about whether this one
8
+ * needs it. An unquoted YAML plain scalar may not begin with a reserved indicator
9
+ * character, so the single most natural way to name a code defect — an identifier in
10
+ * backticks — does not parse; and nothing reports it, because an item that will not parse
11
+ * and an item that was never written look identical. Five of this project's first 49 items
12
+ * were lost that way for up to ten days, and GitHub issue titles are precisely the corpus
13
+ * that produces them.
14
+ *
15
+ * Requirements: roadmap-issue-projection/AC-51 … roadmap-issue-projection/AC-60
16
+ *
17
+ * @module
18
+ */
19
+ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
20
+ import { join, resolve, sep } from 'node:path';
21
+ import { ITEM_FILENAME_RE, ROADMAP_STATUSES, isoDate, primaryCheckout, statusDir, } from '../paths.js';
22
+ import { firstLine } from './gh.js';
23
+ /** Slug used when a title yields nothing usable. */
24
+ export const FALLBACK_SLUG_PREFIX = 'imported-issue';
25
+ /** Keeps filenames reasonable; `ITEM_FILENAME_RE` imposes no length limit itself. */
26
+ const MAX_SLUG_LENGTH = 48;
27
+ /**
28
+ * Parse an issue reference into a comparable tuple.
29
+ *
30
+ * Normalising is the point: `https://github.com/o/r/issues/12`, the same with a trailing
31
+ * slash, with a `#issuecomment-…` fragment, with `www.`, and the
32
+ * `api.github.com/repos/o/r/issues/12` form all denote one issue. Comparing raw strings
33
+ * would report false duplicates in one direction and miss real ones in the other — and a
34
+ * naive substring compare makes `issues/1` match `issues/12`.
35
+ */
36
+ export function parseIssueUrl(raw) {
37
+ let url;
38
+ try {
39
+ url = new URL(raw.trim());
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ if (url.protocol !== 'https:' && url.protocol !== 'http:')
45
+ return null;
46
+ const host = url.hostname.replace(/^www\./, '').toLowerCase();
47
+ const segments = url.pathname.split('/').filter((s) => s.length > 0);
48
+ // api.github.com/repos/<owner>/<repo>/issues/<n>
49
+ if (host === 'api.github.com') {
50
+ if (segments.length !== 5)
51
+ return null;
52
+ if (segments[0] !== 'repos' || segments[3] !== 'issues')
53
+ return null;
54
+ const number = toIssueNumber(segments[4]);
55
+ if (number === null)
56
+ return null;
57
+ return { host: 'github.com', owner: segments[1], repo: segments[2], number };
58
+ }
59
+ // <host>/<owner>/<repo>/issues/<n>
60
+ if (segments.length !== 4)
61
+ return null;
62
+ if (segments[2] !== 'issues')
63
+ return null;
64
+ const number = toIssueNumber(segments[3]);
65
+ if (number === null)
66
+ return null;
67
+ return { host, owner: segments[0], repo: segments[1], number };
68
+ }
69
+ function toIssueNumber(raw) {
70
+ if (!/^[1-9][0-9]*$/.test(raw))
71
+ return null;
72
+ const n = Number(raw);
73
+ return Number.isSafeInteger(n) ? n : null;
74
+ }
75
+ /** Canonical string form of a coordinate, for map keys. */
76
+ export function coordKey(coord) {
77
+ return `${coord.host}/${coord.owner}/${coord.repo}#${coord.number}`;
78
+ }
79
+ /**
80
+ * Every issue already linked by a roadmap item, keyed by coordinate.
81
+ *
82
+ * This reads item **bodies**, which the roadmap skill's frontmatter-only rule otherwise
83
+ * discourages. That rule governs *browse*, which is a list and must stay cheap enough to
84
+ * run over the whole roadmap. Import is neither a list nor cheap-by-contract, and there is
85
+ * nowhere else the local half of a duplicate check could come from — DD-1 means no
86
+ * `remote:` field exists to consult.
87
+ */
88
+ export function linkedCoords(cwd) {
89
+ const root = primaryCheckout(cwd);
90
+ const found = new Map();
91
+ for (const status of ROADMAP_STATUSES) {
92
+ const dir = statusDir(root, status);
93
+ if (!existsSync(dir))
94
+ continue;
95
+ let names;
96
+ try {
97
+ names = readdirSync(dir).sort();
98
+ }
99
+ catch {
100
+ continue;
101
+ }
102
+ for (const filename of names) {
103
+ if (!filename.endsWith('.md'))
104
+ continue;
105
+ let raw;
106
+ try {
107
+ raw = readFileSync(join(dir, filename), 'utf8');
108
+ }
109
+ catch {
110
+ continue;
111
+ }
112
+ for (const match of raw.matchAll(/https?:\/\/[^\s)>\]"']+/g)) {
113
+ const coord = parseIssueUrl(match[0]);
114
+ if (coord === null)
115
+ continue;
116
+ const key = coordKey(coord);
117
+ if (!found.has(key))
118
+ found.set(key, `${status}/${filename}`);
119
+ }
120
+ }
121
+ }
122
+ return found;
123
+ }
124
+ /**
125
+ * A filename-safe slug from an issue title.
126
+ *
127
+ * Must satisfy `ITEM_FILENAME_RE`'s `[a-z0-9][a-z0-9-]*`, which a title of punctuation or
128
+ * of a non-Latin script will not survive — hence the fallback rather than a refusal. An
129
+ * issue that cannot be imported because of its title would be a strange thing to tell
130
+ * somebody.
131
+ */
132
+ export function slugFromTitle(title, number) {
133
+ const slug = title
134
+ .toLowerCase()
135
+ .replace(/[^a-z0-9]+/g, '-')
136
+ .replace(/^-+|-+$/g, '')
137
+ .slice(0, MAX_SLUG_LENGTH)
138
+ .replace(/-+$/g, '');
139
+ if (slug.length === 0 || !/^[a-z0-9]/.test(slug)) {
140
+ return `${FALLBACK_SLUG_PREFIX}-${number}`;
141
+ }
142
+ return slug;
143
+ }
144
+ /** Read the issue. The only `gh` call import makes, and it is a read. */
145
+ function readIssue(gh, url) {
146
+ const result = gh.run(['issue', 'view', url, '--json', 'number,title,url']);
147
+ if (!result.ok)
148
+ return { ok: false, error: firstLine(result.error) };
149
+ try {
150
+ const parsed = JSON.parse(result.stdout);
151
+ if (typeof parsed.number !== 'number' || typeof parsed.title !== 'string' || typeof parsed.url !== 'string') {
152
+ return { ok: false, error: 'gh returned an issue without number, title and url' };
153
+ }
154
+ return { ok: true, issue: { number: parsed.number, title: parsed.title, url: parsed.url } };
155
+ }
156
+ catch (err) {
157
+ return { ok: false, error: `gh returned output that is not JSON: ${err.message}` };
158
+ }
159
+ }
160
+ /** Import one issue into `inbox/`. */
161
+ export function importIssue(gh, cwd, url) {
162
+ const coord = parseIssueUrl(url);
163
+ if (coord === null) {
164
+ return {
165
+ kind: 'refused',
166
+ error: `${JSON.stringify(url)} is not a GitHub issue URL. Expected ` +
167
+ 'https://github.com/<owner>/<repo>/issues/<number>.',
168
+ };
169
+ }
170
+ const linked = linkedCoords(cwd);
171
+ const existing = linked.get(coordKey(coord));
172
+ if (existing !== undefined) {
173
+ return { kind: 'duplicate', existing, coord };
174
+ }
175
+ const read = readIssue(gh, url);
176
+ if (!read.ok)
177
+ return { kind: 'refused', error: read.error };
178
+ const root = primaryCheckout(cwd);
179
+ const inbox = statusDir(root, 'inbox');
180
+ const date = isoDate();
181
+ const compact = date.replace(/-/g, '');
182
+ const slug = slugFromTitle(read.issue.title, coord.number);
183
+ const filename = uniqueFilename(root, compact, slug);
184
+ if (filename === null) {
185
+ return {
186
+ kind: 'refused',
187
+ error: `could not find a free filename for ${compact}-${slug}.md after 50 attempts`,
188
+ };
189
+ }
190
+ const target = resolve(inbox, filename);
191
+ // Second boundary. The slug is already constrained, but a containment check immediately
192
+ // before the write is what stops a slug bug from becoming a path traversal — the
193
+ // secure-defaults rule about validating at more than one boundary.
194
+ if (!target.startsWith(inbox + sep)) {
195
+ return { kind: 'refused', error: `refusing to write outside ${inbox}` };
196
+ }
197
+ if (!ITEM_FILENAME_RE.test(filename)) {
198
+ return { kind: 'refused', error: `derived filename ${filename} is not a valid roadmap item name` };
199
+ }
200
+ writeFileSync(target, renderItem(read.issue, coord, compact, slug, date), 'utf8');
201
+ return { kind: 'written', path: target };
202
+ }
203
+ /**
204
+ * A filename free in **every** status directory.
205
+ *
206
+ * Not just `inbox/`: a filename appearing in two status directories is an error the
207
+ * roadmap module reports rather than resolves, so creating one would be manufacturing it.
208
+ * The `-2` suffix follows the rule the roadmap skill already states.
209
+ */
210
+ function uniqueFilename(root, compact, slug) {
211
+ for (let attempt = 1; attempt <= 50; attempt++) {
212
+ const candidate = attempt === 1 ? `${compact}-${slug}.md` : `${compact}-${slug}-${attempt}.md`;
213
+ const taken = ROADMAP_STATUSES.some((status) => existsSync(join(statusDir(root, status), candidate)));
214
+ if (!taken)
215
+ return candidate;
216
+ }
217
+ return null;
218
+ }
219
+ /**
220
+ * The item file.
221
+ *
222
+ * The Executive Summary is written with every label reading `Not yet decided.` rather than
223
+ * inferred from the issue body. The roadmap skill requires the section of every item and
224
+ * warns that a summary drafted before the detail "is a guess wearing a summary's clothes";
225
+ * filling six labels from an issue nobody has triaged would be exactly that. Triage is
226
+ * where they get answered.
227
+ */
228
+ function renderItem(issue, coord, compact, slug, date) {
229
+ const id = `item-${compact}-${slug}`;
230
+ return [
231
+ '---',
232
+ `id: ${id}`,
233
+ // JSON.stringify, unconditionally. See the module docblock.
234
+ `title: ${JSON.stringify(issue.title)}`,
235
+ `created_at: ${date}`,
236
+ 'source: import',
237
+ 'promoted_to: null',
238
+ 'depends_on: []',
239
+ 'history:',
240
+ ` - { status: inbox, at: ${date} }`,
241
+ '---',
242
+ '',
243
+ `# Idea: ${issue.title}`,
244
+ '',
245
+ '## Executive Summary',
246
+ '',
247
+ '- **What it is** — Not yet decided.',
248
+ '- **Who benefits** — Not yet decided.',
249
+ '- **Customer value** — Not yet decided.',
250
+ '- **Revenue** — Not yet decided.',
251
+ '- **Cost and risk** — Not yet decided.',
252
+ '- **Recommendation** — Not yet decided.',
253
+ '',
254
+ `Imported from ${issue.url} on ${date}. The summary above is deliberately unfilled:`,
255
+ 'six labelled lines guessed from an untriaged issue would read as answered without',
256
+ 'being decided. Fill them in at triage.',
257
+ '',
258
+ '## Source',
259
+ '',
260
+ `- **Issue:** ${issue.url}`,
261
+ `- **Repository:** ${coord.owner}/${coord.repo}`,
262
+ `- **Number:** #${coord.number}`,
263
+ '',
264
+ 'This item is a one-shot import. Nothing reconciles it with the issue afterwards, and',
265
+ 'comments on the issue are not copied here.',
266
+ '',
267
+ ].join('\n');
268
+ }
269
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/roadmap/sync/import.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The label vocabulary the projection uses.
3
+ *
4
+ * Two kinds of label, doing two different jobs:
5
+ *
6
+ * - **The discovery label** (`aidlc` by default) is how the whole issue map is found in
7
+ * one `gh issue list` call. It is identity: an issue without it is invisible to the
8
+ * projection.
9
+ * - **A status label** (`aidlc:backlog`, `aidlc:done`, …) is how a reader tells planned
10
+ * from in-flight without opening anything.
11
+ *
12
+ * The status names are *derived* from `ROADMAP_STATUSES` rather than listed here, so the
13
+ * two vocabularies cannot drift: adding a sixth status becomes a compile-time fact
14
+ * instead of a label nobody remembered to add.
15
+ *
16
+ * `LABEL_COLOR` and `LABEL_DESCRIPTION` exist because `gh label create` exits 1 on a
17
+ * label that already exists, so creation must pass `--force` — and `gh` chooses a
18
+ * *random* colour when none is given, so `--force` without an explicit colour would
19
+ * repaint the label on every single run. Fixed values make the ensure genuinely
20
+ * idempotent.
21
+ *
22
+ * Requirements: roadmap-issue-projection/AC-17, roadmap-issue-projection/AC-43
23
+ *
24
+ * @module
25
+ */
26
+ import { type RoadmapStatus } from '../paths.js';
27
+ /** Default discovery label. Overridable via `roadmap.sync.label`. */
28
+ export declare const DEFAULT_DISCOVERY_LABEL = "aidlc";
29
+ /** GitHub's own label-name limit. */
30
+ export declare const MAX_LABEL_LENGTH = 50;
31
+ /** Prefix marking a label this projection owns. */
32
+ export declare const STATUS_LABEL_PREFIX = "aidlc:";
33
+ /** The label carrying an item's status. */
34
+ export declare function statusLabel(status: RoadmapStatus): string;
35
+ /** Every status label, derived. */
36
+ export declare const ALL_STATUS_LABELS: readonly string[];
37
+ /**
38
+ * True when a label name is one this projection owns.
39
+ *
40
+ * Used to decide what `update-labels` may remove. Anything failing this test is a
41
+ * human's label and is never touched — which is why the update uses `--add-label` /
42
+ * `--remove-label` rather than a wholesale `--label` replacement.
43
+ */
44
+ export declare function isStatusLabel(name: string): boolean;
45
+ /** Fixed colours, so `--force` is idempotent rather than a repaint. */
46
+ export declare const LABEL_COLOR: Readonly<Record<string, string>>;
47
+ /** Fallback colour for a custom discovery label. */
48
+ export declare const FALLBACK_LABEL_COLOR = "5319e7";
49
+ /** Fixed descriptions, for the same reason as the colours. */
50
+ export declare const LABEL_DESCRIPTION: Readonly<Record<string, string>>;
51
+ /** Fallback description for the discovery label, whatever it is named. */
52
+ export declare function discoveryLabelDescription(): string;
53
+ /** Colour for a label name, falling back for a custom discovery label. */
54
+ export declare function colorFor(name: string): string;
55
+ /** Description for a label name, falling back for a custom discovery label. */
56
+ export declare function descriptionFor(name: string): string;
57
+ //# sourceMappingURL=labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAE/C,qCAAqC;AACrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC,mDAAmD;AACnD,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAE5C,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAEzD;AAED,mCAAmC;AACnC,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAAsC,CAAC;AAEtF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOxD,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAM9D,CAAC;AAEF,0EAA0E;AAC1E,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,0EAA0E;AAC1E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The label vocabulary the projection uses.
3
+ *
4
+ * Two kinds of label, doing two different jobs:
5
+ *
6
+ * - **The discovery label** (`aidlc` by default) is how the whole issue map is found in
7
+ * one `gh issue list` call. It is identity: an issue without it is invisible to the
8
+ * projection.
9
+ * - **A status label** (`aidlc:backlog`, `aidlc:done`, …) is how a reader tells planned
10
+ * from in-flight without opening anything.
11
+ *
12
+ * The status names are *derived* from `ROADMAP_STATUSES` rather than listed here, so the
13
+ * two vocabularies cannot drift: adding a sixth status becomes a compile-time fact
14
+ * instead of a label nobody remembered to add.
15
+ *
16
+ * `LABEL_COLOR` and `LABEL_DESCRIPTION` exist because `gh label create` exits 1 on a
17
+ * label that already exists, so creation must pass `--force` — and `gh` chooses a
18
+ * *random* colour when none is given, so `--force` without an explicit colour would
19
+ * repaint the label on every single run. Fixed values make the ensure genuinely
20
+ * idempotent.
21
+ *
22
+ * Requirements: roadmap-issue-projection/AC-17, roadmap-issue-projection/AC-43
23
+ *
24
+ * @module
25
+ */
26
+ import { ROADMAP_STATUSES } from '../paths.js';
27
+ /** Default discovery label. Overridable via `roadmap.sync.label`. */
28
+ export const DEFAULT_DISCOVERY_LABEL = 'aidlc';
29
+ /** GitHub's own label-name limit. */
30
+ export const MAX_LABEL_LENGTH = 50;
31
+ /** Prefix marking a label this projection owns. */
32
+ export const STATUS_LABEL_PREFIX = 'aidlc:';
33
+ /** The label carrying an item's status. */
34
+ export function statusLabel(status) {
35
+ return `${STATUS_LABEL_PREFIX}${status}`;
36
+ }
37
+ /** Every status label, derived. */
38
+ export const ALL_STATUS_LABELS = ROADMAP_STATUSES.map(statusLabel);
39
+ /**
40
+ * True when a label name is one this projection owns.
41
+ *
42
+ * Used to decide what `update-labels` may remove. Anything failing this test is a
43
+ * human's label and is never touched — which is why the update uses `--add-label` /
44
+ * `--remove-label` rather than a wholesale `--label` replacement.
45
+ */
46
+ export function isStatusLabel(name) {
47
+ return ALL_STATUS_LABELS.includes(name);
48
+ }
49
+ /** Fixed colours, so `--force` is idempotent rather than a repaint. */
50
+ export const LABEL_COLOR = {
51
+ [DEFAULT_DISCOVERY_LABEL]: '5319e7',
52
+ [statusLabel('inbox')]: 'bfd4f2',
53
+ [statusLabel('backlog')]: '1d76db',
54
+ [statusLabel('in-progress')]: '0e8a16',
55
+ [statusLabel('done')]: '6f42c1',
56
+ [statusLabel('hold')]: 'd4c5f9',
57
+ };
58
+ /** Fallback colour for a custom discovery label. */
59
+ export const FALLBACK_LABEL_COLOR = '5319e7';
60
+ /** Fixed descriptions, for the same reason as the colours. */
61
+ export const LABEL_DESCRIPTION = {
62
+ [statusLabel('inbox')]: 'AIDLC roadmap: captured, not yet triaged',
63
+ [statusLabel('backlog')]: 'AIDLC roadmap: agreed, not started',
64
+ [statusLabel('in-progress')]: 'AIDLC roadmap: a lifecycle instance exists',
65
+ [statusLabel('done')]: 'AIDLC roadmap: shipped',
66
+ [statusLabel('hold')]: 'AIDLC roadmap: parked or declined',
67
+ };
68
+ /** Fallback description for the discovery label, whatever it is named. */
69
+ export function discoveryLabelDescription() {
70
+ return 'Projected from this repository’s AIDLC roadmap';
71
+ }
72
+ /** Colour for a label name, falling back for a custom discovery label. */
73
+ export function colorFor(name) {
74
+ return LABEL_COLOR[name] ?? FALLBACK_LABEL_COLOR;
75
+ }
76
+ /** Description for a label name, falling back for a custom discovery label. */
77
+ export function descriptionFor(name) {
78
+ return LABEL_DESCRIPTION[name] ?? discoveryLabelDescription();
79
+ }
80
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../../src/roadmap/sync/labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,gBAAgB,EAAsB,MAAM,aAAa,CAAC;AAEnE,qEAAqE;AACrE,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C,qCAAqC;AACrC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAEnC,mDAAmD;AACnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAE5C,2CAA2C;AAC3C,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,OAAO,GAAG,mBAAmB,GAAG,MAAM,EAAE,CAAC;AAC3C,CAAC;AAED,mCAAmC;AACnC,MAAM,CAAC,MAAM,iBAAiB,GAAsB,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAEtF;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAQ,iBAAuC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,MAAM,WAAW,GAAqC;IAC3D,CAAC,uBAAuB,CAAC,EAAE,QAAQ;IACnC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ;IAChC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ;IAClC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ;IACtC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ;IAC/B,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ;CAChC,CAAC;AAEF,oDAAoD;AACpD,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,8DAA8D;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAqC;IACjE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,0CAA0C;IAClE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,EAAE,oCAAoC;IAC9D,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,EAAE,4CAA4C;IAC1E,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,wBAAwB;IAC/C,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,mCAAmC;CAC3D,CAAC;AAEF,0EAA0E;AAC1E,MAAM,UAAU,yBAAyB;IACvC,OAAO,gDAAgD,CAAC;AAC1D,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC;AACnD,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,yBAAyB,EAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The durable link between a roadmap item and its GitHub issue.
3
+ *
4
+ * The link is the item `id`, stamped on the issue as an HTML-comment marker. It is not
5
+ * the `remote:` frontmatter field: creating an issue and writing a local link are two
6
+ * writes to two systems, and every failure of the second leaves an issue with no link,
7
+ * which the next run turns into a duplicate. With the id living server-side there is no
8
+ * second write to fail, so the projection is a convergent rendering rather than
9
+ * stateful sync — and can be thrown away and rebuilt.
10
+ *
11
+ * Two properties matter and both are easy to get wrong:
12
+ *
13
+ * - **Matching is position-independent.** A created body ends with the marker, but as
14
+ * soon as somebody comments or edits, it will not. Matching that depended on position
15
+ * would silently stop finding issues the moment the tracker was used.
16
+ * - **Matching consumes the whole marker, terminator included.** The obvious
17
+ * implementation, `body.includes(id)`, resolves `item-20260907-foo` against the issue
18
+ * for `item-20260907-foo-2` — and that exact pair is manufactured by the roadmap
19
+ * skill's own collision rule, which suffixes a duplicate slug with `-2`. So the id is
20
+ * *extracted* and compared with `===`, never searched for as a substring.
21
+ *
22
+ * Requirements: roadmap-issue-projection/AC-47, roadmap-issue-projection/AC-48,
23
+ * roadmap-issue-projection/AC-49
24
+ *
25
+ * @module
26
+ */
27
+ /** Render the marker for an item id. */
28
+ export declare function renderMarker(id: string): string;
29
+ /**
30
+ * Every item id marked in a body, in order of appearance, de-duplicated.
31
+ *
32
+ * Returns an **array** rather than the first id. A single-id return would make the
33
+ * two-markers case undetectable: the function would pick one and the projection would
34
+ * confidently retitle or close the wrong issue. The plural return costs nothing and
35
+ * makes the ambiguity representable, so `planProjection` can refuse instead of guess.
36
+ */
37
+ export declare function findMarkerIds(body: string): string[];
38
+ //# sourceMappingURL=marker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAWH,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CASpD"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The durable link between a roadmap item and its GitHub issue.
3
+ *
4
+ * The link is the item `id`, stamped on the issue as an HTML-comment marker. It is not
5
+ * the `remote:` frontmatter field: creating an issue and writing a local link are two
6
+ * writes to two systems, and every failure of the second leaves an issue with no link,
7
+ * which the next run turns into a duplicate. With the id living server-side there is no
8
+ * second write to fail, so the projection is a convergent rendering rather than
9
+ * stateful sync — and can be thrown away and rebuilt.
10
+ *
11
+ * Two properties matter and both are easy to get wrong:
12
+ *
13
+ * - **Matching is position-independent.** A created body ends with the marker, but as
14
+ * soon as somebody comments or edits, it will not. Matching that depended on position
15
+ * would silently stop finding issues the moment the tracker was used.
16
+ * - **Matching consumes the whole marker, terminator included.** The obvious
17
+ * implementation, `body.includes(id)`, resolves `item-20260907-foo` against the issue
18
+ * for `item-20260907-foo-2` — and that exact pair is manufactured by the roadmap
19
+ * skill's own collision rule, which suffixes a duplicate slug with `-2`. So the id is
20
+ * *extracted* and compared with `===`, never searched for as a substring.
21
+ *
22
+ * Requirements: roadmap-issue-projection/AC-47, roadmap-issue-projection/AC-48,
23
+ * roadmap-issue-projection/AC-49
24
+ *
25
+ * @module
26
+ */
27
+ /**
28
+ * Marker scanner.
29
+ *
30
+ * The id charset deliberately excludes `-->`'s characters, so the match cannot run past
31
+ * its own terminator. Global, because an issue carrying two markers is a real state the
32
+ * caller has to be able to see.
33
+ */
34
+ const MARKER_RE = /<!--\s*aidlc-item:\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*-->/g;
35
+ /** Render the marker for an item id. */
36
+ export function renderMarker(id) {
37
+ return `<!-- aidlc-item: ${id} -->`;
38
+ }
39
+ /**
40
+ * Every item id marked in a body, in order of appearance, de-duplicated.
41
+ *
42
+ * Returns an **array** rather than the first id. A single-id return would make the
43
+ * two-markers case undetectable: the function would pick one and the projection would
44
+ * confidently retitle or close the wrong issue. The plural return costs nothing and
45
+ * makes the ambiguity representable, so `planProjection` can refuse instead of guess.
46
+ */
47
+ export function findMarkerIds(body) {
48
+ const ids = [];
49
+ // `matchAll` on a fresh iterator each call — the regex is module-level and `g`-flagged,
50
+ // so relying on `lastIndex` across calls would make results depend on call order.
51
+ for (const match of body.matchAll(MARKER_RE)) {
52
+ const id = match[1];
53
+ if (!ids.includes(id))
54
+ ids.push(id);
55
+ }
56
+ return ids;
57
+ }
58
+ //# sourceMappingURL=marker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker.js","sourceRoot":"","sources":["../../../src/roadmap/sync/marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;;;GAMG;AACH,MAAM,SAAS,GAAG,0DAA0D,CAAC;AAE7E,wCAAwC;AACxC,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,oBAAoB,EAAE,MAAM,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,wFAAwF;IACxF,kFAAkF;IAClF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The projection planner. Pure — and the purity is the point.
3
+ *
4
+ * This module imports no `node:fs`, no `node:child_process`, and not `gh.ts`. A test
5
+ * pins that over the import graph, which is what makes "planning needs no network" a
6
+ * property of the code rather than a claim about the test suite: hand the command a
7
+ * runner that throws on every invocation and planning still succeeds, because the
8
+ * planner never had one.
9
+ *
10
+ * Refusals are **values**, in a fixed order, so a tree with two problems always yields
11
+ * the same message and the command layer — not this module — decides the exit code.
12
+ *
13
+ * Requirements: roadmap-issue-projection/AC-14 … roadmap-issue-projection/AC-32,
14
+ * roadmap-issue-projection/AC-44
15
+ *
16
+ * @module
17
+ */
18
+ import type { SyncConfig } from './config.js';
19
+ import { type Plan, type ProjectableItem, type RemoteIssue, type UnprojectableRef } from './types.js';
20
+ /** GitHub's own issue-title limit. */
21
+ export declare const MAX_ISSUE_TITLE_LENGTH = 256;
22
+ export interface PlanInput {
23
+ config: SyncConfig;
24
+ items: readonly ProjectableItem[];
25
+ unprojectable: readonly UnprojectableRef[];
26
+ issues: readonly RemoteIssue[];
27
+ retireIneligible: boolean;
28
+ /** Printed in the plan header and the prompt. */
29
+ repoLabel: string;
30
+ }
31
+ export type PlanResult = {
32
+ kind: 'ok';
33
+ plan: Plan;
34
+ warnings: readonly string[];
35
+ } | {
36
+ kind: 'refused';
37
+ error: string;
38
+ };
39
+ export declare function planProjection(input: PlanInput): PlanResult;
40
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,EAIL,KAAK,IAAI,EAET,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAC;AAEpB,sCAAsC;AACtC,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,aAAa,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC3C,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAgE3D"}