К основному содержанию
скилл CX Codex

add-provider — скилл для Claude Code: новый провайдер в ClaudeBar

Описывает нового AI-провайдера в ClaudeBar одним JSON-файлом. Пригодится, когда нужно подключить вендора без правок на Swift.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add tddworks/ClaudeBar --skill add-provider -a claude-code
Открыть на GitHub

Что делает add-provider

Провайдер здесь — это данные, а не код. Вы кладёте файл Modules/Providers/Resources/Providers/<id>.json, и приложение находит его само: ProviderCatalog.detect() читает все определения в бандле, а также в ~/.claudebar/providers и ~/.claudebar/extensions. Регистрация не нужна, ни одна строка на Swift провайдера не называет.

Определение отвечает на три вопроса: кто это (profile), как входит аккаунт (settings, accounts) и откуда брать расход (dataSources). Внутри каждого источника — три закрытых набора: credential (где лежит ключ), fetch (как спросить) и mapping (как прочитать ответ). Один класс Provider и один тип DataSource прогоняют любое определение. Скрипт-маппер <id>-*.js рядом с JSON нужен только тогда, когда правило не читает формат — например, экран TUI.

К каждому определению идут golden-тесты в Modules/Providers/Tests/<Name>DefinitionTests.swift. Если возможностей языка не хватает — новый вид настройки, случай или воркер — это уже изменение дизайна: сначала правки в TARGET_ARCHITECTURE и CANONICAL_MODEL, потом код, и подтверждение пользователя до него. Имена воркеров берут от протокола, формата или места (JSONRPCFetcher, OAuth2Refresher), имя вендора в Swift модуля не попадает.

Какие задачи решает

  • добавить нового AI-провайдера в ClaudeBar одним JSON-файлом
  • подключить сохранённый ключ старого формата, чтобы он продолжил работать
  • описать вход в аккаунт через файл логина CLI
  • починить определение после смены CLI, API или формата файла у вендора
  • задать внешний вид провайдера: имя, символ, цвет, иконку

Примеры запросов агенту

  • Добавь провайдера Acme в ClaudeBar: ключ лежит в переменной окружения, расход берётся из HTTP API
  • Вендор сменил формат файла логина — обнови определение провайдера и его тесты
  • Сделай определение для нового провайдера и напиши golden-тесты в Modules/Providers/Tests/

Как установить add-provider

1Через Skills CLI (Vercel)

npx skills add tddworks/ClaudeBar --skill add-provider -a claude-code

2Вручную: положить файлы

~/.claude/skills/add-provider/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/add-provider/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о add-provider

Как установить add-provider в Claude Code?

Выполните npx skills add tddworks/ClaudeBar --skill add-provider -a claude-code. Скилл окажется в ~/.claude/skills/add-provider/SKILL.md.

Что делает скилл add-provider?

Проводит по добавлению AI-провайдера в ClaudeBar: файл <id>.json в Modules/Providers/Resources/Providers/, три набора на источник данных и golden-тесты к определению.

Нужно ли писать Swift для нового вендора?

Нет. Определение прогоняют один класс Provider и один тип DataSource. Swift нужен только если язык определений не умеет того, что требует провайдер, — и тогда это изменение дизайна с подтверждением пользователя.

Исходный текст

SKILL.md · Apache-2.0
---
name: add-provider
description: |
  Add an AI provider to ClaudeBar as a JSON definition run by the one generic
  Provider and DataSource. Use this skill when:
  (1) Adding a new AI assistant provider (like Antigravity, Cursor, etc.)
  (2) A provider needs a setting, an added account, or an old saved key kept working
  (3) A provider's CLI, API or file format changed and its definition must follow
  (4) User asks "how do I add a new provider" or "create a provider for X"
---

# Add a Provider to ClaudeBar

A provider is **data**: one file, `Modules/Providers/Resources/Providers/<id>.json`.
**The app finds it** — `ProviderCatalog.detect()` reads every definition in the
bundle (and in `~/.claudebar/providers`, `~/.claudebar/extensions`) and makes
each on one shared `Engine` ([TARGET_ARCHITECTURE §10](../../../docs/architecture/TARGET_ARCHITECTURE.md#10--a-definition-on-disk-is-a-provider)).
There is no registration step and no Swift line that names it.
It is an abstraction layer that answers three questions, each once: **who the
provider is** (`profile`), **how an account is signed in** (`settings`, and
`accounts` when the vendor keeps a login in a folder), and **how to get its
usage** (`dataSources`: where the key is, how to fetch, how to read the
answer). One
`Provider` class and one `DataSource` type run every definition. **You write
no Swift for a vendor**: no `XxxProvider`, no `XxxUsageProbe`, no
`XxxCredentialLoader`.

> Read first: [TARGET_ARCHITECTURE.md](../../../docs/architecture/TARGET_ARCHITECTURE.md)
> (how a definition runs), [MODULAR_DESIGN.md](../../../docs/architecture/MODULAR_DESIGN.md)
> (which module a file goes in), and [CANONICAL_MODEL.md](../../../docs/architecture/CANONICAL_MODEL.md)
> (the words). Every built-in provider is a definition, so there is a worked
> example for most shapes: an API key with a region (`minimax.json`), a CLI's login
> file renewed by OAuth (`grok.json`) or by the CLI itself (`gemini.json`), a TUI
> (`kimi.json`), a browser session over several requests (`alibaba.json`), an app's
> local server (`antigravity.json`), cloud metrics priced into money (`bedrock.json`).
> Their tests are in `Modules/Providers/Tests/`.

**The docs are the source of truth** ([AGENTS.md](../../../AGENTS.md#design-docs-are-the-source-of-truth)).
A provider that fits the definition language needs no design change. One that needs the
language to grow — a new case and worker, a new capability (CANONICAL §2.1), a new setting
kind — is a design change: write it into TARGET_ARCHITECTURE / CANONICAL_MODEL first and ask
the user to confirm before writing code.

## The pieces

| Piece | Where | You touch it when |
|---|---|---|
| `<id>.json` | `Modules/Providers/Resources/Providers/` | always |
| `<id>-*.js` mapping script | beside the JSON | only when no mapping rule can read the format (a TUI screen) |
| golden tests | `Modules/Providers/Tests/<Name>DefinitionTests.swift` | always |
| a generic rule or worker | `Modules/DataSources/` (+ `DataSourcesTests`) | when the definition language can't say what the provider needs |
| look (name, symbol, colour, icon) | `profile.look` in `<id>.json`; the icon image in the asset catalog | always |
| research | `docs/providers/<id>/README.md` (users), `design.md` (contributors) | always |

**The rules** ([MODULAR_DESIGN §3–4](../../../docs/architecture/MODULAR_DESIGN.md#3--the-dependency-rules)):
- No vendor's name in a module's Swift. A worker is named for its protocol, format or place (`JSONRPCFetcher`, `OAuth2Refresher`), never a vendor.
- No `Probe` names. The words are DataSource, Fetch, Mapping, Usage, Quota, Plan and Cost.
- `Modules/*` never `import Domain`. The usage model is `Quotas`; providers, settings and accounts are `Providers`.

## The definition: three closed sums per data source

```jsonc
{
  "profile": {                          // WHO IT IS
    "id": "acme",                       // stable forever: settings and the menu bar key on it
    "name": "Acme",
    "links": { "dashboard": "https://…", "status": "https://…" },
    "look": { "symbol": "bolt.fill", "icon": "AcmeIcon",          // SF Symbol, asset name
              "color": { "light": [0.2, 0.5, 0.9], "dark": [0.3, 0.6, 1.0] },
              "gradientEnd": { "light": [0.1, 0.3, 0.7], "dark": [0.2, 0.4, 0.8] } }
  },
  "cli": "acme",                        // optional
  "enabledByDefault": true,
  "order": 280,                         // its default place in the lineup; none → after the rest, by name
  "guestPasses": {},                    // optional: a capability the engine runs (Claude's)
  "defaultDataSource": "api",
  "dataSources": [
    {
      "kind": "api",                    // what Settings' Data source picker saves as <id>.probeMode
      "label": "API", "summary": "Calls Acme API directly",
      "credential": { … },              // CredentialLookup — where the key is
      "fetch":      { … },              // Fetch — how to ask
      "mapping":    { … },              // Mapping — how to read the answer
      "fallback": "cli"                 // optional: try this kind when this one fails
    }
  ]
}
```

| Sum | Cases |
|---|---|
| `credential` | `environment` (`"loginShell": true` also asks the person's login shell) · `browserStorage` (a site's local storage, one browser profile) · `setting` (a key pasted into ClaudeBar, in the Keychain) · `jsonFile` (paths `$.a.b`, or a list — the first that answers; `~` and `${VAR:-default}`; `record`, `defaults`) · `keychain` (`account`, `encoding: "goKeyringBase64"`) · `browserCookies` (`format: "value"\|"header"`) · `sqlite` · `firstOf` — any of them refined with `match` (a value must fit a pattern, or no key), `with` (values added) and `cookies` (named cookies read out of a Cookie header); plus `"refresh": { "oauth2": … }` or `"refresh": { "cli": … }` (the CLI renews its own file) |
| `fetch` | `http` (`{{token}}`, `{{x#host}}`, `{{x#jwt.claim}}`, `{{system.timeZone}}`) or `"http": { "steps": [ … ] }` (`keep`, `optional`, `unless`, `attempts`, `dropEmpty`) · `jsonRpc` · `cli` — a terminal, for a TUI (`input`, `inputDelay`, `autoResponses`, `readyWhen`, `screen: "rendered"`; Mac, Linux and Windows) · `command` — pipes, exit code reported (Mac and Windows, like `jsonRpc` and `script`) · `file` · `directory` · `sqlite` (rows of an app's own database, read-only) · `localServer` (an app's server on 127.0.0.1, found through its process, on every platform; a test hands the stubbed `make` its `running:` programs) · `cloudWatch` (cloud metrics through a port, priced from a `PriceCatalog`) |
| `mapping` | `json` (below) · `text` (error phrases, then label + regex for % left/used) · `script` (a `.js` file in QuickJS, no I/O, no `Intl`; host `humanDate()`, `jsonDecimal()`, `decimalCents()`, `decimalAdd()`, `decimalMultiply()`; `Date`'s local time is the person's zone on every platform; `context.values` from `"values"`; returns `quotas` (with `group`), `notes`, `plan`, `cost` (with `lines`), `account`, or `error`) |

Per data source, also: `errors` (`http.<status>`, `http.default`,
`cli.missing`, `cli.nonzero`, `cli.failed` → a reason; a 429 is always a rate
limit), `fallbackOn` (hand-off by failure tag), `fallback`, `cache.ttl` (also
the background-refresh floor), `context` (other JSON files the mapping may read),
`recover.patchJSONFile`, `requiresFiles`, `verifyBeforeBackground`. Per
provider: `links.dashboardByPlan` and `accounts` (below). Who is signed in is
never written per data source: it is `accounts.folder.signedInAs`, said once.

### Settings and added accounts

```jsonc
"settings": [
  { "id": "apiKey", "label": "API Key", "kind": "secret", "scope": "account", "for": ["api"] },
  { "id": "region", "label": "Region", "scope": "account", "default": "china",
    "kind": { "choice": [ { "id": "china", "label": "China", "host": "api.acme.cn" },
                          { "id": "intl",  "label": "International", "host": "api.acme.com" } ] } },
  { "id": "home", "label": "Signed-in Folder", "scope": "account", "for": ["cli"], "kind": { "path": { "mustExist": true } } },
  { "id": "authEnvVar", "label": "Environment variable", "default": "ACME_API_KEY" }
]
```

- A kind owns its rule: `secret` (to the Keychain, fills nothing), `choice` (its options carry values), `path` (`mustExist`), `text` (`pattern`).
- `{{setting.region.host}}` fills any string of the definition; a blank setting leaves its template unfilled (a script's `values` drop it).
- `scope: "account"` is what *Add Account* asks for; `"for": [kind]` asks only while that data source is active, and a login without such a value runs only the sources that don't need it.

### Added accounts: what an account's sign-in reaches

**An account shows only what its own sign-in reaches** ([multi-account design](../../../docs/features/providers/multi-account/design.md#what-an-accounts-sign-in-reaches)).
The default account is signed in on this Mac. An added account is signed in
through what the person gave it: a key, a folder, or both. Write every read
**once, for any account**:

- A read that **names the account** is each account's own: `{{account.<id>}}`
  (a folder setting, `{{account.folder}}`) or a `{ "setting": … }` lookup.
- Any other read (an environment variable, a fixed path or Keychain name,
  browser cookies, an app's own database, a log at a fixed place) is **this
  Mac's**: the default account makes it, and an added account leaves it out by
  itself. There is no `accounts.patch`; forgetting a read can't leak.
- A folder an account was given, and a read uses, must still be there, or that
  account is *not signed in*.

| The vendor keeps a login… | You write |
|---|---|
| as a **key** the person can paste (API key, session token, cookie) | an account-scope `secret` setting, and `{ "setting": "<id>" }` somewhere in the lookup chain (append it; keep the default account's order). **No `accounts` block** |
| in a **folder** the person names on the form | an account-scope `path` setting with the vendor's default folder (`"default": "~/.grok"`), reads under `{{account.<id>}}`; `accounts.cli` only if a CLI must be pointed at it |
| in a **folder ClaudeBar checks** (Choose Signed-in Folder, Sign in with browser) | `accounts.folder` and `accounts.signIn`, reads under `{{account.folder}}`, and `accounts.cli` |
| as a key the **CLI** takes | two data sources: one with the account's key handed to the CLI (`{{token}}` in its environment), falling back on `authenticationRequired` to one that runs the CLI as its own login, which is this Mac's (`ampcode.json`) |

```jsonc
"accounts": {
  "folder": {
    "default": "${CODEX_HOME:-~/.codex}",           // where the vendor keeps the default account's login
    "signedInAs": {                                 // "Signed in as …": read once, for every data source
      "file": "{{account.folder}}/auth.json",       // a list where the vendor keeps the default elsewhere
      "id": "$.tokens.account_id",                  // checked before and after every fetch of an added account
      "email": "$.tokens.id_token#jwt.email"        // the pill's; a mapping reads any field as $signedInAs.<field>
    },
    "notSignedIn": "…"                              // optional
  },
  "signIn": { "args": ["login"] },                  // the vendor's login; its program is "cli"
  "cli": {                                          // every CLI run of an added account: fetch, renewal, sign-in
    "environment": { "set": { "CODEX_HOME": "{{account.folder}}" }, "unset": ["OPENAI_API_KEY"] },
    "args": ["-c", "cli_auth_credentials_store=\"file\""]
  }
}
```

Where the vendor keeps the default account somewhere else, write both places
where they are read: a path list (`["{{account.folder}}/.claude.json",
"${CLAUDE_CONFIG_DIR:-~}/.claude.json"]`) or a name worked out from the folder
(`"Claude Code-credentials-{{account.folder#sha256.8}}"`).

### The JSON mapping

```jsonc
"json": {
  "plan":  { "path": "$credential.plan", "plans": { "max": "claudeMax" } },   // or "badges"
  "email": ["$.account.email", "$credential.email"],
  "quotas": [
    { "kind": "session", "at": "$.five_hour", "usedPercent": "utilization",
      "resetsAt": { "iso8601": "resets_at" } },               // or epochSeconds / secondsFromNow
    { "kind": "model", "each": "$.limits", "where": { "path": "kind", "equals": "weekly" },
      "name": { "firstOf": ["model.name"], "firstWord": true, "lowercase": true },
      "usedPercent": "percent", "unique": true, "overLimit": true, "countdown": "hours",
      "window": [{ "seconds": "window_seconds" }, { "days": 7 }] },   // the response's word, else the provider's
    { "kind": "time", "name": "Credits",                             // money, not a percentage:
      "left": { "money": "$.data.remaining", "of": "$.data.limit", "currency": "USD" } }  // no "of" = a balance
  ],
  "cost": [                                                    // the first shape that answers
    { "kind": "extraUsage", "when": { "path": "$.spend.enabled", "equals": true },
      "used":  { "amount": "$.spend.used.amount_minor", "decimals": "$.spend.used.exponent" },
      "limit": { "amount": "$.spend.limit.amount_minor", "decimals": "$.spend.limit.exponent" } }
  ],
  "whenEmpty": { "if": { "path": "$.plan", "equals": "free" }, "quotas": [ … ], "otherwise": "No data yet" },
  "notAnObject": "Failed to parse usage response"
}
```

Paths: `$.a.b` from the root, `a.b` from the current object, `$header.x`, `$key`
(the map key inside `each`), `$credential.x` (a non-secret credential value),
`$signedInAs.field` (who is signed in: a field of `accounts.folder.signedInAs`),
`$context.file.field` (a field of another `context` file the data source reads).
A list of values means *the first that answers*; a number is a constant.

## TDD workflow (Chicago school)

Name each test `should <outcome> [when <situation>]`, in the person's words, never a method, type or mechanism verb → [Naming tests](../implement-feature/references/tdd-patterns.md#naming-tests).

### 1 · Research and fixtures
Find where the usage really comes from (CLI command, endpoint, local file) and
capture **real** responses, redacted, including the failure answers: logged
out, rate limited, a free plan, an empty account. Write what you learned in
`docs/providers/<id>/design.md`.

### 2 · Golden tests first (red)
`Modules/Providers/Tests/<Name>DefinitionTests.swift` runs the real definition
through the real `Provider` over stubbed connections with `StubbedProvider`
(`Tests/Support/Connections.swift`). They fail first because `<id>.json` doesn't exist.

```swift
@MainActor
@Suite
struct AcmeDefinitionTests {
    @Test
    func `should read Acme from its API by default`() throws {
        let acme = try ProviderFactory.builtIn("acme")
        #expect(acme.dataSources.map(\.kind) == ["api"])
        #expect(acme.defaultDataSource == "api")
    }

    @Test
    func `should show the session window when the API answers`() async throws {
        let stub = try StubbedProvider(providerId: "acme")
        defer { stub.cleanUp() }
        stub.environment = ["ACME_API_KEY": "test-key"]
        stub.answerHTTP(#"{"session":{"used_percent":30,"reset_at":1735000000}}"#)

        let usage = try await stub.make("acme").refreshPlain()

        #expect(usage.quota(for: .session)?.percentRemaining == 70)
        #expect(usage.quota(for: .session)?.resetsAt == Date(timeIntervalSince1970: 1735000000))
    }

    @Test
    func `should say the key is missing when the API has no key`() async throws {
        let stub = try StubbedProvider(providerId: "acme")
        defer { stub.cleanUp() }
        let acme = try stub.make("acme")

        await #expect(throws: UsageError.authenticationRequired) { try await acme.refresh(acme.defaultAccount) }
        #expect(acme.defaultAccount.lastFailedStep == .lookup)
    }
}
```

Assert on **state**: the usage, `lastError`, `lastFailedStep`, `answeredBy`, and
files written back. Don't `verify()` calls. Cover every fixture from step 1.

When a provider can have an added account, test that it reaches only its own
sign-in. `definition.dataSources(forAccount:)` is what an added account runs; a
test reading a built-in data source for the default account uses
`definition.asDefaultAccount(kind)` (`Tests/Support/DefaultAccount.swift`), since
`{{account.x}}` is only filled for an account:

```swift
@Test
func `should never read this Mac's key for an added account`() throws {
    let acme = try ProviderFactory.builtIn("acme")

    let added = try #require(try acme.dataSources(forAccount: [:]).first)

    #expect(added.credential == .firstOf([.setting("apiKey")]))   // ACME_API_KEY is this Mac's
}
```

### 3 · Write the definition (green)
Add `<id>.json` until the golden tests pass. `ProviderFactory.builtIn` validates it:
kinds are unique, and the default and every fallback name an existing kind.

### 4 · When the language can't say it
Don't write vendor code. Find the **generic** shape of the need, for example
"a list filtered by a field" or "money in minor units", and add it to
`DataSources` test-first in `Modules/DataSources/Tests/`. Then use it from the
JSON. [ENGINE_DESIGN §1](../../../docs/architecture/ENGINE_DESIGN.md#1--what-each-provider-needed-as-a-general-rule)
lists the pieces each provider needed. Add your row there.

Only a format no rule can read, like a terminal UI screen, gets a mapping
script, `<id>-<what>.js`. Test it through Swift with real captured screens
(see `ClaudeUsageScreenTests`).

### 5 · Give it a look and a place
There is nothing to register: the file being in `Resources/Providers/` makes it
a provider. `"order"` places it in the default lineup (the built-ins use 10, 20,
… 270); leave it out to come after the rest, by name. The person's own order
still wins. `DetectionTests` lists the built-ins in order — add your id there.

Every provider gets the same `Engine`: settings, the vault, the login shell (for
a lookup with `"loginShell": true`), the cloud ports (for a `cloudWatch` fetch),
the guest-passes runner (for `"guestPasses": {}`). Ask for one in the JSON;
never pass one in Swift.

Its name, symbol and colours are `profile.look` in the JSON — no `switch id`
table to edit. Add the icon image to the asset catalog under `look.icon`
([references/provider-icon-guide.md](references/provider-icon-guide.md)).
Settings need no new protocol: the data source choice is
`dataSourceKind(forProvider:)`, and an on/off setting a definition names (for
example `fallback.enabledBySetting`) is `isOn(_:forProvider:)`.

### 6 · Docs and release note
- `docs/providers/<id>/README.md` (what users see, setup, errors) and `design.md` (sources, fields, gotchas).
- A row in the **Providers** table of the root `README.md` — name, what it tracks, a link to its `docs/providers/<id>/README.md` — placed by its `"order"`, so the table follows the default lineup.
- A tile in the landing page's providers grid (`website/public/index.html`, `<div class="pv" data-id="<id>" …>`), in lineup order, and every count on the page raised by one — `npm test` in `website/` reads the definitions and fails until both match.
- One line under `## [Unreleased]` in `CHANGELOG.md`.
- `python3 scripts/gen-docs.py && python3 scripts/check-docs.py --strict`.

## Keeping what people saved

Every built-in provider is already a definition; when you change one, keep
every saved value working. The data source choice is `<id>.probeMode`, and a
setting `foo` is read from `<id>.foo`. A value kept somewhere else gets a row,
never a branch:

- a setting under another key → `JSONSettingsRepository.legacySettingKeys`
- a setting in UserDefaults → `JSONSettingsRepository.legacyDefaultsKeys` (a saved number or list reads as text)
- a secret in UserDefaults or an older Keychain item → `ProviderVault.legacyKeys`
- an added account's value under an older name (`codexHome` → `folder`) → `JSONSettingsRepository.renamedAccountValues`

Each moves to its new place the first time it's saved, and each needs a test.

## Checklist

- [ ] Real fixtures captured (success and every failure), research in `design.md`
- [ ] Golden tests written first and failing
- [ ] `<id>.json` makes them pass; no vendor-named Swift anywhere
- [ ] Any new mapping/fetch/lookup ability added generically to `DataSources`, test-first, and listed in ENGINE_DESIGN §1
- [ ] An added account reaches only its own sign-in: its key is an account-scope setting in the lookup chain, folder reads name `{{account.x}}`, and a test checks `dataSources(forAccount:)`
- [ ] `"order"` set if it has a place in the default lineup, and its id in `DetectionTests`
- [ ] `profile.look` filled in and the icon added to the asset catalog
- [ ] Provider docs, its row in the root `README.md` Providers table, its landing-page tile and counts (`npm test` in `website/`), and the CHANGELOG line written; docs check passes
- [ ] `tuist test` green

© tddworks · Apache-2.0 · tddworks/ClaudeBar

Похожие скиллы

Все скиллы по теме «Настройка агента: память, контекст, субагенты» →
setup-matt-pocock-skills Matt Pocock Скилл настраивает репозиторий для инженерных скиллов Matt Pocock: задаёт трекер задач, метки триажа и расположение доменной документации. Запустите его перед первым использованием остальных скиллов. 1 052 851установок ask-matt Matt Pocock Помогает выбрать подходящий скилл или рабочий поток из набора Matt Pocock. Пригодится, когда непонятно, с чего начать задачу: от идеи до разбора бага. 766 587установок to-tickets Matt Pocock Разбивает план, спецификацию или переписку на небольшие сквозные задачи и отмечает, какие из них блокируют остальные. Пригодится, когда нужно согласовать порядок работы до публикации задач в трекере. 711 230установок writing-plans Jesse Vincent Скилл превращает требования к многошаговой задаче в подробный план реализации до начала работы с кодом. Пригодится, когда исполнителю нужно зафиксировать файлы, интерфейсы и проверки. CX Codex 282 859установок caveman-review JuliusBrussee Пишет замечания к коду одной строкой: место, проблема, исправление. Пригодится, когда ревью диффа или PR надо пройти быстро и без воды. CX Codex +1 411 800установок caveman-compress JuliusBrussee Сжимает CLAUDE.md, списки задач и другие текстовые файлы в краткий формат, сохраняя читаемую резервную копию. CX Codex +1 411 500установок

Из того же набора4

Спонсоры проекта

Компании, которые помогают держать аналитику открытой