-
Notifications
You must be signed in to change notification settings - Fork 35
feat(kiloclaw): add Linear MCP* integration #1407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
e36411a
573d2b8
fb36afa
24753c7
6104be8
43a3aca
1a4d5d4
8c79231
a280237
f3c1b8c
e916293
d7b3ae7
3f7035a
4a9670e
ade1b4e
298b918
eb2418b
b410d4d
1e65e2f
dc6785b
b3fe47b
b466318
432f2fd
3878f20
edac005
b9f6bbf
5ee5389
c1f3cee
d33284e
e9491a6
458dc89
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,8 +31,6 @@ RUN apt-get update \ | |
| | gpg --dearmor --output /usr/share/debsig/keyrings/AC2D62742012EA22/debsig.gpg \ | ||
| && apt-get update \ | ||
| && apt-get install -y --no-install-recommends gh 1password-cli \ | ||
| && apt-get purge -y xz-utils \ | ||
| && apt-get autoremove -y \ | ||
| && rm -rf /var/lib/apt/lists/* \ | ||
| && node --version \ | ||
| && npm --version | ||
|
|
@@ -57,6 +55,13 @@ RUN npm install -g @steipete/summarize@0.12.0 | |
| # Install Kilo CLI (agentic coding assistant for the terminal) | ||
| RUN npm install -g @kilocode/cli@7.0.46 | ||
|
|
||
| # Install Linear CLI (issue tracker) | ||
| RUN npm install -g @schpet/linear-cli@1.11.1 | ||
|
|
||
| # Clean up xz-utils now that Node.js and linear-cli are installed | ||
| RUN apt-get purge -y xz-utils \ | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. linear-cli requires node for install |
||
| && apt-get autoremove -y \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Install Go (available at runtime for users to `go install` additional tools) | ||
| ENV GO_VERSION=1.26.0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -324,7 +324,39 @@ export function configureGitHub(env: EnvLike, deps: BootstrapDeps = defaultDeps) | |
| } | ||
| } | ||
|
|
||
| // ---- Step 6: Onboard / doctor + config patching ---- | ||
| // ---- Step 6: Linear config ---- | ||
|
|
||
| /** | ||
| * Configure or clean up Linear CLI access. | ||
| * When LINEAR_API_KEY is present the CLI reads it natively. | ||
| * When absent, clean up any on-disk credentials left by a previous | ||
| * `linear auth login --plaintext` on the persistent volume. | ||
| * Best-effort: logs warnings on failure, does not throw. | ||
| * | ||
| * The CLI stores two files under ~/.config/linear/: | ||
| * - credentials.toml — workspace list + inline API keys (plaintext mode) | ||
| * - linear.toml — global config that can also carry an api_key field | ||
| * The system keyring is not available in this container (no libsecret-tools), | ||
| * so these files are the only persistence locations. | ||
| */ | ||
| export function configureLinear(env: EnvLike, deps: BootstrapDeps = defaultDeps): void { | ||
| if (env.LINEAR_API_KEY) { | ||
| console.log('Linear CLI configured via LINEAR_API_KEY'); | ||
| } else { | ||
| // Clean up env var if explicitly set to empty | ||
| delete env.LINEAR_API_KEY; | ||
|
evanjacobson marked this conversation as resolved.
|
||
| // Remove any previously stored credentials from the persistent volume. | ||
| // The CLI recreates ~/.config/linear/ via ensureDir on next auth login. | ||
| try { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. separately trying each file delete operation out of an abundance of caution — if one fails, the other one is still tried. |
||
| deps.execFileSync('rm', ['-rf', '/root/.config/linear'], { stdio: 'pipe' }); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } catch { | ||
| // ignore — directory may not exist | ||
| } | ||
| console.log('Linear: not configured (credentials cleared)'); | ||
| } | ||
| } | ||
|
|
||
| // ---- Step 7: Onboard / doctor + config patching ---- | ||
|
|
||
| /** | ||
| * Run openclaw onboard (first boot) or openclaw doctor (subsequent boots), | ||
|
|
@@ -386,7 +418,7 @@ export function runOnboardOrDoctor(env: EnvLike, deps: BootstrapDeps = defaultDe | |
| } | ||
| } | ||
|
|
||
| // ---- Step 7: TOOLS.md Google Workspace section ---- | ||
| // ---- Step 8: TOOLS.md Google Workspace section ---- | ||
|
|
||
| const GOG_MARKER_BEGIN = '<!-- BEGIN:google-workspace -->'; | ||
| const GOG_MARKER_END = '<!-- END:google-workspace -->'; | ||
|
|
@@ -539,7 +571,92 @@ export function updateToolsMd1PasswordSection(env: EnvLike, deps: BootstrapDeps) | |
| } | ||
| } | ||
|
|
||
| // ---- Step 10: Gateway args ---- | ||
| // ---- Step 10: TOOLS.md Linear section ---- | ||
|
|
||
| const LINEAR_MARKER_BEGIN = '<!-- BEGIN:linear -->'; | ||
| const LINEAR_MARKER_END = '<!-- END:linear -->'; | ||
|
|
||
| const LINEAR_TOOLS_SECTION = ` | ||
| ${LINEAR_MARKER_BEGIN} | ||
| ## Linear | ||
|
|
||
| The \`linear\` CLI is configured with your Linear API key. Use it to read and manage issues. | ||
|
|
||
| - Run \`linear --help\` for full command reference; \`--help\` after any subcommand for details. | ||
| - If you don't know the team key, run \`linear team list\`. | ||
|
|
||
| ### Listing issues | ||
|
|
||
| Example — list all issues by priority: | ||
| \`\`\` | ||
| linear issue list --team <key> --sort priority --all-states --all-assignees | ||
| \`\`\` | ||
|
|
||
| **Flags that silently filter results when omitted:** | ||
| - \`--state\` defaults to \`backlog\`. Use \`--all-states\` for all, or \`--state <value>\` to filter to one: triage, backlog, unstarted, started, completed, canceled | ||
| - \`--assignee\` defaults to \`me\`. Use \`--all-assignees\` for all, or \`--assignee <user>\` to filter to one | ||
|
|
||
| ### Writing issue content | ||
| Use file flags for markdown with newlines or special characters: | ||
| - \`--description-file <path>\` for \`issue create/update\` | ||
| - \`--body-file <path>\` for \`comment add/update\` | ||
|
|
||
| ### Config file | ||
| Avoid repeated \`--team\` and \`--sort\` flags with \`.linear.toml\` in the project directory: | ||
| \`\`\`toml | ||
| team = "TEAM_KEY" | ||
| sort = "priority" | ||
| \`\`\` | ||
|
|
||
| ### Gotchas | ||
| - \`--no-pager\` only works on \`issue list\` — errors on other commands | ||
| - GraphQL non-null types (\`String!\`) require heredoc: \`linear api --variable key=val <<'GRAPHQL'\` | ||
|
|
||
| ### Advanced | ||
| - Get API token: \`linear auth token\` | ||
| - Direct GraphQL: \`curl -s -X POST https://api.linear.app/graphql -H "Authorization: $(linear auth token)" -d '{"query":"..."}'\` | ||
| ${LINEAR_MARKER_END}`; | ||
|
|
||
| /** | ||
| * Manage the Linear section in TOOLS.md. | ||
| * | ||
| * When LINEAR_API_KEY is present, append a bounded section so the agent | ||
| * knows the linear CLI is available. When absent, remove any stale section. | ||
| * Idempotent: skips if the marker is already present. | ||
| */ | ||
| export function updateToolsMdLinearSection(env: EnvLike, deps: BootstrapDeps): void { | ||
| if (!deps.existsSync(TOOLS_MD_DEST)) return; | ||
|
|
||
| const content = deps.readFileSync(TOOLS_MD_DEST, 'utf8'); | ||
|
|
||
| if (env.LINEAR_API_KEY) { | ||
| // Linear configured — add section if not already present | ||
| if (!content.includes(LINEAR_MARKER_BEGIN)) { | ||
| deps.writeFileSync(TOOLS_MD_DEST, content + LINEAR_TOOLS_SECTION); | ||
| console.log('TOOLS.md: added Linear section'); | ||
| } else { | ||
| console.log('TOOLS.md: Linear section already present'); | ||
| } | ||
| } else { | ||
| // Linear not configured — remove stale section if present | ||
| if (content.includes(LINEAR_MARKER_BEGIN)) { | ||
| const beginIdx = content.indexOf(LINEAR_MARKER_BEGIN); | ||
| const endIdx = content.indexOf(LINEAR_MARKER_END); | ||
| if (beginIdx !== -1 && endIdx !== -1) { | ||
| const before = content.slice(0, beginIdx).replace(/\n+$/, '\n'); | ||
| const after = content.slice(endIdx + LINEAR_MARKER_END.length).replace(/^\n+/, ''); | ||
| deps.writeFileSync(TOOLS_MD_DEST, before + after); | ||
| console.log('TOOLS.md: removed stale Linear section'); | ||
| } else { | ||
| console.warn( | ||
| 'TOOLS.md: Linear BEGIN marker found but END marker missing, skipping removal' | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // ---- Step 11: Gateway args ---- | ||
|
|
||
| /** | ||
| * Build the gateway CLI arguments array. | ||
|
|
@@ -588,6 +705,10 @@ export async function bootstrap( | |
| configureGitHub(env, deps); | ||
| await yieldToEventLoop(); | ||
|
|
||
| setPhase('linear'); | ||
| configureLinear(env, deps); | ||
| await yieldToEventLoop(); | ||
|
|
||
| const configExists = deps.existsSync(CONFIG_PATH); | ||
| setPhase(configExists ? 'doctor' : 'onboard'); | ||
| runOnboardOrDoctor(env, deps); | ||
|
|
@@ -596,6 +717,7 @@ export async function bootstrap( | |
| updateToolsMdKiloCliSection(env, deps); | ||
| updateToolsMdGoogleSection(env, deps); | ||
| updateToolsMd1PasswordSection(env, deps); | ||
| updateToolsMdLinearSection(env, deps); | ||
|
|
||
| // Write mcporter config for MCP servers (AgentCard, etc.) | ||
| writeMcporterConfig(env); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are security vulnerabilities on a dependency of this package. I have posted a PR to fix them.
I will bump the version to the latest release after it merges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still discussing the changes with the repo owner. This may need to be a follow-up. The vulnerabilities are not exposed in linear-cli regardless.