typeclaw 0.18.0 → 0.19.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/package.json +1 -1
- package/src/agent/index.ts +2 -1
- package/src/agent/model-overrides.ts +77 -0
- package/src/agent/plugin-tools.ts +53 -4
- package/src/agent/tools/grant-role.ts +102 -8
- package/src/bundled-plugins/github-cli-auth/gh-command.ts +372 -0
- package/src/bundled-plugins/github-cli-auth/index.ts +42 -0
- package/src/bundled-plugins/github-cli-auth/token-class.ts +11 -0
- package/src/bundled-plugins/reviewer/skills/code-review.ts +18 -1
- package/src/bundled-plugins/security/policies/secret-exfil-bash.ts +9 -2
- package/src/channels/adapters/discord-bot.ts +21 -4
- package/src/channels/adapters/github/inbound.ts +30 -55
- package/src/channels/adapters/github/index.ts +80 -18
- package/src/channels/adapters/github/membership.ts +4 -0
- package/src/channels/adapters/slack-bot-slash-commands.ts +3 -1
- package/src/channels/adapters/slack-bot.ts +4 -4
- package/src/channels/commands.ts +10 -0
- package/src/channels/engagement.ts +34 -3
- package/src/channels/github-token-bridge.ts +42 -0
- package/src/channels/index.ts +6 -0
- package/src/channels/manager.ts +6 -0
- package/src/channels/membership.ts +9 -0
- package/src/channels/router.ts +155 -37
- package/src/cli/ui.ts +6 -0
- package/src/commands/index.ts +54 -4
- package/src/init/dockerfile.ts +60 -0
- package/src/init/validate-api-key.ts +15 -1
- package/src/plugin/context.ts +8 -0
- package/src/plugin/manager.ts +3 -0
- package/src/plugin/types.ts +6 -0
- package/src/run/bundled-plugins.ts +9 -0
- package/src/run/index.ts +4 -0
- package/src/skills/typeclaw-channel-github/SKILL.md +70 -43
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import agentBrowserPlugin from '@/bundled-plugins/agent-browser'
|
|
2
2
|
import backupPlugin from '@/bundled-plugins/backup'
|
|
3
3
|
import explorerPlugin from '@/bundled-plugins/explorer'
|
|
4
|
+
import githubCliAuthPlugin from '@/bundled-plugins/github-cli-auth'
|
|
4
5
|
import guardPlugin from '@/bundled-plugins/guard'
|
|
5
6
|
import memoryPlugin from '@/bundled-plugins/memory'
|
|
6
7
|
import operatorPlugin from '@/bundled-plugins/operator'
|
|
@@ -28,6 +29,13 @@ import type { ResolvedPlugin } from '@/plugin'
|
|
|
28
29
|
// Reversing this order would make guard advise on the full oversized payload
|
|
29
30
|
// and then tool-result-cap would clobber the advice text along with the rest.
|
|
30
31
|
//
|
|
32
|
+
// `github-cli-auth` is registered AFTER `security` so security's `tool.before`
|
|
33
|
+
// runs its exfil/secret scanners on the bash command first. github-cli-auth
|
|
34
|
+
// injects the minted token via an env overlay (TYPECLAW_INTERNAL_BASH_ENV), not
|
|
35
|
+
// by rewriting the command string, so the token never enters argv or logs — but
|
|
36
|
+
// ordering security first still matters so a blocked command never reaches the
|
|
37
|
+
// mint path at all.
|
|
38
|
+
//
|
|
31
39
|
// `memory` is registered before `backup` so memory's dreaming commits always
|
|
32
40
|
// land in the same git index window before backup's commit-and-push cycle.
|
|
33
41
|
// They commit disjoint paths today (memory/ vs sessions/ + agent changes),
|
|
@@ -37,6 +45,7 @@ export const BUNDLED_PLUGINS: ResolvedPlugin[] = [
|
|
|
37
45
|
{ name: 'security', version: undefined, source: '<bundled>', defined: securityPlugin },
|
|
38
46
|
{ name: 'tool-result-cap', version: undefined, source: '<bundled>', defined: toolResultCapPlugin },
|
|
39
47
|
{ name: 'guard', version: undefined, source: '<bundled>', defined: guardPlugin },
|
|
48
|
+
{ name: 'github-cli-auth', version: undefined, source: '<bundled>', defined: githubCliAuthPlugin },
|
|
40
49
|
{ name: 'memory', version: undefined, source: '<bundled>', defined: memoryPlugin },
|
|
41
50
|
{ name: 'backup', version: undefined, source: '<bundled>', defined: backupPlugin },
|
|
42
51
|
{ name: 'agent-browser', version: undefined, source: '<bundled>', defined: agentBrowserPlugin },
|
package/src/run/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { resolveCapOptionsFromConfig } from '@/bundled-plugins/tool-result-cap'
|
|
|
19
19
|
import {
|
|
20
20
|
createChannelManager,
|
|
21
21
|
createChannelsReloadable,
|
|
22
|
+
createGithubTokenBridge,
|
|
22
23
|
createSubagentCompletionBridge,
|
|
23
24
|
type ChannelManager,
|
|
24
25
|
type SubagentCompletionBridge,
|
|
@@ -138,11 +139,13 @@ export async function startAgent({
|
|
|
138
139
|
|
|
139
140
|
const pluginConfigsByName = loadPluginConfigsSync(cwd)
|
|
140
141
|
const cwdConfig = loadConfigSync(cwd)
|
|
142
|
+
const githubTokenBridge = createGithubTokenBridge()
|
|
141
143
|
const pluginsLoaded = await loadPlugins({
|
|
142
144
|
entries: cwdConfig.plugins,
|
|
143
145
|
agentDir: cwd,
|
|
144
146
|
configsByName: pluginConfigsByName,
|
|
145
147
|
bundled: BUNDLED_PLUGINS,
|
|
148
|
+
resolveGithubTokenForRepo: githubTokenBridge.resolveTokenForRepo,
|
|
146
149
|
...(cwdConfig.roles !== undefined ? { roles: cwdConfig.roles } : {}),
|
|
147
150
|
})
|
|
148
151
|
|
|
@@ -255,6 +258,7 @@ export async function startAgent({
|
|
|
255
258
|
}),
|
|
256
259
|
permissions: pluginsLoaded.permissions,
|
|
257
260
|
claimHandler: claimController.claimHandler,
|
|
261
|
+
githubTokenBridge,
|
|
258
262
|
stream,
|
|
259
263
|
})
|
|
260
264
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: typeclaw-channel-github
|
|
3
|
-
description: Use this skill BEFORE every `channel_reply` or `channel_send` call whose adapter is `github`, AND before composing replies to GitHub-originated inbounds, AND before opening new issues or PRs with `gh`, AND ALWAYS when
|
|
3
|
+
description: Use this skill BEFORE every `channel_reply` or `channel_send` call whose adapter is `github`, AND before composing replies to GitHub-originated inbounds, AND before opening new issues or PRs with `gh`, AND ALWAYS when you are asked to review a PR — whether the inbound says "requested your review on PR #N" / "requested a review from team @… on PR #N", or a human asks for a review in plain language in an issue/PR body or comment ("@bot review this", "can you take a look at #123"). On a review request you delegate the analysis to the `reviewer` subagent, which produces line-anchored findings, then you post them as an inline review via `gh api`. GitHub renders **real markdown** — `**bold**`, `## headings`, `| tables |`, fenced code blocks, and `inline code` all render natively. Use rich markdown freely. GitHub cannot send file attachments via API — do not call `channel_send` with attachments on github chats. GitHub has no typing indicator. PR review threads use `thread` keyed on the root comment id; reply to a thread to stay in it, or omit `thread` to post a top-level issue/PR comment. To open new issues or PRs use the `gh` CLI — `GH_TOKEN` is pre-set by the adapter. Read this skill before composing anything on GitHub.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
GitHub renders normal Markdown in issues, PRs, discussions, and review comments. Use headings, lists, tables, fenced code blocks, links, and inline code when they improve clarity.
|
|
@@ -13,39 +13,44 @@ GitHub renders normal Markdown in issues, PRs, discussions, and review comments.
|
|
|
13
13
|
|
|
14
14
|
A successful `channel_reply` ends your turn by default — the runtime stops the model right after the reply lands. That is correct for a final answer, but it will **silently truncate** a turn that still has work to do. If you post a status line like "Reviewing now, I'll be back with findings" and then expect to keep working (fetch the diff, spawn the reviewer, post the review) in the **same** turn, you must call `channel_reply({ text: "…", continue: true })`. Without `continue: true`, the turn ends at that status reply and the review never runs. Reserve `continue: true` for genuine multi-step turns; the final reply that wraps up the turn omits it.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## What to do, by inbound type
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Every GitHub inbound lands on a `chat` keyed by its subject: `issue:N`, `pr:N`, or `discussion:N`. Pick your action from the kind of thing that arrived. The default action for anything addressed to you is a normal `channel_reply` in that thread; the **PR review flow** below is the one exception that requires delegation.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
| Inbound | Looks like | What to do |
|
|
21
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| **New issue** (`issue:N`) | A freshly opened issue body. | Triage or answer it. `channel_reply` on `issue:N`. Open follow-up issues/PRs with `gh` if needed. |
|
|
23
|
+
| **Issue comment** (`issue:N`) | A comment on an issue. | Reply in the issue thread with `channel_reply`. |
|
|
24
|
+
| **PR conversation comment** (`pr:N`, no `thread`) | A comment on a PR's main conversation (GitHub models PR comments as issue comments). | Reply on the PR with `channel_reply`. **If the text asks you to review → go to the PR review flow.** |
|
|
25
|
+
| **PR review-thread reply** (`pr:N`, `thread` set) | A reply on an existing inline review comment thread. | Stay in the thread: `channel_reply` with `thread` kept as-is. |
|
|
26
|
+
| **A submitted review** (`pr:N`) | Someone submitted a formal review (approve / changes / comment) on a PR. | React if a response is warranted (answer a question, acknowledge changes). `channel_reply` on `pr:N`. |
|
|
27
|
+
| **New discussion / discussion comment** (`discussion:N`) | A discussion thread or a comment in one. | Reply with `channel_reply` on `discussion:N`. |
|
|
28
|
+
| **Review requested** (`pr:N`) | See "When you are being asked to review" below. | **PR review flow.** |
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
gh pr create --repo owner/repo --title "Fix: ..." --head my-branch --base main --body "..."
|
|
26
|
-
```
|
|
30
|
+
### When you are being asked to review
|
|
27
31
|
|
|
28
|
-
|
|
32
|
+
You are being asked to review a PR in **either** of these cases — treat them identically:
|
|
29
33
|
|
|
30
|
-
|
|
34
|
+
- **(A) An explicit review-request inbound.** The message text says **"requested your review on PR #N"** or **"requested a review from team @… on PR #N"**. (You do not need to know how it was triggered — the adapter synthesizes this same text whether a human requested you as a reviewer directly or requested a decoy user account that impersonates you as a GitHub App. From your side it reads the same. See [GitHub decoy reviewer](/docs/internals/github-decoy-reviewer).)
|
|
35
|
+
- **(B) A human asks you to review in plain language** in a PR/issue body or any comment — "@bot review this PR", "can you take a look at #123", "review the changes when you get a chance". There is no synthetic request text here; you recognize the intent from the message.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
Both → run the **PR review flow**. Do not review inline yourself and do not just reply with prose impressions: delegate to the `reviewer` subagent so the analysis runs on the `deep` model, then post its findings as an inline review.
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
A `review_request_removed` inbound ("removed your review request on PR #N") is the inverse: the requester un-assigned you. Cancel any in-flight reviewer subagent (`subagent_cancel`) and do not post a partial review.
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
## PR review flow
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
The `reviewer` subagent is the analyst; you are the integration layer between its output and GitHub's review API. It loads the `code-review` skill on demand and returns line-anchored findings inside a `<review>` block. Your job is mechanics: spawn, wait, translate, post.
|
|
44
|
+
|
|
45
|
+
1. **Confirm the target.** Capture the PR number, the repo, and the head SHA — you may need the SHA to read files at the revision the reviewer analyzed.
|
|
39
46
|
|
|
40
47
|
```sh
|
|
41
48
|
gh pr view <N> --repo owner/repo --json title,body,baseRefName,headRefOid,files
|
|
42
49
|
```
|
|
43
50
|
|
|
44
|
-
2. **Spawn the `reviewer` subagent with the PR target.** Use `run_in_background: true` so you stay responsive while the deep model works. Pass the PR URL (or `owner/repo#N`) plus any context the requester gave you (focus areas, specific files, etc.)
|
|
45
|
-
|
|
46
|
-
If you post an "on it" acknowledgement before fetching the diff or spawning the reviewer, it **must** be `channel_reply({ text: "…", continue: true })` — a bare reply ends the turn and the review never starts (see "Mid-turn status replies need `continue: true`" above).
|
|
51
|
+
2. **Spawn the `reviewer` subagent with the PR target.** Use `run_in_background: true` so you stay responsive while the deep model works. Pass the PR URL (or `owner/repo#N`) plus any context the requester gave you (focus areas, specific files, etc.). The reviewer fetches the diff itself (`gh pr diff`, `gh api /repos/.../pulls/<n>`), loads the `code-review` skill, and returns a `<review>` block whose code findings carry `location="path:line"`.
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
If you post an "on it" acknowledgement before spawning the reviewer, it **must** be `channel_reply({ text: "…", continue: true })` — a bare reply ends the turn and the review never starts (see "Mid-turn status replies need `continue: true`" above).
|
|
49
54
|
|
|
50
55
|
3. **Wait for the completion `<system-reminder>`,** then call `subagent_output({ task_id })` to read the reviewer's final assistant message. The structured payload looks like:
|
|
51
56
|
|
|
@@ -63,11 +68,11 @@ Why delegate: the `reviewer` subagent runs on the `deep` model profile, loads a
|
|
|
63
68
|
</review>
|
|
64
69
|
```
|
|
65
70
|
|
|
66
|
-
4. **Translate findings into a `gh api` review payload.** Each `<finding>` with `severity` of `blocker`, `concern`, or `nit` and a `location="path:line"` becomes one entry in `comments[]`. Compose the inline `body` from the reviewer's `<issue>` + `<evidence>` + `<suggestion>`
|
|
71
|
+
4. **Translate findings into a `gh api` review payload.** Each `<finding>` with `severity` of `blocker`, `concern`, or `nit` and a `location="path:line"` becomes one entry in `comments[]`. Compose the inline `body` from the reviewer's `<issue>` + `<evidence>` + `<suggestion>` verbatim (modulo markdown). Findings whose `location` is `general` (no file:line anchor) go into the top-level review `body` instead. **Skip `praise` findings when building `comments[]`** — if you want to surface them, weave them into the top-level `body`.
|
|
67
72
|
|
|
68
|
-
**The verdict and the inline comments are independent. The verdict sets only the `event` field; it never decides whether you post `comments[]`.** Whenever there is at least one actionable finding (`blocker`/`concern`/`nit`) with a `location="path:line"`, you MUST submit a formal review via `POST /pulls/<N>/reviews` carrying those findings in `comments[]` — including when the verdict is `approve`. An `approve` with three nits is still a formal `APPROVE` review with three inline comments, **not** a plain approval and **not** a flattened summary
|
|
73
|
+
**The verdict and the inline comments are independent. The verdict sets only the `event` field; it never decides whether you post `comments[]`.** Whenever there is at least one actionable finding (`blocker`/`concern`/`nit`) with a `location="path:line"`, you MUST submit a formal review via `POST /pulls/<N>/reviews` carrying those findings in `comments[]` — including when the verdict is `approve`. An `approve` with three nits is still a formal `APPROVE` review with three inline comments, **not** a plain approval and **not** a flattened summary. Collapsing inline findings into a single `channel_reply` or issue comment loses the line anchors the reviewer worked to produce.
|
|
69
74
|
|
|
70
|
-
Map the reviewer's `<verdict>` to the GitHub `event
|
|
75
|
+
Map the reviewer's `<verdict>` to the GitHub `event`, and trust it — do not upgrade `comment` → `APPROVE` to seem agreeable, or downgrade `request-changes` → `COMMENT` to soften the tone:
|
|
71
76
|
|
|
72
77
|
| Reviewer verdict | GitHub `event` |
|
|
73
78
|
| ----------------- | ----------------- |
|
|
@@ -75,22 +80,35 @@ Why delegate: the `reviewer` subagent runs on the `deep` model profile, loads a
|
|
|
75
80
|
| `request-changes` | `REQUEST_CHANGES` |
|
|
76
81
|
| `comment` | `COMMENT` |
|
|
77
82
|
|
|
78
|
-
Then submit the review
|
|
83
|
+
Then submit the review. **Write the JSON payload to a file with the `write` tool, then run a single bare `gh api --input <file>`** — two steps:
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
First write `/tmp/review.json` (via the `write` tool, not bash):
|
|
86
|
+
|
|
87
|
+
```json
|
|
82
88
|
{
|
|
83
89
|
"event": "COMMENT",
|
|
84
90
|
"body": "<reviewer's <summary> goes here>",
|
|
85
91
|
"comments": [
|
|
86
|
-
{
|
|
92
|
+
{
|
|
93
|
+
"path": "src/foo.ts",
|
|
94
|
+
"line": 42,
|
|
95
|
+
"side": "RIGHT",
|
|
96
|
+
"body": "<issue + evidence + suggestion from the reviewer's finding>"
|
|
97
|
+
},
|
|
87
98
|
{ "path": "src/bar.ts", "line": 10, "side": "RIGHT", "body": "..." }
|
|
88
99
|
]
|
|
89
100
|
}
|
|
90
|
-
JSON
|
|
91
101
|
```
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
Then post it:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
gh api -X POST /repos/owner/repo/pulls/<N>/reviews --input /tmp/review.json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**A repo-targeting `gh` command must be a single bare `gh` invocation — no pipes, `;`, `&&`, heredocs, or command substitution.** The `github-cli-auth` plugin injects the GitHub App token into the command's environment, so any sibling/upstream stage in a pipeline would inherit a live token; the runtime blocks those shapes. That is why the old `cat <<'JSON' | gh api --input -` heredoc-pipe no longer works: write the JSON to a file and feed it with `--input <file>` instead. Do **not** use `-f body=...` or `-F 'comments[][body]=...'`: those go through shell argument parsing, so backticks trigger command substitution. The file passes the JSON through untouched — backticks, newlines, and `${...}` all survive verbatim. The same file-then-`--input` pattern applies to any `gh api` POST whose body contains backticks, embedded newlines, or shell metacharacters.
|
|
110
|
+
|
|
111
|
+
Anchor mechanics: `line` is a line number **in the file**, not a position in the diff. `side: RIGHT` is the new revision (default for additions); `side: LEFT` is the old revision (use for comments on removed lines). For multi-line comments, also set `start_line` and `start_side` (same semantics). If you need to read whole files at the PR's head SHA to validate an anchor before posting, use `gh api /repos/owner/repo/contents/<path>?ref=<headRefOid>`.
|
|
94
112
|
|
|
95
113
|
5. **Verify the review actually landed before announcing it.** The `gh api` call can fail silently from the model's perspective — a permission denial, a bad `line` anchor, or a malformed payload returns an error you must not paper over. After submitting, confirm the review exists:
|
|
96
114
|
|
|
@@ -100,23 +118,32 @@ Why delegate: the `reviewer` subagent runs on the `deep` model profile, loads a
|
|
|
100
118
|
|
|
101
119
|
The returned `id`/`state` is your proof the formal review posted. If the call errored or the review is absent, do **not** fall back to a top-level `channel_reply` that _claims_ a review was posted — fix the payload (most often a `line` that isn't part of the diff; re-anchor it or move that finding to the top-level `body`) and resubmit. A trace reply that says "Posted review" when no review exists is worse than silence.
|
|
102
120
|
|
|
103
|
-
6. **End the turn with `skip_response`, not a trace reply.** The formal review from step 4 already landed _in this PR_ — it carries the summary, the verdict, and the inline comments. A `channel_reply` here does **not** go to a separate operator channel; on GitHub it posts another public comment on the same PR. A one-line "Posted review on PR #N: …" narrated into the PR thread is meta-commentary addressed to a phantom operator, and it reads absurdly next to the review it claims to point at. So once step 5 confirms the review exists, call `skip_response({ reason: "review posted via gh api" })` to close the turn silently. Only fall back to `channel_reply` when there was **no** formal review to post — the zero-actionable-findings
|
|
121
|
+
6. **End the turn with `skip_response`, not a trace reply.** The formal review from step 4 already landed _in this PR_ — it carries the summary, the verdict, and the inline comments. A `channel_reply` here does **not** go to a separate operator channel; on GitHub it posts another public comment on the same PR. A one-line "Posted review on PR #N: …" narrated into the PR thread is meta-commentary addressed to a phantom operator, and it reads absurdly next to the review it claims to point at. So once step 5 confirms the review exists, call `skip_response({ reason: "review posted via gh api" })` to close the turn silently. Only fall back to `channel_reply` when there was **no** formal review to post — the zero-actionable-findings branch below uses `channel_reply`/issue comments _as_ the substantive reply.
|
|
122
|
+
|
|
123
|
+
### Zero actionable findings
|
|
124
|
+
|
|
125
|
+
A finding is "actionable" if its severity is `blocker`, `concern`, or `nit`. The inline-review post in step 4 applies whenever the actionable count is **at least one**. When the reviewer returns **exactly zero** actionable findings (only `praise`, or none), there is nothing to anchor inline — handle by verdict:
|
|
104
126
|
|
|
105
|
-
|
|
127
|
+
- `approve` → post a plain `APPROVE` with the `<summary>` as the review body (no `comments[]` array).
|
|
128
|
+
- `comment` → post the summary as a top-level PR comment via `gh api -X POST /repos/.../issues/<N>/comments` instead of submitting an empty review.
|
|
129
|
+
- `request-changes` → submit `REQUEST_CHANGES` with the `<summary>` as the review body and no `comments[]` array. This combination is rare (the reviewer's contract says `request-changes` requires at least one blocker or load-bearing concern); if it happens, faithfully encode the verdict and trust the reviewer's reasoning is in the summary.
|
|
106
130
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
The bundled `agent-browser` is **not** for PR reviews — `gh api` is faster and more reliable. Only use the browser when the API genuinely can't reach what you need.
|
|
132
|
+
|
|
133
|
+
## Opening new issues and PRs
|
|
134
|
+
|
|
135
|
+
The `gh` CLI is pre-authenticated via `GH_TOKEN` (injected by the adapter at startup). Use it to open new issues or PRs:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
# Open a new issue
|
|
139
|
+
gh issue create --repo owner/repo --title "Bug: ..." --body "..."
|
|
140
|
+
|
|
141
|
+
# Open a new PR
|
|
142
|
+
gh pr create --repo owner/repo --title "Fix: ..." --head my-branch --base main --body "..."
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For App auth, `GH_TOKEN` is an installation access token that refreshes automatically — it stays current as long as the adapter is running.
|
|
119
146
|
|
|
120
|
-
|
|
147
|
+
## Self-loop safety
|
|
121
148
|
|
|
122
149
|
The adapter will **not** wake you when you assign yourself as a reviewer (e.g., via `gh pr edit --add-reviewer`). It will only wake you when someone else requests your review.
|