@nonameolsson/nightjar 0.0.0-stage → 0.2.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 (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +405 -2
  3. package/dist/account.d.ts +12 -0
  4. package/dist/account.js +57 -0
  5. package/dist/account.js.map +1 -0
  6. package/dist/adapter-module.d.ts +18 -0
  7. package/dist/adapter-module.js +83 -0
  8. package/dist/adapter-module.js.map +1 -0
  9. package/dist/adapters.d.ts +15 -0
  10. package/dist/adapters.js +160 -0
  11. package/dist/adapters.js.map +1 -0
  12. package/dist/args.d.ts +13 -0
  13. package/dist/args.js +84 -0
  14. package/dist/args.js.map +1 -0
  15. package/dist/ask.d.ts +6 -0
  16. package/dist/ask.js +27 -0
  17. package/dist/ask.js.map +1 -0
  18. package/dist/azure-devops/api.d.ts +34 -0
  19. package/dist/azure-devops/api.js +98 -0
  20. package/dist/azure-devops/api.js.map +1 -0
  21. package/dist/azure-devops/code-host.d.ts +36 -0
  22. package/dist/azure-devops/code-host.js +185 -0
  23. package/dist/azure-devops/code-host.js.map +1 -0
  24. package/dist/azure-devops/fake-azure-devops.d.ts +90 -0
  25. package/dist/azure-devops/fake-azure-devops.js +728 -0
  26. package/dist/azure-devops/fake-azure-devops.js.map +1 -0
  27. package/dist/azure-devops/index.d.ts +8 -0
  28. package/dist/azure-devops/index.js +11 -0
  29. package/dist/azure-devops/index.js.map +1 -0
  30. package/dist/azure-devops/sign-in.d.ts +11 -0
  31. package/dist/azure-devops/sign-in.js +68 -0
  32. package/dist/azure-devops/sign-in.js.map +1 -0
  33. package/dist/azure-devops/text.d.ts +1 -0
  34. package/dist/azure-devops/text.js +41 -0
  35. package/dist/azure-devops/text.js.map +1 -0
  36. package/dist/azure-devops/tracker.d.ts +44 -0
  37. package/dist/azure-devops/tracker.js +333 -0
  38. package/dist/azure-devops/tracker.js.map +1 -0
  39. package/dist/build.d.ts +75 -0
  40. package/dist/build.js +698 -0
  41. package/dist/build.js.map +1 -0
  42. package/dist/check.d.ts +8 -0
  43. package/dist/check.js +25 -0
  44. package/dist/check.js.map +1 -0
  45. package/dist/checks.d.ts +45 -0
  46. package/dist/checks.js +243 -0
  47. package/dist/checks.js.map +1 -0
  48. package/dist/cli.d.ts +2 -0
  49. package/dist/cli.js +11 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/code-host.d.ts +19 -0
  52. package/dist/code-host.js +2 -0
  53. package/dist/code-host.js.map +1 -0
  54. package/dist/config.d.ts +86 -0
  55. package/dist/config.js +294 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/contract/case.d.ts +4 -0
  58. package/dist/contract/case.js +2 -0
  59. package/dist/contract/case.js.map +1 -0
  60. package/dist/contract/code-host.d.ts +24 -0
  61. package/dist/contract/code-host.js +108 -0
  62. package/dist/contract/code-host.js.map +1 -0
  63. package/dist/contract/command.d.ts +2 -0
  64. package/dist/contract/command.js +36 -0
  65. package/dist/contract/command.js.map +1 -0
  66. package/dist/contract/feature-writer.d.ts +3 -0
  67. package/dist/contract/feature-writer.js +101 -0
  68. package/dist/contract/feature-writer.js.map +1 -0
  69. package/dist/contract/run.d.ts +18 -0
  70. package/dist/contract/run.js +89 -0
  71. package/dist/contract/run.js.map +1 -0
  72. package/dist/contract/tracker.d.ts +30 -0
  73. package/dist/contract/tracker.js +250 -0
  74. package/dist/contract/tracker.js.map +1 -0
  75. package/dist/create.d.ts +9 -0
  76. package/dist/create.js +112 -0
  77. package/dist/create.js.map +1 -0
  78. package/dist/doctor.d.ts +4 -0
  79. package/dist/doctor.js +42 -0
  80. package/dist/doctor.js.map +1 -0
  81. package/dist/fakes/code-host.d.ts +21 -0
  82. package/dist/fakes/code-host.js +42 -0
  83. package/dist/fakes/code-host.js.map +1 -0
  84. package/dist/fakes/harness.d.ts +7 -0
  85. package/dist/fakes/harness.js +68 -0
  86. package/dist/fakes/harness.js.map +1 -0
  87. package/dist/fakes/machine.d.ts +11 -0
  88. package/dist/fakes/machine.js +101 -0
  89. package/dist/fakes/machine.js.map +1 -0
  90. package/dist/fakes/tracker.d.ts +40 -0
  91. package/dist/fakes/tracker.js +116 -0
  92. package/dist/fakes/tracker.js.map +1 -0
  93. package/dist/feature-file.d.ts +12 -0
  94. package/dist/feature-file.js +84 -0
  95. package/dist/feature-file.js.map +1 -0
  96. package/dist/git.d.ts +9 -0
  97. package/dist/git.js +12 -0
  98. package/dist/git.js.map +1 -0
  99. package/dist/github/api.d.ts +37 -0
  100. package/dist/github/api.js +66 -0
  101. package/dist/github/api.js.map +1 -0
  102. package/dist/github/code-host.d.ts +19 -0
  103. package/dist/github/code-host.js +105 -0
  104. package/dist/github/code-host.js.map +1 -0
  105. package/dist/github/fake-gh.d.ts +17 -0
  106. package/dist/github/fake-gh.js +51 -0
  107. package/dist/github/fake-gh.js.map +1 -0
  108. package/dist/github/fake-github.d.ts +58 -0
  109. package/dist/github/fake-github.js +285 -0
  110. package/dist/github/fake-github.js.map +1 -0
  111. package/dist/github/labels.d.ts +3 -0
  112. package/dist/github/labels.js +44 -0
  113. package/dist/github/labels.js.map +1 -0
  114. package/dist/github/tracker.d.ts +28 -0
  115. package/dist/github/tracker.js +208 -0
  116. package/dist/github/tracker.js.map +1 -0
  117. package/dist/index.d.ts +16 -0
  118. package/dist/index.js +13 -0
  119. package/dist/index.js.map +1 -0
  120. package/dist/init.d.ts +19 -0
  121. package/dist/init.js +225 -0
  122. package/dist/init.js.map +1 -0
  123. package/dist/ledger.d.ts +13 -0
  124. package/dist/ledger.js +49 -0
  125. package/dist/ledger.js.map +1 -0
  126. package/dist/main.d.ts +11 -0
  127. package/dist/main.js +215 -0
  128. package/dist/main.js.map +1 -0
  129. package/dist/person-config.d.ts +9 -0
  130. package/dist/person-config.js +46 -0
  131. package/dist/person-config.js.map +1 -0
  132. package/dist/plan.d.ts +9 -0
  133. package/dist/plan.js +27 -0
  134. package/dist/plan.js.map +1 -0
  135. package/dist/ports.d.ts +16 -0
  136. package/dist/ports.js +104 -0
  137. package/dist/ports.js.map +1 -0
  138. package/dist/prompt.d.ts +166 -0
  139. package/dist/prompt.js +217 -0
  140. package/dist/prompt.js.map +1 -0
  141. package/dist/queue.d.ts +20 -0
  142. package/dist/queue.js +94 -0
  143. package/dist/queue.js.map +1 -0
  144. package/dist/say.d.ts +2 -0
  145. package/dist/say.js +4 -0
  146. package/dist/say.js.map +1 -0
  147. package/dist/serve.d.ts +10 -0
  148. package/dist/serve.js +85 -0
  149. package/dist/serve.js.map +1 -0
  150. package/dist/session.d.ts +39 -0
  151. package/dist/session.js +144 -0
  152. package/dist/session.js.map +1 -0
  153. package/dist/settings.d.ts +10 -0
  154. package/dist/settings.js +92 -0
  155. package/dist/settings.js.map +1 -0
  156. package/dist/signals.d.ts +5 -0
  157. package/dist/signals.js +13 -0
  158. package/dist/signals.js.map +1 -0
  159. package/dist/skills.d.ts +4 -0
  160. package/dist/skills.js +51 -0
  161. package/dist/skills.js.map +1 -0
  162. package/dist/status-shape.d.ts +53 -0
  163. package/dist/status-shape.js +3 -0
  164. package/dist/status-shape.js.map +1 -0
  165. package/dist/status.d.ts +23 -0
  166. package/dist/status.js +56 -0
  167. package/dist/status.js.map +1 -0
  168. package/dist/tracker.d.ts +56 -0
  169. package/dist/tracker.js +12 -0
  170. package/dist/tracker.js.map +1 -0
  171. package/dist/workflow.d.ts +33 -0
  172. package/dist/workflow.js +53 -0
  173. package/dist/workflow.js.map +1 -0
  174. package/dist/worktree.d.ts +44 -0
  175. package/dist/worktree.js +193 -0
  176. package/dist/worktree.js.map +1 -0
  177. package/docs/ticket-contract.md +95 -0
  178. package/package.json +74 -5
  179. package/plugin/nightjar-status/.claude-plugin/plugin.json +5 -0
  180. package/plugin/nightjar-status/hooks/format.ts +48 -0
  181. package/plugin/nightjar-status/hooks/hooks.json +1 -0
  182. package/plugin/nightjar-status/hooks/register.ts +51 -0
  183. package/schema/factory.schema.json +437 -0
  184. package/skills/nightjar-feature/SKILL.md +73 -0
  185. package/src/status-shape.ts +44 -0
@@ -0,0 +1,42 @@
1
+ export class FakeCodeHost {
2
+ pushes = [];
3
+ pulls = [];
4
+ change(id, over) {
5
+ const known = this.pulls.some((p) => p.id === id);
6
+ if (!known)
7
+ throw new Error(`the fake Code host has no pull request ${String(id)}`);
8
+ this.pulls = this.pulls.map((p) => (p.id === id ? { ...p, ...over(p) } : p));
9
+ }
10
+ push = (folder, branch) => {
11
+ this.pushes.push({ folder, branch });
12
+ return Promise.resolve();
13
+ };
14
+ openPull = (branch) => {
15
+ const found = this.pulls.find((p) => p.head === branch);
16
+ return Promise.resolve(found ? { id: found.id, url: found.url } : null);
17
+ };
18
+ createDraft = (request) => {
19
+ const id = 100 + this.pulls.length;
20
+ const url = `https://code.example/pull/${String(id)}`;
21
+ this.pulls.push({ ...request, id, url, draft: true, references: [], comments: [] });
22
+ return Promise.resolve({ id, url });
23
+ };
24
+ editBody = (pull, body) => {
25
+ this.change(pull.id, () => ({ body }));
26
+ return Promise.resolve();
27
+ };
28
+ placeReferences = (pull, references) => {
29
+ this.change(pull.id, (p) => ({
30
+ references: [
31
+ ...p.references,
32
+ ...references.filter((r) => !p.references.some((q) => q.id === r.id))
33
+ ]
34
+ }));
35
+ return Promise.resolve();
36
+ };
37
+ comment = (pull, body) => {
38
+ this.change(pull.id, (p) => ({ comments: [...p.comments, body] }));
39
+ return Promise.resolve();
40
+ };
41
+ }
42
+ //# sourceMappingURL=code-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-host.js","sourceRoot":"","sources":["../../src/fakes/code-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { CodeHostHarness, CodeHostSubject } from '../contract/code-host.js';
2
+ import type { TrackerHarness } from '../contract/tracker.js';
3
+ import { FakeCodeHost } from './code-host.js';
4
+ export declare const fakeCodeHostHarness: CodeHostHarness;
5
+ export declare function fakeCodeHostSubject(codeHost: FakeCodeHost): CodeHostSubject;
6
+ export declare const fakeTrackerHarness: TrackerHarness;
7
+ export declare const keyedTrackerHarness: (prefix: string) => TrackerHarness;
@@ -0,0 +1,68 @@
1
+ import { FakeCodeHost } from './code-host.js';
2
+ import { FakeTracker } from './tracker.js';
3
+ export const fakeCodeHostHarness = () => Promise.resolve(fakeCodeHostSubject(new FakeCodeHost()));
4
+ // The fake keeps no commits, so each commit gets a folder of its own that names it.
5
+ export function fakeCodeHostSubject(codeHost) {
6
+ const commits = new Map();
7
+ return {
8
+ codeHost,
9
+ base: 'main',
10
+ commit: () => {
11
+ const n = String(commits.size + 1);
12
+ const made = { folder: `/work/${n}`, commit: `c${n}` };
13
+ commits.set(made.folder, made.commit);
14
+ return Promise.resolve(made);
15
+ },
16
+ branch: (name) => {
17
+ const last = codeHost.pushes.findLast((p) => p.branch === name);
18
+ return Promise.resolve(last ? (commits.get(last.folder) ?? null) : null);
19
+ },
20
+ pull: (pull) => {
21
+ const found = codeHost.pulls.find((p) => p.id === pull.id);
22
+ if (found === undefined)
23
+ throw new Error(`the fake Code host has no pull ${String(pull.id)}`);
24
+ return Promise.resolve({
25
+ title: found.title,
26
+ head: found.head,
27
+ base: found.base,
28
+ body: found.body,
29
+ draft: found.draft,
30
+ references: found.references.map((r) => r.text),
31
+ comments: found.comments
32
+ });
33
+ }
34
+ };
35
+ }
36
+ export const fakeTrackerHarness = (world) => keyedTrackerHarness('')(world);
37
+ // The fake Tracker keyed as `prefix` says, so `PROJ-` gives PROJ-1, PROJ-2 and on.
38
+ export const keyedTrackerHarness = (prefix) => (world) => {
39
+ const tracker = new FakeTracker(world.person, () => new Date(), prefix);
40
+ const ids = new Map(world.items.map((item, i) => [item.key, `${prefix}${String(i + 1)}`]));
41
+ const id = (key) => {
42
+ const found = ids.get(key);
43
+ if (found === undefined)
44
+ throw new Error(`the world has no item ${key}`);
45
+ return found;
46
+ };
47
+ for (const item of world.items) {
48
+ tracker.add({
49
+ id: id(item.key),
50
+ title: item.title,
51
+ body: item.body ?? '',
52
+ open: item.open ?? true,
53
+ assignees: item.assignees ?? [],
54
+ signals: item.signals ?? [],
55
+ children: (item.children ?? []).map(id),
56
+ blockedBy: (item.blockedBy ?? []).map(id)
57
+ });
58
+ for (const c of item.comments ?? []) {
59
+ tracker.say(id(item.key), {
60
+ author: c.author,
61
+ body: c.body,
62
+ trusted: world.trusted.includes(c.author)
63
+ });
64
+ }
65
+ }
66
+ return Promise.resolve({ tracker, id });
67
+ };
68
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../src/fakes/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type Tool = 'gh' | 'az' | 'claude';
2
+ export declare const STAND_INS: Record<Tool, string>;
3
+ export interface Machine {
4
+ readonly root: string;
5
+ readonly env: Record<string, string>;
6
+ calls(tool: Tool): string;
7
+ writeConfig(config: Record<string, unknown>): void;
8
+ readConfig(): unknown;
9
+ remove(): void;
10
+ }
11
+ export declare function machine(without?: readonly Tool[]): Machine;
@@ -0,0 +1,101 @@
1
+ // Stand-ins for gh, az and claude on a PATH that holds nothing else, so no spec reaches the real ones.
2
+ // A stand-in may use shell builtins only: sed, grep, awk and the rest are not on that PATH.
3
+ import { spawnSync } from 'node:child_process';
4
+ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ // gh as ann with Feature #1 on o/r; FAKE_GH_EXISTING (comma separated) labels exist; FAKE_GH_SIGNED_OUT fails every call
8
+ const GH = String.raw `#!/bin/bash
9
+ echo "$*" >> "$FAKE_GH_CALLS"
10
+ if [ -n "$FAKE_GH_SIGNED_OUT" ]; then
11
+ echo "To get started with GitHub CLI, please run: gh auth login" >&2; exit 4
12
+ fi
13
+ issue='{"number":1,"title":"One escape","body":"","state":"open","labels":[{"name":"factory-go"}],"assignees":[{"login":"ann"}],"user":{"login":"ann"},"sub_issues_summary":{"total":1}}'
14
+ case "$*" in
15
+ "api user") echo '{"login":"ann"}' ;;
16
+ "api repos/o/r/issues?"*) echo "[$issue]" ;;
17
+ "api -X POST repos/"*"/labels "*)
18
+ all="$*"; name="@{all#*-f name=}"; name="@{name%% -f*}"
19
+ if [[ ",$FAKE_GH_EXISTING," == *",$name,"* ]]; then
20
+ echo '{"message":"Validation Failed","errors":[{"code":"already_exists"}]}'
21
+ echo "gh: Validation Failed (HTTP 422)" >&2; exit 1
22
+ fi
23
+ if [ -n "$FAKE_GH_NO_LABELS" ]; then echo "gh: Resource not accessible by integration (HTTP 403)" >&2; exit 1; fi
24
+ echo '{}' ;;
25
+ "api repos/"*"/labels/"*)
26
+ all="$*"; name="@{all#api repos/*/labels/}"; name="@{name//%20/ }"
27
+ if [[ ",$FAKE_GH_EXISTING," == *",$name,"* ]]; then echo '{}'; else echo "gh: Not Found (HTTP 404)" >&2; exit 1; fi ;;
28
+ *) echo "gh: Not Found (HTTP 404)" >&2; exit 1 ;;
29
+ esac`;
30
+ // az: FAKE_AZ_SIGNED_OUT makes it refuse a token as a signed-out az does
31
+ const AZ = String.raw `#!/bin/bash
32
+ echo "$*" >> "$FAKE_AZ_CALLS"
33
+ if [ -n "$FAKE_AZ_SIGNED_OUT" ]; then
34
+ echo "ERROR: Please run 'az login' to setup account." >&2; exit 1
35
+ fi
36
+ echo '{"accessToken":"fake-azure-devops"}'`;
37
+ // claude: FAKE_CLAUDE_SIGNED_OUT makes `claude auth status --json` exit 1 as a signed-out claude does, and each
38
+ // call notes the CLAUDE_CONFIG_DIR and CLAUDE_CODE_OAUTH_TOKEN it ran with;
39
+ // a headless session prints its init line with the skills of FAKE_CLAUDE_SKILLS (comma separated)
40
+ const CLAUDE = String.raw `#!/bin/bash
41
+ echo "$* [dir=$CLAUDE_CONFIG_DIR token=$CLAUDE_CODE_OAUTH_TOKEN]" >> "$FAKE_CLAUDE_CALLS"
42
+ case "$*" in
43
+ "--version") echo "2.1.314 (Claude Code)" ;;
44
+ "auth status --json")
45
+ if [ -n "$FAKE_CLAUDE_SIGNED_OUT" ]; then echo '{"loggedIn": false}'; exit 1; fi
46
+ echo "{\"loggedIn\":true,\"email\":\"ann@x.io\",\"orgName\":\"Acme\",\"subscriptionType\":\"max\",\"authMethod\":\"claude.ai\",\"configDirectory\":\"@{CLAUDE_CONFIG_DIR:-$HOME/.claude}\"}" ;;
47
+ "-p "*)
48
+ skills=""
49
+ if [ -n "$FAKE_CLAUDE_SKILLS" ]; then skills="\"@{FAKE_CLAUDE_SKILLS//,/\",\"}\""; fi
50
+ echo "{\"type\":\"system\",\"subtype\":\"init\",\"apiKeySource\":\"none\",\"skills\":[$skills]}" ;;
51
+ esac`;
52
+ export const STAND_INS = { gh: GH, az: AZ, claude: CLAUDE };
53
+ // `@{` stands for a shell's own brace expansion, which a template literal cannot hold
54
+ function script(dir, name, body) {
55
+ const path = join(dir, name);
56
+ writeFileSync(path, `${body.replaceAll('@{', '$' + '{')}\n`);
57
+ chmodSync(path, 0o755);
58
+ }
59
+ // `without` leaves a tool off the PATH altogether, as a machine that never installed it.
60
+ export function machine(without = []) {
61
+ const base = realpathSync(mkdtempSync(join(tmpdir(), 'nightjar-machine-')));
62
+ const bin = join(base, 'bin');
63
+ mkdirSync(bin);
64
+ const git = spawnSync('which', ['git'], { encoding: 'utf8' }).stdout.trim();
65
+ symlinkSync(realpathSync(git), join(bin, 'git'));
66
+ for (const tool of Object.keys(STAND_INS)) {
67
+ if (!without.includes(tool))
68
+ script(bin, tool, STAND_INS[tool]);
69
+ }
70
+ const gitconfig = join(base, 'gitconfig');
71
+ writeFileSync(gitconfig, '');
72
+ const root = join(base, 'app');
73
+ mkdirSync(root);
74
+ const env = {
75
+ PATH: bin,
76
+ HOME: join(base, 'home'),
77
+ TMPDIR: base,
78
+ GIT_CONFIG_GLOBAL: gitconfig,
79
+ GIT_CONFIG_NOSYSTEM: '1',
80
+ GIT_TERMINAL_PROMPT: '0',
81
+ FAKE_GH_CALLS: join(base, 'gh-calls.txt'),
82
+ FAKE_AZ_CALLS: join(base, 'az-calls.txt'),
83
+ FAKE_CLAUDE_CALLS: join(base, 'claude-calls.txt')
84
+ };
85
+ for (const calls of [env.FAKE_GH_CALLS, env.FAKE_AZ_CALLS, env.FAKE_CLAUDE_CALLS]) {
86
+ writeFileSync(calls ?? '', '');
87
+ }
88
+ const inRoot = (...args) => spawnSync('git', args, { cwd: root, env, encoding: 'utf8' });
89
+ inRoot('init', '-q', '-b', 'main');
90
+ inRoot('remote', 'add', 'origin', 'git@github.com:o/r.git');
91
+ const file = join(root, 'factory.json');
92
+ return {
93
+ root,
94
+ env,
95
+ calls: (tool) => readFileSync(env[`FAKE_${tool.toUpperCase()}_CALLS`] ?? '', 'utf8'),
96
+ writeConfig: (config) => writeFileSync(file, JSON.stringify(config, null, '\t')),
97
+ readConfig: () => JSON.parse(readFileSync(file, 'utf8')),
98
+ remove: () => rmSync(base, { recursive: true, force: true })
99
+ };
100
+ }
101
+ //# sourceMappingURL=machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine.js","sourceRoot":"","sources":["../../src/fakes/machine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { Signal } from '../signals.js';
2
+ import type { Comment, Feature, FeatureWriter, ItemId, ItemText, Standing, Ticket, TicketReference, Tracker } from '../tracker.js';
3
+ export interface FakeItem {
4
+ readonly id: ItemId;
5
+ readonly title: string;
6
+ readonly body: string;
7
+ readonly open: boolean;
8
+ readonly assignees: readonly string[];
9
+ readonly signals: readonly Signal[];
10
+ readonly children: readonly ItemId[];
11
+ readonly blockedBy: readonly ItemId[];
12
+ readonly comments: readonly Comment[];
13
+ }
14
+ export type NewItem = Pick<FakeItem, 'id' | 'title'> & Partial<FakeItem>;
15
+ export declare class FakeTracker implements Tracker, FeatureWriter {
16
+ readonly person: string;
17
+ private readonly now;
18
+ private readonly prefix;
19
+ readonly items: Map<string, FakeItem>;
20
+ constructor(person?: string, now?: () => Date, prefix?: string);
21
+ add(item: NewItem): this;
22
+ say(id: ItemId, comment: Omit<Comment, 'at'> & Partial<Pick<Comment, 'at'>>): this;
23
+ close(id: ItemId): this;
24
+ signalsOf: (id: ItemId) => readonly Signal[];
25
+ said: (id: ItemId) => string[];
26
+ private get;
27
+ me: () => Promise<string>;
28
+ assignedFeatures: (person: string) => Promise<Feature[]>;
29
+ feature: (id: ItemId) => Promise<Feature>;
30
+ standing: (id: ItemId) => Promise<Standing>;
31
+ tickets: (featureId: ItemId) => Promise<Ticket[]>;
32
+ comments: (id: ItemId) => Promise<Comment[]>;
33
+ addSignal: (id: ItemId, signal: Signal) => Promise<void>;
34
+ removeSignal: (id: ItemId, signal: Signal) => Promise<void>;
35
+ comment: (id: ItemId, body: string) => Promise<void>;
36
+ reference: (id: ItemId) => TicketReference;
37
+ createFeature: ({ title, body }: ItemText) => Promise<ItemId>;
38
+ createTicket: (featureId: ItemId, ticket: ItemText) => Promise<ItemId>;
39
+ block: (id: ItemId, blocker: ItemId) => Promise<void>;
40
+ }
@@ -0,0 +1,116 @@
1
+ export class FakeTracker {
2
+ person;
3
+ now;
4
+ prefix;
5
+ items = new Map();
6
+ constructor(
7
+ // the signed-in person, whose comments are trusted
8
+ person = 'ann', now = () => new Date(),
9
+ // what the ids it makes start with, such as PROJ- for keys as Jira spells them
10
+ prefix = '') {
11
+ this.person = person;
12
+ this.now = now;
13
+ this.prefix = prefix;
14
+ }
15
+ add(item) {
16
+ this.items.set(item.id, {
17
+ body: `The body of ${item.title}.`,
18
+ open: true,
19
+ assignees: [],
20
+ signals: [],
21
+ children: [],
22
+ blockedBy: [],
23
+ comments: [],
24
+ ...item
25
+ });
26
+ return this;
27
+ }
28
+ // A comment by anyone, trusted or not.
29
+ say(id, comment) {
30
+ const item = this.get(id);
31
+ const at = comment.at ?? this.now().toISOString();
32
+ this.items.set(id, { ...item, comments: [...item.comments, { ...comment, at }] });
33
+ return this;
34
+ }
35
+ close(id) {
36
+ this.items.set(id, { ...this.get(id), open: false });
37
+ return this;
38
+ }
39
+ signalsOf = (id) => this.get(id).signals;
40
+ said = (id) => this.get(id).comments.map((c) => c.body);
41
+ get(id) {
42
+ const item = this.items.get(id);
43
+ if (item === undefined)
44
+ throw new Error(`the fake Tracker has no item #${id}`);
45
+ return item;
46
+ }
47
+ me = () => Promise.resolve(this.person);
48
+ assignedFeatures = (person) => Promise.resolve([...this.items.values()]
49
+ .filter((i) => i.open &&
50
+ i.children.length > 0 &&
51
+ i.assignees.includes(person) &&
52
+ i.signals.includes('go'))
53
+ // the fake counts a lower id as older, PROJ-2 before PROJ-10
54
+ .sort((a, b) => a.id.localeCompare(b.id, 'en', { numeric: true }))
55
+ .map(({ id, title, body }) => ({ id, title, body })));
56
+ feature = (id) => {
57
+ const { title, body } = this.get(id);
58
+ return Promise.resolve({ id, title, body });
59
+ };
60
+ standing = (id) => {
61
+ const { assignees, signals } = this.get(id);
62
+ return Promise.resolve({ assignees, signals });
63
+ };
64
+ tickets = (featureId) => Promise.resolve(this.get(featureId).children.map((child) => {
65
+ const t = this.get(child);
66
+ return {
67
+ id: t.id,
68
+ title: t.title,
69
+ body: t.body,
70
+ open: t.open,
71
+ signals: t.signals,
72
+ blockedBy: t.blockedBy.map((b) => ({ id: b, open: this.items.get(b)?.open ?? true }))
73
+ };
74
+ }));
75
+ comments = (id) => Promise.resolve([...this.get(id).comments]);
76
+ addSignal = (id, signal) => {
77
+ const item = this.get(id);
78
+ if (!item.signals.includes(signal)) {
79
+ this.items.set(id, { ...item, signals: [...item.signals, signal] });
80
+ }
81
+ return Promise.resolve();
82
+ };
83
+ removeSignal = (id, signal) => {
84
+ const item = this.get(id);
85
+ this.items.set(id, { ...item, signals: item.signals.filter((s) => s !== signal) });
86
+ return Promise.resolve();
87
+ };
88
+ comment = (id, body) => {
89
+ this.say(id, { author: this.person, body, trusted: true });
90
+ return Promise.resolve();
91
+ };
92
+ reference = (id) => ({ id, text: `#${id}` });
93
+ createFeature = ({ title, body }) => {
94
+ let n = this.items.size + 1;
95
+ while (this.items.has(`${this.prefix}${String(n)}`))
96
+ n += 1;
97
+ const id = `${this.prefix}${String(n)}`;
98
+ this.add({ id, title, body });
99
+ return Promise.resolve(id);
100
+ };
101
+ createTicket = async (featureId, ticket) => {
102
+ const feature = this.get(featureId);
103
+ const id = await this.createFeature(ticket);
104
+ this.items.set(featureId, { ...feature, children: [...feature.children, id] });
105
+ return id;
106
+ };
107
+ block = (id, blocker) => {
108
+ const item = this.get(id);
109
+ this.get(blocker);
110
+ if (!item.blockedBy.includes(blocker)) {
111
+ this.items.set(id, { ...item, blockedBy: [...item.blockedBy, blocker] });
112
+ }
113
+ return Promise.resolve();
114
+ };
115
+ }
116
+ //# sourceMappingURL=tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/fakes/tracker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ItemText } from './tracker.js';
2
+ export declare class FeatureFileError extends Error {
3
+ }
4
+ export interface PlannedTicket extends ItemText {
5
+ readonly number: number;
6
+ readonly blockedBy: readonly number[];
7
+ }
8
+ export interface FeaturePlan {
9
+ readonly feature: ItemText;
10
+ readonly tickets: readonly PlannedTicket[];
11
+ }
12
+ export declare function readFeatureFile(text: string): FeaturePlan;
@@ -0,0 +1,84 @@
1
+ export class FeatureFileError extends Error {
2
+ }
3
+ const TITLE = /^#\s+(.*\S)\s*$/;
4
+ const TICKET = /^##\s+Ticket\s+(\S+?):\s*(.*?)\s*$/i;
5
+ const BLOCKED_BY = /^(?:\*\*)?Blocked by:(?:\*\*)?(.*)$/i;
6
+ // the lines the build reads a Ticket's model and effort from (ticketLine in build.ts)
7
+ const SETTING = /^(?:\*\*)?(Model|Effort):(?:\*\*)?(.*)$/i;
8
+ const CRITERIA = /^#{1,6}\s+Acceptance criteria\s*$/i;
9
+ const CHECKBOX = /^\s*[-*]\s+\[[ xX]\]\s+\S/;
10
+ const FENCE = /^\s*(```|~~~)/;
11
+ const tidy = (lines) => lines.join('\n').trim();
12
+ // The Feature and its Tickets, or a FeatureFileError that names every fault with its line.
13
+ export function readFeatureFile(text) {
14
+ const lines = text.replace(/\r\n/g, '\n').split('\n');
15
+ const faults = [];
16
+ const first = lines.findIndex((l) => l.trim() !== '');
17
+ const title = TITLE.exec(lines[first] ?? '')?.[1];
18
+ if (title === undefined) {
19
+ faults.push(`line ${String(first + 1)}: the file starts with the Feature's title, as "# Title"`);
20
+ }
21
+ const featureLines = [];
22
+ const sections = [];
23
+ let fenced = false;
24
+ lines.forEach((line, i) => {
25
+ if (i <= first)
26
+ return;
27
+ if (FENCE.test(line))
28
+ fenced = !fenced;
29
+ const heading = fenced ? null : TICKET.exec(line);
30
+ if (heading) {
31
+ sections.push({ number: heading[1] ?? '', title: heading[2] ?? '', line: i + 1, lines: [] });
32
+ }
33
+ else
34
+ (sections.at(-1)?.lines ?? featureLines).push(line);
35
+ });
36
+ if (sections.length === 0)
37
+ faults.push('the file holds no Ticket; start each with "## Ticket 1: Title"');
38
+ const tickets = [];
39
+ sections.forEach((section, i) => {
40
+ const number = i + 1;
41
+ const name = `Ticket ${section.number}`;
42
+ const at = `line ${String(section.line)}`;
43
+ if (section.number !== String(number)) {
44
+ faults.push(`${at}: ${name} is the Ticket numbered ${String(number)}; number them 1, 2, 3 in order`);
45
+ }
46
+ if (section.title === '')
47
+ faults.push(`${at}: ${name} has no title`);
48
+ const blockedBy = [];
49
+ const body = [];
50
+ for (const line of section.lines) {
51
+ const blockers = BLOCKED_BY.exec(line.trim());
52
+ if (blockers === null) {
53
+ body.push(line);
54
+ continue;
55
+ }
56
+ const listed = (blockers[1] ?? '').trim();
57
+ if (/^none$/i.test(listed))
58
+ continue;
59
+ for (const part of listed.split(',').map((p) => p.trim())) {
60
+ const blocker = /^\d+$/.test(part) ? Number(part) : NaN;
61
+ if (!(blocker >= 1 && blocker < number)) {
62
+ faults.push(`${at}: ${name} is blocked by "${part}"; name the numbers of Tickets above it, as "**Blocked by:** 1, 2"`);
63
+ }
64
+ else if (!blockedBy.includes(blocker))
65
+ blockedBy.push(blocker);
66
+ }
67
+ }
68
+ for (const line of body) {
69
+ const setting = SETTING.exec(line.trim());
70
+ if (setting !== null && (setting[2] ?? '').trim() === '') {
71
+ faults.push(`${at}: ${name} has an empty ${setting[1] ?? ''} line; give it a value or leave it out`);
72
+ }
73
+ }
74
+ const criteria = body.findIndex((l) => CRITERIA.test(l));
75
+ if (criteria === -1 || !body.slice(criteria).some((l) => CHECKBOX.test(l))) {
76
+ faults.push(`${at}: ${name} has no acceptance criteria; add an "Acceptance criteria" heading with "- [ ]" items`);
77
+ }
78
+ tickets.push({ number, title: section.title, body: tidy(body), blockedBy });
79
+ });
80
+ if (faults.length > 0)
81
+ throw new FeatureFileError(faults.join('\n'));
82
+ return { feature: { title: title ?? '', body: tidy(featureLines) }, tickets };
83
+ }
84
+ //# sourceMappingURL=feature-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-file.js","sourceRoot":"","sources":["../src/feature-file.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,gBAAiB,SAAQ,KAAK;CAAG;AAa9C,MAAM,KAAK,GAAG,iBAAiB,CAAC;AAChC,MAAM,MAAM,GAAG,qCAAqC,CAAC;AACrD,MAAM,UAAU,GAAG,sCAAsC,CAAC;AAC1D,sFAAsF;AACtF,MAAM,OAAO,GAAG,0CAA0C,CAAC;AAC3D,MAAM,QAAQ,GAAG,oCAAoC,CAAC;AACtD,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAC7C,MAAM,KAAK,GAAG,eAAe,CAAC;AAE9B,MAAM,IAAI,GAAG,CAAC,KAAwB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AAUnE,2FAA2F;AAC3F,MAAM,UAAU,eAAe,CAAC,IAAY;IAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CACV,QAAQ,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,0DAA0D,CACnF,CAAC;IACH,CAAC;IACD,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACzB,IAAI,CAAC,IAAI,KAAK;YAAE,OAAO;QACvB,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,GAAG,CAAC,MAAM,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,OAAO,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC9F,CAAC;;YAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IACH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QACxB,MAAM,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAE/E,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;QACrB,MAAM,IAAI,GAAG,UAAU,OAAO,CAAC,MAAM,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,QAAQ,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CACV,GAAG,EAAE,KAAK,IAAI,2BAA2B,MAAM,CAAC,MAAM,CAAC,gCAAgC,CACvF,CAAC;QACH,CAAC;QACD,IAAI,OAAO,CAAC,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,eAAe,CAAC,CAAC;QACrE,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAChB,SAAS;YACV,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,SAAS;YACrC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;gBAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;gBACxD,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,IAAI,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;oBACzC,MAAM,CAAC,IAAI,CACV,GAAG,EAAE,KAAK,IAAI,mBAAmB,IAAI,oEAAoE,CACzG,CAAC;gBACH,CAAC;qBAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAClE,CAAC;QACF,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1C,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CACV,GAAG,EAAE,KAAK,IAAI,iBAAiB,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,wCAAwC,CACvF,CAAC;YACH,CAAC;QACF,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACzD,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,MAAM,CAAC,IAAI,CACV,GAAG,EAAE,KAAK,IAAI,sFAAsF,CACpG,CAAC;QACH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC;AAC/E,CAAC"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export type Env = Readonly<Record<string, string | undefined>>;
2
+ export interface Run {
3
+ readonly ok: boolean;
4
+ readonly out: string;
5
+ readonly err: string;
6
+ readonly missing: boolean;
7
+ }
8
+ export declare function run(command: string, args: readonly string[], cwd: string, env: Env): Promise<Run>;
9
+ export declare const firstLine: (text: string) => string;
package/dist/git.js ADDED
@@ -0,0 +1,12 @@
1
+ // Never through a shell, so no title or branch name is ever parsed as shell text.
2
+ import { execFile } from 'node:child_process';
3
+ export function run(command, args, cwd, env) {
4
+ return new Promise((done) => {
5
+ execFile(command, args, { cwd, env, maxBuffer: 16 * 1024 * 1024 }, (fault, out, err) => {
6
+ const missing = fault?.code === 'ENOENT';
7
+ done({ ok: fault === null, out, err, missing });
8
+ });
9
+ });
10
+ }
11
+ export const firstLine = (text) => text.trim().split('\n')[0] ?? '';
12
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAY9C,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,IAAuB,EAAE,GAAW,EAAE,GAAQ;IAClF,OAAO,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3B,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACtF,MAAM,OAAO,GAAI,KAAsC,EAAE,IAAI,KAAK,QAAQ,CAAC;YAC3E,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,KAAK,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ import { type Env, type Run } from '../git.js';
3
+ export declare class GitHubFailure extends Error {
4
+ }
5
+ export type GhRunner = (args: readonly string[]) => Promise<Run>;
6
+ export declare const ghRunner: (cwd: string, env: Env) => GhRunner;
7
+ export declare const IssueSchema: z.ZodObject<{
8
+ id: z.ZodOptional<z.ZodInt>;
9
+ number: z.ZodInt;
10
+ title: z.ZodString;
11
+ body: z.ZodNullable<z.ZodString>;
12
+ state: z.ZodString;
13
+ labels: z.ZodArray<z.ZodObject<{
14
+ name: z.ZodString;
15
+ }, z.core.$loose>>;
16
+ assignees: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
17
+ login: z.ZodString;
18
+ }, z.core.$loose>>>>;
19
+ user: z.ZodNullable<z.ZodObject<{
20
+ login: z.ZodString;
21
+ }, z.core.$loose>>;
22
+ sub_issues_summary: z.ZodOptional<z.ZodObject<{
23
+ total: z.ZodInt;
24
+ }, z.core.$loose>>;
25
+ pull_request: z.ZodOptional<z.ZodUnknown>;
26
+ }, z.core.$loose>;
27
+ export type RawIssue = z.infer<typeof IssueSchema>;
28
+ export declare const PAGE = "per_page=100";
29
+ export declare class GitHubApi {
30
+ readonly repo: string;
31
+ private readonly gh;
32
+ constructor(repo: string, gh: GhRunner);
33
+ raw(args: readonly string[]): Promise<Run>;
34
+ call(what: string, args: readonly string[]): Promise<unknown>;
35
+ parse<T>(what: string, args: readonly string[], schema: z.ZodType<T>): Promise<T>;
36
+ }
37
+ export declare function repoFromRemote(url: string): string | null;