@rasensio/aidlc 1.20.0 → 1.22.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/roadmap.d.ts +66 -0
- package/dist/commands/roadmap.d.ts.map +1 -0
- package/dist/commands/roadmap.js +206 -0
- package/dist/commands/roadmap.js.map +1 -0
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +4 -0
- package/dist/menu/roster.js.map +1 -1
- package/dist/roadmap/paths.d.ts +21 -0
- package/dist/roadmap/paths.d.ts.map +1 -1
- package/dist/roadmap/paths.js +17 -1
- package/dist/roadmap/paths.js.map +1 -1
- package/dist/roadmap/sync/body.d.ts +123 -0
- package/dist/roadmap/sync/body.d.ts.map +1 -0
- package/dist/roadmap/sync/body.js +289 -0
- package/dist/roadmap/sync/body.js.map +1 -0
- package/dist/roadmap/sync/collect.d.ts +54 -0
- package/dist/roadmap/sync/collect.d.ts.map +1 -0
- package/dist/roadmap/sync/collect.js +138 -0
- package/dist/roadmap/sync/collect.js.map +1 -0
- package/dist/roadmap/sync/config.d.ts +95 -0
- package/dist/roadmap/sync/config.d.ts.map +1 -0
- package/dist/roadmap/sync/config.js +236 -0
- package/dist/roadmap/sync/config.js.map +1 -0
- package/dist/roadmap/sync/discover.d.ts +56 -0
- package/dist/roadmap/sync/discover.d.ts.map +1 -0
- package/dist/roadmap/sync/discover.js +126 -0
- package/dist/roadmap/sync/discover.js.map +1 -0
- package/dist/roadmap/sync/execute.d.ts +41 -0
- package/dist/roadmap/sync/execute.d.ts.map +1 -0
- package/dist/roadmap/sync/execute.js +174 -0
- package/dist/roadmap/sync/execute.js.map +1 -0
- package/dist/roadmap/sync/gh.d.ts +74 -0
- package/dist/roadmap/sync/gh.d.ts.map +1 -0
- package/dist/roadmap/sync/gh.js +110 -0
- package/dist/roadmap/sync/gh.js.map +1 -0
- package/dist/roadmap/sync/import.d.ts +73 -0
- package/dist/roadmap/sync/import.d.ts.map +1 -0
- package/dist/roadmap/sync/import.js +269 -0
- package/dist/roadmap/sync/import.js.map +1 -0
- package/dist/roadmap/sync/labels.d.ts +57 -0
- package/dist/roadmap/sync/labels.d.ts.map +1 -0
- package/dist/roadmap/sync/labels.js +80 -0
- package/dist/roadmap/sync/labels.js.map +1 -0
- package/dist/roadmap/sync/markdown.d.ts +79 -0
- package/dist/roadmap/sync/markdown.d.ts.map +1 -0
- package/dist/roadmap/sync/markdown.js +165 -0
- package/dist/roadmap/sync/markdown.js.map +1 -0
- package/dist/roadmap/sync/marker.d.ts +47 -0
- package/dist/roadmap/sync/marker.d.ts.map +1 -0
- package/dist/roadmap/sync/marker.js +73 -0
- package/dist/roadmap/sync/marker.js.map +1 -0
- package/dist/roadmap/sync/plan.d.ts +47 -0
- package/dist/roadmap/sync/plan.d.ts.map +1 -0
- package/dist/roadmap/sync/plan.js +290 -0
- package/dist/roadmap/sync/plan.js.map +1 -0
- package/dist/roadmap/sync/render.d.ts +28 -0
- package/dist/roadmap/sync/render.d.ts.map +1 -0
- package/dist/roadmap/sync/render.js +112 -0
- package/dist/roadmap/sync/render.js.map +1 -0
- package/dist/roadmap/sync/sanitize.d.ts +56 -0
- package/dist/roadmap/sync/sanitize.d.ts.map +1 -0
- package/dist/roadmap/sync/sanitize.js +140 -0
- package/dist/roadmap/sync/sanitize.js.map +1 -0
- package/dist/roadmap/sync/types.d.ts +139 -0
- package/dist/roadmap/sync/types.d.ts.map +1 -0
- package/dist/roadmap/sync/types.js +50 -0
- package/dist/roadmap/sync/types.js.map +1 -0
- package/package.json +2 -2
|
@@ -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,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where markdown is code and where it is prose.
|
|
3
|
+
*
|
|
4
|
+
* One primitive, three consumers — the sanitizer, the item-reference linker, and the
|
|
5
|
+
* marker scanner. They have to agree: if the sanitizer defuses something the marker
|
|
6
|
+
* scanner still sees as a comment, the projection refuses runs for a hazard that is no
|
|
7
|
+
* longer there; if they disagree the other way, a quoted marker injects itself into an
|
|
8
|
+
* issue. Agreement is cheap to state as a shared function and expensive to maintain as a
|
|
9
|
+
* convention across three regexes.
|
|
10
|
+
*
|
|
11
|
+
* Measured on this project's roadmap (76 items, 451,503 characters of body text) on
|
|
12
|
+
* 2026-09-07: 40 of the 41 hazard spans a naive sanitizer would rewrite are **inside
|
|
13
|
+
* code** — `@clack/prompts`, `actions/checkout@v4`, CSS colour hexes such as `#0891b2`
|
|
14
|
+
* that a `#123` pattern also matches, and one HTML comment. Exactly one is in prose. So
|
|
15
|
+
* code-awareness is not a refinement of the sanitizer; it is the difference between
|
|
16
|
+
* corrupting 40 spans and defusing 1.
|
|
17
|
+
*
|
|
18
|
+
* The inline-code pattern here is deliberately stronger than the prior art in
|
|
19
|
+
* `packages/website/scripts/blog/mdx-escape.mjs`, whose `` /(`[^`\n]*`)/ `` recognises
|
|
20
|
+
* single-backtick spans only. Two of the figures above were wrong under that weaker
|
|
21
|
+
* pattern: it read a prose `#17` as code, and it could not see that all 32 item-id
|
|
22
|
+
* references are code spans. Tightening the pattern is the reason this module exists
|
|
23
|
+
* rather than a detail of it — which is also why `mdx-escape.mjs` is left alone: it
|
|
24
|
+
* escapes for a different dialect, in a different package, and widening it would be a
|
|
25
|
+
* change to the blog pipeline nobody asked for.
|
|
26
|
+
*
|
|
27
|
+
* Requirements: richer-issue-bodies/AC-8
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
/** One inline code span, with its bounds in the source and its content between the ticks. */
|
|
32
|
+
export interface CodeSpan {
|
|
33
|
+
/** Index of the first backtick. */
|
|
34
|
+
start: number;
|
|
35
|
+
/** Index one past the last backtick. */
|
|
36
|
+
end: number;
|
|
37
|
+
/** Text between the backtick runs, unmodified. */
|
|
38
|
+
content: string;
|
|
39
|
+
/** How many backticks delimit it, so a rewrite can re-wrap with the same number. */
|
|
40
|
+
ticks: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A byte per character: 1 inside a fenced block or an inline code span, 0 in prose.
|
|
44
|
+
*
|
|
45
|
+
* Fence opener and closer lines are themselves marked as code. That is deliberate: an
|
|
46
|
+
* info string such as ```` ```ts ```` is not prose, and a rule that rewrote it would
|
|
47
|
+
* change what language a block claims to be.
|
|
48
|
+
*
|
|
49
|
+
* Linear in the length of the text: one pass over lines, and one regex walk per prose
|
|
50
|
+
* line.
|
|
51
|
+
*/
|
|
52
|
+
export declare function codeMask(text: string): Uint8Array;
|
|
53
|
+
/**
|
|
54
|
+
* Every inline code span outside a fenced block, in order of appearance.
|
|
55
|
+
*
|
|
56
|
+
* Spans *inside* a fence are not returned: their backticks are literal text there, and a
|
|
57
|
+
* caller rewriting one would be editing the contents of a code block.
|
|
58
|
+
*/
|
|
59
|
+
export declare function inlineCodeSpans(text: string): CodeSpan[];
|
|
60
|
+
/**
|
|
61
|
+
* Rewrite the prose runs of a document, copying every code run byte-identical.
|
|
62
|
+
*
|
|
63
|
+
* `fn` is called once per maximal prose run. It may return text of any length — offsets
|
|
64
|
+
* are not preserved across the call, which is why the mask is computed once up front
|
|
65
|
+
* rather than consulted per match.
|
|
66
|
+
*/
|
|
67
|
+
export declare function mapProse(text: string, fn: (prose: string) => string): string;
|
|
68
|
+
/**
|
|
69
|
+
* The last index at or before `limit` where a line break sits in **prose**.
|
|
70
|
+
*
|
|
71
|
+
* Used to choose a truncation point. A blank line inside a fenced block is masked as
|
|
72
|
+
* code, so restricting the cut to an unmasked newline is what stops truncation leaving a
|
|
73
|
+
* fence open — a body ending mid-fence renders the rest of the issue as one code block.
|
|
74
|
+
*
|
|
75
|
+
* Returns -1 when no such position exists, which the caller must treat as "cannot
|
|
76
|
+
* truncate safely" rather than as position 0.
|
|
77
|
+
*/
|
|
78
|
+
export declare function lastProseBreakBefore(text: string, limit: number): number;
|
|
79
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAkBH,6FAA6F;AAC7F,MAAM,WAAW,QAAQ;IACvB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAkCjD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CA2BxD;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAc5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMxE"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where markdown is code and where it is prose.
|
|
3
|
+
*
|
|
4
|
+
* One primitive, three consumers — the sanitizer, the item-reference linker, and the
|
|
5
|
+
* marker scanner. They have to agree: if the sanitizer defuses something the marker
|
|
6
|
+
* scanner still sees as a comment, the projection refuses runs for a hazard that is no
|
|
7
|
+
* longer there; if they disagree the other way, a quoted marker injects itself into an
|
|
8
|
+
* issue. Agreement is cheap to state as a shared function and expensive to maintain as a
|
|
9
|
+
* convention across three regexes.
|
|
10
|
+
*
|
|
11
|
+
* Measured on this project's roadmap (76 items, 451,503 characters of body text) on
|
|
12
|
+
* 2026-09-07: 40 of the 41 hazard spans a naive sanitizer would rewrite are **inside
|
|
13
|
+
* code** — `@clack/prompts`, `actions/checkout@v4`, CSS colour hexes such as `#0891b2`
|
|
14
|
+
* that a `#123` pattern also matches, and one HTML comment. Exactly one is in prose. So
|
|
15
|
+
* code-awareness is not a refinement of the sanitizer; it is the difference between
|
|
16
|
+
* corrupting 40 spans and defusing 1.
|
|
17
|
+
*
|
|
18
|
+
* The inline-code pattern here is deliberately stronger than the prior art in
|
|
19
|
+
* `packages/website/scripts/blog/mdx-escape.mjs`, whose `` /(`[^`\n]*`)/ `` recognises
|
|
20
|
+
* single-backtick spans only. Two of the figures above were wrong under that weaker
|
|
21
|
+
* pattern: it read a prose `#17` as code, and it could not see that all 32 item-id
|
|
22
|
+
* references are code spans. Tightening the pattern is the reason this module exists
|
|
23
|
+
* rather than a detail of it — which is also why `mdx-escape.mjs` is left alone: it
|
|
24
|
+
* escapes for a different dialect, in a different package, and widening it would be a
|
|
25
|
+
* change to the blog pipeline nobody asked for.
|
|
26
|
+
*
|
|
27
|
+
* Requirements: richer-issue-bodies/AC-8
|
|
28
|
+
*
|
|
29
|
+
* @module
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* A fence opener or closer: up to three leading spaces, then three or more backticks or
|
|
33
|
+
* tildes. CommonMark's rule, and the same one `mdx-escape.mjs` applies.
|
|
34
|
+
*/
|
|
35
|
+
const FENCE_RE = /^\s{0,3}(`{3,}|~{3,})/;
|
|
36
|
+
/**
|
|
37
|
+
* One inline code span.
|
|
38
|
+
*
|
|
39
|
+
* A run of N backticks closed by a run of **exactly** N — not by a prefix of a longer
|
|
40
|
+
* run, which is what the lookarounds buy. Without them `` ``a`` `` reads as a one-backtick
|
|
41
|
+
* span containing `` `a `` and the classification of everything after it on the line
|
|
42
|
+
* shifts by one span.
|
|
43
|
+
*/
|
|
44
|
+
const INLINE_CODE_RE = /(?<!`)(`+)(?!`)([^\n]*?)(?<!`)\1(?!`)/g;
|
|
45
|
+
/**
|
|
46
|
+
* A byte per character: 1 inside a fenced block or an inline code span, 0 in prose.
|
|
47
|
+
*
|
|
48
|
+
* Fence opener and closer lines are themselves marked as code. That is deliberate: an
|
|
49
|
+
* info string such as ```` ```ts ```` is not prose, and a rule that rewrote it would
|
|
50
|
+
* change what language a block claims to be.
|
|
51
|
+
*
|
|
52
|
+
* Linear in the length of the text: one pass over lines, and one regex walk per prose
|
|
53
|
+
* line.
|
|
54
|
+
*/
|
|
55
|
+
export function codeMask(text) {
|
|
56
|
+
const mask = new Uint8Array(text.length);
|
|
57
|
+
let pos = 0;
|
|
58
|
+
let fence = null;
|
|
59
|
+
for (const line of text.split('\n')) {
|
|
60
|
+
const opener = FENCE_RE.exec(line);
|
|
61
|
+
if (opener !== null) {
|
|
62
|
+
const marker = opener[1];
|
|
63
|
+
if (fence === null) {
|
|
64
|
+
fence = marker;
|
|
65
|
+
}
|
|
66
|
+
else if (marker[0] === fence[0] && marker.length >= fence.length) {
|
|
67
|
+
fence = null;
|
|
68
|
+
}
|
|
69
|
+
// `+ 1` covers the trailing newline. Without it the line breaks *between* fenced
|
|
70
|
+
// lines read as prose, and `lastProseBreakBefore` would happily choose one — which is
|
|
71
|
+
// exactly the mid-fence truncation it exists to prevent. Found by that test, not by
|
|
72
|
+
// reading this loop.
|
|
73
|
+
mask.fill(1, pos, pos + line.length + 1);
|
|
74
|
+
}
|
|
75
|
+
else if (fence !== null) {
|
|
76
|
+
mask.fill(1, pos, pos + line.length + 1);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
80
|
+
let match;
|
|
81
|
+
while ((match = INLINE_CODE_RE.exec(line)) !== null) {
|
|
82
|
+
mask.fill(1, pos + match.index, pos + match.index + match[0].length);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// +1 for the newline `split` consumed. The final line has no newline, but writing
|
|
86
|
+
// past the end of a `fill` range is a no-op, so no special case is needed.
|
|
87
|
+
pos += line.length + 1;
|
|
88
|
+
}
|
|
89
|
+
return mask;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Every inline code span outside a fenced block, in order of appearance.
|
|
93
|
+
*
|
|
94
|
+
* Spans *inside* a fence are not returned: their backticks are literal text there, and a
|
|
95
|
+
* caller rewriting one would be editing the contents of a code block.
|
|
96
|
+
*/
|
|
97
|
+
export function inlineCodeSpans(text) {
|
|
98
|
+
const spans = [];
|
|
99
|
+
let pos = 0;
|
|
100
|
+
let fence = null;
|
|
101
|
+
for (const line of text.split('\n')) {
|
|
102
|
+
const opener = FENCE_RE.exec(line);
|
|
103
|
+
if (opener !== null) {
|
|
104
|
+
const marker = opener[1];
|
|
105
|
+
if (fence === null)
|
|
106
|
+
fence = marker;
|
|
107
|
+
else if (marker[0] === fence[0] && marker.length >= fence.length)
|
|
108
|
+
fence = null;
|
|
109
|
+
}
|
|
110
|
+
else if (fence === null) {
|
|
111
|
+
INLINE_CODE_RE.lastIndex = 0;
|
|
112
|
+
let match;
|
|
113
|
+
while ((match = INLINE_CODE_RE.exec(line)) !== null) {
|
|
114
|
+
spans.push({
|
|
115
|
+
start: pos + match.index,
|
|
116
|
+
end: pos + match.index + match[0].length,
|
|
117
|
+
content: match[2],
|
|
118
|
+
ticks: match[1].length,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
pos += line.length + 1;
|
|
123
|
+
}
|
|
124
|
+
return spans;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Rewrite the prose runs of a document, copying every code run byte-identical.
|
|
128
|
+
*
|
|
129
|
+
* `fn` is called once per maximal prose run. It may return text of any length — offsets
|
|
130
|
+
* are not preserved across the call, which is why the mask is computed once up front
|
|
131
|
+
* rather than consulted per match.
|
|
132
|
+
*/
|
|
133
|
+
export function mapProse(text, fn) {
|
|
134
|
+
const mask = codeMask(text);
|
|
135
|
+
const out = [];
|
|
136
|
+
let runStart = 0;
|
|
137
|
+
for (let i = 1; i <= text.length; i++) {
|
|
138
|
+
const boundary = i === text.length || mask[i] !== mask[runStart];
|
|
139
|
+
if (!boundary)
|
|
140
|
+
continue;
|
|
141
|
+
const run = text.slice(runStart, i);
|
|
142
|
+
out.push(mask[runStart] === 1 ? run : fn(run));
|
|
143
|
+
runStart = i;
|
|
144
|
+
}
|
|
145
|
+
return out.join('');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The last index at or before `limit` where a line break sits in **prose**.
|
|
149
|
+
*
|
|
150
|
+
* Used to choose a truncation point. A blank line inside a fenced block is masked as
|
|
151
|
+
* code, so restricting the cut to an unmasked newline is what stops truncation leaving a
|
|
152
|
+
* fence open — a body ending mid-fence renders the rest of the issue as one code block.
|
|
153
|
+
*
|
|
154
|
+
* Returns -1 when no such position exists, which the caller must treat as "cannot
|
|
155
|
+
* truncate safely" rather than as position 0.
|
|
156
|
+
*/
|
|
157
|
+
export function lastProseBreakBefore(text, limit) {
|
|
158
|
+
const mask = codeMask(text);
|
|
159
|
+
for (let i = Math.min(limit, text.length - 1); i >= 0; i--) {
|
|
160
|
+
if (text[i] === '\n' && mask[i] === 0)
|
|
161
|
+
return i;
|
|
162
|
+
}
|
|
163
|
+
return -1;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../../src/roadmap/sync/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
* A third property was added by `richer-issue-bodies`:
|
|
23
|
+
*
|
|
24
|
+
* - **Matching is code-aware.** Once item prose is projected into the body, a marker
|
|
25
|
+
* quoted inside a fence or an inline code span is *visible code*, not a comment —
|
|
26
|
+
* GitHub renders it as text. A scanner that counted it would refuse runs as ambiguous
|
|
27
|
+
* over something no reader sees as a marker. The corpus holds exactly one such
|
|
28
|
+
* occurrence today, in a code span, so this is a measured case rather than a
|
|
29
|
+
* hypothetical one. The invariant to keep is that the scanner and the renderer agree.
|
|
30
|
+
*
|
|
31
|
+
* Requirements: roadmap-issue-projection/AC-47, roadmap-issue-projection/AC-48,
|
|
32
|
+
* roadmap-issue-projection/AC-49, richer-issue-bodies/AC-15
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
/** Render the marker for an item id. */
|
|
37
|
+
export declare function renderMarker(id: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Every item id marked in a body, in order of appearance, de-duplicated.
|
|
40
|
+
*
|
|
41
|
+
* Returns an **array** rather than the first id. A single-id return would make the
|
|
42
|
+
* two-markers case undetectable: the function would pick one and the projection would
|
|
43
|
+
* confidently retitle or close the wrong issue. The plural return costs nothing and
|
|
44
|
+
* makes the ambiguity representable, so `planProjection` can refuse instead of guess.
|
|
45
|
+
*/
|
|
46
|
+
export declare function findMarkerIds(body: string): string[];
|
|
47
|
+
//# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAaH,wCAAwC;AACxC,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAapD"}
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
* A third property was added by `richer-issue-bodies`:
|
|
23
|
+
*
|
|
24
|
+
* - **Matching is code-aware.** Once item prose is projected into the body, a marker
|
|
25
|
+
* quoted inside a fence or an inline code span is *visible code*, not a comment —
|
|
26
|
+
* GitHub renders it as text. A scanner that counted it would refuse runs as ambiguous
|
|
27
|
+
* over something no reader sees as a marker. The corpus holds exactly one such
|
|
28
|
+
* occurrence today, in a code span, so this is a measured case rather than a
|
|
29
|
+
* hypothetical one. The invariant to keep is that the scanner and the renderer agree.
|
|
30
|
+
*
|
|
31
|
+
* Requirements: roadmap-issue-projection/AC-47, roadmap-issue-projection/AC-48,
|
|
32
|
+
* roadmap-issue-projection/AC-49, richer-issue-bodies/AC-15
|
|
33
|
+
*
|
|
34
|
+
* @module
|
|
35
|
+
*/
|
|
36
|
+
import { codeMask } from './markdown.js';
|
|
37
|
+
/**
|
|
38
|
+
* Marker scanner.
|
|
39
|
+
*
|
|
40
|
+
* The id charset deliberately excludes `-->`'s characters, so the match cannot run past
|
|
41
|
+
* its own terminator. Global, because an issue carrying two markers is a real state the
|
|
42
|
+
* caller has to be able to see.
|
|
43
|
+
*/
|
|
44
|
+
const MARKER_RE = /<!--\s*aidlc-item:\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*-->/g;
|
|
45
|
+
/** Render the marker for an item id. */
|
|
46
|
+
export function renderMarker(id) {
|
|
47
|
+
return `<!-- aidlc-item: ${id} -->`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Every item id marked in a body, in order of appearance, de-duplicated.
|
|
51
|
+
*
|
|
52
|
+
* Returns an **array** rather than the first id. A single-id return would make the
|
|
53
|
+
* two-markers case undetectable: the function would pick one and the projection would
|
|
54
|
+
* confidently retitle or close the wrong issue. The plural return costs nothing and
|
|
55
|
+
* makes the ambiguity representable, so `planProjection` can refuse instead of guess.
|
|
56
|
+
*/
|
|
57
|
+
export function findMarkerIds(body) {
|
|
58
|
+
const ids = [];
|
|
59
|
+
const mask = codeMask(body);
|
|
60
|
+
// `matchAll` on a fresh iterator each call — the regex is module-level and `g`-flagged,
|
|
61
|
+
// so relying on `lastIndex` across calls would make results depend on call order.
|
|
62
|
+
for (const match of body.matchAll(MARKER_RE)) {
|
|
63
|
+
// A marker inside a fence or an inline code span is code the reader can see, not a
|
|
64
|
+
// comment. Skipping it is what keeps this scanner in step with GitHub's renderer.
|
|
65
|
+
if (mask[match.index] === 1)
|
|
66
|
+
continue;
|
|
67
|
+
const id = match[1];
|
|
68
|
+
if (!ids.includes(id))
|
|
69
|
+
ids.push(id);
|
|
70
|
+
}
|
|
71
|
+
return ids;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=marker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marker.js","sourceRoot":"","sources":["../../../src/roadmap/sync/marker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;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,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5B,wFAAwF;IACxF,kFAAkF;IAClF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,mFAAmF;QACnF,kFAAkF;QAClF,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QACtC,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,47 @@
|
|
|
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 BodyContext } from './body.js';
|
|
19
|
+
import type { SyncConfig } from './config.js';
|
|
20
|
+
import { type Plan, type ProjectableItem, type RemoteIssue, type UnprojectableRef } from './types.js';
|
|
21
|
+
/** GitHub's own issue-title limit. */
|
|
22
|
+
export declare const MAX_ISSUE_TITLE_LENGTH = 256;
|
|
23
|
+
export interface PlanInput {
|
|
24
|
+
config: SyncConfig;
|
|
25
|
+
items: readonly ProjectableItem[];
|
|
26
|
+
unprojectable: readonly UnprojectableRef[];
|
|
27
|
+
issues: readonly RemoteIssue[];
|
|
28
|
+
retireIneligible: boolean;
|
|
29
|
+
/** Printed in the plan header and the prompt. */
|
|
30
|
+
repoLabel: string;
|
|
31
|
+
/**
|
|
32
|
+
* Everything body rendering needs. Required rather than defaulted: a silent default
|
|
33
|
+
* would render every body with no permalinks and no reference links, and nothing
|
|
34
|
+
* anywhere would say so.
|
|
35
|
+
*/
|
|
36
|
+
bodyContext: BodyContext;
|
|
37
|
+
}
|
|
38
|
+
export type PlanResult = {
|
|
39
|
+
kind: 'ok';
|
|
40
|
+
plan: Plan;
|
|
41
|
+
warnings: readonly string[];
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'refused';
|
|
44
|
+
error: string;
|
|
45
|
+
};
|
|
46
|
+
export declare function planProjection(input: PlanInput): PlanResult;
|
|
47
|
+
//# 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,EAAmD,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9F,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;IAClB;;;;OAIG;IACH,WAAW,EAAE,WAAW,CAAC;CAC1B;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"}
|