@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.
- package/dist/change/cross-check.d.ts +28 -0
- package/dist/change/cross-check.d.ts.map +1 -0
- package/dist/change/cross-check.js +53 -0
- package/dist/change/cross-check.js.map +1 -0
- package/dist/change/impact.d.ts +57 -0
- package/dist/change/impact.d.ts.map +1 -0
- package/dist/change/impact.js +288 -0
- package/dist/change/impact.js.map +1 -0
- package/dist/change/index.d.ts +12 -0
- package/dist/change/index.d.ts.map +1 -0
- package/dist/change/index.js +11 -0
- package/dist/change/index.js.map +1 -0
- package/dist/change/parser.d.ts +52 -0
- package/dist/change/parser.d.ts.map +1 -0
- package/dist/change/parser.js +339 -0
- package/dist/change/parser.js.map +1 -0
- package/dist/change/reopen.d.ts +80 -0
- package/dist/change/reopen.d.ts.map +1 -0
- package/dist/change/reopen.js +227 -0
- package/dist/change/reopen.js.map +1 -0
- package/dist/change/types.d.ts +144 -0
- package/dist/change/types.d.ts.map +1 -0
- package/dist/change/types.js +8 -0
- package/dist/change/types.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/amend.d.ts +22 -0
- package/dist/commands/amend.d.ts.map +1 -0
- package/dist/commands/amend.js +355 -0
- package/dist/commands/amend.js.map +1 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +6 -0
- package/dist/commands/docs.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 +188 -0
- package/dist/commands/roadmap.js.map +1 -0
- package/dist/core/lifecycle.d.ts +3 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +13 -2
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/types.d.ts +37 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/menu/roster.d.ts.map +1 -1
- package/dist/menu/roster.js +8 -0
- package/dist/menu/roster.js.map +1 -1
- package/dist/roadmap/sync/body.d.ts +37 -0
- package/dist/roadmap/sync/body.d.ts.map +1 -0
- package/dist/roadmap/sync/body.js +52 -0
- package/dist/roadmap/sync/body.js.map +1 -0
- package/dist/roadmap/sync/collect.d.ts +45 -0
- package/dist/roadmap/sync/collect.d.ts.map +1 -0
- package/dist/roadmap/sync/collect.js +93 -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 +164 -0
- package/dist/roadmap/sync/execute.js.map +1 -0
- package/dist/roadmap/sync/gh.d.ts +64 -0
- package/dist/roadmap/sync/gh.d.ts.map +1 -0
- package/dist/roadmap/sync/gh.js +86 -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/marker.d.ts +38 -0
- package/dist/roadmap/sync/marker.d.ts.map +1 -0
- package/dist/roadmap/sync/marker.js +58 -0
- package/dist/roadmap/sync/marker.js.map +1 -0
- package/dist/roadmap/sync/plan.d.ts +40 -0
- package/dist/roadmap/sync/plan.d.ts.map +1 -0
- package/dist/roadmap/sync/plan.js +272 -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 +110 -0
- package/dist/roadmap/sync/render.js.map +1 -0
- package/dist/roadmap/sync/types.d.ts +111 -0
- package/dist/roadmap/sync/types.d.ts.map +1 -0
- package/dist/roadmap/sync/types.js +43 -0
- package/dist/roadmap/sync/types.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The body of a created issue.
|
|
3
|
+
*
|
|
4
|
+
* Generated, fixed-shape, and carrying **no item prose except through the title** —
|
|
5
|
+
* which lives in the issue title, not here. That is a deliberate narrowing, and it
|
|
6
|
+
* closes three problems with one decision:
|
|
7
|
+
*
|
|
8
|
+
* - **Marker injection.** An item that merely *quotes* a marker — this feature's own
|
|
9
|
+
* requirements document does — would put a second marker into a different issue, making
|
|
10
|
+
* the id→issue map ambiguous enough to retitle or close the wrong issue.
|
|
11
|
+
* - **Notification side effects.** Item prose containing `@name` or `#123` sends
|
|
12
|
+
* notifications and creates cross-links the moment it is published, which is the class
|
|
13
|
+
* of harm the per-status eligibility rule exists to prevent.
|
|
14
|
+
* - **Field limits.** GitHub caps bodies at 65,536 characters. A generated body is
|
|
15
|
+
* bounded by construction, so no truncation logic exists to get wrong.
|
|
16
|
+
*
|
|
17
|
+
* The alternative considered was projecting each item's Executive Summary with
|
|
18
|
+
* marker-shaped and mention-shaped spans neutralised. Rejected: neutralising `@` and `#`
|
|
19
|
+
* mangles legitimate text, and "which spans are dangerous" becomes a judgement somebody
|
|
20
|
+
* has to maintain. A reader who wants the detail follows the link.
|
|
21
|
+
*
|
|
22
|
+
* The body is never rewritten after creation. Comments are the reason to have a tracker
|
|
23
|
+
* at all, and an update that replaced the body would delete the context around them.
|
|
24
|
+
*
|
|
25
|
+
* Requirements: roadmap-issue-projection/AC-23, roadmap-issue-projection/AC-47
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import { renderMarker } from './marker.js';
|
|
30
|
+
/**
|
|
31
|
+
* Render the body for a newly created issue.
|
|
32
|
+
*
|
|
33
|
+
* Ends with the marker on its own line. That is a creation-time shape only — matching
|
|
34
|
+
* never depends on it (see `marker.ts`).
|
|
35
|
+
*/
|
|
36
|
+
export function renderIssueBody(item) {
|
|
37
|
+
return [
|
|
38
|
+
'Projected from this repository’s AIDLC roadmap.',
|
|
39
|
+
'',
|
|
40
|
+
`- **Item:** \`${item.id}\``,
|
|
41
|
+
`- **File:** \`${item.relPath}\``,
|
|
42
|
+
'',
|
|
43
|
+
'The roadmap file is the source of truth. Edit it there — this issue is a rendering',
|
|
44
|
+
'of it, and its title and `aidlc:` labels are overwritten by `aidlc roadmap sync`.',
|
|
45
|
+
'Comments here are never copied back into the repository, and this description is',
|
|
46
|
+
'never rewritten after the issue is created.',
|
|
47
|
+
'',
|
|
48
|
+
renderMarker(item.id),
|
|
49
|
+
'',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../../src/roadmap/sync/body.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAqB;IACnD,OAAO;QACL,iDAAiD;QACjD,EAAE;QACF,iBAAiB,IAAI,CAAC,EAAE,IAAI;QAC5B,iBAAiB,IAAI,CAAC,OAAO,IAAI;QACjC,EAAE;QACF,oFAAoF;QACpF,mFAAmF;QACnF,kFAAkF;QAClF,6CAA6C;QAC7C,EAAE;QACF,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the roadmap off disk into what the planner needs.
|
|
3
|
+
*
|
|
4
|
+
* Reads only. It does not judge: every refusal lives in `planProjection`, in one tested
|
|
5
|
+
* order, so a tree with two problems always produces the same message.
|
|
6
|
+
*
|
|
7
|
+
* Two things this fixes about the obvious implementation:
|
|
8
|
+
*
|
|
9
|
+
* - `listStatus` silently skips an item whose frontmatter will not parse, because
|
|
10
|
+
* `readItem` returns null for both "absent" and "unparseable". A projection built on it
|
|
11
|
+
* alone would inherit that: an item invisible in the repo *and* on the board, with
|
|
12
|
+
* nothing anywhere saying so. Five of this project's first 49 items were invisible for
|
|
13
|
+
* up to ten days from exactly this. So `unparseableItems` is read alongside it.
|
|
14
|
+
* - An item can parse perfectly and still carry no `id` — `readItem` returns `id: null`
|
|
15
|
+
* for a missing or empty key. `unparseableItems` does not detect that class, so
|
|
16
|
+
* `aidlc doctor` is silent about it. It is collected here as its own class so the
|
|
17
|
+
* refusal message can send the user somewhere useful.
|
|
18
|
+
*
|
|
19
|
+
* Everything resolves through `primaryCheckout`. A roadmap read from a worktree's copy is
|
|
20
|
+
* a roadmap stranded on that branch, and for an inbox that is indistinguishable from
|
|
21
|
+
* losing it.
|
|
22
|
+
*
|
|
23
|
+
* Requirements: roadmap-issue-projection/AC-33, roadmap-issue-projection/AC-34,
|
|
24
|
+
* roadmap-issue-projection/AC-51
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { ProjectableItem, UnprojectableRef } from './types.js';
|
|
29
|
+
export interface Collected {
|
|
30
|
+
/** The primary checkout's working tree — everything below is relative to it. */
|
|
31
|
+
root: string;
|
|
32
|
+
items: readonly ProjectableItem[];
|
|
33
|
+
unprojectable: readonly UnprojectableRef[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A title for an item whose frontmatter has none.
|
|
37
|
+
*
|
|
38
|
+
* The filename slug, de-hyphenated. Refusing would be worse: the item is perfectly
|
|
39
|
+
* projectable and the slug is a serviceable title, so a missing title is a cosmetic gap
|
|
40
|
+
* and not a reason to block the whole run.
|
|
41
|
+
*/
|
|
42
|
+
export declare function titleFromFilename(filename: string): string;
|
|
43
|
+
/** Read every status directory of the primary checkout containing `cwd`. */
|
|
44
|
+
export declare function collectItems(cwd: string): Collected;
|
|
45
|
+
//# sourceMappingURL=collect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAWH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,SAAS;IACxB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,aAAa,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CA2CnD"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the roadmap off disk into what the planner needs.
|
|
3
|
+
*
|
|
4
|
+
* Reads only. It does not judge: every refusal lives in `planProjection`, in one tested
|
|
5
|
+
* order, so a tree with two problems always produces the same message.
|
|
6
|
+
*
|
|
7
|
+
* Two things this fixes about the obvious implementation:
|
|
8
|
+
*
|
|
9
|
+
* - `listStatus` silently skips an item whose frontmatter will not parse, because
|
|
10
|
+
* `readItem` returns null for both "absent" and "unparseable". A projection built on it
|
|
11
|
+
* alone would inherit that: an item invisible in the repo *and* on the board, with
|
|
12
|
+
* nothing anywhere saying so. Five of this project's first 49 items were invisible for
|
|
13
|
+
* up to ten days from exactly this. So `unparseableItems` is read alongside it.
|
|
14
|
+
* - An item can parse perfectly and still carry no `id` — `readItem` returns `id: null`
|
|
15
|
+
* for a missing or empty key. `unparseableItems` does not detect that class, so
|
|
16
|
+
* `aidlc doctor` is silent about it. It is collected here as its own class so the
|
|
17
|
+
* refusal message can send the user somewhere useful.
|
|
18
|
+
*
|
|
19
|
+
* Everything resolves through `primaryCheckout`. A roadmap read from a worktree's copy is
|
|
20
|
+
* a roadmap stranded on that branch, and for an inbox that is indistinguishable from
|
|
21
|
+
* losing it.
|
|
22
|
+
*
|
|
23
|
+
* Requirements: roadmap-issue-projection/AC-33, roadmap-issue-projection/AC-34,
|
|
24
|
+
* roadmap-issue-projection/AC-51
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { ITEM_FILENAME_RE, ROADMAP_DIR, ROADMAP_STATUSES, listStatus, primaryCheckout, unparseableItems, } from '../paths.js';
|
|
29
|
+
/**
|
|
30
|
+
* A title for an item whose frontmatter has none.
|
|
31
|
+
*
|
|
32
|
+
* The filename slug, de-hyphenated. Refusing would be worse: the item is perfectly
|
|
33
|
+
* projectable and the slug is a serviceable title, so a missing title is a cosmetic gap
|
|
34
|
+
* and not a reason to block the whole run.
|
|
35
|
+
*/
|
|
36
|
+
export function titleFromFilename(filename) {
|
|
37
|
+
const match = ITEM_FILENAME_RE.exec(filename);
|
|
38
|
+
const slug = match?.[2] ?? filename.replace(/\.md$/, '');
|
|
39
|
+
return slug.split('-').join(' ');
|
|
40
|
+
}
|
|
41
|
+
/** Read every status directory of the primary checkout containing `cwd`. */
|
|
42
|
+
export function collectItems(cwd) {
|
|
43
|
+
const root = primaryCheckout(cwd);
|
|
44
|
+
const items = [];
|
|
45
|
+
const unprojectable = [];
|
|
46
|
+
// Unparseable first, so its entries keep their own reasons rather than being
|
|
47
|
+
// overwritten by the weaker "no id" classification below.
|
|
48
|
+
const unparseableLabels = new Set();
|
|
49
|
+
for (const bad of unparseableItems(root)) {
|
|
50
|
+
unparseableLabels.add(bad.label);
|
|
51
|
+
unprojectable.push({
|
|
52
|
+
label: bad.label,
|
|
53
|
+
status: bad.status,
|
|
54
|
+
cls: 'unparseable',
|
|
55
|
+
reason: bad.detail === null ? bad.reason : `${bad.reason}: ${bad.detail}`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
for (const status of ROADMAP_STATUSES) {
|
|
59
|
+
for (const item of listStatus(root, status)) {
|
|
60
|
+
const label = `${status}/${item.filename}`;
|
|
61
|
+
if (unparseableLabels.has(label))
|
|
62
|
+
continue;
|
|
63
|
+
if (item.id === null) {
|
|
64
|
+
unprojectable.push({
|
|
65
|
+
label,
|
|
66
|
+
status,
|
|
67
|
+
cls: 'missing-id',
|
|
68
|
+
reason: 'frontmatter has no `id:` value',
|
|
69
|
+
});
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
items.push({
|
|
73
|
+
id: item.id,
|
|
74
|
+
title: item.title ?? titleFromFilename(item.filename),
|
|
75
|
+
status,
|
|
76
|
+
filename: item.filename,
|
|
77
|
+
relPath: relPathOf(status, item.filename),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { root, items, unprojectable };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Repo-relative path of an item, with forward slashes on every platform.
|
|
85
|
+
*
|
|
86
|
+
* Built from the known layout rather than by `relative()` over the item's absolute path,
|
|
87
|
+
* so the result is identical on Windows without a separator dance. The value ends up in
|
|
88
|
+
* an issue body, where a backslash would render wrong.
|
|
89
|
+
*/
|
|
90
|
+
function relPathOf(status, filename) {
|
|
91
|
+
return `${ROADMAP_DIR}/${status}/${filename}`;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=collect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect.js","sourceRoot":"","sources":["../../../src/roadmap/sync/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,gBAAgB,GAEjB,MAAM,aAAa,CAAC;AAUrB;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAElC,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,MAAM,aAAa,GAAuB,EAAE,CAAC;IAE7C,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,KAAK,MAAM,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjC,aAAa,CAAC,IAAI,CAAC;YACjB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,GAAG,EAAE,aAAa;YAClB,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE;SAC1E,CAAC,CAAC;IACL,CAAC;IAED,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC3C,IAAI,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC3C,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;gBACrB,aAAa,CAAC,IAAI,CAAC;oBACjB,KAAK;oBACL,MAAM;oBACN,GAAG,EAAE,YAAY;oBACjB,MAAM,EAAE,gCAAgC;iBACzC,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC;gBACT,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACrD,MAAM;gBACN,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;aAC1C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,SAAS,CAAC,MAAqB,EAAE,QAAgB;IACxD,OAAO,GAAG,WAAW,IAAI,MAAM,IAAI,QAAQ,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `roadmap.sync` section of `.aidlc/config.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* Follows the convention every other module's config reader in this package follows —
|
|
5
|
+
* there is no central config loader here, ten call sites each parse the file inline —
|
|
6
|
+
* with one boundary drawn explicitly, because the requirements review found revision 1
|
|
7
|
+
* contradicting itself about it:
|
|
8
|
+
*
|
|
9
|
+
* - A malformed **container** reads as *not configured*: unparseable YAML, `roadmap`
|
|
10
|
+
* not a mapping, `roadmap.sync` not a mapping. One bad section must never break an
|
|
11
|
+
* unrelated command.
|
|
12
|
+
* - Anything wrong **inside** a well-formed `sync` mapping is a *named refusal*,
|
|
13
|
+
* including a wrong-typed value. `provider: 123` is a mistake somebody made on
|
|
14
|
+
* purpose and wants told about; silently reading it as "you have not configured this"
|
|
15
|
+
* would send them looking in the wrong place.
|
|
16
|
+
*
|
|
17
|
+
* The section is read from the **primary checkout**, the same tree the items come from.
|
|
18
|
+
* Items and eligibility are one fact; reading them from two trees would let a feature
|
|
19
|
+
* branch change what a colleague's `sync` publishes.
|
|
20
|
+
*
|
|
21
|
+
* Requirements: roadmap-issue-projection/AC-1 … roadmap-issue-projection/AC-9
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { type RoadmapStatus } from '../paths.js';
|
|
26
|
+
/** The `roadmap.sync` section, validated. */
|
|
27
|
+
export interface SyncConfig {
|
|
28
|
+
provider: 'github';
|
|
29
|
+
/** `owner/name`, or null when `gh` resolves the current repository. */
|
|
30
|
+
repo: string | null;
|
|
31
|
+
statuses: readonly RoadmapStatus[];
|
|
32
|
+
label: string;
|
|
33
|
+
}
|
|
34
|
+
export type SyncConfigResult = {
|
|
35
|
+
kind: 'ok';
|
|
36
|
+
config: SyncConfig;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'not-configured';
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'invalid';
|
|
41
|
+
error: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The default eligible statuses.
|
|
45
|
+
*
|
|
46
|
+
* The line falls between "thinking" and "work". `inbox/` means captured, not committed
|
|
47
|
+
* to — projecting it puts issues on a board for decisions nobody has made. `hold/`
|
|
48
|
+
* holds parked and declined work, which on this project means pricing thinking and a
|
|
49
|
+
* competitor teardown; a markdown file in a repo and an indexed, notification-generating
|
|
50
|
+
* issue are not the same exposure even when both are technically public.
|
|
51
|
+
*/
|
|
52
|
+
export declare const DEFAULT_STATUSES: readonly RoadmapStatus[];
|
|
53
|
+
/**
|
|
54
|
+
* Validate a repository coordinate. Shared by the config value and `--repo`, so the two
|
|
55
|
+
* can never diverge.
|
|
56
|
+
*/
|
|
57
|
+
export declare function validateRepo(raw: unknown, source: string): {
|
|
58
|
+
ok: true;
|
|
59
|
+
repo: string;
|
|
60
|
+
} | {
|
|
61
|
+
ok: false;
|
|
62
|
+
error: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Validate the discovery label.
|
|
66
|
+
*
|
|
67
|
+
* A comma is refused explicitly rather than folded into a charset message, because the
|
|
68
|
+
* failure it causes is silent and expensive: `gh`'s `--label` is a comma-split slice, so
|
|
69
|
+
* `aidlc,urgent` would AND two labels in discovery, return an empty issue map, and
|
|
70
|
+
* re-create every issue on every run. The discovery label *is* the identity of a
|
|
71
|
+
* projected issue, so a wrong one is a duplicate-issue generator, not a cosmetic error.
|
|
72
|
+
*/
|
|
73
|
+
export declare function validateLabel(raw: unknown): {
|
|
74
|
+
ok: true;
|
|
75
|
+
label: string;
|
|
76
|
+
} | {
|
|
77
|
+
ok: false;
|
|
78
|
+
error: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Read and validate the `roadmap.sync` section from a checkout's config.
|
|
82
|
+
*
|
|
83
|
+
* @param primaryCheckoutRoot - The primary checkout's working tree.
|
|
84
|
+
*/
|
|
85
|
+
export declare function readSyncConfig(primaryCheckoutRoot: string): SyncConfigResult;
|
|
86
|
+
/**
|
|
87
|
+
* Apply a `--repo` flag over a config, with identical validation.
|
|
88
|
+
*
|
|
89
|
+
* The flag wins. It is the likelier source of a wrong value — typed by hand, once —
|
|
90
|
+
* which is why it goes through the same validator rather than a looser one.
|
|
91
|
+
*/
|
|
92
|
+
export declare function withRepoOverride(config: SyncConfig, repo: string | undefined): SyncConfigResult;
|
|
93
|
+
/** The message for a project that has not opted in. */
|
|
94
|
+
export declare function notConfiguredMessage(): string;
|
|
95
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGnE,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,uEAAuE;IACvE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EAAuC,CAAC;AAkC7F;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAmBpH;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA0BtG;AA+BD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,mBAAmB,EAAE,MAAM,GAAG,gBAAgB,CAiD5E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAK/F;AAED,uDAAuD;AACvD,wBAAgB,oBAAoB,IAAI,MAAM,CAW7C"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `roadmap.sync` section of `.aidlc/config.yaml`.
|
|
3
|
+
*
|
|
4
|
+
* Follows the convention every other module's config reader in this package follows —
|
|
5
|
+
* there is no central config loader here, ten call sites each parse the file inline —
|
|
6
|
+
* with one boundary drawn explicitly, because the requirements review found revision 1
|
|
7
|
+
* contradicting itself about it:
|
|
8
|
+
*
|
|
9
|
+
* - A malformed **container** reads as *not configured*: unparseable YAML, `roadmap`
|
|
10
|
+
* not a mapping, `roadmap.sync` not a mapping. One bad section must never break an
|
|
11
|
+
* unrelated command.
|
|
12
|
+
* - Anything wrong **inside** a well-formed `sync` mapping is a *named refusal*,
|
|
13
|
+
* including a wrong-typed value. `provider: 123` is a mistake somebody made on
|
|
14
|
+
* purpose and wants told about; silently reading it as "you have not configured this"
|
|
15
|
+
* would send them looking in the wrong place.
|
|
16
|
+
*
|
|
17
|
+
* The section is read from the **primary checkout**, the same tree the items come from.
|
|
18
|
+
* Items and eligibility are one fact; reading them from two trees would let a feature
|
|
19
|
+
* branch change what a colleague's `sync` publishes.
|
|
20
|
+
*
|
|
21
|
+
* Requirements: roadmap-issue-projection/AC-1 … roadmap-issue-projection/AC-9
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { parse as parseYaml } from 'yaml';
|
|
28
|
+
import { ROADMAP_STATUSES } from '../paths.js';
|
|
29
|
+
import { DEFAULT_DISCOVERY_LABEL, MAX_LABEL_LENGTH } from './labels.js';
|
|
30
|
+
/**
|
|
31
|
+
* The default eligible statuses.
|
|
32
|
+
*
|
|
33
|
+
* The line falls between "thinking" and "work". `inbox/` means captured, not committed
|
|
34
|
+
* to — projecting it puts issues on a board for decisions nobody has made. `hold/`
|
|
35
|
+
* holds parked and declined work, which on this project means pricing thinking and a
|
|
36
|
+
* competitor teardown; a markdown file in a repo and an indexed, notification-generating
|
|
37
|
+
* issue are not the same exposure even when both are technically public.
|
|
38
|
+
*/
|
|
39
|
+
export const DEFAULT_STATUSES = ['backlog', 'in-progress', 'done'];
|
|
40
|
+
/** `owner/name`, each half on a conservative charset. */
|
|
41
|
+
const REPO_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
42
|
+
/** Three slash-separated parts — `gh`'s `[HOST/]OWNER/REPO`, which v1 does not support. */
|
|
43
|
+
const GHE_REPO_RE = /^[^/]+\/[^/]+\/[^/]+$/;
|
|
44
|
+
/** Absolute path of a checkout's config file. */
|
|
45
|
+
function configPath(root) {
|
|
46
|
+
return join(root, '.aidlc', 'config.yaml');
|
|
47
|
+
}
|
|
48
|
+
/** The whole config document, or `{}` when absent or unusable. */
|
|
49
|
+
function readDocument(root) {
|
|
50
|
+
const path = configPath(root);
|
|
51
|
+
if (!existsSync(path))
|
|
52
|
+
return {};
|
|
53
|
+
try {
|
|
54
|
+
const parsed = parseYaml(readFileSync(path, 'utf8'));
|
|
55
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
56
|
+
return {};
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Describe a value's type for an error message, without printing a whole object. */
|
|
64
|
+
function describe(value) {
|
|
65
|
+
if (value === null)
|
|
66
|
+
return 'null';
|
|
67
|
+
if (Array.isArray(value))
|
|
68
|
+
return 'a list';
|
|
69
|
+
if (typeof value === 'object')
|
|
70
|
+
return 'a mapping';
|
|
71
|
+
return JSON.stringify(value);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate a repository coordinate. Shared by the config value and `--repo`, so the two
|
|
75
|
+
* can never diverge.
|
|
76
|
+
*/
|
|
77
|
+
export function validateRepo(raw, source) {
|
|
78
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
79
|
+
return { ok: false, error: `${source} must be a string of the form owner/name, got ${describe(raw)}` };
|
|
80
|
+
}
|
|
81
|
+
if (REPO_RE.test(raw))
|
|
82
|
+
return { ok: true, repo: raw };
|
|
83
|
+
if (GHE_REPO_RE.test(raw)) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
error: `${source} ${JSON.stringify(raw)} looks like gh's HOST/OWNER/REPO form. ` +
|
|
87
|
+
'GitHub Enterprise is not supported in this version — use owner/name against github.com.',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: `${source} ${JSON.stringify(raw)} is not of the form owner/name ` +
|
|
93
|
+
'(each half must start with a letter or digit and contain only letters, digits, dot, dash, underscore)',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Validate the discovery label.
|
|
98
|
+
*
|
|
99
|
+
* A comma is refused explicitly rather than folded into a charset message, because the
|
|
100
|
+
* failure it causes is silent and expensive: `gh`'s `--label` is a comma-split slice, so
|
|
101
|
+
* `aidlc,urgent` would AND two labels in discovery, return an empty issue map, and
|
|
102
|
+
* re-create every issue on every run. The discovery label *is* the identity of a
|
|
103
|
+
* projected issue, so a wrong one is a duplicate-issue generator, not a cosmetic error.
|
|
104
|
+
*/
|
|
105
|
+
export function validateLabel(raw) {
|
|
106
|
+
if (typeof raw !== 'string') {
|
|
107
|
+
return { ok: false, error: `roadmap.sync.label must be a string, got ${describe(raw)}` };
|
|
108
|
+
}
|
|
109
|
+
if (raw.length === 0) {
|
|
110
|
+
return { ok: false, error: 'roadmap.sync.label must not be empty' };
|
|
111
|
+
}
|
|
112
|
+
if (raw.includes(',')) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
error: `roadmap.sync.label ${JSON.stringify(raw)} contains a comma. gh splits --label on ` +
|
|
116
|
+
'commas, so this would search for several labels at once, find nothing, and re-create ' +
|
|
117
|
+
'every issue on every run.',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (/\s/.test(raw)) {
|
|
121
|
+
return { ok: false, error: `roadmap.sync.label ${JSON.stringify(raw)} contains whitespace` };
|
|
122
|
+
}
|
|
123
|
+
if (raw.length > MAX_LABEL_LENGTH) {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
error: `roadmap.sync.label is ${raw.length} characters; the maximum is ${MAX_LABEL_LENGTH}`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return { ok: true, label: raw };
|
|
130
|
+
}
|
|
131
|
+
/** Validate the eligible-status list. */
|
|
132
|
+
function validateStatuses(raw) {
|
|
133
|
+
if (!Array.isArray(raw)) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
error: `roadmap.sync.statuses must be a list, got ${describe(raw)} (valid names: ${ROADMAP_STATUSES.join(', ')})`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const statuses = [];
|
|
140
|
+
for (const entry of raw) {
|
|
141
|
+
if (typeof entry !== 'string' || !ROADMAP_STATUSES.includes(entry)) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
error: `roadmap.sync.statuses contains ${describe(entry)}; valid names are ${ROADMAP_STATUSES.join(', ')}`,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (!statuses.includes(entry))
|
|
148
|
+
statuses.push(entry);
|
|
149
|
+
}
|
|
150
|
+
if (!statuses.includes('done')) {
|
|
151
|
+
return {
|
|
152
|
+
ok: false,
|
|
153
|
+
error: 'roadmap.sync.statuses must include `done`. Without it the close path never runs, so ' +
|
|
154
|
+
'issues for finished work stay open forever and the projection never converges.',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { ok: true, statuses };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Read and validate the `roadmap.sync` section from a checkout's config.
|
|
161
|
+
*
|
|
162
|
+
* @param primaryCheckoutRoot - The primary checkout's working tree.
|
|
163
|
+
*/
|
|
164
|
+
export function readSyncConfig(primaryCheckoutRoot) {
|
|
165
|
+
const doc = readDocument(primaryCheckoutRoot);
|
|
166
|
+
const roadmap = doc.roadmap;
|
|
167
|
+
if (roadmap === undefined)
|
|
168
|
+
return { kind: 'not-configured' };
|
|
169
|
+
if (typeof roadmap !== 'object' || roadmap === null || Array.isArray(roadmap)) {
|
|
170
|
+
return { kind: 'not-configured' };
|
|
171
|
+
}
|
|
172
|
+
const sync = roadmap.sync;
|
|
173
|
+
if (sync === undefined)
|
|
174
|
+
return { kind: 'not-configured' };
|
|
175
|
+
if (typeof sync !== 'object' || sync === null || Array.isArray(sync)) {
|
|
176
|
+
return { kind: 'not-configured' };
|
|
177
|
+
}
|
|
178
|
+
const s = sync;
|
|
179
|
+
if (s.provider === undefined) {
|
|
180
|
+
return { kind: 'invalid', error: 'roadmap.sync.provider is required; the only supported value is `github`' };
|
|
181
|
+
}
|
|
182
|
+
if (s.provider !== 'github') {
|
|
183
|
+
return {
|
|
184
|
+
kind: 'invalid',
|
|
185
|
+
error: `roadmap.sync.provider ${describe(s.provider)} is not supported; the only supported value is \`github\``,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
let repo = null;
|
|
189
|
+
if (s.repo !== undefined && s.repo !== null) {
|
|
190
|
+
const checked = validateRepo(s.repo, 'roadmap.sync.repo');
|
|
191
|
+
if (!checked.ok)
|
|
192
|
+
return { kind: 'invalid', error: checked.error };
|
|
193
|
+
repo = checked.repo;
|
|
194
|
+
}
|
|
195
|
+
let statuses = DEFAULT_STATUSES;
|
|
196
|
+
if (s.statuses !== undefined) {
|
|
197
|
+
const checked = validateStatuses(s.statuses);
|
|
198
|
+
if (!checked.ok)
|
|
199
|
+
return { kind: 'invalid', error: checked.error };
|
|
200
|
+
statuses = checked.statuses;
|
|
201
|
+
}
|
|
202
|
+
let label = DEFAULT_DISCOVERY_LABEL;
|
|
203
|
+
if (s.label !== undefined) {
|
|
204
|
+
const checked = validateLabel(s.label);
|
|
205
|
+
if (!checked.ok)
|
|
206
|
+
return { kind: 'invalid', error: checked.error };
|
|
207
|
+
label = checked.label;
|
|
208
|
+
}
|
|
209
|
+
return { kind: 'ok', config: { provider: 'github', repo, statuses, label } };
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Apply a `--repo` flag over a config, with identical validation.
|
|
213
|
+
*
|
|
214
|
+
* The flag wins. It is the likelier source of a wrong value — typed by hand, once —
|
|
215
|
+
* which is why it goes through the same validator rather than a looser one.
|
|
216
|
+
*/
|
|
217
|
+
export function withRepoOverride(config, repo) {
|
|
218
|
+
if (repo === undefined)
|
|
219
|
+
return { kind: 'ok', config };
|
|
220
|
+
const checked = validateRepo(repo, '--repo');
|
|
221
|
+
if (!checked.ok)
|
|
222
|
+
return { kind: 'invalid', error: checked.error };
|
|
223
|
+
return { kind: 'ok', config: { ...config, repo: checked.repo } };
|
|
224
|
+
}
|
|
225
|
+
/** The message for a project that has not opted in. */
|
|
226
|
+
export function notConfiguredMessage() {
|
|
227
|
+
return ('roadmap projection is not configured. Add a `roadmap.sync` section to ' +
|
|
228
|
+
'.aidlc/config.yaml:\n\n' +
|
|
229
|
+
' roadmap:\n' +
|
|
230
|
+
' sync:\n' +
|
|
231
|
+
' provider: github\n' +
|
|
232
|
+
` statuses: [${DEFAULT_STATUSES.join(', ')}]\n\n` +
|
|
233
|
+
`By default ${DEFAULT_STATUSES.join(', ')} are projected; inbox and hold are not, ` +
|
|
234
|
+
'because an untriaged idea and a parked one are not decisions anybody has published.');
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/roadmap/sync/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the id → issue map the planner reconciles against.
|
|
3
|
+
*
|
|
4
|
+
* One `gh issue list` invocation over the discovery label. Three details are load-bearing
|
|
5
|
+
* and all three were wrong in the first draft:
|
|
6
|
+
*
|
|
7
|
+
* - **`--limit` must be explicit.** `gh issue list` defaults to 30 results. A single call
|
|
8
|
+
* without a limit therefore returns at most 30 issues on a repo that has more, and every
|
|
9
|
+
* item missing from the map plans a `create` — producing exactly the duplicate issues the
|
|
10
|
+
* whole design exists to prevent, on every run, undetectably.
|
|
11
|
+
* - **Truncation must be a refusal, not a shrug.** A response of N issues is otherwise
|
|
12
|
+
* indistinguishable from "N is all there are". So the requested limit travels with the
|
|
13
|
+
* result and `count === limit` refuses before anything can plan a create.
|
|
14
|
+
* - **`labels` are objects.** `gh issue list --json labels` returns
|
|
15
|
+
* `{id,name,description,color}`, not strings. Treating them as strings makes every
|
|
16
|
+
* comparison against `aidlc:<status>` fail, so every item plans `update-labels` on every
|
|
17
|
+
* run and the projection never converges. They are flattened to names here, at the
|
|
18
|
+
* boundary, once.
|
|
19
|
+
*
|
|
20
|
+
* Requirements: roadmap-issue-projection/AC-11, roadmap-issue-projection/AC-12,
|
|
21
|
+
* roadmap-issue-projection/AC-13
|
|
22
|
+
*
|
|
23
|
+
* @module
|
|
24
|
+
*/
|
|
25
|
+
import { type GhRunner } from './gh.js';
|
|
26
|
+
import type { RemoteIssue } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* How many issues one map may hold.
|
|
29
|
+
*
|
|
30
|
+
* `gh` pages internally up to `--limit`, so this is one paginated sequence rather than a
|
|
31
|
+
* cap on a single page. A repo that reaches it gets a named refusal; that is a better
|
|
32
|
+
* outcome than a partial map, and better than reimplementing cursors against an interface
|
|
33
|
+
* (`gh issue list`) that exposes none.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ISSUE_LIST_LIMIT = 1000;
|
|
36
|
+
export type DiscoverResult = {
|
|
37
|
+
kind: 'ok';
|
|
38
|
+
issues: readonly RemoteIssue[];
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'truncated';
|
|
41
|
+
count: number;
|
|
42
|
+
limit: number;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'failed';
|
|
45
|
+
error: string;
|
|
46
|
+
};
|
|
47
|
+
export interface DiscoverOpts {
|
|
48
|
+
repo: string | null;
|
|
49
|
+
label: string;
|
|
50
|
+
limit?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Read every labelled issue, open and closed. */
|
|
53
|
+
export declare function buildIssueMap(gh: GhRunner, opts: DiscoverOpts): DiscoverResult;
|
|
54
|
+
/** The message for a truncated map. */
|
|
55
|
+
export declare function truncatedMessage(count: number, limit: number): string;
|
|
56
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kDAAkD;AAClD,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,cAAc,CAoD9E;AAmCD,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMrE"}
|