typeclaw 0.34.1 → 0.35.1

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 (39) hide show
  1. package/package.json +3 -1
  2. package/src/agent/plugin-tools.ts +71 -13
  3. package/src/agent/provider-error.ts +10 -0
  4. package/src/agent/session-origin.ts +26 -0
  5. package/src/agent/tools/channel-disengage.ts +13 -9
  6. package/src/bundled-plugins/github-cli-auth/gh-command.ts +124 -6
  7. package/src/bundled-plugins/github-cli-auth/git-command.ts +172 -26
  8. package/src/bundled-plugins/github-cli-auth/index.ts +46 -7
  9. package/src/bundled-plugins/github-cli-auth/token-class.ts +13 -0
  10. package/src/bundled-plugins/security/policies/prompt-injection.ts +33 -2
  11. package/src/channels/adapters/github/inbound.ts +41 -3
  12. package/src/channels/adapters/slack-bot.ts +17 -9
  13. package/src/channels/continuation-willingness.ts +331 -0
  14. package/src/channels/github-review-claim.ts +105 -0
  15. package/src/channels/github-token-bridge.ts +7 -0
  16. package/src/channels/router.ts +103 -24
  17. package/src/cli/channel.ts +102 -11
  18. package/src/cli/qr.ts +130 -0
  19. package/src/config/config.ts +98 -2
  20. package/src/container/start.ts +12 -0
  21. package/src/init/dockerfile.ts +64 -0
  22. package/src/init/line-auth.ts +8 -3
  23. package/src/inspect/live.ts +128 -13
  24. package/src/plugin/context.ts +5 -1
  25. package/src/plugin/manager.ts +2 -0
  26. package/src/plugin/types.ts +1 -0
  27. package/src/run/index.ts +1 -0
  28. package/src/sandbox/availability.ts +87 -19
  29. package/src/sandbox/build.ts +27 -0
  30. package/src/sandbox/index.ts +10 -0
  31. package/src/sandbox/package-install.ts +23 -0
  32. package/src/sandbox/policy.ts +31 -0
  33. package/src/sandbox/symlinks.ts +34 -0
  34. package/src/sandbox/writable-zones.ts +164 -4
  35. package/src/server/index.ts +5 -1
  36. package/src/shared/protocol.ts +22 -11
  37. package/src/skills/typeclaw-channel-github/SKILL.md +4 -2
  38. package/src/skills/typeclaw-github-contributing/SKILL.md +124 -0
  39. package/typeclaw.schema.json +32 -1
@@ -145,7 +145,7 @@ The `reviewer` subagent is the analyst; you are the integration layer between it
145
145
 
146
146
  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:
147
147
 
148
- First write `/tmp/review-<N>.json` (via the `write` tool, not bash) — `/tmp` is per-session scratch, and the `<N>` keeps concurrent reviews in one session from colliding:
148
+ First write `/tmp/review-<N>.json` (via the `write` tool, not bash) — `/tmp` is per-session scratch, and the `<N>` keeps concurrent reviews in one session from colliding. **Every `gh api --input` payload — review JSON, dismissal JSON, top-level issue-comment JSON — is throwaway scratch and MUST be written under `/tmp/`, never the workspace/agent folder.** A path with no leading `/tmp/` (e.g. a bare `review.json`, `review_comment.json`, or `review-<N>-approve.json`) lands in the agent root and gets force-committed by the backup loop, littering the repo with review payloads. Always prefix the path with `/tmp/`; never write a `*.json` scratch file to the workspace:
149
149
 
150
150
  ```json
151
151
  {
@@ -190,7 +190,7 @@ The `reviewer` subagent is the analyst; you are the integration layer between it
190
190
  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:
191
191
 
192
192
  - `approve` → post a plain `APPROVE` with the `<summary>` as the review body (no `comments[]` array). **Post the `<summary>` verbatim — do not pad it back into a play-by-play.** The reviewer's contract already makes the summary a terse, author-facing verdict justification (no process narration, no "I loaded the X skill", no recap of what the PR does); your job is to forward it, not re-expand it. **This is still a formal review via `POST /pulls/<N>/reviews`, NOT a `channel_reply`.** A zero-findings approval is the single most common place this goes wrong: with nothing to anchor inline, the model is tempted to just `channel_reply({ text: "Approved …" })` and end the turn. That posts a plain PR comment and leaves the PR **"awaiting review"** with no approval — the verdict never reaches GitHub's review API. Never start a top-level `channel_reply` on a `pr:N` with "Approved" / "LGTM" / "Request changes": those are verdicts, and verdicts are always formal reviews. Submit the `APPROVE` via `gh api`, confirm it landed (step 5), then `skip_response`. **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`.)
193
- - `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 (you have an unresolved blocking obligation — a formal `CHANGES_REQUESTED` **or** an unretracted flat-comment blocker), a top-level comment discharges neither. 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 a formal block is resolved but approval is disabled, `REQUEST_CHANGES` if not resolved).
193
+ - `comment` → post the summary as a top-level PR comment via `gh api -X POST /repos/.../issues/<N>/comments`, feeding the body from a `/tmp/` scratch file: write `/tmp/review-comment-<N>.json` (e.g. `{ "body": "<summary>" }`) with the `write` tool, then `gh api -X POST /repos/owner/repo/issues/<N>/comments --input /tmp/review-comment-<N>.json`. As with the review payload, this scratch file goes under `/tmp/`, **never** a bare `review_comment.json` in the workspace — the file-then-`--input` shape also keeps backticks/newlines in the summary from being mangled by shell parsing. Submit an empty review instead of this comment only when a formal review is required. **Exception — re-reviews:** if this is a re-review (you have an unresolved blocking obligation — a formal `CHANGES_REQUESTED` **or** an unretracted flat-comment blocker), a top-level comment discharges neither. 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 a formal block is resolved but approval is disabled, `REQUEST_CHANGES` if not resolved).
194
194
  - `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.
195
195
 
196
196
  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.
@@ -272,6 +272,8 @@ gh pr create --repo owner/repo --title "Fix: ..." --head my-branch --base main -
272
272
 
273
273
  For App auth, `GH_TOKEN` is an installation access token that refreshes automatically — it stays current as long as the adapter is running.
274
274
 
275
+ Before you compose the issue/PR body, read `typeclaw-github-contributing` — it covers the target repo's contribution etiquette (fill the issue/PR template if one exists, honor `CONTRIBUTING.md`, match the repo's title conventions, search for duplicates first). Opening an issue or PR that ignores the repo's template reads as careless; following it reads as someone who belongs. That skill applies whenever you open a new issue/PR, whether or not the work arrived through this channel.
276
+
275
277
  ## Self-loop safety
276
278
 
277
279
  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.
@@ -0,0 +1,124 @@
1
+ ---
2
+ name: typeclaw-github-contributing
3
+ description: Use this skill BEFORE you open a new issue or pull request on GitHub with `gh issue create` / `gh pr create` (or before composing the `--body`/`--title` for one, or editing an issue/PR body after the fact). Triggers include any `gh issue create`, `gh pr create`, `gh api … /issues`, `gh api … /pulls`, any time you are about to file a bug report, feature request, or PR against a repo — yours or someone else's — and any phrasing like "open an issue", "file a bug", "raise a PR", "submit a pull request". Read it first, because every repo has its own contribution rules — an issue/PR template, a CONTRIBUTING.md, a title convention — and a maintainer's first impression of your contribution is whether you bothered to follow them. Ignoring a template that the repo author wrote on purpose reads as careless; following it reads as someone who belongs. This is platform etiquette, independent of how the work reached you (it applies whether the request came through the github channel or you decided to file on your own).
4
+ ---
5
+
6
+ # typeclaw-github-contributing
7
+
8
+ When you open an issue or a PR on GitHub, you are writing into **someone else's repository** — even when it's nominally "yours," it usually has collaborators, a history, and conventions that predate this turn. The repo's maintainers encoded what they want from a contribution in a few well-known files. Honoring them is not bureaucracy; it's the difference between a contribution that gets triaged and merged and one that gets a "please use the template" and sits.
9
+
10
+ This skill is the **etiquette of contributing to a GitHub repo**. It is not about replying to inbound GitHub events or running PR reviews — that's `typeclaw-channel-github`. It is not about committing to your own agent folder — that's `typeclaw-git`. It is specifically about _what you put on GitHub the moment you open a new issue or PR._
11
+
12
+ ## The rule
13
+
14
+ **Before you open an issue or PR, find the repo's contribution conventions and follow them. If a template exists, fill it — don't bypass it. If a CONTRIBUTING file exists, read it and honor it. Match the title style the repo already uses.**
15
+
16
+ The principle underneath all five rules below: **the repo already told you how it wants contributions; your job is to look before you write, not to impose your defaults.**
17
+
18
+ ## Do this first — read the repo before you write
19
+
20
+ A contribution composed from your defaults and one composed from the repo's conventions look completely different to a maintainer. The five-second check that separates them:
21
+
22
+ **Templates do not live in one fixed spot — GitHub resolves them from several.** Probing only `.github/PULL_REQUEST_TEMPLATE.md` is the trap: a repo that keeps its template at the root, under `docs/`, or in a `PULL_REQUEST_TEMPLATE/` directory will look template-less to that one check, and you'll bypass a convention the maintainer actually set. GitHub looks in **three base locations** — the repo root, `docs/`, and `.github/` — and the filename is **case-insensitive** (`PULL_REQUEST_TEMPLATE.md`, `pull_request_template.md`). It also supports a **`PULL_REQUEST_TEMPLATE/` _directory_** of multiple named templates (same three base locations). Issue templates follow the same pattern: a single `.github/ISSUE_TEMPLATE.md`, or — far more common now — an `ISSUE_TEMPLATE/` directory of forms (`*.md` / `*.yml`).
23
+
24
+ The cheapest reliable check is to list each base directory once and scan the names, rather than guessing exact filenames:
25
+
26
+ ```sh
27
+ # PR template — scan all three base dirs for any case of the file OR a PULL_REQUEST_TEMPLATE/ dir.
28
+ # (Listing the dir surfaces both the single-file and the multi-template-directory forms at once.)
29
+ for base in "" "docs/" ".github/"; do
30
+ gh api "repos/OWNER/REPO/contents/${base}" --jq '.[].name' 2>/dev/null \
31
+ | grep -iE '^pull_request_template(\.md)?$'
32
+ done
33
+ # If a PULL_REQUEST_TEMPLATE/ directory exists in ANY base, list the choices inside it
34
+ # (the directory form is supported at root, docs/, and .github/ alike):
35
+ for base in "" "docs/" ".github/"; do
36
+ gh api "repos/OWNER/REPO/contents/${base}PULL_REQUEST_TEMPLATE" --jq '.[].name' 2>/dev/null
37
+ done
38
+
39
+ # Issue templates — the modern form is an ISSUE_TEMPLATE/ directory of forms;
40
+ # the legacy form is a single ISSUE_TEMPLATE.md. Check both, in all three base dirs.
41
+ for base in "" "docs/" ".github/"; do
42
+ gh api "repos/OWNER/REPO/contents/${base}ISSUE_TEMPLATE" --jq '.[].name' 2>/dev/null # directory of forms
43
+ gh api "repos/OWNER/REPO/contents/${base}" --jq '.[].name' 2>/dev/null \
44
+ | grep -iE '^issue_template(\.md)?$' # single legacy file
45
+ done
46
+
47
+ # Contribution guide (root or .github/)
48
+ gh api repos/OWNER/REPO/contents/CONTRIBUTING.md 2>/dev/null
49
+ gh api repos/OWNER/REPO/contents/.github/CONTRIBUTING.md 2>/dev/null
50
+
51
+ # What do existing titles look like?
52
+ gh pr list --repo OWNER/REPO --state all --limit 20 --json title --jq '.[].title'
53
+ gh issue list --repo OWNER/REPO --state all --limit 20 --json title --jq '.[].title'
54
+
55
+ # Is this a duplicate?
56
+ gh issue list --repo OWNER/REPO --search "<keywords from what you're about to file>" --state all
57
+ ```
58
+
59
+ A `grep` that matches **nothing across all three base dirs** is your evidence there is genuinely no template — a single 404 on `.github/PULL_REQUEST_TEMPLATE.md` is not. You don't need every command every time, but you do need to _look in all the supported places_ before concluding "no template" and composing from your own defaults. The cost is a few API calls; the cost of skipping it is bypassing a convention the repo set on purpose — the exact failure this skill exists to prevent.
60
+
61
+ ## The five rules
62
+
63
+ ### 1. Fill the issue/PR template if one exists
64
+
65
+ If the discovery scan above surfaced a template in **any** of the supported locations (root, `docs/`, or `.github/` — single file or directory), the maintainers want every issue/PR to follow that shape. Fetch its content, read its sections, and produce a `--body` that fills each one with real content.
66
+
67
+ - For PRs, the template often has a checklist ("- [ ] tests added", "- [ ] docs updated"). Fill the prose sections; for checkboxes, check only what is genuinely true and leave the rest unchecked — don't tick a box you can't back up.
68
+ - A **`PULL_REQUEST_TEMPLATE/` directory** holds more than one PR template; pick the one that matches your change and fill it. (GitHub can also select one via a `?template=` URL param, but when you're filing through `gh` you choose by reading the directory and using the right file as your body.)
69
+ - An **`ISSUE_TEMPLATE/` directory** likewise holds multiple issue forms (bug report, feature request, etc.) — pick the one that matches what you're filing. A bug filed against the feature-request template is noise. Forms may be YAML (`.yml` issue forms) rather than markdown; translate their fields into a sensible body, or use the matching markdown template if one is offered.
70
+ - A repo with genuinely no template (the scan matched nothing in all three base dirs) gives you latitude — but a clear, scannable body (what / why / how, repro steps for bugs) is still the courteous default.
71
+
72
+ ### 2. Don't bypass the template to file faster
73
+
74
+ This is rule 1's hard edge and the failure this skill most exists to prevent. When a template has required sections or a checklist, **fill or honor them — do not delete them to save effort.** Stripping the template, leaving placeholder text (`<!-- describe your change -->`) un-replaced, or dumping a one-liner where the template asked for repro steps all read as "I didn't bother." If a section genuinely doesn't apply, say so explicitly ("N/A — no user-facing change") rather than silently removing it; the maintainer can tell the difference between _considered and skipped_ and _ignored_.
75
+
76
+ ### 3. Follow CONTRIBUTING.md
77
+
78
+ If the repo has a `CONTRIBUTING.md` (root or `.github/`), read it before you open anything. It encodes rules the templates can't — branch naming, whether PRs go against `main` or `develop`, whether a linked issue is required first, commit sign-off (DCO), the expected PR description format, "open an issue before a large PR," and so on. These are the maintainers' actual process; violating them is the most common reason a well-intentioned PR gets bounced. Honor what it says even when it differs from your habits.
79
+
80
+ ### 4. Match the repo's title conventions
81
+
82
+ A repo's existing issue/PR titles tell you the house style. Pull the last ~20 (commands above) and infer the pattern, then match it:
83
+
84
+ - **Conventional-commit prefixes** (`feat:`, `fix:`, `docs(scope):`) — if the PR list is full of them, your PR title uses one too.
85
+ - **Ticket/issue references** (`[PROJ-123]`, `(#456)`) — if titles routinely carry them and you have a ref, include it.
86
+ - **Sentence case vs. lowercase, imperative mood** — small things, but matching them signals you read the log.
87
+
88
+ If there's no discernible pattern, a clear imperative summary (`Fix race in port allocation`) is the safe default. The point is never to invent your own scheme on top of an established one.
89
+
90
+ ### 5. Search for duplicates before opening
91
+
92
+ Before filing an issue, search existing issues (open _and_ closed — a closed one may carry the resolution or the maintainer's "won't fix" rationale). If a matching thread exists:
93
+
94
+ - **Open and relevant** → add your context as a comment on that thread instead of opening a duplicate. Duplicates fragment the discussion and annoy triagers.
95
+ - **Closed as resolved** → check whether the fix is in the version you're on before re-filing; if it's a regression, reference the old issue in your new one.
96
+
97
+ The same applies to PRs: a quick `gh pr list --search` avoids opening a PR for something already in flight.
98
+
99
+ ## Workflow
100
+
101
+ 1. **Identify the target repo** (`OWNER/REPO`) and whether you're filing an issue or a PR.
102
+ 2. **Read the room** — run the checks in "Do this first": templates, CONTRIBUTING, existing titles, duplicate search.
103
+ 3. **Compose to the conventions** — fill the template, honor CONTRIBUTING, match the title style. Write the body to a file when it's long or contains backticks/markdown that shell-quoting would mangle, then pass it with `--body-file`.
104
+ 4. **Open it** with `gh`:
105
+ ```sh
106
+ gh issue create --repo OWNER/REPO --title "<conventional title>" --body-file /tmp/issue-body.md
107
+ gh pr create --repo OWNER/REPO --title "<conventional title>" --body-file /tmp/pr-body.md --base <branch>
108
+ ```
109
+ 5. **Verify it landed** as intended (`gh issue view` / `gh pr view`) — confirm the template rendered and nothing got truncated.
110
+
111
+ ## Things you must not do
112
+
113
+ - **Do not open an issue/PR without checking for a template.** A repo that ships a template wants it used; skipping the check is how you end up filing against conventions you never looked at.
114
+ - **Do not strip, gut, or placeholder-leave a template** to file faster. Fill it, or explicitly mark sections N/A. An empty template body is worse than a thoughtful free-form one.
115
+ - **Do not ignore CONTRIBUTING.md** because its rules differ from your defaults. Its rules win in its repo.
116
+ - **Do not invent a title scheme** when the repo already has one. Match what's there.
117
+ - **Do not file a duplicate** without searching first. Comment on the existing thread instead.
118
+ - **Do not tick checklist boxes you can't back up.** A checked "tests added" with no tests is a false claim the reviewer will catch.
119
+
120
+ ## What this skill does not cover
121
+
122
+ - **Replying to inbound GitHub events, and running PR reviews** — `typeclaw-channel-github`. That skill owns triage of github-channel inbounds, formal reviews via the reviews API, and resolving review threads. This skill owns only the act of _opening_ a new issue/PR.
123
+ - **Committing to your own agent folder** — `typeclaw-git`. Local commit hygiene and decision-context messages live there; this skill is about GitHub artifacts, not your local history.
124
+ - **The `gh` CLI's full surface** — auth, sub-commands, flags. Defer to `gh <command> --help`. Under the github channel adapter, `GH_TOKEN` is pre-injected; see `typeclaw-channel-github` for the single-bare-invocation constraint that applies to repo-targeting `gh` calls (no pipes, `;`, `&&`, heredocs, or command substitution).
@@ -1310,13 +1310,44 @@
1310
1310
  },
1311
1311
  "sandbox": {
1312
1312
  "default": {
1313
- "realProc": false
1313
+ "realProc": false,
1314
+ "writablePaths": [],
1315
+ "symlinks": []
1314
1316
  },
1315
1317
  "type": "object",
1316
1318
  "properties": {
1317
1319
  "realProc": {
1318
1320
  "default": false,
1319
1321
  "type": "boolean"
1322
+ },
1323
+ "writablePaths": {
1324
+ "default": [],
1325
+ "type": "array",
1326
+ "items": {
1327
+ "type": "string",
1328
+ "minLength": 1
1329
+ }
1330
+ },
1331
+ "symlinks": {
1332
+ "default": [],
1333
+ "type": "array",
1334
+ "items": {
1335
+ "type": "object",
1336
+ "properties": {
1337
+ "from": {
1338
+ "type": "string",
1339
+ "minLength": 1
1340
+ },
1341
+ "to": {
1342
+ "type": "string",
1343
+ "minLength": 1
1344
+ }
1345
+ },
1346
+ "required": [
1347
+ "from",
1348
+ "to"
1349
+ ]
1350
+ }
1320
1351
  }
1321
1352
  }
1322
1353
  },