loreli 0.0.0 → 2.0.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 (104) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +710 -97
  3. package/bin/loreli.js +89 -0
  4. package/package.json +77 -14
  5. package/packages/README.md +101 -0
  6. package/packages/action/README.md +98 -0
  7. package/packages/action/prompts/action.md +172 -0
  8. package/packages/action/src/index.js +684 -0
  9. package/packages/agent/README.md +606 -0
  10. package/packages/agent/src/backends/claude.js +387 -0
  11. package/packages/agent/src/backends/codex.js +351 -0
  12. package/packages/agent/src/backends/cursor.js +371 -0
  13. package/packages/agent/src/backends/index.js +486 -0
  14. package/packages/agent/src/base.js +138 -0
  15. package/packages/agent/src/cli.js +275 -0
  16. package/packages/agent/src/discover.js +396 -0
  17. package/packages/agent/src/factory.js +124 -0
  18. package/packages/agent/src/index.js +12 -0
  19. package/packages/agent/src/models.js +159 -0
  20. package/packages/agent/src/output.js +62 -0
  21. package/packages/agent/src/session.js +162 -0
  22. package/packages/agent/src/trace.js +186 -0
  23. package/packages/classify/README.md +136 -0
  24. package/packages/classify/prompts/blocker.md +12 -0
  25. package/packages/classify/prompts/feedback.md +14 -0
  26. package/packages/classify/prompts/pane-state.md +20 -0
  27. package/packages/classify/src/index.js +81 -0
  28. package/packages/config/README.md +898 -0
  29. package/packages/config/src/defaults.js +145 -0
  30. package/packages/config/src/index.js +223 -0
  31. package/packages/config/src/schema.js +291 -0
  32. package/packages/config/src/validate.js +160 -0
  33. package/packages/context/README.md +165 -0
  34. package/packages/context/src/index.js +198 -0
  35. package/packages/hub/README.md +338 -0
  36. package/packages/hub/src/base.js +154 -0
  37. package/packages/hub/src/github.js +1597 -0
  38. package/packages/hub/src/index.js +79 -0
  39. package/packages/hub/src/labels.js +48 -0
  40. package/packages/identity/README.md +288 -0
  41. package/packages/identity/src/index.js +620 -0
  42. package/packages/identity/src/themes/avatar.js +217 -0
  43. package/packages/identity/src/themes/digimon.js +217 -0
  44. package/packages/identity/src/themes/dragonball.js +217 -0
  45. package/packages/identity/src/themes/lotr.js +217 -0
  46. package/packages/identity/src/themes/marvel.js +217 -0
  47. package/packages/identity/src/themes/pokemon.js +217 -0
  48. package/packages/identity/src/themes/starwars.js +217 -0
  49. package/packages/identity/src/themes/transformers.js +217 -0
  50. package/packages/identity/src/themes/zelda.js +217 -0
  51. package/packages/knowledge/README.md +217 -0
  52. package/packages/knowledge/src/index.js +243 -0
  53. package/packages/log/README.md +93 -0
  54. package/packages/log/src/index.js +252 -0
  55. package/packages/marker/README.md +200 -0
  56. package/packages/marker/src/index.js +184 -0
  57. package/packages/mcp/README.md +323 -0
  58. package/packages/mcp/instructions.md +126 -0
  59. package/packages/mcp/scaffolding/.agents/skills/loreli-context/SKILL.md +89 -0
  60. package/packages/mcp/scaffolding/ISSUE_TEMPLATE/config.yml +2 -0
  61. package/packages/mcp/scaffolding/ISSUE_TEMPLATE/loreli.yml +83 -0
  62. package/packages/mcp/scaffolding/loreli.yml +491 -0
  63. package/packages/mcp/scaffolding/mcp-configs/.codex/config.toml +4 -0
  64. package/packages/mcp/scaffolding/mcp-configs/.cursor/mcp.json +14 -0
  65. package/packages/mcp/scaffolding/mcp-configs/.mcp.json +14 -0
  66. package/packages/mcp/scaffolding/pull-request.md +23 -0
  67. package/packages/mcp/src/index.js +600 -0
  68. package/packages/mcp/src/tools/agent-context.js +44 -0
  69. package/packages/mcp/src/tools/agents.js +450 -0
  70. package/packages/mcp/src/tools/context.js +200 -0
  71. package/packages/mcp/src/tools/github.js +1163 -0
  72. package/packages/mcp/src/tools/hitl.js +162 -0
  73. package/packages/mcp/src/tools/index.js +18 -0
  74. package/packages/mcp/src/tools/refactor.js +227 -0
  75. package/packages/mcp/src/tools/repo.js +44 -0
  76. package/packages/mcp/src/tools/start.js +904 -0
  77. package/packages/mcp/src/tools/status.js +149 -0
  78. package/packages/mcp/src/tools/work.js +134 -0
  79. package/packages/orchestrator/README.md +192 -0
  80. package/packages/orchestrator/src/index.js +1492 -0
  81. package/packages/planner/README.md +251 -0
  82. package/packages/planner/prompts/plan-reviewer.md +109 -0
  83. package/packages/planner/prompts/planner.md +191 -0
  84. package/packages/planner/prompts/tiebreaker-reviewer.md +71 -0
  85. package/packages/planner/src/index.js +1381 -0
  86. package/packages/review/README.md +129 -0
  87. package/packages/review/prompts/reviewer.md +158 -0
  88. package/packages/review/src/index.js +1403 -0
  89. package/packages/risk/README.md +178 -0
  90. package/packages/risk/prompts/risk.md +272 -0
  91. package/packages/risk/src/index.js +439 -0
  92. package/packages/session/README.md +165 -0
  93. package/packages/session/src/index.js +215 -0
  94. package/packages/test-utils/README.md +96 -0
  95. package/packages/test-utils/src/index.js +354 -0
  96. package/packages/tmux/README.md +261 -0
  97. package/packages/tmux/src/index.js +501 -0
  98. package/packages/workflow/README.md +317 -0
  99. package/packages/workflow/prompts/preamble.md +14 -0
  100. package/packages/workflow/src/index.js +660 -0
  101. package/packages/workflow/src/proof-of-life.js +74 -0
  102. package/packages/workspace/README.md +143 -0
  103. package/packages/workspace/src/index.js +1127 -0
  104. package/index.js +0 -8
@@ -0,0 +1,338 @@
1
+ # loreli/hub
2
+
3
+ Provider-agnostic git hosting abstraction for Loreli. Encapsulates all interactions with GitHub (and future GitLab, Gitea) behind a consistent API.
4
+
5
+ ## API Reference
6
+
7
+ ### Factory
8
+
9
+ ```js
10
+ import { hub } from 'loreli/hub';
11
+
12
+ // Auto-detect from GITHUB_TOKEN
13
+ const h = hub();
14
+
15
+ // Explicit provider and token
16
+ const h = hub({ provider: 'github', token: process.env.GITHUB_TOKEN });
17
+
18
+ // Token resolution: explicit param > config.get('github.token') > process.env.GITHUB_TOKEN
19
+ ```
20
+
21
+ ### `BaseHub` (Abstract Interface)
22
+
23
+ All methods accept `repo` as an `"owner/name"` string.
24
+
25
+ #### Issues
26
+
27
+ | Method | Signature | Description |
28
+ |--------|-----------|-------------|
29
+ | `issues` | `(repo, { state?, labels? })` | List issues |
30
+ | `issue` | `(repo, number)` | Get single issue |
31
+ | `open` | `(repo, { title, body, labels? })` | Create issue |
32
+ | `comment` | `(repo, number, body)` | Add comment |
33
+ | `comments` | `(repo, number)` | List comments |
34
+ | `sub` | `(repo, parent, childId)` | Add sub-issue (childId is database `id`, not `number`) |
35
+ | `subs` | `(repo, number)` | List sub-issues of a parent |
36
+
37
+ #### Pull Requests
38
+
39
+ | Method | Signature | Description |
40
+ |--------|-----------|-------------|
41
+ | `pulls` | `(repo, { state? })` | List PRs |
42
+ | `pull` | `(repo, number)` | Get single PR |
43
+ | `propose` | `(repo, { title, body, head, base?, labels? })` | Create PR (applies labels when provided) |
44
+ | `merge` | `(repo, number, { method? })` | Merge PR |
45
+ | `closePull` | `(repo, number)` | Close PR without merging |
46
+ | `files` | `(repo, number)` | List changed files |
47
+
48
+ #### Reviews
49
+
50
+ | Method | Signature | Description |
51
+ |--------|-----------|-------------|
52
+ | `review` | `(repo, number, { body, event?, comments? })` | Create review |
53
+ | `reviews` | `(repo, number)` | List reviews |
54
+ | `annotate` | `(repo, number, { body, path, line, commit })` | Line comment |
55
+
56
+ #### Contents
57
+
58
+ | Method | Signature | Description |
59
+ |--------|-----------|-------------|
60
+ | `read` | `(repo, path, { ref? })` | Get file/directory |
61
+ | `write` | `(repo, path, { content, message?, branch? })` | Create/update file |
62
+ | `tree` | `(repo, { path? })` | List directory tree |
63
+
64
+ #### Repository
65
+
66
+ | Method | Signature | Description |
67
+ |--------|-----------|-------------|
68
+ | `repo` | `(repo)` | Get repository info |
69
+ | `branch` | `(repo, name)` | Get branch info |
70
+ | `fork` | `(repo, { name, from? })` | Create branch |
71
+
72
+ #### Labels
73
+
74
+ | Method | Signature | Description |
75
+ |--------|-----------|-------------|
76
+ | `labels` | `(repo)` | List all labels in a repository |
77
+ | `ensure` | `(repo, definitions[])` | Idempotent: create labels that don't exist, skip existing |
78
+ | `label` | `(repo, number, labels[])` | Add labels to an issue or PR |
79
+ | `unlabel` | `(repo, number, name)` | Remove a label from an issue or PR (idempotent — 404 swallowed) |
80
+
81
+ The `ensure` method is the primary label management API. It accepts an array of `{ name, color, description }` objects and creates only the labels that are missing. Existing labels are left unchanged.
82
+
83
+ The following example shows how start uses `ensure` to create agent tracking labels:
84
+
85
+ ```js
86
+ import { definitions } from 'loreli/hub';
87
+
88
+ // Build label definitions with deterministic text-hex colors
89
+ const defs = definitions(['loreli', 'loreli:anthropic', 'loreli:planner']);
90
+ await h.ensure('owner/repo', defs);
91
+
92
+ // Apply labels to a PR
93
+ await h.label('owner/repo', 42, ['loreli', 'loreli:anthropic', 'loreli:planner']);
94
+ ```
95
+
96
+ #### Label Definitions Helper
97
+
98
+ The `definitions()` function from `loreli/hub/labels` generates label objects with deterministic hex colors via [text-hex](https://www.npmjs.com/package/text-hex):
99
+
100
+ ```js
101
+ import { definitions } from 'loreli/hub';
102
+
103
+ definitions(['loreli:anthropic', 'loreli:planner', 'loreli:approved']);
104
+ // [
105
+ // { name: 'loreli:anthropic', color: '5d3ea8', description: 'AI provider: anthropic' },
106
+ // { name: 'loreli:planner', color: '8f4c2a', description: 'Agent role: planner' },
107
+ // { name: 'loreli:approved', color: 'a3b84c', description: 'Plan discussion approved for promotion to issue' }
108
+ // ]
109
+ ```
110
+
111
+ All Loreli-managed labels are namespaced with a `loreli:` prefix to avoid collisions with existing repository labels:
112
+
113
+ | Label | Description |
114
+ |-------|-------------|
115
+ | `loreli` | Managed by Loreli agent orchestration |
116
+ | `loreli:planner` | Agent role: planner |
117
+ | `loreli:action` | Agent role: action |
118
+ | `loreli:reviewer` | Agent role: reviewer |
119
+ | `loreli:approved` | Plan discussion approved for promotion to issue |
120
+ | `loreli:changes-requested` | Plan discussion needs revision |
121
+ | `loreli:needs-attention` | Escalated — requires human attention |
122
+ | `loreli:<provider>` | AI provider (e.g. `loreli:openai`, `loreli:anthropic`) |
123
+ | `loreli:<model>` | AI model (e.g. `loreli:claude-sonnet-4`) |
124
+
125
+ #### Discussions (GitHub Discussions via GraphQL)
126
+
127
+ | Method | Signature | Description |
128
+ |--------|-----------|-------------|
129
+ | `category` | `(repo, name)` | Find a discussion category by name |
130
+ | `discuss` | `(repo, { title, body, categoryId, repositoryId })` | Create a discussion |
131
+ | `discussions` | `(repo, categoryId)` | List discussions in a category |
132
+ | `discussion` | `(repo, number)` | Get a single discussion with labels and comments |
133
+ | `discussionComments` | `(discussionId)` | List comments on a discussion |
134
+ | `discussionComment` | `(discussionId, body)` | Add a comment to a discussion |
135
+ | `updateDiscussion` | `(discussionId, { title?, body? })` | Update discussion title/body |
136
+ | `closeDiscussion` | `(discussionId)` | Close a discussion |
137
+ | `deleteDiscussion` | `(discussionId)` | Delete a discussion |
138
+ | `_applyDiscussionLabels` | `(repo, discussionId, labelNames)` | Apply labels to a discussion |
139
+ | `removeDiscussionLabels` | `(repo, discussionId, labelNames)` | Remove labels from a discussion |
140
+
141
+ Discussions are used as the planning primitive. The "Loreli" category must be created manually via GitHub repository settings (the API does not support programmatic category creation). All content-producing methods (`discuss`, `discussionComment`, `updateDiscussion`) automatically stamp the body with the agent's identity signature.
142
+
143
+ The following diagram shows the discussion lifecycle during planning:
144
+
145
+ ```mermaid
146
+ sequenceDiagram
147
+ participant P as Planner Agent
148
+ participant R as Reviewer Agent
149
+ participant O as Orchestrator
150
+ participant Hub as loreli/hub
151
+ participant GH as GitHub Discussions
152
+
153
+ O->>Hub: category(repo, 'Loreli')
154
+ Hub->>GH: GraphQL: discussionCategories
155
+ GH-->>Hub: category id
156
+ P->>Hub: discuss(repo, { title, body, categoryId })
157
+ Hub->>GH: createDiscussion
158
+ GH-->>Hub: discussion
159
+ R->>Hub: _applyDiscussionLabels(repo, id, ['loreli:approved'])
160
+ Hub->>GH: addLabelsToLabelable
161
+ Note over P,GH: After approval
162
+ O->>Hub: open(repo, { title, body })
163
+ Hub->>GH: issues.create
164
+ GH-->>Hub: issue
165
+ O->>Hub: closeDiscussion(id)
166
+ Hub->>GH: closeDiscussion
167
+ ```
168
+
169
+ #### Human In The Loop (HITL)
170
+
171
+ | Method | Signature | Description |
172
+ |--------|-----------|-------------|
173
+ | `assign` | `(repo, number, usernames)` | Assign users to an issue or PR |
174
+ | `request` | `(repo, number, usernames)` | Request review from users on a PR |
175
+
176
+ ### Hub Method Flow
177
+
178
+ The following diagram shows how the orchestrator interacts with the hub and GitHub API during key operations:
179
+
180
+ ```mermaid
181
+ sequenceDiagram
182
+ participant Orch as Orchestrator
183
+ participant Hub as loreli/hub
184
+ participant GH as GitHub API
185
+
186
+ Orch->>Hub: ensure(repo, labels)
187
+ Hub->>GH: listLabelsForRepo + createLabel
188
+ GH-->>Hub: labels created
189
+
190
+ Orch->>Hub: propose(repo, opts)
191
+ Hub->>GH: pulls.create + addLabels
192
+ GH-->>Hub: PR data
193
+
194
+ Orch->>Hub: request(repo, pr, users)
195
+ Hub->>GH: pulls.requestReviewers
196
+ GH-->>Hub: ok
197
+
198
+ Orch->>Hub: assign(repo, pr, users)
199
+ Hub->>GH: issues.addAssignees
200
+ GH-->>Hub: ok
201
+ ```
202
+
203
+ ### Rate Limit Tracking
204
+
205
+ `GitHubHub` automatically tracks GitHub API rate limits by extracting `X-RateLimit-*` headers from every response. The system provides three capabilities:
206
+
207
+ #### Automatic Header Extraction
208
+
209
+ Every REST and GraphQL call updates internal rate limit state:
210
+
211
+ ```js
212
+ const h = new GitHubHub({ token });
213
+ await h.issues('owner/repo');
214
+
215
+ // Rate limit state is now populated
216
+ console.log(h.rateLimit);
217
+ // { remaining: 4950, limit: 5000, used: 50, reset: 1700000000 }
218
+ ```
219
+
220
+ #### `rates()` — Formatted Rate Limit Info
221
+
222
+ Returns the current rate limit state in a display-friendly format with a computed ratio.
223
+
224
+ | Field | Type | Description |
225
+ |-------|------|-------------|
226
+ | `remaining` | `number\|null` | Requests remaining in the current window |
227
+ | `limit` | `number\|null` | Total requests allowed in the current window |
228
+ | `used` | `number\|null` | Requests already consumed |
229
+ | `reset` | `string\|null` | ISO 8601 timestamp when the window resets |
230
+ | `ratio` | `number\|null` | Fraction remaining (e.g. `0.84` = 84%) |
231
+
232
+ ```js
233
+ const rl = h.rates();
234
+ console.log(`${rl.remaining}/${rl.limit} (${Math.round(rl.ratio * 100)}%) resets ${rl.reset}`);
235
+ // 4200/5000 (84%) resets 2026-02-13T12:00:00.000Z
236
+ ```
237
+
238
+ The `team_status` MCP tool automatically includes rate limit info in its dashboard output.
239
+
240
+ #### Low-Limit Warning Callback
241
+
242
+ Register a callback to receive warnings when remaining requests drop below 20% of the limit:
243
+
244
+ ```js
245
+ h.onRateLimitWarning = function warn({ remaining, limit, reset, ratio }) {
246
+ console.warn(`Rate limit low: ${remaining}/${limit} (${Math.round(ratio * 100)}%), resets ${reset}`);
247
+ };
248
+ ```
249
+
250
+ #### Exponential Backoff on 429
251
+
252
+ When GitHub returns a `429 Too Many Requests` response (secondary rate limit), the hub automatically retries with exponential backoff and jitter:
253
+
254
+ - Up to 3 retry attempts
255
+ - Respects the `Retry-After` header when present
256
+ - Falls back to exponential backoff (1s, 2s, 4s base + random jitter)
257
+ - Non-429 errors are thrown immediately
258
+
259
+ ```mermaid
260
+ sequenceDiagram
261
+ participant App
262
+ participant Hub as GitHubHub
263
+ participant GH as GitHub API
264
+
265
+ App->>Hub: issues(repo)
266
+ Hub->>GH: GET /repos/{owner}/{repo}/issues
267
+ GH-->>Hub: 429 + Retry-After: 2
268
+ Note over Hub: Wait 2 seconds
269
+ Hub->>GH: GET /repos/{owner}/{repo}/issues (retry 1)
270
+ GH-->>Hub: 200 + X-RateLimit-Remaining: 4500
271
+ Hub-->>App: [issues]
272
+ ```
273
+
274
+ ### Eventual Consistency (`_settle`)
275
+
276
+ GitHub's API exhibits eventual consistency — newly created resources may not appear in list endpoints immediately after creation. Without handling this, callers that create a resource and then query for it risk getting stale results.
277
+
278
+ `GitHubHub` addresses this transparently via an internal `_settle` mechanism. Every mutation method (`open`, `comment`, `write`, `propose`, `discuss`, `fork`) verifies the created resource is visible in the corresponding list or get endpoint before returning. This uses exponential backoff with a configurable retry count and delay cap:
279
+
280
+ ```js
281
+ // Internal method — not part of the public API
282
+ async _settle(verify, label, { retries = 5, base = 300, cap = 5000 } = {})
283
+ ```
284
+
285
+ | Parameter | Type | Default | Description |
286
+ |-----------|------|---------|-------------|
287
+ | `verify` | `() => Promise<boolean>` | — | Returns `true` when the resource is visible |
288
+ | `label` | `string` | — | Descriptive label for debug logging |
289
+ | `retries` | `number` | `5` | Maximum verification attempts |
290
+ | `base` | `number` | `300` | Base delay in ms (doubles each attempt) |
291
+ | `cap` | `number` | `5000` | Maximum delay per attempt in ms |
292
+
293
+ If all retries are exhausted without the resource becoming visible, a warning is logged but no error is thrown — the resource was created successfully, only the index is stale.
294
+
295
+ Discussion mutations use direct GraphQL node queries by ID instead of relying on list endpoints, as list queries can have especially severe eventual consistency delays.
296
+
297
+ **Impact on callers**: No action needed. All hub methods return only after their resource is settled. Tests and orchestrator code do not need `setTimeout` delays after hub calls.
298
+
299
+ ### Pagination
300
+
301
+ All list methods (`issues`, `pulls`, `comments`, `reviews`, `files`) use Octokit's `paginate()` helper with `per_page: 100` to fetch all pages automatically. Callers always receive the complete result set, regardless of how many items exist.
302
+
303
+ Without pagination, GitHub's default page size of 30 items silently truncates results, which can cause agents to miss issues, PRs, or review comments.
304
+
305
+ ### Pretest Cleanup
306
+
307
+ Integration tests create real GitHub resources (issues, PRs, branches, discussions). To ensure a clean starting state, the repository includes a `scripts/clean-test-repo.js` script wired as a `pretest` hook in `package.json`:
308
+
309
+ ```json
310
+ {
311
+ "scripts": {
312
+ "pretest": "node scripts/clean-test-repo.js",
313
+ "test": "node --test ..."
314
+ }
315
+ }
316
+ ```
317
+
318
+ The script uses `GitHubHub` with `paginate()` to:
319
+ 1. Close all open issues and PRs
320
+ 2. Delete all non-default branches
321
+ 3. Delete all discussions in the "Loreli" category
322
+
323
+ This prevents leftover test data from causing false claims, stale label collisions, or flaky assertions in subsequent runs. Individual tests also track their resources via the `Cleanup` helper class in `packages/hub/test/helpers.js`.
324
+
325
+ ### Normalized Return Shapes
326
+
327
+ All responses are normalized to provider-agnostic objects:
328
+
329
+ - **Issue**: `{ id, number, title, body, state, author, url, labels, created, updated }` (`id` is the GitHub database ID, needed for the sub-issues API)
330
+ - **Pull**: `{ number, title, body, state, head, base, author, url, labels, merged, created, updated }`
331
+ - **Comment**: `{ id, body, author, created }`
332
+ - **Review**: `{ id, body, state, author, submitted }`
333
+
334
+ ## Environment Variables
335
+
336
+ | Variable | Description |
337
+ |----------|-------------|
338
+ | `GITHUB_TOKEN` | GitHub personal access token (required for GitHubHub) |
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Abstract base class defining all git-hosting operations Loreli needs.
3
+ *
4
+ * Subclasses (GitHubHub, future GitLabHub) implement each method
5
+ * using their provider's API client. Methods are organized by domain:
6
+ * Issues, Pull Requests, Reviews, Contents, Repository, Labels, Projects.
7
+ *
8
+ * Convention: `repo` is always an "owner/name" string.
9
+ *
10
+ * Identity scoping: call `hub.as(identity, role)` to get a scoped
11
+ * instance that auto-appends agent signatures to all written content.
12
+ */
13
+ export class BaseHub {
14
+ /**
15
+ * @param {string} _repo
16
+ * @param {object} [_opts]
17
+ * @throws {Error} Always — subclass must override.
18
+ */
19
+ async issues(_repo, _opts) { throw new Error('issues: not implemented'); }
20
+
21
+ /** @param {string} _repo @param {number} _number */
22
+ async issue(_repo, _number) { throw new Error('issue: not implemented'); }
23
+
24
+ /** @param {string} _repo @param {object} _opts */
25
+ async open(_repo, _opts) { throw new Error('open: not implemented'); }
26
+
27
+ /** @param {string} _repo @param {number} _number @param {object} _fields */
28
+ async update(_repo, _number, _fields) { throw new Error('update: not implemented'); }
29
+
30
+ /** @param {string} _repo @param {number} _number @param {string} _body */
31
+ async comment(_repo, _number, _body) { throw new Error('comment: not implemented'); }
32
+
33
+ /** @param {string} _repo @param {number} _number */
34
+ async comments(_repo, _number) { throw new Error('comments: not implemented'); }
35
+
36
+ // Pull Requests
37
+ /** @param {string} _repo @param {object} [_opts] */
38
+ async pulls(_repo, _opts) { throw new Error('pulls: not implemented'); }
39
+
40
+ /** @param {string} _repo @param {number} _number */
41
+ async pull(_repo, _number) { throw new Error('pull: not implemented'); }
42
+
43
+ /** @param {string} _repo @param {object} _opts */
44
+ async propose(_repo, _opts) { throw new Error('propose: not implemented'); }
45
+
46
+ /** @param {string} _repo @param {number} _number @param {object} [_opts] */
47
+ async merge(_repo, _number, _opts) { throw new Error('merge: not implemented'); }
48
+
49
+ /** @param {string} _repo @param {number} _number */
50
+ async closePull(_repo, _number) { throw new Error('closePull: not implemented'); }
51
+
52
+ /** @param {string} _repo @param {number} _number */
53
+ async files(_repo, _number) { throw new Error('files: not implemented'); }
54
+
55
+ /** @param {string} _repo @param {number} _number @param {number} [_maxBytes] */
56
+ async diff(_repo, _number, _maxBytes) { throw new Error('diff: not implemented'); }
57
+
58
+ // Reviews
59
+ /** @param {string} _repo @param {number} _number @param {object} _opts */
60
+ async review(_repo, _number, _opts) { throw new Error('review: not implemented'); }
61
+
62
+ /** @param {string} _repo @param {number} _number */
63
+ async reviews(_repo, _number) { throw new Error('reviews: not implemented'); }
64
+
65
+ /** @param {string} _repo @param {number} _number @param {object} _opts */
66
+ async annotate(_repo, _number, _opts) { throw new Error('annotate: not implemented'); }
67
+
68
+ // Contents
69
+ /** @param {string} _repo @param {string} _path @param {object} [_opts] */
70
+ async read(_repo, _path, _opts) { throw new Error('read: not implemented'); }
71
+
72
+ /** @param {string} _repo @param {string} _path @param {object} _opts */
73
+ async write(_repo, _path, _opts) { throw new Error('write: not implemented'); }
74
+
75
+ /** @param {string} _repo @param {object} [_opts] */
76
+ async tree(_repo, _opts) { throw new Error('tree: not implemented'); }
77
+
78
+ // Repository
79
+ /** @param {string} _repo */
80
+ async repo(_repo) { throw new Error('repo: not implemented'); }
81
+
82
+ /** @param {string} _repo @param {string} _name */
83
+ async branch(_repo, _name) { throw new Error('branch: not implemented'); }
84
+
85
+ /** @param {string} _repo @param {object} _opts */
86
+ async fork(_repo, _opts) { throw new Error('fork: not implemented'); }
87
+
88
+ // Rate Limits
89
+ /**
90
+ * Get current rate limit information.
91
+ *
92
+ * @returns {{remaining: number|null, limit: number|null, reset: string|null, used: number|null, ratio: number|null}}
93
+ */
94
+ rates() { return { remaining: null, limit: null, used: null, reset: null, ratio: null }; }
95
+
96
+ // Labels
97
+ /** @param {string} _repo @param {number} _number @param {string[]} _labels */
98
+ async label(_repo, _number, _labels) { throw new Error('label: not implemented'); }
99
+
100
+ /** @param {string} _repo */
101
+ async labels(_repo) { throw new Error('labels: not implemented'); }
102
+
103
+ /** @param {string} _repo @param {Array<{name: string, color: string, description?: string}>} _labels */
104
+ async ensure(_repo, _labels) { throw new Error('ensure: not implemented'); }
105
+
106
+ // Discussions (planning)
107
+ /** @param {string} _repo @param {string} _name */
108
+ async category(_repo, _name) { throw new Error('category: not implemented'); }
109
+
110
+ /** @param {string} _repo @param {object} _opts */
111
+ async discuss(_repo, _opts) { throw new Error('discuss: not implemented'); }
112
+
113
+ /** @param {string} _repo @param {string} _categoryId */
114
+ async discussions(_repo, _categoryId) { throw new Error('discussions: not implemented'); }
115
+
116
+ /** @param {string} _repo @param {number} _number */
117
+ async discussion(_repo, _number) { throw new Error('discussion: not implemented'); }
118
+
119
+ /** @param {string} _discussionId */
120
+ async discussionComments(_discussionId) { throw new Error('discussionComments: not implemented'); }
121
+
122
+ /** @param {string} _discussionId @param {string} _body */
123
+ async discussionComment(_discussionId, _body) { throw new Error('discussionComment: not implemented'); }
124
+
125
+ /** @param {string} _discussionId @param {object} _opts */
126
+ async updateDiscussion(_discussionId, _opts) { throw new Error('updateDiscussion: not implemented'); }
127
+
128
+ /** @param {string} _discussionId */
129
+ async closeDiscussion(_discussionId) { throw new Error('closeDiscussion: not implemented'); }
130
+
131
+ /** @param {string} _discussionId */
132
+ async deleteDiscussion(_discussionId) { throw new Error('deleteDiscussion: not implemented'); }
133
+
134
+ // Search & Commits
135
+ /** @param {string} _repo @param {string} _sha */
136
+ async associatedPulls(_repo, _sha) { throw new Error('associatedPulls: not implemented'); }
137
+
138
+ /** @param {string} _repo @param {string} _query */
139
+ async searchIssues(_repo, _query) { throw new Error('searchIssues: not implemented'); }
140
+
141
+ // Sub-issues
142
+ /** @param {string} _repo @param {number} _parent @param {number} _childId */
143
+ async sub(_repo, _parent, _childId) { throw new Error('sub: not implemented'); }
144
+
145
+ /** @param {string} _repo @param {number} _number */
146
+ async subs(_repo, _number) { throw new Error('subs: not implemented'); }
147
+
148
+ // Human In The Loop (HITL)
149
+ /** @param {string} _repo @param {number} _number @param {string[]} _usernames */
150
+ async assign(_repo, _number, _usernames) { throw new Error('assign: not implemented'); }
151
+
152
+ /** @param {string} _repo @param {number} _number @param {string[]} _usernames */
153
+ async request(_repo, _number, _usernames) { throw new Error('request: not implemented'); }
154
+ }