tissues 0.3.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/LICENSE +21 -0
- package/README.md +353 -0
- package/bin/gitissues.js +11 -0
- package/package.json +56 -0
- package/src/cli.js +64 -0
- package/src/commands/auth.js +62 -0
- package/src/commands/create.js +360 -0
- package/src/commands/list.js +59 -0
- package/src/commands/open.js +17 -0
- package/src/commands/status.js +166 -0
- package/src/lib/attribution.js +216 -0
- package/src/lib/config.js +16 -0
- package/src/lib/db.js +436 -0
- package/src/lib/dedup.js +205 -0
- package/src/lib/defaults.js +252 -0
- package/src/lib/gh.js +273 -0
- package/src/lib/repo-picker.js +54 -0
- package/src/lib/safety.js +259 -0
- package/src/lib/templates.js +220 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Caleb Ogden
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# ghissue
|
|
2
|
+
|
|
3
|
+
AI-enhanced GitHub issue creation with built-in safety guardrails.
|
|
4
|
+
|
|
5
|
+
Create structured, deduplicated GitHub issues from the command line — with circuit breakers and rate limiting to prevent runaway issue creation when used inside AI agent workflows.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/ghissue)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g ghissue
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires Node.js >= 18.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Authenticate (auto-detects gh CLI token)
|
|
26
|
+
ghissue auth
|
|
27
|
+
|
|
28
|
+
# Set your active repo
|
|
29
|
+
ghissue open
|
|
30
|
+
|
|
31
|
+
# Create an issue interactively
|
|
32
|
+
ghissue create
|
|
33
|
+
|
|
34
|
+
# Check safety status before running in CI
|
|
35
|
+
ghissue status
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
### `ghissue auth`
|
|
43
|
+
|
|
44
|
+
Authenticate with GitHub. Auto-detects your `gh` CLI token if you're already logged in via `gh`. Falls back to GitHub OAuth Device Flow if not.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ghissue auth
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### `ghissue open`
|
|
51
|
+
|
|
52
|
+
Set the active repository context. All subsequent commands use this repo unless overridden with `--repo`.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
ghissue open
|
|
56
|
+
# prompts: pick a repo from your GitHub account
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### `ghissue create`
|
|
60
|
+
|
|
61
|
+
Create a new GitHub issue. Runs dedup checks and safety gates before creating anything.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
ghissue create [options]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Options:**
|
|
68
|
+
|
|
69
|
+
| Flag | Description |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `--repo <owner/name>` | Override active repo |
|
|
72
|
+
| `--template <name>` | Template to use: `bug`, `feature`, `security`, `performance`, `refactor` |
|
|
73
|
+
| `--title <title>` | Issue title (skips interactive prompt) |
|
|
74
|
+
| `--body <text>` | Issue body / description (skips interactive prompt) |
|
|
75
|
+
| `--labels <labels>` | Comma-separated labels to apply |
|
|
76
|
+
| `--agent <name>` | Agent identifier for attribution (default: `human`) |
|
|
77
|
+
| `--session <id>` | Session ID for attribution and idempotency |
|
|
78
|
+
| `--force` | Skip dedup warnings (still blocks on exact matches) |
|
|
79
|
+
| `--dry-run` | Check dedup and safety without creating the issue |
|
|
80
|
+
|
|
81
|
+
**Examples:**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Interactive
|
|
85
|
+
ghissue create
|
|
86
|
+
|
|
87
|
+
# Fully scripted (no prompts)
|
|
88
|
+
ghissue create \
|
|
89
|
+
--repo owner/my-repo \
|
|
90
|
+
--template bug \
|
|
91
|
+
--title "Login fails on Safari 17" \
|
|
92
|
+
--body "Reproducible on fresh profile. Console shows CORS error." \
|
|
93
|
+
--labels "bug,P1"
|
|
94
|
+
|
|
95
|
+
# From an AI agent
|
|
96
|
+
ghissue create \
|
|
97
|
+
--agent claude-opus-4-6 \
|
|
98
|
+
--session abc123 \
|
|
99
|
+
--template feature \
|
|
100
|
+
--title "Add dark mode toggle" \
|
|
101
|
+
--dry-run
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `ghissue list`
|
|
105
|
+
|
|
106
|
+
Browse open issues for the active repo.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
ghissue list
|
|
110
|
+
ghissue list --repo owner/other-repo
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### `ghissue status`
|
|
114
|
+
|
|
115
|
+
Show circuit breaker state and rate limit usage for the active repo.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
ghissue status
|
|
119
|
+
ghissue status --agent claude-opus-4-6
|
|
120
|
+
ghissue status --reset # force-reset circuit breaker to closed
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Output:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
Circuit Breaker: closed ✓
|
|
127
|
+
Rate Limit: 3/10 per hour (7 remaining)
|
|
128
|
+
Burst: 1/5 in last 5 min
|
|
129
|
+
Global: 3/30 per hour (27 remaining)
|
|
130
|
+
|
|
131
|
+
Last issue created: 12 minutes ago
|
|
132
|
+
Fingerprints stored: 47
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Safety Features
|
|
138
|
+
|
|
139
|
+
ghissue is designed to be called from AI agent loops. The safety infrastructure prevents one misconfigured agent from flooding a repo with issues.
|
|
140
|
+
|
|
141
|
+
### Circuit Breaker
|
|
142
|
+
|
|
143
|
+
Three-state machine (closed / half-open / open) per repo+agent pair.
|
|
144
|
+
|
|
145
|
+
- **Closed** — normal operation
|
|
146
|
+
- **Open** — creation blocked; cooldown in effect (default: 30 minutes)
|
|
147
|
+
- **Half-open** — one probe request allowed through to test recovery
|
|
148
|
+
|
|
149
|
+
The circuit trips after 3 blocked attempts (dedup blocks count as failures). If the probe succeeds, the circuit resets to closed automatically.
|
|
150
|
+
|
|
151
|
+
To inspect or reset:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
ghissue status
|
|
155
|
+
ghissue status --reset
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Rate Limiting
|
|
159
|
+
|
|
160
|
+
Three independent rate limits, all checked before creating:
|
|
161
|
+
|
|
162
|
+
| Limit | Default | Scope |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| Per-agent hourly | 10 issues/hour | per repo + agent |
|
|
165
|
+
| Per-agent burst | 5 issues / 5 minutes | per repo + agent |
|
|
166
|
+
| Global hourly | 30 issues/hour | per repo, all agents combined |
|
|
167
|
+
|
|
168
|
+
All limits are configurable in `.gitissues/config.json`.
|
|
169
|
+
|
|
170
|
+
### Deduplication (3 layers)
|
|
171
|
+
|
|
172
|
+
Dedup runs cheapest-first before every `create`:
|
|
173
|
+
|
|
174
|
+
1. **Idempotency key** (O(1) DB lookup) — blocks if the same `agent + trigger + issueType + repo` combination has already created an issue this session.
|
|
175
|
+
2. **Content fingerprint** (O(1) DB lookup) — SHA-256 of normalized title + first 500 chars of body. Blocks exact re-submissions even across sessions.
|
|
176
|
+
3. **Fuzzy title match** (O(n) GitHub API) — Levenshtein similarity against all open issues. Blocks at >95% similarity; warns at >80%.
|
|
177
|
+
|
|
178
|
+
A `block` always prevents creation. A `warn` prompts interactively (or is skipped with `--force`).
|
|
179
|
+
|
|
180
|
+
### Attribution Tracking
|
|
181
|
+
|
|
182
|
+
Every issue created by ghissue includes a machine-readable `<!-- gitissues-meta -->` block. See [Attribution](#attribution) below.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Configuration
|
|
187
|
+
|
|
188
|
+
Configuration is loaded and merged from four sources in ascending priority order:
|
|
189
|
+
|
|
190
|
+
1. Built-in defaults
|
|
191
|
+
2. User-level config: `~/.config/gitissues/config.json`
|
|
192
|
+
3. Repo-level config: `.gitissues/config.json`
|
|
193
|
+
4. Environment variables: `GITISSUES_*`
|
|
194
|
+
|
|
195
|
+
### Example `.gitissues/config.json`
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"safety": {
|
|
200
|
+
"maxPerHour": 5,
|
|
201
|
+
"burstLimit": 3,
|
|
202
|
+
"burstWindowMinutes": 5,
|
|
203
|
+
"tripThreshold": 3,
|
|
204
|
+
"cooldownMinutes": 30,
|
|
205
|
+
"globalMaxPerHour": 20
|
|
206
|
+
},
|
|
207
|
+
"dedup": {
|
|
208
|
+
"blockAbove": 0.95,
|
|
209
|
+
"warnAbove": 0.80,
|
|
210
|
+
"fingerprintTTLDays": 90
|
|
211
|
+
},
|
|
212
|
+
"attribution": {
|
|
213
|
+
"required": false,
|
|
214
|
+
"defaultAgent": "human"
|
|
215
|
+
},
|
|
216
|
+
"templates": {
|
|
217
|
+
"dir": ".gitissues/templates",
|
|
218
|
+
"default": "bug"
|
|
219
|
+
},
|
|
220
|
+
"hooks": {
|
|
221
|
+
"postCreate": "scripts/notify-slack.sh"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Environment Variables
|
|
227
|
+
|
|
228
|
+
Environment variables follow the pattern `GITISSUES_<SECTION>_<KEY>`:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
GITISSUES_SAFETY_MAX_PER_HOUR=5
|
|
232
|
+
GITISSUES_SAFETY_BURST_LIMIT=3
|
|
233
|
+
GITISSUES_SAFETY_GLOBAL_MAX_PER_HOUR=20
|
|
234
|
+
GITISSUES_ATTRIBUTION_DEFAULT_AGENT=my-agent
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Hooks
|
|
238
|
+
|
|
239
|
+
`postCreate` runs a shell command after every successful issue creation. Three environment variables are injected:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
GITISSUES_REPO # owner/repo
|
|
243
|
+
GITISSUES_ISSUE_NUMBER # 142
|
|
244
|
+
GITISSUES_ISSUE_URL # https://github.com/owner/repo/issues/142
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Templates
|
|
250
|
+
|
|
251
|
+
### Built-in Templates
|
|
252
|
+
|
|
253
|
+
| Key | Description |
|
|
254
|
+
|---|---|
|
|
255
|
+
| `default` | Generic summary + details + additional context |
|
|
256
|
+
| `bug` | Steps to reproduce, expected vs actual behavior, environment |
|
|
257
|
+
| `feature` | Motivation, proposed solution, alternatives |
|
|
258
|
+
| `security` | Severity, affected components, suggested fix |
|
|
259
|
+
| `performance` | Current metric, target metric, affected area |
|
|
260
|
+
| `refactor` | Motivation, scope, risk assessment |
|
|
261
|
+
|
|
262
|
+
### Custom Templates
|
|
263
|
+
|
|
264
|
+
Place `.md` files in `.gitissues/templates/` (repo-level) or `~/.config/gitissues/templates/` (user-level). Repo templates take priority over user templates, which take priority over built-ins.
|
|
265
|
+
|
|
266
|
+
Template files support `{{variable}}` substitution:
|
|
267
|
+
|
|
268
|
+
| Variable | Value |
|
|
269
|
+
|---|---|
|
|
270
|
+
| `{{title}}` | Issue title |
|
|
271
|
+
| `{{description}}` | User's description |
|
|
272
|
+
| `{{agent}}` | Agent identifier |
|
|
273
|
+
| `{{session}}` | Session ID |
|
|
274
|
+
| `{{date}}` | ISO date (YYYY-MM-DD) |
|
|
275
|
+
| `{{repo}}` | Repository (owner/name) |
|
|
276
|
+
|
|
277
|
+
**Example** `.gitissues/templates/incident.md`:
|
|
278
|
+
|
|
279
|
+
```markdown
|
|
280
|
+
name: Incident Report
|
|
281
|
+
|
|
282
|
+
## Incident: {{title}}
|
|
283
|
+
|
|
284
|
+
**Date:** {{date}}
|
|
285
|
+
**Reported by:** {{agent}}
|
|
286
|
+
|
|
287
|
+
## What happened
|
|
288
|
+
|
|
289
|
+
{{description}}
|
|
290
|
+
|
|
291
|
+
## Impact
|
|
292
|
+
|
|
293
|
+
## Timeline
|
|
294
|
+
|
|
295
|
+
## Resolution
|
|
296
|
+
|
|
297
|
+
## Prevention
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Use it with `--template incident`.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Attribution
|
|
305
|
+
|
|
306
|
+
Every issue created by ghissue includes a machine-readable HTML comment at the bottom of the body. GitHub renders it as invisible text; it does not appear in the rendered issue.
|
|
307
|
+
|
|
308
|
+
```html
|
|
309
|
+
<!-- gitissues-meta
|
|
310
|
+
agent: claude-opus-4-6
|
|
311
|
+
session: abc123
|
|
312
|
+
pid: 84921
|
|
313
|
+
trigger: cli-create
|
|
314
|
+
fingerprint: sha256:3f2a1b...
|
|
315
|
+
created_at: 2026-02-19T15:30:00.000Z
|
|
316
|
+
created_via: ghissue-cli/0.3.0
|
|
317
|
+
-->
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The block is used by ghissue to:
|
|
321
|
+
|
|
322
|
+
- Power content fingerprint dedup (prevents re-creating identical issues)
|
|
323
|
+
- Track which agent created which issue
|
|
324
|
+
- Enable idempotency for agent workflows
|
|
325
|
+
|
|
326
|
+
You can pass additional fields via `--agent`, `--session`, and the programmatic API.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## State Database
|
|
331
|
+
|
|
332
|
+
ghissue stores local state in a SQLite database at:
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
.gitissues/data/gitissues.db
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**No separate SQLite install.** The CLI uses [better-sqlite3](https://github.com/WiseLibs/better-sqlite3), which compiles SQLite into the Node addon at `npm install` time. Users do not need to install SQLite on their system. SQLite is a common choice for CLI tool state when you need queryable, structured data (rate limits, dedup history, circuit breaker); simpler config lives in JSON (e.g. `conf` store).
|
|
339
|
+
|
|
340
|
+
**What it stores:**
|
|
341
|
+
|
|
342
|
+
- `fingerprints` — SHA-256 hashes of previously created issues (for dedup layer 2)
|
|
343
|
+
- `rate_events` — timestamped creation events (for rate limiting)
|
|
344
|
+
- `circuit_breaker` — circuit state per repo+agent (status, failure count, cooldown)
|
|
345
|
+
- `idempotency_keys` — deterministic keys for agent-driven idempotency
|
|
346
|
+
|
|
347
|
+
The database file can be committed to share dedup history across a team, or added to `.gitignore` to keep it local. It is created automatically on first use.
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
MIT
|
package/bin/gitissues.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tissues",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "AI-enhanced GitHub issue creation with built-in safety guardrails. Wraps gh CLI with circuit breakers, rate limiting, dedup, and templates.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tissues": "./bin/gitissues.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"dev": "node bin/gitissues.js",
|
|
11
|
+
"watch": "node --watch bin/gitissues.js --help",
|
|
12
|
+
"test": "node --test src/**/*.test.js",
|
|
13
|
+
"audit": "node scripts/audit.js",
|
|
14
|
+
"build": "node scripts/build.js",
|
|
15
|
+
"prepublishOnly": "node scripts/audit.js",
|
|
16
|
+
"dev:install": "node scripts/dev-install.js",
|
|
17
|
+
"dev:uninstall": "node scripts/dev-install.js --uninstall"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"git",
|
|
21
|
+
"github",
|
|
22
|
+
"issues",
|
|
23
|
+
"ai",
|
|
24
|
+
"cli",
|
|
25
|
+
"safety",
|
|
26
|
+
"dedup",
|
|
27
|
+
"circuit-breaker"
|
|
28
|
+
],
|
|
29
|
+
"author": "Caleb Ogden",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"homepage": "https://ghissue.dev",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/calebogden/ghissue.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/calebogden/ghissue/issues"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"bin",
|
|
41
|
+
"src",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@inquirer/prompts": "^7.0.0",
|
|
50
|
+
"better-sqlite3": "^11.0.0",
|
|
51
|
+
"chalk": "^5.3.0",
|
|
52
|
+
"commander": "^12.0.0",
|
|
53
|
+
"conf": "^13.0.0",
|
|
54
|
+
"ora": "^8.0.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { authCommand } from './commands/auth.js'
|
|
3
|
+
import { openCommand } from './commands/open.js'
|
|
4
|
+
import { createCommand } from './commands/create.js'
|
|
5
|
+
import { listCommand } from './commands/list.js'
|
|
6
|
+
import { statusCommand } from './commands/status.js'
|
|
7
|
+
import { store } from './lib/config.js'
|
|
8
|
+
import { requireGh, getAuthStatus } from './lib/gh.js'
|
|
9
|
+
import chalk from 'chalk'
|
|
10
|
+
|
|
11
|
+
export const program = new Command()
|
|
12
|
+
|
|
13
|
+
function authDescription() {
|
|
14
|
+
const base = 'Authenticate with GitHub (via gh CLI)'
|
|
15
|
+
try {
|
|
16
|
+
const status = getAuthStatus()
|
|
17
|
+
const active = status.accounts?.find(a => a.active)
|
|
18
|
+
if (!active) return base
|
|
19
|
+
const check = chalk.green('✔')
|
|
20
|
+
return `${base} ${check} ${chalk.dim(active.login)}`
|
|
21
|
+
} catch {
|
|
22
|
+
return base
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.name('tissues')
|
|
28
|
+
.description('AI-enhanced GitHub issue creation from the command line.')
|
|
29
|
+
.version('0.3.0')
|
|
30
|
+
.hook('preAction', (_thisCommand, actionCommand) => {
|
|
31
|
+
const name = actionCommand.name()
|
|
32
|
+
|
|
33
|
+
// completion doesn't need gh installed
|
|
34
|
+
if (name === 'completion') return
|
|
35
|
+
|
|
36
|
+
// Ensure gh is installed before any command
|
|
37
|
+
requireGh()
|
|
38
|
+
|
|
39
|
+
// Show active repo context on every command (except auth/open)
|
|
40
|
+
if (name !== 'auth' && name !== 'open' && name !== 'login' && name !== 'status' && name !== 'switch' && name !== 'logout') {
|
|
41
|
+
const activeRepo = store.get('activeRepo')
|
|
42
|
+
if (activeRepo) {
|
|
43
|
+
console.log(chalk.dim(`Working in: ${activeRepo}\n`))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
program.addHelpText(
|
|
49
|
+
'after',
|
|
50
|
+
`
|
|
51
|
+
|
|
52
|
+
Repo: https://github.com/calebogden/ghissue
|
|
53
|
+
`,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
program.hook('preAction', () => {
|
|
57
|
+
// Lazily resolve auth description only when a command actually runs
|
|
58
|
+
authCommand.description(authDescription())
|
|
59
|
+
})
|
|
60
|
+
program.addCommand(authCommand)
|
|
61
|
+
program.addCommand(openCommand)
|
|
62
|
+
program.addCommand(createCommand)
|
|
63
|
+
program.addCommand(listCommand)
|
|
64
|
+
program.addCommand(statusCommand)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import {
|
|
3
|
+
requireGh,
|
|
4
|
+
checkScopes,
|
|
5
|
+
authLogin,
|
|
6
|
+
authStatus,
|
|
7
|
+
authSwitch,
|
|
8
|
+
authLogout,
|
|
9
|
+
} from '../lib/gh.js'
|
|
10
|
+
import chalk from 'chalk'
|
|
11
|
+
|
|
12
|
+
export const authCommand = new Command('auth')
|
|
13
|
+
.description('Authenticate with GitHub (via gh CLI)')
|
|
14
|
+
.action(function () {
|
|
15
|
+
this.help()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// Subcommands
|
|
19
|
+
authCommand.addCommand(
|
|
20
|
+
new Command('login')
|
|
21
|
+
.description('Log in to GitHub (opens browser)')
|
|
22
|
+
.action(() => {
|
|
23
|
+
requireGh()
|
|
24
|
+
authLogin()
|
|
25
|
+
}),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
authCommand.addCommand(
|
|
29
|
+
new Command('status')
|
|
30
|
+
.description('Show auth status, accounts, and scopes')
|
|
31
|
+
.action(() => {
|
|
32
|
+
requireGh()
|
|
33
|
+
authStatus()
|
|
34
|
+
|
|
35
|
+
const { ok, missing } = checkScopes()
|
|
36
|
+
if (!ok) {
|
|
37
|
+
console.log()
|
|
38
|
+
console.log(chalk.yellow('⚠ Missing required scope: ') + chalk.bold(missing.join(', ')))
|
|
39
|
+
console.log(chalk.dim(' Fix with: ') + chalk.cyan(`gh auth refresh -s ${missing.join(',')}`))
|
|
40
|
+
} else {
|
|
41
|
+
console.log(chalk.green('\n✓ Token has required scopes for issue creation'))
|
|
42
|
+
}
|
|
43
|
+
}),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
authCommand.addCommand(
|
|
47
|
+
new Command('switch')
|
|
48
|
+
.description('Switch active GitHub account')
|
|
49
|
+
.action(() => {
|
|
50
|
+
requireGh()
|
|
51
|
+
authSwitch()
|
|
52
|
+
}),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
authCommand.addCommand(
|
|
56
|
+
new Command('logout')
|
|
57
|
+
.description('Log out of GitHub')
|
|
58
|
+
.action(() => {
|
|
59
|
+
requireGh()
|
|
60
|
+
authLogout()
|
|
61
|
+
}),
|
|
62
|
+
)
|