typeclaw 0.21.0 → 0.23.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 +2 -1
- package/src/agent/index.ts +55 -1
- package/src/agent/loop-guard.ts +180 -53
- package/src/agent/session-origin.ts +41 -2
- package/src/bundled-plugins/bun-hygiene/README.md +82 -0
- package/src/bundled-plugins/bun-hygiene/index.ts +11 -0
- package/src/bundled-plugins/bun-hygiene/policy.ts +318 -0
- package/src/bundled-plugins/github-cli-auth/gh-command.ts +98 -6
- package/src/bundled-plugins/github-cli-auth/graphql-auth-nudge.ts +80 -0
- package/src/bundled-plugins/github-cli-auth/index.ts +7 -0
- package/src/bundled-plugins/memory/memory-logger.ts +34 -12
- package/src/bundled-plugins/reviewer/skills/code-review.ts +8 -0
- package/src/channels/adapters/discord-bot.ts +8 -0
- package/src/channels/adapters/github/inbound.ts +23 -1
- package/src/channels/adapters/github/index.ts +9 -0
- package/src/channels/adapters/slack-bot.ts +112 -5
- package/src/channels/adapters/telegram-bot.ts +11 -0
- package/src/channels/manager.ts +8 -0
- package/src/channels/router.ts +100 -15
- package/src/channels/schema.ts +18 -0
- package/src/channels/types.ts +27 -0
- package/src/cli/dreams.ts +2 -1
- package/src/cli/inspect-controller.ts +92 -0
- package/src/cli/inspect.ts +21 -123
- package/src/cli/ui.ts +34 -0
- package/src/commands/index.ts +5 -2
- package/src/config/config.ts +89 -0
- package/src/inspect/index.ts +8 -26
- package/src/inspect/live.ts +17 -3
- package/src/inspect/loop.ts +23 -17
- package/src/mcp/catalog.ts +29 -0
- package/src/mcp/client.ts +236 -0
- package/src/mcp/index.ts +25 -0
- package/src/mcp/manager.ts +156 -0
- package/src/mcp/tools.ts +190 -0
- package/src/permissions/builtins.ts +9 -0
- package/src/reload/format.ts +14 -0
- package/src/reload/index.ts +1 -0
- package/src/run/bundled-plugins.ts +7 -0
- package/src/run/channel-session-factory.ts +3 -0
- package/src/run/index.ts +38 -1
- package/src/server/command-runner.ts +5 -0
- package/src/server/index.ts +4 -0
- package/src/skills/typeclaw-channel-github/SKILL.md +83 -13
- package/src/skills/typeclaw-config/SKILL.md +1 -1
- package/src/skills/typeclaw-git/SKILL.md +1 -1
- package/typeclaw.schema.json +82 -0
|
@@ -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 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.
|
|
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. When a review comment **you authored** gets addressed — the author pushed a fix or replied that resolves it — verify the fix at the PR's head SHA and then resolve the thread with the `resolveReviewThread` GraphQL mutation (see "Resolving review threads you authored" below); resolving is the close-out that tells the author the concern is settled. 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.
|
|
@@ -8,6 +8,7 @@ GitHub renders normal Markdown in issues, PRs, discussions, and review comments.
|
|
|
8
8
|
- Do not send attachments on GitHub chats; the adapter rejects them.
|
|
9
9
|
- There is no typing indicator.
|
|
10
10
|
- For PR review threads, keep `thread` set to reply in-place. Omit `thread` for a top-level PR/issue comment.
|
|
11
|
+
- When a review comment **you authored** has been addressed, resolve its thread — see "Resolving review threads you authored" below. The base principle is **whoever opened the thread closes it**: you resolve only the threads you started, never a human's.
|
|
11
12
|
|
|
12
13
|
## Mid-turn status replies need `continue: true`
|
|
13
14
|
|
|
@@ -17,15 +18,15 @@ A successful `channel_reply` ends your turn by default — the runtime stops the
|
|
|
17
18
|
|
|
18
19
|
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
20
|
|
|
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.**
|
|
21
|
+
| Inbound | Looks like | What to do |
|
|
22
|
+
| -------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| **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. |
|
|
24
|
+
| **Issue comment** (`issue:N`) | A comment on an issue. | Reply in the issue thread with `channel_reply`. |
|
|
25
|
+
| **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.** |
|
|
26
|
+
| **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. **If it addresses a comment you authored → verify and resolve the thread (below).** |
|
|
27
|
+
| **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`. |
|
|
28
|
+
| **New discussion / discussion comment** (`discussion:N`) | A discussion thread or a comment in one. | Reply with `channel_reply` on `discussion:N`. |
|
|
29
|
+
| **Review requested** (`pr:N`) | See "When you are being asked to review" below. | **PR review flow.** |
|
|
29
30
|
|
|
30
31
|
### When you are being asked to review
|
|
31
32
|
|
|
@@ -42,14 +43,28 @@ A `review_request_removed` inbound ("removed your review request on PR #N") is t
|
|
|
42
43
|
|
|
43
44
|
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
|
|
|
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.
|
|
46
|
+
1. **Confirm the target, and check whether you already reviewed it.** Capture the PR number, the repo, and the head SHA — you may need the SHA to read files at the revision the reviewer analyzed.
|
|
46
47
|
|
|
47
48
|
```sh
|
|
48
49
|
gh pr view <N> --repo owner/repo --json title,body,baseRefName,headRefOid,files
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
Then check for a **prior review by you** — this is what makes the current request a _re-review_ (the author pushed fixes and re-requested you after you previously blocked the PR):
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
gh api --paginate --slurp /repos/owner/repo/pulls/<N>/reviews --jq 'add | [.[] | select(.user.login == "<your-login>" and (.state == "CHANGES_REQUESTED" or .state == "APPROVED"))] | last | .state'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If that prints `CHANGES_REQUESTED`, treat the current request as a **re-review** and carry that fact into the spawn in step 2; any other output (including empty) means no live block, so handle the request normally. (`<your-login>` is your GitHub App login, typically `name[bot]`.)
|
|
59
|
+
|
|
60
|
+
Two things make this query load-bearing — both are bugs if you simplify it:
|
|
61
|
+
- **Filter to _decision_ states, not the latest review row.** GitHub's sticky block is cleared only by a later `APPROVED` (or a dismissal) from the same reviewer — a later `COMMENTED` review does **not** clear it. So a history of `CHANGES_REQUESTED` → `COMMENTED` is _still blocked_, even though the latest row is `COMMENTED`. Selecting `last` over the raw review list would misread that as "not a re-review". Filtering to `{CHANGES_REQUESTED, APPROVED}` first, then taking `last`, asks the right question: "what is my latest _blocking decision_, ignoring non-deciding comments?" (Dismissed reviews surface as `state: "DISMISSED"`, so they're correctly excluded from the decision set too.)
|
|
62
|
+
- **`--paginate --slurp` is mandatory.** GitHub returns reviews 30 per page; a bot on a long-lived PR can have its blocking `CHANGES_REQUESTED` past the first page. Without paginating, that review is invisible and a genuine re-review silently falls back to the plain-comment path. `--slurp` collects every page into one array of arrays; the `add` concatenates them before filtering.
|
|
63
|
+
|
|
51
64
|
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"`.
|
|
52
65
|
|
|
66
|
+
**If step 1 found a prior `CHANGES_REQUESTED` review, say so in the spawn payload** — e.g. _"This is a re-review: you previously requested changes on this PR (the prior blockers were …). Verify they are resolved and return `approve` or `request-changes` — a re-review must re-decide the blocking state, not return `comment`."_ The reviewer's `code-review` skill enforces the same rule, but telling it the prior verdict is what lets it apply that rule; a fresh reviewer session has no memory of your earlier review.
|
|
67
|
+
|
|
53
68
|
Do **not** post an "on it" acknowledgement comment before spawning the reviewer — the runtime already adds an :eyes: reaction to the PR the moment it engages, so a "looking into this" comment is redundant noise. Just spawn the reviewer with `run_in_background: true` and keep working; the formal review is your reply. If you want to acknowledge explicitly, use `channel_react({ emoji: "eyes" })`, which reacts without posting a comment.
|
|
54
69
|
|
|
55
70
|
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:
|
|
@@ -80,6 +95,19 @@ The `reviewer` subagent is the analyst; you are the integration layer between it
|
|
|
80
95
|
| `request-changes` | `REQUEST_CHANGES` |
|
|
81
96
|
| `comment` | `COMMENT` |
|
|
82
97
|
|
|
98
|
+
**Operator approval policy.** If the inbound carries a note that PR approval is disabled (`channels.github.review.approve: false` — the adapter appends "Operator policy: PR approval is disabled for this agent" to the message), you must **not** submit an `APPROVE`. Map an `approve` verdict to `COMMENT` instead: post the same `<summary>` and all inline `comments[]` as a `COMMENT` review, just without the formal approval. `request-changes` and `comment` verdicts are unaffected (they never approve). Absent that note, approval is enabled and the table above applies unchanged.
|
|
99
|
+
|
|
100
|
+
**Re-review.** If step 1 established this is a re-review (your latest blocking decision was `CHANGES_REQUESTED`), the result MUST clear or re-assert that block — never a top-level PR comment. On GitHub, `CHANGES_REQUESTED` is sticky: **only** a fresh `APPROVE` from you, or a dismissal of your prior review, clears it. A plain issue comment does **not** clear it, and — critically — **neither does a `COMMENT` review.** So even if the reviewer returns zero actionable findings, do **not** take the `comment` → top-level-comment branch below for a re-review. The reviewer's skill is instructed not to return `comment` on a re-review; if it does anyway despite a reachable diff, prefer `approve` when the prior blockers are visibly resolved in the diff, otherwise `request-changes` — and say which in your reasoning. Resolve the re-review by verdict:
|
|
101
|
+
- **`request-changes`** — submit a fresh `REQUEST_CHANGES` review (re-asserts the block with the new findings). Straightforward.
|
|
102
|
+
- **`approve`, approval enabled** — submit `APPROVE`. This clears the block.
|
|
103
|
+
- **`approve`, approval disabled (`channels.github.review.approve: false`)** — you cannot `APPROVE`, and a `COMMENT` review will **not** clear the sticky block, so the PR would stay blocked by your stale review. Clear it explicitly by **dismissing your own prior `CHANGES_REQUESTED` review**. Grab that review's `id` by re-running the step-1 query with the trailing filter changed from `| .state` to `| {state, id}` (same `select`), take the entry whose `state` is `CHANGES_REQUESTED`, then:
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
gh api -X PUT /repos/owner/repo/pulls/<N>/reviews/<review_id>/dismissals -f message="Blockers resolved; dismissing my prior changes request per operator approval-disabled policy." -f event=DISMISS
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This transitions your review to `DISMISSED` and unblocks the PR without an approval. It needs the bot's installation to have **write** access (or to be on the branch's "who can dismiss reviews" list); if the dismissal returns 403, the block cannot be cleared under this policy — post the `<summary>` as a `COMMENT` review and say plainly in the body that the prior changes-request stands until a human dismisses it, rather than implying the PR is unblocked.
|
|
110
|
+
|
|
83
111
|
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:
|
|
84
112
|
|
|
85
113
|
First write `/tmp/review.json` (via the `write` tool, not bash):
|
|
@@ -126,12 +154,54 @@ The `reviewer` subagent is the analyst; you are the integration layer between it
|
|
|
126
154
|
|
|
127
155
|
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:
|
|
128
156
|
|
|
129
|
-
- `approve` → post a plain `APPROVE` with the `<summary>` as the review body (no `comments[]` array).
|
|
130
|
-
- `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.
|
|
157
|
+
- `approve` → post a plain `APPROVE` with the `<summary>` as the review body (no `comments[]` array). **If the operator approval policy above disabled approval, submit a `COMMENT` review instead — same `<summary>` as the review body, `event: "COMMENT"`, no `comments[]` array. Keep it a formal review, not a top-level issue comment, so the review metadata and flow are preserved.** (Re-review caveat: a `COMMENT` review does **not** clear a sticky `CHANGES_REQUESTED` block. If this is a re-review under approval-disabled policy, follow the step-4 re-review branch — dismiss your prior review — instead of relying on this `COMMENT`.)
|
|
158
|
+
- `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. **Exception — re-reviews:** if this is a re-review (your latest blocking decision was `CHANGES_REQUESTED`), a top-level comment does not clear the sticky block. Do not use this branch; resolve it via the step-4 re-review branch (`APPROVE` if resolved and approval is enabled, the dismissal endpoint if resolved but approval is disabled, `REQUEST_CHANGES` if not resolved).
|
|
131
159
|
- `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.
|
|
132
160
|
|
|
133
161
|
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.
|
|
134
162
|
|
|
163
|
+
## Resolving review threads you authored
|
|
164
|
+
|
|
165
|
+
A review you posted leaves inline comment threads open on the PR. When one of **your** threads is addressed — the author pushed a fix, or replied that they handled it — close it out by **resolving the thread**. Leaving it open after the concern is settled reads as if you never noticed; a resolved thread is the signal that the loop is closed.
|
|
166
|
+
|
|
167
|
+
**The base principle: whoever opened the thread closes it.** Resolve only threads whose root comment **you** authored. Never resolve a human reviewer's thread on your behalf — that erases their open question. The thread you can resolve is the one you started; the inbound that brings you here is a **review-thread reply on `pr:N` with `thread` set**, replying inside a thread you opened.
|
|
168
|
+
|
|
169
|
+
### When a thread counts as addressed
|
|
170
|
+
|
|
171
|
+
Do not resolve on a bare "done" claim. A reply that says "fixed" is a prompt to check, not proof. Before resolving, **verify the fix at the PR's current head SHA**:
|
|
172
|
+
|
|
173
|
+
1. Re-read the PR head: `gh pr view <N> --repo owner/repo --json headRefOid` gives you the SHA the author's latest push landed on.
|
|
174
|
+
2. Read the lines your comment anchored to, at that SHA: `gh api /repos/owner/repo/contents/<path>?ref=<headRefOid>` (or `gh pr diff <N>` to see what the new push changed). Confirm the change actually addresses the concern your comment raised — not a different line, not a partial fix.
|
|
175
|
+
3. Only when the code at head genuinely resolves the finding do you resolve the thread. If the fix is partial or misses the point, **reply in the thread** explaining what's still open and leave it unresolved.
|
|
176
|
+
|
|
177
|
+
If the author merely **replied** without pushing (e.g. "this is intentional because …") and their reasoning settles it, that is also "addressed" — **resolve first, then optionally leave a one-line acknowledgement.** Order matters: a bare `channel_reply` ends your turn (see "Mid-turn status replies need `continue: true`" above), so acknowledging _before_ you resolve would stop the turn and the `resolveReviewThread` mutation would never run, leaving the thread open. Resolve, then reply. If you genuinely want to acknowledge before resolving, the acknowledgement must use `channel_reply({ …, continue: true })` so the turn survives long enough to resolve. If their reasoning does **not** settle it, keep the thread open and answer.
|
|
178
|
+
|
|
179
|
+
### How to resolve — `resolveReviewThread` GraphQL mutation
|
|
180
|
+
|
|
181
|
+
There is no REST endpoint for this. Resolution is a GraphQL mutation that takes the thread's **node id** (`PRRT_…`), not the comment's numeric id. Two steps: find the thread id, then resolve it.
|
|
182
|
+
|
|
183
|
+
1. **Find the node id of the thread you authored.** Query the PR's review threads and pick the one whose root comment is yours and matches the `thread` you're replying in:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
gh api graphql -f query='query($owner:String!,$name:String!,$number:Int!,$after:String){repository(owner:$owner,name:$name){pullRequest(number:$number){reviewThreads(first:100,after:$after){pageInfo{hasNextPage endCursor}nodes{id isResolved comments(first:1){nodes{databaseId author{login}}}}}}}}' -F owner=OWNER -F name=REPO -F number=N
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Match on the root comment: its `comments.nodes[0].databaseId` equals the root comment id (the `thread` value the inbound carried), and `author.login` is you. Skip threads already `isResolved: true`.
|
|
190
|
+
|
|
191
|
+
**Paginate until you find the match — `first:100` is one page, not all threads.** A busy PR can carry more than 100 review threads, and yours may sit past the first page; stopping at page one would silently miss it and leave your thread open. Omit `-F after=…` on the first call, then while `pageInfo.hasNextPage` is true and you have not yet matched the `databaseId`, re-run the same query with `-F after=<endCursor>` from the previous page. Stop the moment the target thread is found (no need to walk the rest) or when `hasNextPage` is false (the thread is genuinely absent — don't fabricate a node id).
|
|
192
|
+
|
|
193
|
+
2. **Resolve it** with the node id from step 1:
|
|
194
|
+
|
|
195
|
+
```sh
|
|
196
|
+
gh api graphql -f query='mutation($threadId:ID!){resolveReviewThread(input:{threadId:$threadId}){thread{id isResolved}}}' -F threadId=PRRT_xxx
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The returned `isResolved: true` is your proof it landed. As with every repo-targeting `gh` call, this is a **single bare `gh` invocation** — no pipes, `;`, `&&`, heredocs, or command substitution (the `github-cli-auth` plugin injects the App token into the command's environment; a pipeline would leak it). `-F` passes the id as a typed variable, so there is no shell-metacharacter hazard for the simple id/number values here.
|
|
200
|
+
|
|
201
|
+
### Self-loop safety — resolving never wakes you
|
|
202
|
+
|
|
203
|
+
Resolving your own thread is safe from the self-response loop. The `pull_request_review_thread.resolved` webhook that GitHub emits carries **you** as its `sender`, and the inbound classifier maps `pull_request_review_thread` events to their `sender` (not the PR opener) for the self-author drop — so the bot resolving a thread is recognized as self-authored and dropped, exactly like the decoy-reviewer cleanup in the PR review flow. You will not be re-woken by your own resolution. See "Self-loop safety" below.
|
|
204
|
+
|
|
135
205
|
## Opening new issues and PRs
|
|
136
206
|
|
|
137
207
|
The `gh` CLI is pre-authenticated via `GH_TOKEN` (injected by the adapter at startup). Use it to open new issues or PRs:
|
|
@@ -434,7 +434,7 @@ The toggle-driven apt install benefits from BuildKit `--mount=type=cache` on `/v
|
|
|
434
434
|
|
|
435
435
|
## Gitignore
|
|
436
436
|
|
|
437
|
-
`typeclaw start` rewrites the agent folder's `.gitignore` from a template baked into the typeclaw CLI on **every** invocation, then auto-commits it when the agent folder is a git repo and the file changed. The template protects two categories: truly-ignored paths (`secrets.json`, `.env`, `.env.local`, `auth.json`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) and system-managed runtime state (`sessions/`, `memory
|
|
437
|
+
`typeclaw start` rewrites the agent folder's `.gitignore` from a template baked into the typeclaw CLI on **every** invocation, then auto-commits it when the agent folder is a git repo and the file changed. The template protects two categories: truly-ignored paths (`secrets.json`, `.env`, `.env.local`, `auth.json`, `node_modules/`, `workspace/`, `mounts/`, `channels/`, `Dockerfile`, `.DS_Store`) and system-managed runtime state (`sessions/`, `memory/`) that TypeClaw, not the agent, commits on its own schedule. Editing `.gitignore` by hand is temporary; the next `typeclaw start` overwrites it.
|
|
438
438
|
|
|
439
439
|
The `git.ignore.append` field (introduced when the legacy top-level `gitignore` key was nested under the `git` namespace for future extensibility — see **Legacy migration**) is the supported escape hatch for additional local ignore patterns. It is an array of strings, each treated as a single `.gitignore` line. The CLI splices them into the autogenerated `.gitignore` before TypeClaw's protected rules, prefixed with a `# Custom entries from typeclaw.json#git.ignore.append.` comment.
|
|
440
440
|
|
|
@@ -10,7 +10,7 @@ Your agent folder is a git repo. Almost every file in it (`typeclaw.json`, `cron
|
|
|
10
10
|
The contents of `.gitignore` split into two distinct categories — the distinction matters for this skill:
|
|
11
11
|
|
|
12
12
|
- **Truly ignored** (`secrets.json`, `.env`, `node_modules/`, `workspace/`, `mounts/`, `Dockerfile`, `.DS_Store`) — never in history, ever. Secrets, runtime junk, your free-write zone, and regenerated-on-start system files.
|
|
13
|
-
- **System-managed** (`sessions/`, `memory
|
|
13
|
+
- **System-managed** (`sessions/`, `memory/`) — gitignored so _you_ don't stage them, but TypeClaw force-commits them on its own schedule. `sessions/` is auto-backed up by the runtime; `memory/` is committed by the dreaming subagent. Treat them as runtime-owned: do not `git add` them, do not write commit messages about them, and do not be alarmed when they appear in `git log`. (`channels/` is also gitignored, but it is _not_ force-committed — it's runtime-owned local state that stays out of history entirely.)
|
|
14
14
|
|
|
15
15
|
Everything not in either bucket is yours to commit.
|
|
16
16
|
|
package/typeclaw.schema.json
CHANGED
|
@@ -104,6 +104,76 @@
|
|
|
104
104
|
]
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"type": "array",
|
|
109
|
+
"items": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"properties": {
|
|
112
|
+
"name": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"pattern": "^[a-z0-9][a-z0-9-_]*$"
|
|
115
|
+
},
|
|
116
|
+
"description": {
|
|
117
|
+
"type": "string"
|
|
118
|
+
},
|
|
119
|
+
"enabled": {
|
|
120
|
+
"default": true,
|
|
121
|
+
"type": "boolean"
|
|
122
|
+
},
|
|
123
|
+
"timeoutMs": {
|
|
124
|
+
"type": "integer",
|
|
125
|
+
"exclusiveMinimum": 0,
|
|
126
|
+
"maximum": 600000
|
|
127
|
+
},
|
|
128
|
+
"command": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"minLength": 1
|
|
131
|
+
},
|
|
132
|
+
"args": {
|
|
133
|
+
"default": [],
|
|
134
|
+
"type": "array",
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "string"
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"url": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"format": "uri"
|
|
142
|
+
},
|
|
143
|
+
"env": {
|
|
144
|
+
"type": "object",
|
|
145
|
+
"propertyNames": {
|
|
146
|
+
"type": "string",
|
|
147
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
|
|
148
|
+
},
|
|
149
|
+
"additionalProperties": {
|
|
150
|
+
"anyOf": [
|
|
151
|
+
{
|
|
152
|
+
"type": "string",
|
|
153
|
+
"minLength": 1
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"type": "object",
|
|
157
|
+
"properties": {
|
|
158
|
+
"value": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"minLength": 1
|
|
161
|
+
},
|
|
162
|
+
"env": {
|
|
163
|
+
"type": "string",
|
|
164
|
+
"minLength": 1
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
"required": [
|
|
173
|
+
"name"
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
},
|
|
107
177
|
"plugins": {
|
|
108
178
|
"default": [],
|
|
109
179
|
"type": "array",
|
|
@@ -475,6 +545,18 @@
|
|
|
475
545
|
"items": {
|
|
476
546
|
"type": "string"
|
|
477
547
|
}
|
|
548
|
+
},
|
|
549
|
+
"review": {
|
|
550
|
+
"default": {
|
|
551
|
+
"approve": true
|
|
552
|
+
},
|
|
553
|
+
"type": "object",
|
|
554
|
+
"properties": {
|
|
555
|
+
"approve": {
|
|
556
|
+
"default": true,
|
|
557
|
+
"type": "boolean"
|
|
558
|
+
}
|
|
559
|
+
}
|
|
478
560
|
}
|
|
479
561
|
}
|
|
480
562
|
},
|