Clineをコマンドラインから使いたい！

## はじめに

みなさんClineを使っていますか？Clineは便利なツールでソースコードの作成のみならずMCPサーバーを使用することで幅広いタスクをこなすことができます。
Clineを手元で使う分にはいいんですがVS Codeの拡張機能なのでCIのパイプラインに組み込みづらいという欠点があります。
コマンドラインから使えるAIエージェント[^1]も存在するんですが、API Providerが指定できなかったり、何よりClineではないという問題[^2]があります。
エディタとコマンドラインで共通のツールを使えたほうが便利だと思ったのでClineをコマンドラインで扱うためのcline-cliを作成しました。

https://github.com/yaegaki/cline-cli

現在はまだまだ開発途中のもので動作が怪しい部分もありますが最低限動くようになってきたので紹介させていただきます。

:::note warn  
cline-cliは開発中のツールで今すぐプロダクションで使用できるレベルには到達していません。
現時点(2025/4/28)では遊びで触ることができるくらいの完成度です。
:::

## インストール

cline-cliはnpmを使って簡単にインストールできます。

```sh
npm install -g @yaegaki/cline-cli
```

インストール後、`cline-cli`コマンドが使用できるようになります。
また、グローバルにインストールせずにnpxを使って実行することもできます。

```sh
npx -y @yaegaki/cline-cli init
```

### 初期設定

cline-cliを使用する前には初期化が必要です。

```sh
cline-cli init
```

このコマンドを実行することでcline-cliの設定ファイルが作成されます。
作成された設定ファイルにAPI Providerや使用するモデルの設定を行います。

```sh
# 設定ファイルを編集する
vim ~/.cline_cli/cline_cli_settings.json
```

```jsonc
{
  "globalState": {

    // API Providerと使用するモデル
    "apiProvider": "",
    "apiModelId": "",
    
    // Google Vertex AI関連の設定
    "vertexProjectId": "",
    "vertexRegion": "",
    
    // OpenAI関連の設定
    "openAIBaseUrl": "",
    "openAIModelId": "",
    // 省略...
  }
}
```

API Providerやモデルの名前は本家ClineのUIに表示されるものを使用します。ただし、API Providerは小文字だったり微妙に差異があります。

```ts:使用できるAPI Provider
export type ApiProvider =
	| "anthropic"
	| "openrouter"
	| "bedrock"
	| "vertex"
	| "openai"
	| "ollama"
	| "lmstudio"
	| "gemini"
	| "openai-native"
	| "requesty"
	| "together"
	| "deepseek"
	| "qwen"
	| "doubao"
	| "mistral"
	// | "vscode-lm" vscode-lmは非対応
	| "cline"
	| "litellm"
	| "asksage"
	| "xai"
	| "sambanova"
```

実際に設定するとこんな感じになります。

```jsonc:anthropicの場合
{
  "globalState": {
    "apiProvider": "anthropic",
    "apiModelId": "claude-3-7-sonnet-20250219",
    // 省略...
  }
}
```

```jsonc:GCP Vertex AIの場合
{
  "globalState": {
    "apiProvider": "vertex",
    "apiModelId": "claude-3-7-sonnet@20250219",
    "vertexProjectId": "your-gcp-project-id",
    "vertexRegion": "us-central1",
    // 省略...
  }
}
```

### APIキーの設定

APIキーについては設定ファイルではなく、環境変数経由で渡すようにしています。コマンド実行時に以下のいずれかの環境変数を設定しておく必要があります。 

```
API_KEY                 # Anthropic API key
OPEN_ROUTER_API_KEY     # OpenRouter API key
CLINE_API_KEY           # Cline API key
AWS_ACCESS_KEY          # AWS access key
AWS_SECRET_KEY          # AWS secret key
AWS_SESSION_TOKEN       # AWS session token
OPEN_AI_API_KEY         # OpenAI API key
GEMINI_API_KEY          # Gemini API key
OPEN_AI_NATIVE_API_KEY  # OpenAI Native API key
DEEP_SEEK_API_KEY       # DeepSeek API key
REQUESTY_API_KEY        # Requesty API key
TOGETHER_API_KEY        # Together API key
QWEN_API_KEY            # Qwen API key
DOUBAO_API_KEY          # Doubao API key
MISTRAL_API_KEY         # Mistral API key
LITE_LLM_API_KEY        # LiteLLM API key
ASKSAGE_API_KEY         # AskSage API key
XAI_API_KEY             # XAI API key
SAMBANOVA_API_KEY       # Sambanova API key
```

```sh
# Anthropicの場合
API_KEY=YOUR_API_KEY cline-cli task "実行するタスク"
```

## 基本的な使い方

`task` コマンドでタスクを開始することができます。

```sh
cline-cli task "ブラウザで遊べるゲームを作成して"
```

オプションを何も指定しない場合はインタラクティブモードになり、対話的に実行できます。
CIのパイプラインに組み込む場合など対話的ではなく自動で実行したい場合は以下のオプションを指定します。

```sh
# clineからの質問に自動で答える
cline-cli task "ブラウザで遊べるゲームを作成して" --full-auto

# 一部のMCPサーバーのツールの使用を自動で許可する
cline-cli task "ブラウザで遊べるゲームを作成して" --full-auto --auto-approve-mcp
```

タスクの再開を制御するためのオプションも存在します。

```sh
# 同じタスクで中断しているものがあれば再開する
# 同じタスクですでに完了しているものがある場合は何もしない
cline-cli task "ブラウザで遊べるゲームを作成して" --resume

# 同じタスクで中断しているものがあれば再開する
# 同じタスクですでに完了しているものがある場合は新しくタスクを開始する
cline-cli task "ブラウザで遊べるゲームを作成して" --resume-or-new
```

ほかには設定ファイルやClineがファイルを保存するパスを設定するオプションもあります。

```sh
# 設定ファイルを指定する
# デフォルトは "~/.cline_cli/cline_cli_settings.json"
cline-cli task "ブラウザで遊べるゲームを作成して" --settings "./path/to/settings.json"

# ワークスペースを指定する
# デフォルトは "./"
cline-cli task "ブラウザで遊べるゲームを作成して" --workspace "./path/to/workspace"

# Clineがファイルを保存するパスを設定する
# デフォルトは "~/.cline_cli/storage"
cline-cli task "ブラウザで遊べるゲームを作成して" --storage "./path/to/storage"
```

## cline-cliの仕組み

cline-cliの仕組みはシンプルでClineがvscodeに依存している部分をcline-cliのコードに差し替えています。
具体的にはClineのコードで `import * as vscode from "vscode"` となっている部分を書き換えて `import * as vscode from "vscode-interface"` としています。これによってClineがVS Codeに依存している部分で任意の処理を挟むことができます。 

例えば、Clineのファイルを編集するツールである `write_to_file` や `replace_in_file` から使用される `vscode.workspace.applyEdit` は以下のような実装に差し替えられています。

```ts:workspaceの実装
    applyEdit(edit: WorkspaceEdit): Thenable<boolean> {
        return this._applyEdit(edit) 
    }

    private async _applyEdit(edit: WorkspaceEdit): Promise<boolean> {
        if (edit.uri == null || edit.range == null) {
            return false
        }

        const result = this.replace(edit.uri, edit.range, edit.newText ?? "")
        if (result) {
            this.onChangeFilesEmitter.fire(edit.uri)
        }
        return result
    }

    private replace(uri: Uri, range: Range, text: string) {
        try {
            const file = nodeFs.readFileSync(uri.fsPath, 'utf-8')
            let result = ""
            const lines = file.split('\n')
            for (let line = 0; line < lines.length; line++) {
                const lineText = lines[line]
                const suffix = line + 1 == lines.length ? "" : "\n"

                if (line < range.start.line || line > range.end.line) {
                    result += lineText + suffix
                    continue
                }

                if (line === range.start.line) {
                    const needToAdd = range.start.character

                    for (let c = 0; c < needToAdd && c < lineText.length; c++) {
                        result += lineText[c]
                    }

                    result += text
                }

                if (line === range.end.line) {
                    for (let c = range.end.character; c < lineText.length; c++) {
                        result += lineText[c]
                    }

                    result += suffix
                }
            }

            nodeFs.writeFileSync(uri.fsPath, result, "utf-8")
            return true
        }
        catch {
            return false
        }
    }
```

https://github.com/yaegaki/cline-cli/blob/f3fa98ba26a23749fbb5c33259d1feeb8eb35cbf/src/cline_cli/workspaceImpl.ts#L29C1-L84C6

Clineのソースコードをほぼそのまま流用しているので挙動の再限度を高くしやすく、アップデートにも追従しやすくなっています。  
(アップデートに関しては今後のCline側の変更によってはどうなるかわからない部分ではあります）  

## 最後に

まだ作ったばかりで全然自分でも試せてないですが、github actionsへ組み込んだりすれば面白いんじゃないかなと思っています。
現状はコマンドが実行できないのでやれることが少なくなってしまっているのでこの辺は対応していきたいですね。

## おまけ

cline-cliで `ブラウザで遊べるゲームを作成して` というタスクをお願いするとターゲットクリックゲームを作成してくれました。  

```sh
cline-cli task "ブラウザで遊べるゲームを作成して" --full-auto
```

```
Cline message:
type: say
say: completion_result
text:
ブラウザで遊べるターゲットクリックゲームを作成しました。

作成したファイル:
1. index.html - ゲームの構造
2. style.css - ゲームのスタイル
3. game.js - ゲームのロジック

ゲームの遊び方:
1. 「ゲームスタート」ボタンをクリックしてゲームを開始します
2. 青い背景のゲームエリアにランダムに表示される赤い円形のターゲットをクリックします
3. クリックするたびにスコアが1点増加します
4. ゲームは30秒間続きます
5. 時間が終了すると、最終スコアが表示されます

> index.htmlファイルをブラウザで開くだけで、このゲームをプレイすることができます。ゲームはシンプルですが、反射神経と素早さを試すことができる楽しいものになっています。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42193/7caee959-fac2-4193-9b98-6df19dfabcf5.png)


[^1]: [openai/codex](https://github.com/openai/codex) や [claude-code](https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/overview)など、ほかにもいろいろあります
[^2]: .clinerulesが使えなかったりする