Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# MINIMAX_API_KEY= # --provider minimax or minimax-cn (MiniMax-M3)
# PATINA_API_KEY_FILE= # or read any key from a file (recommended for CI)

# Local subscription CLIs (claude / codex / gemini / kimi) need no key here —
# Local subscription CLIs (claude / codex / gemini) need no key here —
# a logged-in seat is the credential. See also PATINA_LIVE_* and
# PATINA_LIVE_JUDGE_* (fixed-judge scoring) in tests/quality/README.md.

Expand Down
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ node <skill-directory>/bin/patina-skill.js --input <source-file> [--lang ko] [--

백엔드와 한계:

- 헬퍼는 선택된 백엔드 하나의 준비 상태만 검사한다. 선택은 플래그나 설정 파일 어디서든 올 수 있다. `patina doctor`가 다른 백엔드를 통과시킨 사실은 선택된 백엔드의 준비 증거가 아니고, 백엔드 대체 체인도 없다. 선택된 백엔드가 실패하면 다른 백엔드로 몰래 바꾸지 않고 실패로 보고한다. 명시적으로 선택된 `kimi-cli`는 `backend_argv_exposes_input`으로 거부된다.
- 헬퍼는 선택된 백엔드 하나의 준비 상태만 검사한다. 선택은 플래그나 설정 파일 어디서든 올 수 있다. `patina doctor`가 다른 백엔드를 통과시킨 사실은 선택된 백엔드의 준비 증거가 아니고, 백엔드 대체 체인도 없다. 선택된 백엔드가 실패하면 다른 백엔드로 몰래 바꾸지 않고 실패로 보고한다.
- 입력은 20,000자·80,000바이트 상한이고, 설정과 실행을 합한 전체 예산은 180초다.
- 파일이 여러 개면 파일마다 헬퍼를 따로 호출한다. 실패한 파일은 코드를 기록하고 원본을 바꾸지 않은 채 다음 파일로 넘어간다. 실패한 파일을 인라인으로 재작성하지 않는다.

Expand Down
1 change: 0 additions & 1 deletion bin/patina-skill.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ export async function runSkill(argv, { spawnImpl = spawn, signal } = {}) {
model: resolveLocalCliModel({ backendName: backend.name, model: resolved.model, modelSource: resolved.modelSource }),
modelSource: selected.model ? 'flag' : config.model ? 'config' : resolved.modelSource,
};
if (backend.name === 'kimi-cli') fail('backend_argv_exposes_input');
if (backend.name === 'openai-http') {
if (!resolved.apiKey) fail('backend_auth_missing');
} else {
Expand Down
33 changes: 3 additions & 30 deletions docs/AUTHENTICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ patina runs through one of several backends. Pick whichever matches your existin
| `codex-cli` | `codex login` | **Free** (ChatGPT OAuth) |
| `claude-cli` | `claude auth login` (one-time interactive OAuth) | **Free** (Claude subscription) |
| `gemini-cli` | `gemini` (one-time interactive OAuth) or `GEMINI_API_KEY=...` | **Free** (Code Assist OAuth or AI Studio) |
| `kimi-cli` | `kimi login` (one-time browser OAuth) or `KIMI_API_KEY=...` | Kimi account / Moonshot API |
| OpenAI-compatible HTTP | `PATINA_API_KEY=...` | Per provider |
| Google Gemini (HTTP) | `GEMINI_API_KEY=...` + `--provider gemini` | Free tier |
| Groq | `GROQ_API_KEY=...` + `--provider groq` | Free tier |
Expand All @@ -24,7 +23,7 @@ patina auth login codex-cli # confirm, then run `codex login`
patina --list-backends # backend selectors + auth state
```

Backend selection requires an explicit signal: pass `--backend <name>` directly, pass a comma-separated fallback chain such as `--backend claude-cli,codex-cli`, or use `--model <prefix>` (`codex-*`, `claude-*`, `gemini-*`, `kimi-*` route to the matching local CLI). With no flags and no API key, patina exits with an error rather than silently dispatching to a coding agent. See [issue #88](https://github.com/devswha/patina/issues/88) for the rationale.
Backend selection requires an explicit signal: pass `--backend <name>` directly, pass a comma-separated fallback chain such as `--backend claude-cli,codex-cli`, or use `--model <prefix>` (`codex-*`, `claude-*`, `gemini-*` route to the matching local CLI). With no flags and no API key, patina exits with an error rather than silently dispatching to a coding agent. See [issue #88](https://github.com/devswha/patina/issues/88) for the rationale.

## Environment variables

Expand All @@ -35,7 +34,7 @@ PATINA_MODEL=gpt-5.5 # HTTP/OpenAI default model
```

`--base-url`, `--model`, `--api-key-file`, and `--provider` flags override these per run.
When you do not pass `--model`, patina uses its strongest documented default per backend: `gpt-5.5` for OpenAI HTTP and `codex-cli`, `claude-sonnet-4-6` for `claude-cli`, `gemini-2.5-pro` for Gemini HTTP/CLI, `kimi-code/kimi-for-coding` for `kimi-cli`, and `kimi-k2.5` for Kimi/Moonshot HTTP. Exact selector aliases such as `--model codex`, `--model claude`, `--model gemini`, and `--model kimi` still route to the local CLI while using that backend default.
When you do not pass `--model`, patina uses its strongest documented default per backend: `gpt-5.5` for OpenAI HTTP and `codex-cli`, `claude-sonnet-4-6` for `claude-cli`, `gemini-2.5-pro` for Gemini HTTP/CLI, and `kimi-k2.5` for Kimi/Moonshot HTTP. Exact selector aliases such as `--model codex`, `--model claude`, and `--model gemini` still route to the local CLI while using that backend default.

## codex-cli backend

Expand Down Expand Up @@ -91,32 +90,6 @@ patina --model gemini-3-flash-preview --lang ko input.txt # auto-routes

Notes: patina passes `--skip-trust` because the prompt runs from a fresh temp directory (containment for prompt-injection in user text), disables MCP servers, and passes a per-call `--policy` file that denies every tool (`toolName = "*"`), so the model receives no tool definitions and cannot spend turns on `run_shell_command` or `write_file`; image input uses `@file` includes, which the CLI resolves itself. Default timeout is higher than other CLIs because gemini's startup latency is longer.

## kimi-cli backend

Spawns local [`kimi`](https://moonshotai.github.io/kimi-cli/) in prompt mode (`--prompt <text>`, stream-JSON output). It works with Kimi Code CLI browser login, `KIMI_API_KEY`, or `MOONSHOT_API_KEY`. Kimi Code is an agent runtime, so patina treats it conservatively in batch mode: compact prompt mode, default max concurrency `1`, and default retries `0`. The default local CLI model is `kimi-code/kimi-for-coding`; the CLI display name may differ from Moonshot HTTP model IDs.

```bash
kimi login # one-time browser OAuth, OR
patina auth login kimi-cli # same, with confirmation
export KIMI_API_KEY="..." # optional API key path
patina --backend kimi-cli --lang ko input.txt
patina --model kimi --lang ko input.txt # routes to kimi-cli, uses backend default
```

Kimi Code prompt mode can auto-approve tools even without `--yolo` or `--auto`,
so patina passes an explicit agent profile with `tools: []` and
`subagents: []`, plus an empty skills directory, on every text request. The
profile treats source prose as reference data and does not inherit workspace
instructions, plugins, or a coding agent's tool permissions; global Kimi
settings are unchanged. This requires Kimi Code 0.29 or newer. Older clients
without agent-file support fail with an upgrade message instead of falling
back to an unrestricted print mode. A live check against 0.29.1 requested a
harmless file write in an isolated directory and confirmed a zero-tool
request trace with no file created. The restriction covers tool access only,
not patina's pattern or meaning-preservation rules. Because the text travels
as a `--prompt` argument, other local users who can list processes may see it.
See Kimi's [agent customization guide](https://moonshotai.github.io/kimi-code/en/customization/agents).

## agy-cli backend (Antigravity CLI)

Spawns local [`agy`](https://antigravity.google/docs/cli) (Google's Antigravity
Expand Down Expand Up @@ -158,7 +131,7 @@ Use `--yes` only for automation where the launch is already intentional:
patina auth login codex-cli --yes
```

> **Mode support:** `codex-cli`, `claude-cli`, `gemini-cli`, `kimi-cli`, and `agy-cli` can be used as rewrite backends without `PATINA_API_KEY` when their local CLIs are already authenticated.
> **Mode support:** `codex-cli`, `claude-cli`, `gemini-cli`, and `agy-cli` can be used as rewrite backends without `PATINA_API_KEY` when their local CLIs are already authenticated.

For large rewrite batches, prefer `openai-http` or another stateless
OpenAI-compatible HTTP provider over local agent CLIs. Batch mode exposes
Expand Down
19 changes: 3 additions & 16 deletions docs/AUTHENTICATION_KR.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ patina는 여러 백엔드 가운데 하나로 실행됩니다. 지금 쓰는
| `codex-cli` | `codex login` | **Free** (ChatGPT OAuth) |
| `claude-cli` | `claude auth login` (one-time interactive OAuth) | **Free** (Claude subscription) |
| `gemini-cli` | `gemini` (one-time interactive OAuth) or `GEMINI_API_KEY=...` | **Free** (Code Assist OAuth or AI Studio) |
| `kimi-cli` | `kimi login` (one-time browser OAuth) or `KIMI_API_KEY=...` | Kimi account / Moonshot API |
| OpenAI-compatible HTTP | `PATINA_API_KEY=...` | Per provider |
| Google Gemini (HTTP) | `GEMINI_API_KEY=...` + `--provider gemini` | Free tier |
| Groq | `GROQ_API_KEY=...` + `--provider groq` | Free tier |
Expand All @@ -24,7 +23,7 @@ patina auth login codex-cli # confirm 후 `codex login` 실행
patina --list-backends # backend selector와 auth 상태
```

백엔드는 명시적으로 골라야 합니다. `--backend <name>`을 직접 넘기거나, `--backend claude-cli,codex-cli`처럼 우선순위를 적거나, `--model <prefix>`를 쓰세요. `codex-*`, `claude-*`, `gemini-*`, `kimi-*`는 각각 해당 CLI로 연결됩니다. 아무 플래그도 없고 API 키도 없으면 patina는 다른 에이전트로 몰래 넘기지 않고 바로 오류로 끝납니다. 배경은 [issue #88](https://github.com/devswha/patina/issues/88)를 보면 됩니다.
백엔드는 명시적으로 골라야 합니다. `--backend <name>`을 직접 넘기거나, `--backend claude-cli,codex-cli`처럼 우선순위를 적거나, `--model <prefix>`를 쓰세요. `codex-*`, `claude-*`, `gemini-*`는 각각 해당 CLI로 연결됩니다. 아무 플래그도 없고 API 키도 없으면 patina는 다른 에이전트로 몰래 넘기지 않고 바로 오류로 끝납니다. 배경은 [issue #88](https://github.com/devswha/patina/issues/88)를 보면 됩니다.

## Environment variables

Expand All @@ -35,7 +34,7 @@ PATINA_MODEL=gpt-5.5 # HTTP/OpenAI default model
```

`--base-url`, `--model`, `--api-key-file`, `--provider` 플래그는 그 실행에 한해서 이 값을 덮어씁니다.
`--model`을 따로 주지 않으면 patina는 백엔드마다 문서에 적어 둔 기본 모델을 씁니다. OpenAI HTTP와 `codex-cli`는 `gpt-5.5`, `claude-cli`는 `claude-sonnet-4-6`, Gemini HTTP/CLI는 `gemini-2.5-pro`, `kimi-cli`는 `kimi-code/kimi-for-coding`, Kimi/Moonshot HTTP는 `kimi-k2.5`입니다. `--model codex`, `--model claude`, `--model gemini`, `--model kimi` 같은 별칭도 각 CLI로 라우팅되지만 실제로 넘기는 값은 그 백엔드의 기본 모델입니다.
`--model`을 따로 주지 않으면 patina는 백엔드마다 문서에 적어 둔 기본 모델을 씁니다. OpenAI HTTP와 `codex-cli`는 `gpt-5.5`, `claude-cli`는 `claude-sonnet-4-6`, Gemini HTTP/CLI는 `gemini-2.5-pro`, Kimi/Moonshot HTTP는 `kimi-k2.5`입니다. `--model codex`, `--model claude`, `--model gemini` 같은 별칭도 각 CLI로 라우팅되지만 실제로 넘기는 값은 그 백엔드의 기본 모델입니다.

## codex-cli backend

Expand Down Expand Up @@ -92,18 +91,6 @@ patina --model gemini-3-flash-preview --lang ko input.txt # auto-routes
아예 받지 않으므로 `run_shell_command`나 `write_file`에 턴을 쓰지 못합니다. 이미지 입력은 CLI가
직접 해석하는 `@file` include를 씁니다. gemini는 시작 지연이 길어 기본 timeout이 다른 CLI보다 깁니다.

## kimi-cli backend

로컬 [`kimi`](https://moonshotai.github.io/kimi-cli/)를 prompt mode(`--prompt <text>`, stream-JSON 출력)로 실행합니다. Kimi Code CLI 브라우저 로그인이나 `KIMI_API_KEY`, `MOONSHOT_API_KEY` 중 하나로 인증할 수 있습니다. Kimi Code는 agent runtime이므로 batch 모드에서는 보수적으로 다룹니다: compact prompt mode, 기본 동시성 `1`, 기본 retry `0`. 로컬 CLI 기본 모델은 `kimi-code/kimi-for-coding`이며, CLI 표시 이름은 Moonshot HTTP 모델 ID와 다를 수 있습니다.

```bash
kimi login # one-time browser OAuth, OR
patina auth login kimi-cli # same, with confirmation
export KIMI_API_KEY="..." # optional API key path
patina --backend kimi-cli --lang ko input.txt
patina --model kimi --lang ko input.txt # kimi-cli로 라우팅하고 backend 기본값 사용
```

## agy-cli backend (Antigravity CLI)

로컬 [`agy`](https://antigravity.google/docs/cli)(Google Antigravity CLI — 무료 및 Google One 계정의 Gemini CLI 후속)를
Expand Down Expand Up @@ -140,7 +127,7 @@ patina auth login codex-cli --yes

참고: patina는 사용자 텍스트 안의 prompt injection 영향을 줄이려고 새 임시 디렉터리에서 프롬프트를 실행하고 `--skip-trust`를 함께 넘깁니다. gemini는 시작이 느린 편이라 기본 timeout도 다른 CLI보다 더 길게 잡혀 있습니다.

> **지원 범위:** `codex-cli`, `claude-cli`, `gemini-cli`, `kimi-cli`는 로컬 CLI 로그인만 되어 있으면 `PATINA_API_KEY` 없이 rewrite 백엔드로 쓸 수 있습니다. 반면 API 기반 score/audit 경로는 계속 설정된 HTTP/evaluator 키를 사용합니다.
> **지원 범위:** `codex-cli`, `claude-cli`, `gemini-cli`는 로컬 CLI 로그인만 되어 있으면 `PATINA_API_KEY` 없이 rewrite 백엔드로 쓸 수 있습니다. 반면 API 기반 score/audit 경로는 계속 설정된 HTTP/evaluator 키를 사용합니다.

대량 rewrite batch는 가능한 한 로컬 agent CLI보다 `openai-http` 같은 stateless
OpenAI-compatible HTTP provider를 쓰세요. Batch 모드는 `--timeout-ms`,
Expand Down
8 changes: 3 additions & 5 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,8 @@ All backends share the same invocation contract:
`invoke({ prompt, model, modelSource, signal, timeout, maxRetries }): Promise<string>`.
Local CLI backends honor `AbortSignal` by killing their child process. When no
explicit model is set, local backends pass the strongest documented default to
their CLI (`gpt-5.5`, `claude-sonnet-4-6`, `gemini-2.5-pro`, or
`kimi-code/kimi-for-coding`); the HTTP backend bridges the same signal into
fetch.
their CLI (`gpt-5.5`, `claude-sonnet-4-6`, or `gemini-2.5-pro`); the HTTP
backend bridges the same signal into fetch.

## Batch safety controls

Expand All @@ -267,7 +266,6 @@ Defaults are intentionally conservative:
| `codex-cli` | minimal | 2 | 0 |
| `claude-cli` | minimal | 1 | 0 |
| `gemini-cli` | minimal | 2 | 0 |
| `kimi-cli` | minimal | 1 | 0 |
| `agy-cli` | minimal | 1 | 0 |

Local CLIs are agent runtimes, not stateless completion APIs. For large rewrite
Expand All @@ -276,7 +274,7 @@ when you have measured the backend:

```bash
patina --batch --backend openai-http --max-concurrency 4 --max-retries 2 docs/*.md
patina --batch --backend kimi-cli --max-concurrency 1 --max-retries 0 docs/*.md
patina --batch --backend claude-cli --max-concurrency 1 --max-retries 0 docs/*.md
```

Circuit breakers stop batch mode after repeated failure instead of burning quota:
Expand Down
4 changes: 2 additions & 2 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ See [ETHICS.md](ETHICS.md) for the intended-use position statement.
## Does it work without an API key?

Yes. `--score --offline` and the `patina-score` precommit gate need no backend.
LLM-backed modes can use a logged-in local Codex, Claude, Gemini, or Kimi CLI
instead of an API key. See [Authentication](AUTHENTICATION.md).
LLM-backed modes can use a logged-in local Codex, Claude, or Gemini CLI instead
of an API key. See [Authentication](AUTHENTICATION.md).

## Does patina send my text anywhere?

Expand Down
2 changes: 1 addition & 1 deletion docs/FAQ_KR.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ score는 0부터 100까지의 대략적인 편집 신호입니다. 낮을수록

네. `--score --offline`과 `patina-score` precommit gate에는 backend가
필요하지 않습니다. LLM 기반 모드는 API key 대신 로그인된 Codex, Claude,
Gemini, Kimi CLI를 사용할 수 있습니다. [Authentication](AUTHENTICATION.md)을
Gemini CLI를 사용할 수 있습니다. [Authentication](AUTHENTICATION.md)을
참고하세요.

## 입력한 텍스트가 외부로 전송되나요?
Expand Down
2 changes: 1 addition & 1 deletion docs/FLAG-PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Basis: local checkout plus `node bin/patina.js --help` and `SKILL.md` reviewed o
| `--model <id>` | ✓ | — | CLI single-backend model selection. |
| `--api-key-file <path>` | ✓ | — | CLI auth. |
| `--base-url <url>` | ✓ | — | CLI provider/backend config. |
| `--backend <name[,name]>` | ✓ | — | CLI backend selection and explicit fallback chains (`openai-http`, `codex-cli`, `claude-cli`, `gemini-cli`, `kimi-cli`, `agy-cli`). |
| `--backend <name[,name]>` | ✓ | — | CLI backend selection and explicit fallback chains (`openai-http`, `codex-cli`, `claude-cli`, `gemini-cli`, `agy-cli`). |
| `--list-backends` | ✓ | — | CLI diagnostics with selectors and auth state. |
| `--provider <name>` | ✓ | — | CLI provider preset. |
| `--config <path>` | ✓ | — | CLI config override. |
Expand Down
2 changes: 1 addition & 1 deletion scripts/persona-ablation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ function createBackendRunner({ repoRoot, lang, config, backendName }) {
const useHttp = process.env.PATINA_API_KEY || process.env.OPENAI_API_KEY;
const name = backendName || (useHttp ? 'openai-http' : null);
if (!name) {
throw new Error('live calibration needs a backend: pass --backend <codex-cli|claude-cli|gemini-cli|kimi-cli|openai-http> or set PATINA_API_KEY');
throw new Error('live calibration needs a backend: pass --backend <codex-cli|claude-cli|gemini-cli|openai-http> or set PATINA_API_KEY');
}
const { backends } = selectBackendChain({ name });
const callLLM = ({ prompt, signal, timeout }) => invokeBackendChain({ backends, prompt, signal, timeout, maxConcurrency: 1, maxRetries: 1 });
Expand Down
Loading
Loading