@rasensio/aidlc 1.20.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 (59) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +2 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/roadmap.d.ts +66 -0
  5. package/dist/commands/roadmap.d.ts.map +1 -0
  6. package/dist/commands/roadmap.js +188 -0
  7. package/dist/commands/roadmap.js.map +1 -0
  8. package/dist/menu/roster.d.ts.map +1 -1
  9. package/dist/menu/roster.js +4 -0
  10. package/dist/menu/roster.js.map +1 -1
  11. package/dist/roadmap/sync/body.d.ts +37 -0
  12. package/dist/roadmap/sync/body.d.ts.map +1 -0
  13. package/dist/roadmap/sync/body.js +52 -0
  14. package/dist/roadmap/sync/body.js.map +1 -0
  15. package/dist/roadmap/sync/collect.d.ts +45 -0
  16. package/dist/roadmap/sync/collect.d.ts.map +1 -0
  17. package/dist/roadmap/sync/collect.js +93 -0
  18. package/dist/roadmap/sync/collect.js.map +1 -0
  19. package/dist/roadmap/sync/config.d.ts +95 -0
  20. package/dist/roadmap/sync/config.d.ts.map +1 -0
  21. package/dist/roadmap/sync/config.js +236 -0
  22. package/dist/roadmap/sync/config.js.map +1 -0
  23. package/dist/roadmap/sync/discover.d.ts +56 -0
  24. package/dist/roadmap/sync/discover.d.ts.map +1 -0
  25. package/dist/roadmap/sync/discover.js +126 -0
  26. package/dist/roadmap/sync/discover.js.map +1 -0
  27. package/dist/roadmap/sync/execute.d.ts +41 -0
  28. package/dist/roadmap/sync/execute.d.ts.map +1 -0
  29. package/dist/roadmap/sync/execute.js +164 -0
  30. package/dist/roadmap/sync/execute.js.map +1 -0
  31. package/dist/roadmap/sync/gh.d.ts +64 -0
  32. package/dist/roadmap/sync/gh.d.ts.map +1 -0
  33. package/dist/roadmap/sync/gh.js +86 -0
  34. package/dist/roadmap/sync/gh.js.map +1 -0
  35. package/dist/roadmap/sync/import.d.ts +73 -0
  36. package/dist/roadmap/sync/import.d.ts.map +1 -0
  37. package/dist/roadmap/sync/import.js +269 -0
  38. package/dist/roadmap/sync/import.js.map +1 -0
  39. package/dist/roadmap/sync/labels.d.ts +57 -0
  40. package/dist/roadmap/sync/labels.d.ts.map +1 -0
  41. package/dist/roadmap/sync/labels.js +80 -0
  42. package/dist/roadmap/sync/labels.js.map +1 -0
  43. package/dist/roadmap/sync/marker.d.ts +38 -0
  44. package/dist/roadmap/sync/marker.d.ts.map +1 -0
  45. package/dist/roadmap/sync/marker.js +58 -0
  46. package/dist/roadmap/sync/marker.js.map +1 -0
  47. package/dist/roadmap/sync/plan.d.ts +40 -0
  48. package/dist/roadmap/sync/plan.d.ts.map +1 -0
  49. package/dist/roadmap/sync/plan.js +272 -0
  50. package/dist/roadmap/sync/plan.js.map +1 -0
  51. package/dist/roadmap/sync/render.d.ts +28 -0
  52. package/dist/roadmap/sync/render.d.ts.map +1 -0
  53. package/dist/roadmap/sync/render.js +110 -0
  54. package/dist/roadmap/sync/render.js.map +1 -0
  55. package/dist/roadmap/sync/types.d.ts +111 -0
  56. package/dist/roadmap/sync/types.d.ts.map +1 -0
  57. package/dist/roadmap/sync/types.js +43 -0
  58. package/dist/roadmap/sync/types.js.map +1 -0
  59. package/package.json +2 -2
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Apply a plan. The only module in this feature that writes anything to GitHub.
3
+ *
4
+ * It imports no `node:fs` — deliberately, and pinned by a test. `sync` writes nothing to
5
+ * disk, and the cheapest way to keep that true is for the writing module to have no
6
+ * ability to. Snapshotting a directory around a run would be the weaker check: cost
7
+ * capture writes into `.aidlc/` during a session, so a whole-tree snapshot is either
8
+ * flaky or has to exclude so much that it stops proving anything.
9
+ *
10
+ * A `create` is **one** invocation carrying the discovery label, the status label, and the
11
+ * body marker. If the label were applied by a second call, a process death between the two
12
+ * would strand an issue invisible to discovery — which the next run would duplicate. One
13
+ * invocation makes the issue discoverable the instant it exists, and that atomicity is
14
+ * what the whole no-local-state design rests on.
15
+ *
16
+ * Execution **aborts on the first failure**. A rate limit or an authorization failure will
17
+ * fail everything after it too, so continuing would turn one legible error into forty.
18
+ *
19
+ * Requirements: roadmap-issue-projection/AC-23, roadmap-issue-projection/AC-41,
20
+ * roadmap-issue-projection/AC-42, roadmap-issue-projection/AC-43,
21
+ * roadmap-issue-projection/AC-46
22
+ *
23
+ * @module
24
+ */
25
+ import { renderIssueBody } from './body.js';
26
+ import { firstLine, repoArgs } from './gh.js';
27
+ import { colorFor, descriptionFor } from './labels.js';
28
+ import { isWrite } from './types.js';
29
+ export function applyPlan(gh, plan, discoveryLabel) {
30
+ const writes = plan.lines.filter((l) => isWrite(l.outcome));
31
+ const applied = [];
32
+ const ensured = new Set();
33
+ for (let i = 0; i < writes.length; i++) {
34
+ const line = writes[i];
35
+ const labelFailure = ensureLabels(gh, plan, line, discoveryLabel, ensured);
36
+ if (labelFailure !== null) {
37
+ return { applied, failure: { line, error: labelFailure }, notAttempted: writes.slice(i) };
38
+ }
39
+ const result = gh.run(invocationFor(plan, line, discoveryLabel));
40
+ if (!result.ok) {
41
+ return {
42
+ applied,
43
+ failure: { line, error: firstLine(result.error) },
44
+ notAttempted: writes.slice(i + 1),
45
+ };
46
+ }
47
+ applied.push(line);
48
+ }
49
+ return { applied, failure: null, notAttempted: [] };
50
+ }
51
+ /** The `gh` argv for one write outcome. */
52
+ export function invocationFor(plan, line, discoveryLabel) {
53
+ const repo = [...repoArgs(plan.repo)];
54
+ switch (line.outcome) {
55
+ case 'create': {
56
+ if (line.item === null)
57
+ throw new Error('create without an item');
58
+ return [
59
+ 'issue',
60
+ 'create',
61
+ ...repo,
62
+ '--title',
63
+ line.item.title,
64
+ '--body',
65
+ renderIssueBody(line.item),
66
+ '--label',
67
+ discoveryLabel,
68
+ ...line.addLabels.flatMap((l) => ['--label', l]),
69
+ ];
70
+ }
71
+ case 'update-title': {
72
+ if (line.item === null || line.issueNumber === null) {
73
+ throw new Error('update-title without an item and issue');
74
+ }
75
+ return ['issue', 'edit', String(line.issueNumber), ...repo, '--title', line.item.title];
76
+ }
77
+ case 'update-labels': {
78
+ if (line.issueNumber === null)
79
+ throw new Error('update-labels without an issue');
80
+ // --add-label / --remove-label, never a wholesale --label: a human's labels on a
81
+ // projected issue are theirs, and replacing the set would silently delete them.
82
+ return [
83
+ 'issue',
84
+ 'edit',
85
+ String(line.issueNumber),
86
+ ...repo,
87
+ ...line.addLabels.flatMap((l) => ['--add-label', l]),
88
+ ...line.removeLabels.flatMap((l) => ['--remove-label', l]),
89
+ ];
90
+ }
91
+ case 'close': {
92
+ if (line.issueNumber === null)
93
+ throw new Error('close without an issue');
94
+ return ['issue', 'close', String(line.issueNumber), ...repo];
95
+ }
96
+ default:
97
+ throw new Error(`not a write outcome: ${line.outcome}`);
98
+ }
99
+ }
100
+ /**
101
+ * Create every label this line needs, once per run.
102
+ *
103
+ * `gh label create` **exits 1 when the label already exists**, so bare creation plus
104
+ * abort-on-first-failure would kill every run after the first. `--force` fixes that — but
105
+ * `gh` chooses a *random* colour when none is given, so `--force` alone would repaint the
106
+ * label on every run. The colour and description are therefore always passed, from a fixed
107
+ * table, which makes the ensure genuinely idempotent rather than merely non-failing.
108
+ */
109
+ function ensureLabels(gh, plan, line, discoveryLabel, ensured) {
110
+ const needed = line.outcome === 'create' ? [discoveryLabel, ...line.addLabels] : [...line.addLabels];
111
+ for (const name of needed) {
112
+ if (ensured.has(name))
113
+ continue;
114
+ const result = gh.run([
115
+ 'label',
116
+ 'create',
117
+ name,
118
+ ...repoArgs(plan.repo),
119
+ '--color',
120
+ colorFor(name),
121
+ '--description',
122
+ descriptionFor(name),
123
+ '--force',
124
+ ]);
125
+ if (!result.ok) {
126
+ return `could not create the label ${JSON.stringify(name)}: ${firstLine(result.error)}`;
127
+ }
128
+ ensured.add(name);
129
+ }
130
+ return null;
131
+ }
132
+ /** Render an execution report for stdout. */
133
+ export function renderExecReport(report) {
134
+ const out = [];
135
+ if (report.applied.length > 0) {
136
+ out.push(`Applied ${report.applied.length} write${report.applied.length === 1 ? '' : 's'}:`);
137
+ for (const l of report.applied)
138
+ out.push(` ${l.outcome} ${subjectOf(l)}`);
139
+ }
140
+ else {
141
+ out.push('No writes were applied.');
142
+ }
143
+ if (report.failure !== null) {
144
+ out.push('');
145
+ out.push(`Stopped at ${report.failure.line.outcome} ${subjectOf(report.failure.line)}`);
146
+ out.push(` gh said: ${report.failure.error}`);
147
+ if (report.notAttempted.length > 0) {
148
+ out.push(` ${report.notAttempted.length} further write(s) were not attempted:`);
149
+ for (const l of report.notAttempted)
150
+ out.push(` ${l.outcome} ${subjectOf(l)}`);
151
+ }
152
+ out.push('');
153
+ out.push('Re-run once the cause is fixed — the projection is idempotent, so completed writes are not repeated.');
154
+ }
155
+ return out.join('\n');
156
+ }
157
+ function subjectOf(line) {
158
+ if (line.item !== null) {
159
+ return `${line.item.status}/${line.item.filename}` +
160
+ (line.issueNumber === null ? '' : ` (#${line.issueNumber})`);
161
+ }
162
+ return `#${line.issueNumber}`;
163
+ }
164
+ //# sourceMappingURL=execute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/roadmap/sync/execute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The single subprocess boundary between this feature and the `gh` CLI.
3
+ *
4
+ * Every `gh` invocation in the projection goes through `GhRunner.run(args)`, and
5
+ * `args` is an array. That is structural rather than habitual on purpose: item titles
6
+ * and config values come from tracked files, so in a cloned repo they are the repo
7
+ * author's strings, not the user's. With one seam there is no call site for somebody
8
+ * in a hurry to add a template string or `shell: true` to, and the fake runner used in
9
+ * tests records every argv — which is what lets the token-secrecy property be checked
10
+ * by *value* (set `GH_TOKEN` to a sentinel, assert it appears in no recorded argv)
11
+ * rather than by grepping the source for a variable name. This project has broken
12
+ * prose-scoped greps six separate times.
13
+ *
14
+ * Nothing here reads `GH_TOKEN` or `GITHUB_TOKEN`. `gh` owns the credential; this
15
+ * module owns the argv.
16
+ *
17
+ * Requirements: roadmap-issue-projection/AC-30, roadmap-issue-projection/AC-50
18
+ *
19
+ * @module
20
+ */
21
+ export type GhResult = {
22
+ ok: true;
23
+ stdout: string;
24
+ } | {
25
+ ok: false;
26
+ error: string;
27
+ exitCode: number | null;
28
+ };
29
+ export interface GhRunner {
30
+ run(args: readonly string[]): GhResult;
31
+ }
32
+ /**
33
+ * 256 MiB.
34
+ *
35
+ * `execFileSync` defaults to 1 MiB, and discovery asks for every projected issue's
36
+ * body so the marker can be read. Measured: the default throws `ENOBUFS` at roughly
37
+ * 2 MiB of output, which a few hundred issues with ordinary bodies will exceed — two
38
+ * orders of magnitude below the 1000-issue map this feature otherwise supports. The
39
+ * failure is a thrown error rather than a truncated read, so it would be loud; it
40
+ * would also be entirely avoidable, which is why the limit is stated.
41
+ */
42
+ export declare const GH_MAX_BUFFER: number;
43
+ /** The real runner. Argument array, no shell, explicit buffer. */
44
+ export declare function realGhRunner(): GhRunner;
45
+ /**
46
+ * Check that `gh` is present and authenticated.
47
+ *
48
+ * **Authentication only.** `gh auth status` cannot tell whether the token may write
49
+ * issues on the target repository, so a token that is authenticated but not authorized
50
+ * passes here and fails on the first write — where `applyPlan` names the item and
51
+ * prints `gh`'s own message. Claiming otherwise would put the check in the wrong
52
+ * place and make the first-run failure less legible, not more.
53
+ */
54
+ export declare function ghPreflight(gh: GhRunner): {
55
+ ok: true;
56
+ } | {
57
+ ok: false;
58
+ error: string;
59
+ };
60
+ /** First non-empty line of a `gh` message, for one-line error reporting. */
61
+ export declare function firstLine(text: string): string;
62
+ /** `['--repo', repo]` when a coordinate was resolved, else `[]`. */
63
+ export declare function repoArgs(repo: string | null): readonly string[];
64
+ //# sourceMappingURL=gh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/gh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,MAAM,QAAQ,GAChB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,QAAQ;IACvB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,QAAoB,CAAC;AAE/C,kEAAkE;AAClE,wBAAgB,YAAY,IAAI,QAAQ,CAkBvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CASrF;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,MAAM,EAAE,CAE/D"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The single subprocess boundary between this feature and the `gh` CLI.
3
+ *
4
+ * Every `gh` invocation in the projection goes through `GhRunner.run(args)`, and
5
+ * `args` is an array. That is structural rather than habitual on purpose: item titles
6
+ * and config values come from tracked files, so in a cloned repo they are the repo
7
+ * author's strings, not the user's. With one seam there is no call site for somebody
8
+ * in a hurry to add a template string or `shell: true` to, and the fake runner used in
9
+ * tests records every argv — which is what lets the token-secrecy property be checked
10
+ * by *value* (set `GH_TOKEN` to a sentinel, assert it appears in no recorded argv)
11
+ * rather than by grepping the source for a variable name. This project has broken
12
+ * prose-scoped greps six separate times.
13
+ *
14
+ * Nothing here reads `GH_TOKEN` or `GITHUB_TOKEN`. `gh` owns the credential; this
15
+ * module owns the argv.
16
+ *
17
+ * Requirements: roadmap-issue-projection/AC-30, roadmap-issue-projection/AC-50
18
+ *
19
+ * @module
20
+ */
21
+ import { execFileSync } from 'node:child_process';
22
+ /**
23
+ * 256 MiB.
24
+ *
25
+ * `execFileSync` defaults to 1 MiB, and discovery asks for every projected issue's
26
+ * body so the marker can be read. Measured: the default throws `ENOBUFS` at roughly
27
+ * 2 MiB of output, which a few hundred issues with ordinary bodies will exceed — two
28
+ * orders of magnitude below the 1000-issue map this feature otherwise supports. The
29
+ * failure is a thrown error rather than a truncated read, so it would be loud; it
30
+ * would also be entirely avoidable, which is why the limit is stated.
31
+ */
32
+ export const GH_MAX_BUFFER = 256 * 1024 * 1024;
33
+ /** The real runner. Argument array, no shell, explicit buffer. */
34
+ export function realGhRunner() {
35
+ return {
36
+ run(args) {
37
+ try {
38
+ const stdout = execFileSync('gh', [...args], {
39
+ encoding: 'utf8',
40
+ stdio: ['ignore', 'pipe', 'pipe'],
41
+ maxBuffer: GH_MAX_BUFFER,
42
+ });
43
+ return { ok: true, stdout };
44
+ }
45
+ catch (err) {
46
+ const e = err;
47
+ const stderr = typeof e.stderr === 'string' ? e.stderr : e.stderr?.toString('utf8');
48
+ const message = (stderr ?? e.message ?? 'gh invocation failed').trim();
49
+ return { ok: false, error: message, exitCode: e.status ?? null };
50
+ }
51
+ },
52
+ };
53
+ }
54
+ /**
55
+ * Check that `gh` is present and authenticated.
56
+ *
57
+ * **Authentication only.** `gh auth status` cannot tell whether the token may write
58
+ * issues on the target repository, so a token that is authenticated but not authorized
59
+ * passes here and fails on the first write — where `applyPlan` names the item and
60
+ * prints `gh`'s own message. Claiming otherwise would put the check in the wrong
61
+ * place and make the first-run failure less legible, not more.
62
+ */
63
+ export function ghPreflight(gh) {
64
+ const result = gh.run(['auth', 'status']);
65
+ if (result.ok)
66
+ return { ok: true };
67
+ return {
68
+ ok: false,
69
+ error: 'gh is not available or not authenticated — install the GitHub CLI and run ' +
70
+ `\`gh auth login\`, then try again.\n gh said: ${firstLine(result.error)}`,
71
+ };
72
+ }
73
+ /** First non-empty line of a `gh` message, for one-line error reporting. */
74
+ export function firstLine(text) {
75
+ for (const line of text.split('\n')) {
76
+ const trimmed = line.trim();
77
+ if (trimmed.length > 0)
78
+ return trimmed;
79
+ }
80
+ return text.trim();
81
+ }
82
+ /** `['--repo', repo]` when a coordinate was resolved, else `[]`. */
83
+ export function repoArgs(repo) {
84
+ return repo === null ? [] : ['--repo', repo];
85
+ }
86
+ //# sourceMappingURL=gh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gh.js","sourceRoot":"","sources":["../../../src/roadmap/sync/gh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUlD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C,kEAAkE;AAClE,MAAM,UAAU,YAAY;IAC1B,OAAO;QACL,GAAG,CAAC,IAAuB;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE;oBAC3C,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;oBACjC,SAAS,EAAE,aAAa;iBACzB,CAAC,CAAC;gBACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,MAAM,CAAC,GAAG,GAA6E,CAAC;gBACxF,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACpF,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,sBAAsB,CAAC,CAAC,IAAI,EAAE,CAAC;gBACvE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YACnE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,EAAY;IACtC,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1C,IAAI,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACnC,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EACH,4EAA4E;YAC5E,kDAAkD,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;KAC9E,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;AACrB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,QAAQ,CAAC,IAAmB;IAC1C,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,73 @@
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 { type GhRunner } from './gh.js';
20
+ /** A GitHub issue, reduced to what identifies it. */
21
+ export interface IssueCoord {
22
+ host: string;
23
+ owner: string;
24
+ repo: string;
25
+ number: number;
26
+ }
27
+ export type ImportResult = {
28
+ kind: 'written';
29
+ path: string;
30
+ } | {
31
+ kind: 'duplicate';
32
+ existing: string;
33
+ coord: IssueCoord;
34
+ } | {
35
+ kind: 'refused';
36
+ error: string;
37
+ };
38
+ /** Slug used when a title yields nothing usable. */
39
+ export declare const FALLBACK_SLUG_PREFIX = "imported-issue";
40
+ /**
41
+ * Parse an issue reference into a comparable tuple.
42
+ *
43
+ * Normalising is the point: `https://github.com/o/r/issues/12`, the same with a trailing
44
+ * slash, with a `#issuecomment-…` fragment, with `www.`, and the
45
+ * `api.github.com/repos/o/r/issues/12` form all denote one issue. Comparing raw strings
46
+ * would report false duplicates in one direction and miss real ones in the other — and a
47
+ * naive substring compare makes `issues/1` match `issues/12`.
48
+ */
49
+ export declare function parseIssueUrl(raw: string): IssueCoord | null;
50
+ /** Canonical string form of a coordinate, for map keys. */
51
+ export declare function coordKey(coord: IssueCoord): string;
52
+ /**
53
+ * Every issue already linked by a roadmap item, keyed by coordinate.
54
+ *
55
+ * This reads item **bodies**, which the roadmap skill's frontmatter-only rule otherwise
56
+ * discourages. That rule governs *browse*, which is a list and must stay cheap enough to
57
+ * run over the whole roadmap. Import is neither a list nor cheap-by-contract, and there is
58
+ * nowhere else the local half of a duplicate check could come from — DD-1 means no
59
+ * `remote:` field exists to consult.
60
+ */
61
+ export declare function linkedCoords(cwd: string): Map<string, string>;
62
+ /**
63
+ * A filename-safe slug from an issue title.
64
+ *
65
+ * Must satisfy `ITEM_FILENAME_RE`'s `[a-z0-9][a-z0-9-]*`, which a title of punctuation or
66
+ * of a non-Latin script will not survive — hence the fallback rather than a refusal. An
67
+ * issue that cannot be imported because of its title would be a strange thing to tell
68
+ * somebody.
69
+ */
70
+ export declare function slugFromTitle(title: string, number: number): string;
71
+ /** Import one issue into `inbox/`. */
72
+ export declare function importIssue(gh: GhRunner, cwd: string, url: string): ImportResult;
73
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../src/roadmap/sync/import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAYH,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnD,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC,oDAAoD;AACpD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAKrD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA2B5D;AAQD,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA8B7D;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAYnE;AAuBD,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CA+ChF"}