13 KiB
Claude Code + Ollama Cloud 替换 Cursor 自动化 — 可行性调研
Date: 2026-08-27 Status: feasible for triage; keep the existing control plane Scope: replace Cursor CLI as the agent runner, not rewrite GitHub routing
Why this exists
The current pipeline is .github/workflows/ai-automation.yml plus
scripts/ai-automation.cjs. Live mode is triage_only: classify issues,
do not implement, do not run the Codex fix loop.
A production classify run failed because Cursor hit its usage limit:
ActionRequiredError: You've hit your usage limit Get Cursor Pro for more Agent usage
Example: https://github.com/binaricat/Netcatty/actions/runs/33050247387
(job Classify issue, step Research external context for classification).
The goal is to keep issue triage running by swapping the agent from Cursor CLI
to Claude Code, with Ollama Cloud as the Anthropic-compatible backend. Secrets
stay in GitHub Actions (set via gh), never in the repo.
Verdict
Yes, this is feasible. Do not replace the workflow with
anthropics/claude-code-action. Keep the existing router, labels, rate limits,
bot identity, isolated research workspace, and publish steps. Only replace the
agent invocation.
| Layer | Keep or replace | Why |
|---|---|---|
| Event routing, labels, daily limits, handoff comments | Keep | Pure GitHub control plane in ai-automation.cjs |
Codex @codex review loop |
Keep | Independent of Cursor; currently paused by triage_only |
| Isolated research workspace + imgproxy screenshots | Keep | Safety contract is still needed |
Frozen helper copy, leak scan, no GITHUB_TOKEN in agent |
Keep | Same threat model |
| Cursor CLI install, AppArmor sandbox, API-key fd bridge | Replace | This is the part that is out of credits |
agent -p --sandbox enabled |
Replace | claude --bare -p with dontAsk + allowlist |
Current production path that must come back first: research + classify. Implement / follow-up / Codex-fix jobs can stay paused until triage is green.
How the current pipeline actually works
The YAML is large because it is an orchestrator, not “run an agent on the issue”. Jobs:
route— decideissue_classify/issue_followup/codex_loop/ skipclassify— prepare issue JSON, isolated web research, classify, apply labelsimplement/followup/codex_loop— gated off intriage_only- Codex re-request and source-issue cleanup — GitHub-only, no Cursor
Classify currently shells out to Cursor like this:
sudo --preserve-env=HOME,RUNNER_TEMP,GITHUB_WORKSPACE \
"$RUNNER_TEMP/ai-claude-authenticated" \
-p --mode=ask --trust --sandbox enabled --model auto --output-format text \
--workspace "$GITHUB_WORKSPACE" \
"$PROMPT"
Research uses the same binary in an empty temp workspace, --output-format stream-json, then parseExternalResearchStream() requires a real completed
WebSearch/WebFetch tool event. Classify must write
.ai-runtime/classification.json with category, confidence, summary,
reasoning, reply, code_paths, code_findings.
That JSON contract is owned by normalizeClassification() in
scripts/ai-automation.cjs, not by the checked-in
.github/ai/schemas/classification.schema.json (the schema file is stale:
it omits already_available, code_paths, and code_findings).
Claude Code as a headless runner
Official headless mode is claude -p (docs):
- Exit 0 / non-zero for scripts
--bareskips hooks, skills, plugins, MCP, CLAUDE.md (recommended for CI)--output-format jsonorstream-json--json-schemacan enforce the classification object--permission-mode dontAskis the documented “locked-down CI” mode: only pre-allowed tools run; everything else is denied, never prompted--allowedTools/--disallowedToolsfor the allowlist- Auth in
-p/--bareisANTHROPIC_API_KEYorANTHROPIC_AUTH_TOKEN, not a Claude.ai subscription login
Do not use --dangerously-skip-permissions for classify. Classify is
read-only. dontAsk plus Read/Grep/Glob (and maybe Bash(rg *)) is enough.
Do not adopt anthropics/claude-code-action as the new workflow. That
action is built for @claude mentions, the Claude GitHub App, and posting
its own comments. This repo already has netcatty-bot, admission quotas,
untrusted-issue sanitization, and a separate research pass. The official
action would fight that control plane. Invoke the CLI the same way Cursor is
invoked today.
Ollama Cloud as the Anthropic endpoint
Ollama documents an Anthropic Messages compatibility layer, including tools, streaming, vision, and thinking (docs).
Two ways to reach cloud models:
- Local Ollama proxying
:cloudmodels —ANTHROPIC_BASE_URL=http://localhost:11434. Needs a local Ollama daemon and sign-in. Wrong for GitHub-hosted runners. - Direct ollama.com API —
ANTHROPIC_BASE_URL=https://ollama.complus an Ollama Cloud API key. This is the CI path.
Ollama Cloud’s native auth is Authorization: Bearer $OLLAMA_API_KEY against
https://ollama.com. Claude Code maps:
| Claude Code env | HTTP header | Use with Ollama Cloud |
|---|---|---|
ANTHROPIC_BASE_URL |
API host | https://ollama.com |
ANTHROPIC_AUTH_TOKEN |
Authorization: Bearer … |
the Ollama Cloud key |
ANTHROPIC_API_KEY |
X-Api-Key |
leave empty, or same key if a probe requires it |
A user-reported Ollama docs bug (#13854)
says setting ANTHROPIC_API_KEY alone is not enough for cloud; the Bearer
token (ANTHROPIC_AUTH_TOKEN) plus https://ollama.com is.
Must pin the model. Claude Code defaults to Anthropic IDs such as
claude-sonnet-4-6. Ollama Cloud will 404 those. Also set the Haiku/Sonnet/Opus
alias env vars so background/compaction calls do not fall back to Claude names:
ANTHROPIC_MODEL=<ollama-cloud-id>
ANTHROPIC_DEFAULT_HAIKU_MODEL=<same or cheaper cloud id>
ANTHROPIC_DEFAULT_SONNET_MODEL=<same>
ANTHROPIC_DEFAULT_OPUS_MODEL=<same>
Ollama’s Claude Code page recommends coding cloud models such as
glm-4.7:cloud, minimax-m2.1:cloud, and shows
kimi-k2.7-code:cloud in the manual example. Pick one coding model and keep
it in a repo variable, not hardcoded in YAML, so it can change without a
workflow PR.
Ollama does not support prompt caching, /v1/messages/count_tokens, or
forcing tool_choice. Fine for classify. Token counts are approximations.
Web research gap (the one real risk)
Cursor research depends on Cursor’s built-in WebSearch/WebFetch, then the helper asserts a completed web-tool event in the stream.
Claude Code’s WebSearch/WebFetch are Anthropic server-side tools. They are
not guaranteed to work when ANTHROPIC_BASE_URL is https://ollama.com.
Ollama has its own REST APIs instead:
POST https://ollama.com/api/web_searchPOST https://ollama.com/api/web_fetch
Those use the same Cloud API key. ollama launch claude wires them for local
use; a raw claude -p on a GHA runner may not.
Recommended research strategy for this repo:
- Keep the empty-temp-workspace + imgproxy design.
- Prefer a control-plane research helper that calls Ollama
web_search/web_fetch(or Claude Code WebSearch if a smoke test proves it works against ollama.com). - Rewrite
parseExternalResearchStream()to accept Claudestream-jsonor a small JSONL log from the control-plane helper. Do not drop the “must have a real source URL” check. - Classify still runs with WebSearch/WebFetch denied.
If research cannot get a source for a needed external fact, keep today’s
behavior: RESEARCH_BLOCKED → maintainer handoff. Do not invent sources.
Security mapping
Keep these invariants from .github/ai/README.md:
- Agent steps get no GitHub token
- Automation never publishes
.github/or automation scripts - Issue text is sanitized before prompts
- Output is scanned for the provider secret
- External research has no repo checkout and no GitHub credentials
Cursor-specific pieces we will not copy 1:1:
- AppArmor profile from
downloads.cursor.com - fd-preload that injects
--api-keywithout putting it in env/argv
Claude Code requires ANTHROPIC_AUTH_TOKEN in the process environment.
Mitigations:
- Stage the key the same way (
install -m 0400intoRUNNER_TEMP), export only on theclaudeinvocation, never as a job-wideenv: --bareso project.mcp.json/ hooks cannot run--permission-mode dontAsk+ denyWebSearch/WebFetch/Edit/Writeon classify- Claude sandbox (
sandbox.enabled) if the GHA image can load it; if not, fail closed for implement later, but classify can ship ondontAsk+ no GitHub token - Continue leak scans against
ANTHROPIC_AUTH_TOKEN
Do not run claude as root/sudo. Cursor’s launcher used setpriv to drop
privileges after reading the key. Claude Code refuses
--dangerously-skip-permissions as root; classify should run as the runner
user.
What to change (phased)
Phase 0 — secrets, no YAML behavior change
Set via gh against binaricat/Netcatty. Values never land in git.
# Key: GitHub secret (Bearer token for ollama.com)
printf '%s' "$OLLAMA_API_KEY" | gh secret set ANTHROPIC_AUTH_TOKEN -R binaricat/Netcatty
# Host: repo variable is enough (not a credential)
gh variable set ANTHROPIC_BASE_URL -R binaricat/Netcatty --body 'https://ollama.com'
# Model: repo variable so it can change without rotating the key
gh variable set CLAUDE_CODE_MODEL -R binaricat/Netcatty --body 'kimi-k2.7-code:cloud'
Optional aliases, same key:
gh variable set ANTHROPIC_DEFAULT_HAIKU_MODEL -R binaricat/Netcatty --body 'kimi-k2.7-code:cloud'
gh variable set ANTHROPIC_DEFAULT_SONNET_MODEL -R binaricat/Netcatty --body 'kimi-k2.7-code:cloud'
gh variable set ANTHROPIC_DEFAULT_OPUS_MODEL -R binaricat/Netcatty --body 'kimi-k2.7-code:cloud'
Do not commit the key, put it in .github/workflows/*.yml literals, or
paste it into issues/PRs.
Existing ANTHROPIC_AUTH_TOKEN can stay until Cursor jobs are deleted.
Phase 1 — restore triage (this unblocks production)
In .github/workflows/ai-automation.yml classify job:
- Install Claude Code:
curl -fsSL https://claude.ai/install.sh | bash - Drop Cursor CLI install, credential bridge, AppArmor sandbox host
- Research step: isolated workspace + Ollama web APIs or proven Claude WebSearch; parse a Claude/control-plane research envelope
- Classify step:
claude --bare -p "$PROMPT" \
--permission-mode dontAsk \
--allowedTools "Read,Grep,Glob" \
--disallowedTools "WebSearch,WebFetch,Edit,Write,NotebookEdit" \
--output-format json \
--json-schema "$(cat "$RUNNER_TEMP/classification.schema.json")" \
--model "$CLAUDE_CODE_MODEL"
- Point prompts at
.ai-runtime/issue.jsonstill (runtime path rename can wait) - Update
parseClassificationFileto read Claude--output-format json(structured_outputorresult) in addition to raw JSON files - Keep
applyClassification, daily limits, Slack, failure handoff - Replace
sandbox_smokewith a cheap authenticatedclaude -pping against Ollama Cloud
Also refresh .github/ai/schemas/classification.schema.json so it matches
CATEGORIES + code_paths / code_findings. Use that schema with
--json-schema.
Helper script: add prepareClaudeCliSettings() next to
prepareAiCliSettings(). Do not rename ai-automation.cjs in this
phase (issue-format, markers, tests all import that path).
Phase 2 — implement / follow-up (only after triage is stable)
Same CLI, wider allowlist (Edit, Write, Bash for tests), still no GitHub
token, still deny .github/ publishes. Restore AI_AUTOMATION_MODE=full
only after classify quality looks acceptable on real issues.
Out of scope unless asked
- Installing the Claude GitHub App /
@claudecomments - Switching review from Codex to Claude
- Running a local Ollama daemon on GHA
- Paying Anthropic first-party API (the point of Ollama Cloud)
Suggested classify invocation contract
Env on the agent step only:
ANTHROPIC_BASE_URL: ${{ vars.ANTHROPIC_BASE_URL }}
ANTHROPIC_AUTH_TOKEN: (staged file, not a job-wide secret expansion in logs)
ANTHROPIC_API_KEY: ''
ANTHROPIC_MODEL: ${{ vars.CLAUDE_CODE_MODEL }}
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1'
GITHUB_TOKEN: ''
GH_TOKEN: ''
Prompt files stay under .github/ai/prompts/ for now. Swap the one
Cursor-specific sentence in research.md (“Use only Cursor's built-in
WebSearch and WebFetch tools”) to the new research tools.
Sources
- Claude Code headless /
-p: https://code.claude.com/docs/en/headless - Claude Code GitHub Actions (why we are not using the Action as the orchestrator): https://code.claude.com/docs/en/github-actions.md
- Permission mode
dontAsk: https://code.claude.com/docs/en/permission-modes.md - Env vars
ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY: https://code.claude.com/docs/en/env-vars - Ollama Anthropic compatibility: https://docs.ollama.com/api/anthropic-compatibility
- Ollama Claude Code integration: https://docs.ollama.com/integrations/claude-code
- Ollama Cloud API host: https://docs.ollama.com/cloud
- Ollama web_search / web_fetch: https://docs.ollama.com/capabilities/web-search
- Cloud auth token vs API key: https://github.com/ollama/ollama/issues/13854
- Live Cursor failure: GitHub Actions run 33050247387,
ActionRequiredError: You've hit your usage limit