{"id":"add-linear","name":"add-linear","summary":"Chat SDKによるリニアチャネル統合を追加してください。コメントスレッドを会話として発信してください。","body":"# Add Linear Channel\n\nAdds Linear support via the Chat SDK bridge. The agent participates in issue\ncomment threads. Every comment on a Linear issue triggers the agent — no\n@-mention needed. NanoClaw doesn't ship channels in trunk — this skill copies the\nLinear adapter in from the `channels` branch.\n\nThe mechanical steps under **Apply** carry `nc:` directive fences: an agent reads\nthe prose and applies them, and a parser can apply them deterministically from\nthe same document. Every directive is idempotent, so the whole skill is safe to\nre-run; anything a parser can't apply falls back to the prose beside it.\n\n## Prerequisites\n\n**Recommended:** Create a Linear **OAuth application** so the agent posts as an app identity, not as you. This prevents the adapter from filtering your own comments as self-messages.\n\n1. Go to [Linear Settings > API > OAuth Applications](https://linear.app/settings/api/applications/new)\n2. Create an app (e.g. \"NanoClaw Bot\")\n   - Developer URL: your repo URL (e.g. `https://github.com/your-org/nanoclaw`)\n   - Callback URL: `http://localhost`\n3. After creating, click the app and enable **Client credentials** under grant types\n4. Copy the **Client ID** and **Client Secret**\n\n**Alternative:** Use a Personal API Key (`LINEAR_API_KEY`) for simpler setup. The agent will post as you, and your own comments will be filtered (other team members' comments still work).\n\n## Apply\n\nLinear OAuth apps post and read comments under an app identity that can't be\n@-mentioned; the adapter's declared channel defaults therefore respond to plain\ncomments rather than mention-only, and the wiring below sets that same pattern\nmode explicitly.\n\n### 1. Copy the adapter and its registration test\n\nFetch the `channels` branch and copy the Linear adapter and its registration\ntest into `src/channels/` (overwrite — the branch is canonical):\n\n```nc:copy from-branch:channels\nsrc/channels/linear.ts\nsrc/channels/linear-registration.test.ts\n```\n\n### 2. Register the adapter\n\nAppend the self-registration import to the channel barrel (skipped if the line\nis already present). This one line is the skill's only reach-in into the channel\nregistry:\n\n```nc:append to:src/channels/index.ts\nimport './linear.js';\n```\n\n### 3. Install the adapter package\n\nPinned to an exact version — the supply-chain policy rejects ranges and `latest`:\n\n```nc:dep\n@chat-adapter/linear@4.29.0\n```\n\n### 4. Build and validate\n\nBuild first: it guards the typed `createChatSdkBridge(...)` core call and proves\nthe dependency is installed. Then run the one integration test.\n\n```nc:run effect:build\npnpm run build\n```\n```nc:run effect:test\npnpm exec vitest run src/channels/linear-registration.test.ts\n```\n\nBoth must be clean before proceeding. `linear-registration.test.ts` imports the\nreal channel barrel and asserts the registry contains `linear`. It goes red if\nthe `import './linear.js';` line is deleted or drifts, if the barrel fails to\nevaluate, or if `@chat-adapter/linear` isn't installed (the import throws) — so\nit also covers the dependency from step 3. End-to-end message delivery against a\nreal Linear workspace is verified manually once the service is running — see\nWiring and Next Steps.\n\n## Credentials\n\nLinear app and webhook setup is human and interactive — these steps are prose\n(no parser can click through the Linear UI), except the final env write.\n\n### 1. Set up a webhook\n\n1. Go to **Linear Settings** > **API** > **Webhooks** > **New webhook**\n2. Label: `NanoClaw`\n3. URL: `https://your-domain/webhook/linear` (the shared webhook server, default port 3000)\n4. Team: select the team you want to monitor\n5. Events: check **Comment**\n6. Save — copy the **signing secret**\n\nNote: Linear webhook delivery may be delayed 1-5 minutes for new webhooks. This is normal.\n\n### 2. Store the credentials\n\nCapture the values, then write them. `prompt` only *asks* and binds the answer\nto a name; a separate directive consumes it. Here they go to `.env`\n(set-if-absent — a value you've already filled in is never overwritten) and sync\nto the container.\n\nUse **either** the OAuth app credentials (recommended) **or** a Personal API key.\nFor the API-key path, paste `none` at the OAuth prompts and set `LINEAR_API_KEY`\nin `.env` by hand (commented in the template below). `LINEAR_BOT_USERNAME` is the\ndisplay name for the bot, used for self-message detection when using a Personal\nAPI Key. `LINEAR_TEAM_KEY` is the Linear team key (e.g. `ENG`, `NAN`) — find it\nin Linear under Settings > Teams; all issues in this team route to one messaging\ngroup.\n\n```nc:prompt linear_client_id secret\nPaste the OAuth Client ID — Linear Settings > API > OAuth Applications. Paste `none` if using a Personal API key instead.\n```\n```nc:prompt linear_client_secret secret\nPaste the OAuth Client Secret. Paste `none` if using a Personal API key instead.\n```\n```nc:prompt linear_webhook_secret secret\nPaste the webhook signing secret from the webhook you just created.\n```\n```nc:prompt linear_team_key\nEnter the Linear team key (e.g. `ENG`, `NAN`) — Settings > Teams.\n```\n```nc:prompt linear_bot_username\nEnter the bot display name (e.g. `NanoClaw Bot`).\n```\n```nc:env-set\nLINEAR_CLIENT_ID={{linear_client_id}}\nLINEAR_CLIENT_SECRET={{linear_client_secret}}\nLINEAR_WEBHOOK_SECRET={{linear_webhook_secret}}\nLINEAR_TEAM_KEY={{linear_team_key}}\nLINEAR_BOT_USERNAME={{linear_bot_username}}\n```\nIf you went the Personal API key route, add this line to `.env` instead of the\nOAuth pair (agent posts as you, your own comments are filtered):\n\n```bash\nLINEAR_API_KEY=lin_api_...\n```\n\n## Wiring\n\nLinear is team-routed: the assistant watches one team and answers *every* comment\non its issues (it can't be @-mentioned). Wire the team you set up to an agent —\npick which one should answer (`ncl groups list` shows their folders). The host\nservice must be running — `ncl` connects to it over a Unix socket.\n\nThe sender policy depends on the workspace: a private workspace can use `public`\n(only workspace members can comment anyway); a public workspace should use\n`strict` so only registered members may talk to the agent.\n\n```nc:prompt agent_folder\nWhich agent should answer Linear comments? Enter its folder (run `ncl groups list`).\n```\n```nc:prompt linear_sender_policy normalize:lower validate:^(public|strict)$\nIs this a private or public Linear workspace? Enter `public` for a private workspace (only members can comment) or `strict` for a public workspace (only registered members may talk to the agent).\n```\n```nc:run effect:wire\nncl messaging-groups create --channel-type linear --platform-id linear:{{linear_team_key}} --is-group 1 --unknown-sender-policy {{linear_sender_policy}} --name {{linear_team_key}}\nncl wirings create --channel-type linear --platform-id linear:{{linear_team_key}} --agent-group {{agent_folder}} --engage-mode pattern --engage-pattern . --session-mode per-thread\n```\n\nThe explicit `pattern` engage mode with pattern `.` matches the Linear adapter's\ndeclared channel defaults — Linear can't be @-mentioned, so the agent answers\nevery comment. Each issue thread becomes its own conversation. There's no\nwelcome — Linear has no direct message, so the assistant greets people when it\nfirst answers a comment. If you chose `strict`, register the people who may talk\nto the agent (see the GitHub skill for adding members).\n\n## Next Steps\n\nIf you're in the middle of `/setup`, return to the setup flow now.\n\nOtherwise, restart the service to pick up the new channel.\n\nRun from your NanoClaw project root:\n\n```bash\nsource setup/lib/install-slug.sh\nlaunchctl kickstart -k gui/$(id -u)/$(launchd_label)  # macOS\nsystemctl --user restart $(systemd_unit)              # Linux\n```\n\n## Channel Info\n\n- **type**: `linear`\n- **terminology**: Linear has \"teams\" containing \"issues.\" Each issue's comment thread is a separate conversation.\n- **how-to-find-id**: The platform ID is `linear:<TEAM_KEY>` (e.g. `linear:ENG`). Find your team key in Linear under Settings > Teams. Each issue becomes its own thread automatically.\n- **supports-threads**: yes (issue comment threads are native conversations)\n- **typical-use**: Webhook-driven — the agent receives all issue comment events and responds automatically. No @-mention needed (Linear OAuth apps can't be @-mentioned).\n- **default-isolation**: Use `per-thread` session mode. Each issue comment thread gets its own isolated agent session.\n\n## Troubleshooting\n\n**Comments never reach the agent.** New Linear webhooks can lag 1–5 minutes, so wait before digging. Then check the webhook in Linear Settings → API → Webhooks: the URL must be your public host at `/webhook/linear` (shared webhook server, port 3000), the right team selected, and the **Comment** event checked. A mismatch between the webhook's signing secret and `LINEAR_WEBHOOK_SECRET` makes deliveries fail signature verification silently — re-copy the secret from the webhook page.\n\n**OAuth credentials rejected.** The Client ID and Secret come from Linear Settings → API → OAuth Applications, and the app must have **Client credentials** enabled under grant types after creation — without that toggle the token exchange 401s. If you meant to use a Personal API key instead, answer `none` at both OAuth prompts and set `LINEAR_API_KEY` in `.env` by hand.\n\n**The agent ignores your own comments.** That's Personal-API-key mode working as designed: comments from the key's account are filtered as self-messages so the bot doesn't answer itself. Other members' comments still trigger it; if it must answer you too, switch to the OAuth app identity.\n\n**Sender-policy answer rejected, or issues route nowhere.** The policy must be exactly `public` or `strict` (lowercase), and `LINEAR_TEAM_KEY` must be the short team key (e.g. `ENG`) from Settings → Teams — all issues in that one team route to the messaging group.\n\n**Wired but dead.** Run `pnpm exec vitest run src/channels/linear-registration.test.ts` — red means the barrel import or the `@chat-adapter/linear` install drifted, so re-run the Apply steps. If green, restart the service (see Next Steps) so the adapter and `.env` values are live.","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-linear","license":"MIT","category":"writing","lang":"en","tokens":2395,"stars":0,"calls30d":1,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"apply-fixtures.json","size":606,"sha256":"45b347534565929806988e6776af1b767a81ec02c76fa73fcbfe102a6928bb59"},{"path":"REMOVE.md","size":815,"sha256":"3692d20554ab840ddff0f3a893e0088a89738583df78a34eb6d1f0df555585c2"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["linear.app"]}}