📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
{
|
||||
"name": "brooks-lint",
|
||||
"description": "AI code reviews grounded in twelve classic engineering books — decay risk diagnostics with book citations, severity labels, and six analysis modes (PR review, architecture audit, tech debt, test quality, health dashboard, full-sweep auto-fix)",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"source": "./",
|
||||
"author": {
|
||||
"name": "hyhmrright",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "brooks-lint",
|
||||
"description": "AI code reviews grounded in twelve classic engineering books — decay risk diagnostics with book citations, severity labels, and six analysis modes (PR review, architecture audit, tech debt, test quality, health dashboard, full-sweep auto-fix)",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"author": {
|
||||
"name": "hyhmrright",
|
||||
"email": "hyhmrright@gmail.com"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "brooks-lint",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"description": "AI code reviews grounded in twelve classic engineering books — decay risk diagnostics with book citations, severity labels, and six analysis modes (PR review, architecture audit, tech debt, test quality, health dashboard, full-sweep auto-fix)",
|
||||
"author": {
|
||||
"name": "hyhmrright",
|
||||
|
||||
@@ -24,6 +24,9 @@ inputs:
|
||||
model:
|
||||
description: "Claude model to use"
|
||||
default: "claude-sonnet-4-6"
|
||||
api-base-url:
|
||||
description: "Anthropic-compatible /v1/messages endpoint to call instead of api.anthropic.com (empty = Anthropic)"
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
score:
|
||||
@@ -71,10 +74,17 @@ runs:
|
||||
shell: bash
|
||||
env:
|
||||
ANTHROPIC_API_KEY: ${{ inputs.anthropic-api-key }}
|
||||
API_BASE_URL: ${{ inputs.api-base-url }}
|
||||
SARIF_FILE: ${{ inputs.sarif-file }}
|
||||
MODE: ${{ inputs.mode }}
|
||||
MODEL: ${{ inputs.model }}
|
||||
run: |
|
||||
# Exported only when the input is set: naming ANTHROPIC_BASE_URL in the
|
||||
# env block above would blank out one inherited from the job env. The
|
||||
# SDK reads the variable itself, so nothing downstream needs a flag.
|
||||
if [ -n "$API_BASE_URL" ]; then
|
||||
export ANTHROPIC_BASE_URL="$API_BASE_URL"
|
||||
fi
|
||||
sarif_arg=()
|
||||
if [ -n "$SARIF_FILE" ]; then
|
||||
sarif_arg=(--sarif-out "$SARIF_FILE")
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
name: Star history
|
||||
|
||||
# GitHub restricted the stargazers API to a repo's own admins and collaborators,
|
||||
# so no third-party service can chart this repo any more. GITHUB_TOKEN can read
|
||||
# it, so we regenerate the committed SVG here instead.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "17 3 * * 1" # Mondays, 03:17 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: star-history
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
refresh:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Regenerate the chart
|
||||
run: node scripts/gen-star-history.mjs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Commit when the chart moved
|
||||
run: |
|
||||
if git diff --quiet -- assets/star-history.svg assets/star-history.json; then
|
||||
echo "No change."
|
||||
exit 0
|
||||
fi
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git commit -m "chore: refresh the star history chart" -- assets/star-history.svg assets/star-history.json
|
||||
git push
|
||||
@@ -2,6 +2,28 @@
|
||||
|
||||
All notable changes to brooks-lint are documented here.
|
||||
|
||||
## [1.5.0] - 2026-08-14
|
||||
|
||||
### Added
|
||||
|
||||
- **DeepSeek Harness (`dsh`) support** — DeepSeek AI's open-source agent harness
|
||||
loads standard Agent Skills through `packages/skill/skill-filesystem`, so all
|
||||
six modes run with no conversion. `./scripts/install.sh dsh` installs into
|
||||
`$DSH_HOME/skills` (default `~/.dsh/skills`); `--project` targets
|
||||
`./.dsh/skills`. dsh also scans `~/.agents/skills`, so the existing
|
||||
vendor-neutral `install.sh agents` install already covered it. New guide at
|
||||
[`docs/dsh-setup.md`](docs/dsh-setup.md), with the platform added to the README
|
||||
install table in all six languages and to `docs/getting-started.md`.
|
||||
|
||||
Three details of dsh's loader matter and are documented in the guide: discovery
|
||||
is one level deep (`<root>/<name>/SKILL.md`), which the installer's flat layout
|
||||
already satisfies so `../_shared/` resolves; skill names must be kebab-case,
|
||||
which `brooks-*` already is; and dsh recognises a whitespace-bounded `/name`
|
||||
token anywhere in a message, so `/brooks-review` and the other five work from
|
||||
its `/` menu or typed inline. dsh also reads `AGENTS.md` — `$DSH_HOME/AGENTS.md`
|
||||
plus every file from the project root down to the working directory — so the
|
||||
repo's Iron Law and Health Score rules load the same way they do elsewhere.
|
||||
|
||||
## [1.4.3] - 2026-08-04
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -36,6 +36,8 @@ Guidance for Claude Code when modifying this repository. For repo layout, instal
|
||||
- `` field `commands` is not accepted `` — `"commands": []` is what stops the CLI from migrating `commands/*.md` into duplicate `source-command-brooks-*` skills (issue #22). Confirmed by installing both ways under an isolated `CODEX_HOME`: drop the field and Codex regenerates `.codex-plugin/migrated-command-skills/`. The CLI's `RawPluginManifest` accepts `commands`; only the ingestion schema rejects it.
|
||||
- `` skill `_shared` is missing `SKILL.md` `` — the validator only skips dot-prefixed dirs, and `_shared/` is a shared-framework dir by design (see above). Renaming it to `.shared/` would also require changing `install.sh`'s `cp -R "$SRC"/*`, whose glob does not match dot-dirs.
|
||||
- **GitHub Action cache:** `.github/actions/brooks-lint/action.yml` uses `actions/cache@v4` with built-in cache-hit guard — do NOT add a manual directory check.
|
||||
- **Adding a platform is derived, not hardcoded:** create `docs/<name>-setup.md`, add the platform to `install.sh`'s `PLATFORMS` **and** both `global_dir()` / `project_dir()` case tables, then link the new guide from every `README*.md` and `docs/getting-started.md`. `npm run validate` derives both sides (`scripts/platforms.mjs`) and fails on any gap — a guide missing from one translation, or a `PLATFORMS` entry with no directory mapping. Do not add a hand-maintained platform list anywhere.
|
||||
- **Star history is data-first, never hand-drawn:** `assets/star-history.json` (raw `starred_at` timestamps, no usernames) is the source of truth; `assets/star-history.svg` is a pure function of it. Never hand-edit the SVG — `npm run validate` re-renders and fails on any mismatch. `node scripts/gen-star-history.mjs` refetches (needs `GITHUB_TOKEN` or `gh auth`, since GitHub restricted the stargazers API to admins/collaborators on 2026-06-30); `--render-only` redraws offline from the committed data. The render must stay deterministic — anchoring the time axis to the clock would make the weekly workflow commit noise on every run.
|
||||
- **Custom risks:** Teams add project-specific risk codes via `custom-risks-guide.md` in their project root. Template lives at `skills/_shared/custom-risks-guide.md`.
|
||||
|
||||
## How the Skills Work
|
||||
|
||||
+26
-12
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -78,7 +78,7 @@ Luego solo pide ("revisa este PR", "audita la arquitectura"), o ejecuta uno de l
|
||||
([qué hace cada uno](#comandos-de-barra)).
|
||||
|
||||
Cada hallazgo se devuelve como **Síntoma → Origen → Consecuencia → Remedio** con una cita de libro y
|
||||
una puntuación de salud de 0 a 100. Las opciones completas de instalación (8 plataformas más) y la
|
||||
una puntuación de salud de 0 a 100. Las opciones completas de instalación (9 plataformas más) y la
|
||||
configuración de CI/CD están [más abajo](#instalación).
|
||||
|
||||
## Los doce libros
|
||||
@@ -273,7 +273,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # pídelo dentro
|
||||
|
||||
O usa el instalador de abajo: `./scripts/install.sh gemini` / `./scripts/install.sh codex`.
|
||||
|
||||
### Cualquier otra plataforma — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### Cualquier otra plataforma — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint se distribuye como [Agent Skills](https://agentskills.io) estándar. **Cualquier agente que cargue Agent
|
||||
Skills ejecuta los seis modos sin conversión alguna** — un solo comando los instala:
|
||||
@@ -281,7 +281,7 @@ Skills ejecuta los seis modos sin conversión alguna** — un solo comando los i
|
||||
```bash
|
||||
# elige tu plataforma; --project instala en el repositorio actual en lugar de en tu configuración global
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
El instalador copia los skills **de forma plana** en la carpeta correcta, de modo que el framework compartido
|
||||
@@ -298,11 +298,12 @@ El instalador copia los skills **de forma plana** en la carpeta correcta, de mod
|
||||
| GitHub Copilot | `.github/skills` (`--project`) | `.claude/skills`, `AGENTS.md` | [configuración](docs/copilot-setup.md) |
|
||||
| Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [configuración](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [configuración](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [configuración](docs/dsh-setup.md) |
|
||||
|
||||
Kiro y Factory Droid también registran `/brooks-review` automáticamente. ¿Nuevo en los skills, o usas un
|
||||
agente que no aparece aquí? Consulta **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
Kiro, Factory Droid y DeepSeek Harness también registran `/brooks-review` automáticamente. ¿Nuevo en los
|
||||
skills, o usas un agente que no aparece aquí? Consulta **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
|
||||
> **🧪 Estado de verificación.** Claude Code, Gemini CLI y Codex CLI están verificados por el mantenedor. Las ocho
|
||||
> **🧪 Estado de verificación.** Claude Code, Gemini CLI y Codex CLI están verificados por el mantenedor. Las nueve
|
||||
> plataformas anteriores están documentadas a partir de la especificación oficial de skills de cada herramienta y verificadas a nivel
|
||||
> de diseño de archivos (el instalador está probado), pero el mantenedor aún no las ha ejecutado de extremo a extremo en cada plataforma. ¿Probaste
|
||||
> alguna — funciona **o** está rota? [Abre un issue](https://github.com/hyhmrright/brooks-lint/issues/new) con
|
||||
@@ -322,10 +323,11 @@ agente que no aparece aquí? Consulta **[docs/getting-started.md](docs/getting-s
|
||||
|
||||
**Sintaxis por plataforma.** Claude Code también acepta la forma con espacio de nombres
|
||||
`/brooks-lint:brooks-review` — las formas cortas las instala el hook session-start al iniciar la primera
|
||||
sesión. Codex CLI usa `$brooks-review`. Gemini CLI usa la tabla tal cual. OpenCode, Cursor, Antigravity y pi
|
||||
invocan los Agent Skills desde la `description` de cada skill, así que basta con pedirlo ("revisa este PR",
|
||||
"¿dónde está nuestra peor deuda técnica?"); para invocarlos explícitamente usa la sintaxis propia de cada
|
||||
plataforma (pi registra cada skill como `/skill:brooks-review`). En todas las plataformas los skills también
|
||||
sesión. Codex CLI usa `$brooks-review`. Gemini CLI usa la tabla tal cual. OpenCode, Cursor, Antigravity, pi y
|
||||
DeepSeek Harness invocan los Agent Skills desde la `description` de cada skill, así que basta con pedirlo
|
||||
("revisa este PR", "¿dónde está nuestra peor deuda técnica?"); para invocarlos explícitamente usa la sintaxis
|
||||
propia de cada plataforma (pi registra cada skill como `/skill:brooks-review`; dsh usa la tabla tal cual, desde
|
||||
su menú `/` o escrito a mano). En todas las plataformas los skills también
|
||||
se activan solos cuando hablas de calidad de código, arquitectura o salud de las pruebas.
|
||||
|
||||
> Las revisiones de PR incluyen automáticamente una comprobación rápida de pruebas (Step 7, ligera; se omite
|
||||
@@ -448,6 +450,18 @@ La action publica la revisión como un comentario del PR y, opcionalmente, hace
|
||||
|
||||
`fail-on-regression` lee `.brooks-lint-history.json`, así que confirma ese archivo para imponer "sin nuevas regresiones". Definir `sarif-file` hace que los hallazgos aparezcan en línea en la pestaña **Files changed** del PR y requiere el permiso `security-events: write` en el job.
|
||||
|
||||
**Endpoint de API personalizado.** `api-base-url` apunta la action a cualquier endpoint `/v1/messages` compatible con Anthropic —un proxy autoalojado, una pasarela de LLM, un espejo regional— en lugar de `api.anthropic.com`. Pasa la clave de ese endpoint como `anthropic-api-key` y el id de modelo que espera como `model`:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint envía tu diff al host que indiques aquí, así que apúntalo solo a uno en el que confíes con tu código fuente. Ejecutar `scripts/ci-review.mjs` por tu cuenta no necesita ninguna opción: el SDK de Anthropic lee `ANTHROPIC_BASE_URL` directamente.
|
||||
|
||||
**Coste:** ~$0,05–0,15 por ejecución de PR, según el tamaño del diff y el modelo. Se recomienda ejecutar solo en eventos `pull_request`.
|
||||
|
||||
## Hoja de ruta
|
||||
@@ -486,7 +500,7 @@ síntesis de sus ideas, aplicada a la evaluación moderna de la calidad del cód
|
||||
|
||||
## Historial de estrellas
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+25
-11
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -78,7 +78,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
`/brooks-health`、`/brooks-sweep`([それぞれの機能](#スラッシュコマンド))。
|
||||
|
||||
すべての指摘は **症状 → 根源 → 結果 → 対策** の形式で、書籍の出典と 0〜100 の健全性スコアとともに
|
||||
返されます。完全なインストール方法(さらに 8 つのプラットフォーム)と CI/CD のセットアップは
|
||||
返されます。完全なインストール方法(さらに 9 つのプラットフォーム)と CI/CD のセットアップは
|
||||
[以下](#インストール)を参照してください。
|
||||
|
||||
## 十二冊の書籍
|
||||
@@ -273,7 +273,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # Codex セッ
|
||||
|
||||
または下記のインストーラーを使用:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。
|
||||
|
||||
### その他すべてのプラットフォーム — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### その他すべてのプラットフォーム — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint は標準的な [Agent Skills](https://agentskills.io) として配布されています。**Agent
|
||||
Skills を読み込むエージェントなら、どれも変換なしで六つすべてのモードを実行できます**——1 つのコマンドでインストールできます:
|
||||
@@ -281,7 +281,7 @@ Skills を読み込むエージェントなら、どれも変換なしで六つ
|
||||
```bash
|
||||
# プラットフォームを選択;--project はグローバル設定ではなく現在のリポジトリにインストール
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
インストーラーはスキルをあなたのプラットフォームに適したフォルダへ**フラット**にコピーするため、共有フレームワーク
|
||||
@@ -299,11 +299,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
| GitHub Copilot | `.github/skills`(`--project`) | `.claude/skills`、`AGENTS.md` | [設定](docs/copilot-setup.md) |
|
||||
| Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [設定](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [設定](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [設定](docs/dsh-setup.md) |
|
||||
|
||||
Kiro と Factory Droid は `/brooks-review` も自動登録します。スキルが初めて、または上記にないエージェントを
|
||||
お使いですか? **[docs/getting-started.md](docs/getting-started.md)** を参照してください。
|
||||
Kiro、Factory Droid、DeepSeek Harness は `/brooks-review` も自動登録します。スキルが初めて、または
|
||||
上記にないエージェントをお使いですか? **[docs/getting-started.md](docs/getting-started.md)** を参照してください。
|
||||
|
||||
> **🧪 検証状況。** Claude Code、Gemini CLI、Codex CLI はメンテナーによって検証済みです。上記の八つの
|
||||
> **🧪 検証状況。** Claude Code、Gemini CLI、Codex CLI はメンテナーによって検証済みです。上記の九つの
|
||||
> プラットフォームは各ツールの公式スキル仕様から文書化され、ファイルレイアウトのレベルで検証されています
|
||||
> (インストーラーはテスト済み)が、メンテナーがすべてのプラットフォームでエンドツーエンドに実行したわけ
|
||||
> ではまだありません。どれかを試した——動いた **または** 壊れた? プラットフォーム、バージョン、見たこと
|
||||
@@ -323,10 +324,11 @@ Kiro と Factory Droid は `/brooks-review` も自動登録します。スキル
|
||||
|
||||
**プラットフォーム別の構文。** Claude Code は名前空間付きの完全形 `/brooks-lint:brooks-review` も受け付けます
|
||||
——短縮形は session-start フックが最初のセッション開始時に自動インストールします。Codex CLI は
|
||||
`$brooks-review`。Gemini CLI は上の表のとおり。OpenCode、Cursor、Antigravity、pi は各スキルの
|
||||
`description` から Agent Skills を呼び出すので、話しかけるだけで十分です(「この PR をレビューして」
|
||||
`$brooks-review`。Gemini CLI は上の表のとおり。OpenCode、Cursor、Antigravity、pi、DeepSeek Harness は
|
||||
各スキルの `description` から Agent Skills を呼び出すので、話しかけるだけで十分です(「この PR をレビューして」
|
||||
「最悪の技術的負債はどこ?」)。明示的に呼び出す場合は各プラットフォームの構文を使います(pi は各スキルを
|
||||
`/skill:brooks-review` として登録)。どのプラットフォームでも、コード品質・アーキテクチャ・テストの健全性に
|
||||
`/skill:brooks-review` として登録。dsh は上の表のとおりで、`/` メニューから選ぶか直接入力)。どの
|
||||
プラットフォームでも、コード品質・アーキテクチャ・テストの健全性に
|
||||
ついて話すと、スキルは自動的にトリガーされます。
|
||||
|
||||
> PR レビューには軽量な Step 7 クイックテストチェックが自動的に含まれます(ドキュメントのみの diff では
|
||||
@@ -448,6 +450,18 @@ jobs:
|
||||
|
||||
`fail-on-regression` は `.brooks-lint-history.json` を読み取るため、そのファイルをコミットすれば「新たな回帰なし」を強制できます。`sarif-file` を設定すると、指摘が PR の **Files changed** タブにインラインで表示されるようになり、ジョブに `security-events: write` 権限が必要になります。
|
||||
|
||||
**カスタム API エンドポイント。** `api-base-url` を指定すると、action は `api.anthropic.com` ではなく Anthropic 互換の `/v1/messages` エンドポイント(自前のプロキシ、LLM ゲートウェイ、リージョンミラーなど)を呼び出します。そのエンドポイントのキーを `anthropic-api-key` に、期待されるモデル id を `model` に渡してください:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint はここで指定したホストに diff を送信するため、ソースコードを預けられる相手だけを指定してください。`scripts/ci-review.mjs` を自分で実行する場合はフラグは不要です — Anthropic SDK が `ANTHROPIC_BASE_URL` を直接読み取ります。
|
||||
|
||||
**コスト:** PR 実行ごとにおよそ $0.05〜0.15、diff のサイズとモデルによります。`pull_request` イベントのみで実行することを推奨します。
|
||||
|
||||
## ロードマップ
|
||||
@@ -486,7 +500,7 @@ MIT License — 詳細は [LICENSE](LICENSE) を参照してください。
|
||||
|
||||
## スター履歴
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+25
-11
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -78,7 +78,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
`/brooks-sweep`([각각 하는 일](#슬래시-명령)).
|
||||
|
||||
모든 진단은 도서 출처와 0–100 건강 점수와 함께 **증상 → 근원 → 결과 → 처방** 형태로 돌아옵니다. 전체
|
||||
설치 옵션(추가 8개 플랫폼)과 CI/CD 설정은 [아래](#설치)를 참고하세요.
|
||||
설치 옵션(추가 9개 플랫폼)과 CI/CD 설정은 [아래](#설치)를 참고하세요.
|
||||
|
||||
## 열두 권의 책
|
||||
|
||||
@@ -272,7 +272,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # Codex 세션
|
||||
|
||||
또는 아래 설치기를 사용하세요: `./scripts/install.sh gemini` / `./scripts/install.sh codex`.
|
||||
|
||||
### 그 밖의 모든 플랫폼 — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### 그 밖의 모든 플랫폼 — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint는 표준 [Agent Skills](https://agentskills.io) 형태로 배포됩니다. **Agent
|
||||
Skills를 로드하는 모든 에이전트는 변환 없이 여섯 가지 모드를 모두 실행합니다** — 한 줄의 명령으로 설치됩니다:
|
||||
@@ -280,7 +280,7 @@ Skills를 로드하는 모든 에이전트는 변환 없이 여섯 가지 모드
|
||||
```bash
|
||||
# 플랫폼을 고르세요; --project는 전역 설정 대신 현재 저장소에 설치합니다
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
설치기는 스킬을 당신의 플랫폼에 맞는 폴더로 **평평하게** 복사하므로, 공유 프레임워크(`../_shared/`)가
|
||||
@@ -297,12 +297,13 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
| GitHub Copilot | `.github/skills`(`--project`) | `.claude/skills`, `AGENTS.md` | [설정](docs/copilot-setup.md) |
|
||||
| Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [설정](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [설정](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [설정](docs/dsh-setup.md) |
|
||||
|
||||
Kiro와 Factory Droid는 `/brooks-review`도 자동 등록합니다. 스킬이 처음이거나 위 목록에 없는 에이전트를
|
||||
쓰시나요? **[docs/getting-started.md](docs/getting-started.md)**를 참고하세요.
|
||||
Kiro, Factory Droid, DeepSeek Harness는 `/brooks-review`도 자동 등록합니다. 스킬이 처음이거나 위
|
||||
목록에 없는 에이전트를 쓰시나요? **[docs/getting-started.md](docs/getting-started.md)**를 참고하세요.
|
||||
|
||||
> **🧪 검증 상태.** Claude Code, Gemini CLI, Codex CLI는 메인테이너가 검증했습니다. 위
|
||||
> 여덟 개 플랫폼은 각 도구의 공식 스킬 명세를 토대로 문서화되었고 파일 레이아웃
|
||||
> 아홉 개 플랫폼은 각 도구의 공식 스킬 명세를 토대로 문서화되었고 파일 레이아웃
|
||||
> 수준에서 검증되었으나(설치기는 테스트되었음), 메인테이너가 모든 플랫폼에서 end-to-end로 직접 실행해 보지는
|
||||
> 못했습니다. 어떤 것을 시도해 보셨나요 — 잘 되든 **안 되든**? 플랫폼, 버전, 본 결과를 담아
|
||||
> [이슈를 열어 주세요](https://github.com/hyhmrright/brooks-lint/issues/new).
|
||||
@@ -322,9 +323,10 @@ Kiro와 Factory Droid는 `/brooks-review`도 자동 등록합니다. 스킬이
|
||||
|
||||
**플랫폼별 문법.** Claude Code는 네임스페이스가 붙은 전체 형식 `/brooks-lint:brooks-review`도 받습니다
|
||||
— 짧은 형식은 session-start 훅이 첫 세션 시작 시 자동 설치합니다. Codex CLI는 `$brooks-review`를 씁니다.
|
||||
Gemini CLI는 위 표 그대로입니다. OpenCode, Cursor, Antigravity, pi는 각 스킬의 `description`에서 Agent
|
||||
Skills를 호출하므로 그냥 요청하면 됩니다("이 PR을 리뷰해줘", "우리 최악의 기술 부채는 어디야?"). 명시적
|
||||
호출이 필요하면 각 플랫폼의 문법을 쓰세요(pi는 각 스킬을 `/skill:brooks-review`로 등록). 모든
|
||||
Gemini CLI는 위 표 그대로입니다. OpenCode, Cursor, Antigravity, pi, DeepSeek Harness는 각 스킬의
|
||||
`description`에서 Agent Skills를 호출하므로 그냥 요청하면 됩니다("이 PR을 리뷰해줘", "우리 최악의 기술
|
||||
부채는 어디야?"). 명시적 호출이 필요하면 각 플랫폼의 문법을 쓰세요(pi는 각 스킬을
|
||||
`/skill:brooks-review`로 등록; dsh는 위 표 그대로이며 `/` 메뉴에서 고르거나 직접 입력). 모든
|
||||
플랫폼에서 코드 품질, 아키텍처, 테스트 건강을 이야기하면 스킬이 자동으로 트리거됩니다.
|
||||
|
||||
> PR 리뷰에는 가벼운 Step 7 빠른 테스트 점검이 자동으로 포함됩니다(문서만 바뀐 diff에서는 건너뜀).
|
||||
@@ -446,6 +448,18 @@ jobs:
|
||||
|
||||
`fail-on-regression`은 `.brooks-lint-history.json`을 읽으므로, "새로운 회귀 없음"을 강제하려면 그 파일을 커밋하세요. `sarif-file`을 설정하면 진단이 PR의 **Files changed** 탭에 인라인으로 나타나며, job에 `security-events: write` 권한이 필요합니다.
|
||||
|
||||
**사용자 지정 API 엔드포인트.** `api-base-url`을 지정하면 action이 `api.anthropic.com` 대신 Anthropic 호환 `/v1/messages` 엔드포인트(자체 호스팅 프록시, LLM 게이트웨이, 리전 미러)를 호출합니다. 해당 엔드포인트의 키를 `anthropic-api-key`로, 기대하는 모델 id를 `model`로 전달하세요:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint는 여기에 지정한 호스트로 diff를 전송하므로, 소스 코드를 맡겨도 되는 곳만 지정하세요. `scripts/ci-review.mjs`를 직접 실행할 때는 플래그가 필요 없습니다 — Anthropic SDK가 `ANTHROPIC_BASE_URL`을 직접 읽습니다.
|
||||
|
||||
**비용:** PR 실행당 약 $0.05–0.15로, diff 크기와 모델에 따라 다릅니다. `pull_request` 이벤트에서만 실행할 것을 권장합니다.
|
||||
|
||||
## 로드맵
|
||||
@@ -484,7 +498,7 @@ MIT License — 자세한 내용은 [LICENSE](LICENSE)를 참고하세요.
|
||||
|
||||
## Star 히스토리
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+25
-11
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -78,7 +78,7 @@ Then just ask ("review this PR", "audit the architecture"), or run one of the si
|
||||
([what each one does](#slash-commands)).
|
||||
|
||||
Every finding comes back as **Symptom → Source → Consequence → Remedy** with a book citation and a
|
||||
0–100 Health Score. Full install options (8 more platforms) and CI/CD setup are [below](#installation).
|
||||
0–100 Health Score. Full install options (9 more platforms) and CI/CD setup are [below](#installation).
|
||||
|
||||
## The Twelve Books
|
||||
|
||||
@@ -272,7 +272,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # ask inside a C
|
||||
|
||||
Or use the installer below: `./scripts/install.sh gemini` / `./scripts/install.sh codex`.
|
||||
|
||||
### Every other platform — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### Every other platform — OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint ships as standard [Agent Skills](https://agentskills.io). **Any agent that loads Agent
|
||||
Skills runs all six modes with no conversion** — one command installs them:
|
||||
@@ -280,7 +280,7 @@ Skills runs all six modes with no conversion** — one command installs them:
|
||||
```bash
|
||||
# pick your platform; --project installs into the current repo instead of your global config
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <platform> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
The installer copies the skills **flat** into the right folder, so the shared framework
|
||||
@@ -297,12 +297,13 @@ The installer copies the skills **flat** into the right folder, so the shared fr
|
||||
| GitHub Copilot | `.github/skills` (`--project`) | `.claude/skills`, `AGENTS.md` | [setup](docs/copilot-setup.md) |
|
||||
| Kiro (AWS) | `~/.kiro/skills` | `AGENTS.md` | [setup](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [setup](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness (`dsh`) | `~/.dsh/skills` | `~/.agents/skills`, `AGENTS.md` | [setup](docs/dsh-setup.md) |
|
||||
|
||||
Kiro and Factory Droid also auto-register `/brooks-review`. New to skills, or using an agent not
|
||||
listed? See **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
Kiro, Factory Droid, and DeepSeek Harness also auto-register `/brooks-review`. New to skills, or
|
||||
using an agent not listed? See **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
|
||||
> **🧪 Verification status.** Claude Code, Gemini CLI, and Codex CLI are maintainer-verified. The
|
||||
> eight platforms above are documented from each tool's official skill spec and verified at the
|
||||
> nine platforms above are documented from each tool's official skill spec and verified at the
|
||||
> file-layout level (the installer is tested), but not yet run end-to-end by the maintainer on every
|
||||
> platform. Tried one — working **or** broken?
|
||||
> [Open an issue](https://github.com/hyhmrright/brooks-lint/issues/new) with the platform, version,
|
||||
@@ -323,9 +324,10 @@ listed? See **[docs/getting-started.md](docs/getting-started.md)**.
|
||||
**Syntax by platform.** Claude Code also accepts the namespaced form
|
||||
`/brooks-lint:brooks-review` — short forms are auto-installed on first session start by the
|
||||
session-start hook. Codex CLI uses `$brooks-review`. Gemini CLI uses the table as written.
|
||||
OpenCode, Cursor, Antigravity, and pi invoke Agent Skills from each skill's `description`, so
|
||||
just ask ("review this PR", "where's our worst tech debt?"); for explicit invocation use the
|
||||
platform's own syntax (pi registers each skill as `/skill:brooks-review`). On every platform the
|
||||
OpenCode, Cursor, Antigravity, pi, and DeepSeek Harness invoke Agent Skills from each skill's
|
||||
`description`, so just ask ("review this PR", "where's our worst tech debt?"); for explicit
|
||||
invocation use the platform's own syntax (pi registers each skill as `/skill:brooks-review`; dsh
|
||||
takes the table as written, from its `/` menu or typed inline). On every platform the
|
||||
skills also trigger automatically when you discuss code quality, architecture, or test health.
|
||||
|
||||
> PR reviews include a lightweight Step 7 Quick Test Check automatically (skipped for docs-only
|
||||
@@ -448,6 +450,18 @@ The action posts the review as a PR comment and optionally fails the check if th
|
||||
|
||||
`fail-on-regression` reads `.brooks-lint-history.json`, so commit that file to enforce "no new regressions". Setting `sarif-file` makes findings appear inline on the PR's **Files changed** tab and requires `security-events: write` permission on the job.
|
||||
|
||||
**Custom API endpoint.** `api-base-url` points the action at any Anthropic-compatible `/v1/messages` endpoint — a self-hosted proxy, an LLM gateway, a regional mirror — instead of `api.anthropic.com`. Pass that endpoint's key as `anthropic-api-key` and the model id it expects as `model`:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint sends your diff to whatever host you name here, so only point it at one you trust with your source. Running `scripts/ci-review.mjs` yourself needs no flag at all — the Anthropic SDK reads `ANTHROPIC_BASE_URL` directly.
|
||||
|
||||
**Cost:** ~$0.05–0.15 per PR run depending on diff size and model. Recommend running on `pull_request` events only.
|
||||
|
||||
## Roadmap
|
||||
@@ -486,7 +500,7 @@ their ideas, applied to modern code quality assessment.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+25
-11
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -76,7 +76,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
装好后直接开口("审查这个 PR""审计架构"),或运行六个命令之一——`/brooks-review`、`/brooks-audit`、
|
||||
`/brooks-debt`、`/brooks-test`、`/brooks-health`、`/brooks-sweep`([各自的作用](#斜杠命令))。
|
||||
|
||||
每条诊断都以 **症状 → 根源 → 后果 → 对策** 返回,附书目出处和 0–100 健康分。完整安装方式(另外 8 个
|
||||
每条诊断都以 **症状 → 根源 → 后果 → 对策** 返回,附书目出处和 0–100 健康分。完整安装方式(另外 9 个
|
||||
平台)和 CI/CD 配置见[下文](#安装)。
|
||||
|
||||
## 十二本书
|
||||
@@ -270,7 +270,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # 在 Codex 会
|
||||
|
||||
或使用下面的安装器:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。
|
||||
|
||||
### 其它所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### 其它所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint 以标准 [Agent Skills](https://agentskills.io) 形式分发。**任何加载 Agent Skills 的 agent
|
||||
都能无需任何转换运行全部六种模式**——一条命令即可安装:
|
||||
@@ -278,7 +278,7 @@ brooks-lint 以标准 [Agent Skills](https://agentskills.io) 形式分发。**
|
||||
```bash
|
||||
# 选择你的平台;加 --project 装进当前仓库而非全局配置
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台>
|
||||
# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
安装器会把技能**扁平**拷进该平台对应的文件夹,让共享框架(`../_shared/`)始终正确解析——你不可能装错布局。
|
||||
@@ -294,11 +294,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
| GitHub Copilot | `.github/skills`(`--project`) | `.claude/skills`、`AGENTS.md` | [配置](docs/copilot-setup.md) |
|
||||
| Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [配置](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [配置](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [配置](docs/dsh-setup.md) |
|
||||
|
||||
Kiro 与 Factory Droid 还会自动注册 `/brooks-review`。不熟悉 skills、或用的是上面没列出的 agent?
|
||||
见 **[docs/getting-started.md](docs/getting-started.md)**。
|
||||
Kiro、Factory Droid 与 DeepSeek Harness 还会自动注册 `/brooks-review`。不熟悉 skills、或用的是上面
|
||||
没列出的 agent?见 **[docs/getting-started.md](docs/getting-started.md)**。
|
||||
|
||||
> **🧪 验证状态。** Claude Code、Gemini CLI、Codex CLI 已由维护者验证。上面八个平台依据各工具官方技能规范编写,
|
||||
> **🧪 验证状态。** Claude Code、Gemini CLI、Codex CLI 已由维护者验证。上面九个平台依据各工具官方技能规范编写,
|
||||
> 并已在文件布局层面验证(安装器经过测试),但维护者尚未在每个平台端到端实跑。在某平台试过了——无论成功**还是**失败?
|
||||
> 请[提一个 issue](https://github.com/hyhmrright/brooks-lint/issues/new),附上平台、版本和你看到的结果。
|
||||
> 用的是其它兼容 Agent Skills 的 agent?它几乎肯定以同样方式工作——告诉我们,我们会补上。
|
||||
@@ -316,9 +317,10 @@ Kiro 与 Factory Droid 还会自动注册 `/brooks-review`。不熟悉 skills、
|
||||
|
||||
**各平台语法。** Claude Code 也接受带命名空间的完整形式 `/brooks-lint:brooks-review`——短命令由
|
||||
session-start 钩子在首次会话启动时自动安装。Codex CLI 用 `$brooks-review`。Gemini CLI 直接用上表。
|
||||
OpenCode、Cursor、Antigravity、pi 依据每个技能的 `description` 自动调用 Agent Skills,直接提问即可
|
||||
("审查这个 PR"、"我们最糟的技术债在哪");需要显式调用时用各平台自己的语法(pi 把每个技能注册为
|
||||
`/skill:brooks-review`)。在所有平台上,当你讨论代码质量、架构或测试健康时,这些技能也会自动触发。
|
||||
OpenCode、Cursor、Antigravity、pi、DeepSeek Harness 依据每个技能的 `description` 自动调用 Agent
|
||||
Skills,直接提问即可("审查这个 PR"、"我们最糟的技术债在哪");需要显式调用时用各平台自己的语法
|
||||
(pi 把每个技能注册为 `/skill:brooks-review`;dsh 直接用上表,可从 `/` 菜单选或手打)。在所有平台上,
|
||||
当你讨论代码质量、架构或测试健康时,这些技能也会自动触发。
|
||||
|
||||
> PR 审查会自动包含一个轻量的第 7 步快速测试检查(对纯文档 diff 会跳过)。需要完整的测试审查请用
|
||||
> `/brooks-test`;需要某个维度的深度诊断时,请用该维度的专项技能,而不是 `/brooks-health`。
|
||||
@@ -437,6 +439,18 @@ jobs:
|
||||
|
||||
`fail-on-regression` 读取 `.brooks-lint-history.json`,因此提交该文件即可强制"无新增回归"。设置 `sarif-file` 会让诊断直接显示在 PR 的 **Files changed** 标签页,并需要 job 具备 `security-events: write` 权限。
|
||||
|
||||
**自定义 API 端点。** `api-base-url` 让 action 改为调用任意 Anthropic 兼容的 `/v1/messages` 端点 —— 自建代理、LLM 网关或区域镜像 —— 而不是 `api.anthropic.com`。把该端点的密钥作为 `anthropic-api-key` 传入,把它期望的模型 id 作为 `model` 传入:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint 会把你的 diff 发送到这里填写的主机,因此只应指向你信任其接触源码的一方。自己运行 `scripts/ci-review.mjs` 完全不需要参数 —— Anthropic SDK 会直接读取 `ANTHROPIC_BASE_URL`。
|
||||
|
||||
**成本:** 每次 PR 运行约 $0.05–0.15,取决于 diff 大小和模型。建议仅在 `pull_request` 事件上运行。
|
||||
|
||||
## 路线图
|
||||
@@ -472,7 +486,7 @@ MIT License——详见 [LICENSE](LICENSE)。
|
||||
|
||||
## Star 历史
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+25
-11
@@ -27,7 +27,7 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/version-1.4.3-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/version-1.5.0-blue.svg" alt="Version">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License">
|
||||
<img src="https://img.shields.io/badge/Claude_Code-Plugin-blueviolet.svg" alt="Claude Code Plugin">
|
||||
<img src="https://img.shields.io/badge/Codex_CLI-Skill-orange.svg" alt="Codex CLI Skill">
|
||||
@@ -76,7 +76,7 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
裝好後直接開口(「審查這個 PR」「稽核架構」),或執行六個命令之一——`/brooks-review`、`/brooks-audit`、
|
||||
`/brooks-debt`、`/brooks-test`、`/brooks-health`、`/brooks-sweep`([各自的作用](#斜線命令))。
|
||||
|
||||
每條診斷都以 **症狀 → 根源 → 後果 → 對策** 回傳,附書目出處和 0–100 健康分。完整安裝方式(另外 8 個
|
||||
每條診斷都以 **症狀 → 根源 → 後果 → 對策** 回傳,附書目出處和 0–100 健康分。完整安裝方式(另外 9 個
|
||||
平台)和 CI/CD 設定見[下文](#安裝)。
|
||||
|
||||
## 十二本書
|
||||
@@ -270,7 +270,7 @@ Install the brooks-lint skill from hyhmrright/brooks-lint # 在 Codex 工
|
||||
|
||||
或使用下面的安裝器:`./scripts/install.sh gemini` / `./scripts/install.sh codex`。
|
||||
|
||||
### 其他所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid
|
||||
### 其他所有平台——OpenCode · Cursor · Windsurf · Antigravity · pi · Copilot · Kiro · Factory Droid · DeepSeek Harness
|
||||
|
||||
brooks-lint 以標準 [Agent Skills](https://agentskills.io) 形式散布。**任何載入 Agent Skills 的 agent
|
||||
都能無需任何轉換執行全部六種模式**——一條命令即可安裝:
|
||||
@@ -278,7 +278,7 @@ brooks-lint 以標準 [Agent Skills](https://agentskills.io) 形式散布。**
|
||||
```bash
|
||||
# 選擇你的平台;加 --project 裝進當前儲存庫而非全域設定
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <平台>
|
||||
# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini · codex · agents
|
||||
# <平台> = opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh · gemini · codex · agents
|
||||
```
|
||||
|
||||
安裝器會把技能**扁平**複製進該平台對應的資料夾,讓共享框架(`../_shared/`)始終正確解析——你不可能裝錯佈局。
|
||||
@@ -294,11 +294,12 @@ curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts
|
||||
| GitHub Copilot | `.github/skills`(`--project`) | `.claude/skills`、`AGENTS.md` | [設定](docs/copilot-setup.md) |
|
||||
| Kiro(AWS) | `~/.kiro/skills` | `AGENTS.md` | [設定](docs/kiro-setup.md) |
|
||||
| Factory Droid | `~/.factory/skills` | `AGENTS.md` | [設定](docs/factory-droid-setup.md) |
|
||||
| DeepSeek Harness(`dsh`) | `~/.dsh/skills` | `~/.agents/skills`、`AGENTS.md` | [設定](docs/dsh-setup.md) |
|
||||
|
||||
Kiro 與 Factory Droid 還會自動註冊 `/brooks-review`。不熟悉 skills、或用的是上面沒列出的 agent?
|
||||
見 **[docs/getting-started.md](docs/getting-started.md)**。
|
||||
Kiro、Factory Droid 與 DeepSeek Harness 還會自動註冊 `/brooks-review`。不熟悉 skills、或用的是上面
|
||||
沒列出的 agent?見 **[docs/getting-started.md](docs/getting-started.md)**。
|
||||
|
||||
> **🧪 驗證狀態。** Claude Code、Gemini CLI、Codex CLI 已由維護者驗證。上面八個平台依據各工具官方技能規範撰寫,
|
||||
> **🧪 驗證狀態。** Claude Code、Gemini CLI、Codex CLI 已由維護者驗證。上面九個平台依據各工具官方技能規範撰寫,
|
||||
> 並已在檔案佈局層面驗證(安裝器經過測試),但維護者尚未在每個平台端到端實跑。在某平台試過了——無論成功**還是**失敗?
|
||||
> 請[提一個 issue](https://github.com/hyhmrright/brooks-lint/issues/new),附上平台、版本和你看到的結果。
|
||||
> 用的是其他相容 Agent Skills 的 agent?它幾乎肯定以同樣方式運作——告訴我們,我們會補上。
|
||||
@@ -316,9 +317,10 @@ Kiro 與 Factory Droid 還會自動註冊 `/brooks-review`。不熟悉 skills、
|
||||
|
||||
**各平台語法。** Claude Code 也接受帶命名空間的完整形式 `/brooks-lint:brooks-review`——短命令由
|
||||
session-start 鉤子在首次工作階段啟動時自動安裝。Codex CLI 用 `$brooks-review`。Gemini CLI 直接用上表。
|
||||
OpenCode、Cursor、Antigravity、pi 依據每個技能的 `description` 自動呼叫 Agent Skills,直接提問即可
|
||||
(「審查這個 PR」、「我們最糟的技術債在哪」);需要顯式呼叫時用各平台自己的語法(pi 把每個技能註冊為
|
||||
`/skill:brooks-review`)。在所有平台上,當你討論程式碼品質、架構或測試健康時,這些技能也會自動觸發。
|
||||
OpenCode、Cursor、Antigravity、pi、DeepSeek Harness 依據每個技能的 `description` 自動呼叫 Agent
|
||||
Skills,直接提問即可(「審查這個 PR」、「我們最糟的技術債在哪」);需要顯式呼叫時用各平台自己的語法
|
||||
(pi 把每個技能註冊為 `/skill:brooks-review`;dsh 直接用上表,可從 `/` 選單挑或手打)。在所有平台上,
|
||||
當你討論程式碼品質、架構或測試健康時,這些技能也會自動觸發。
|
||||
|
||||
> PR 審查會自動包含一個輕量的第 7 步快速測試檢查(對純文件 diff 會跳過)。需要完整的測試稽核請用
|
||||
> `/brooks-test`;需要某個維度的深度診斷時,請用該維度的專項技能,而不是 `/brooks-health`。
|
||||
@@ -437,6 +439,18 @@ jobs:
|
||||
|
||||
`fail-on-regression` 讀取 `.brooks-lint-history.json`,因此提交該檔案即可強制「無新增回歸」。設定 `sarif-file` 會讓診斷直接顯示在 PR 的 **Files changed** 分頁,並需要 job 具備 `security-events: write` 權限。
|
||||
|
||||
**自訂 API 端點。** `api-base-url` 讓 action 改為呼叫任意 Anthropic 相容的 `/v1/messages` 端點 —— 自建代理、LLM 閘道或區域鏡像 —— 而不是 `api.anthropic.com`。把該端點的金鑰作為 `anthropic-api-key` 傳入,把它預期的模型 id 作為 `model` 傳入:
|
||||
|
||||
```yaml
|
||||
with:
|
||||
mode: review
|
||||
api-base-url: https://your-gateway.example.com
|
||||
anthropic-api-key: ${{ secrets.GATEWAY_API_KEY }}
|
||||
model: gateway-model-id
|
||||
```
|
||||
|
||||
brooks-lint 會把你的 diff 傳送到這裡填寫的主機,因此只應指向你信任其接觸原始碼的一方。自己執行 `scripts/ci-review.mjs` 完全不需要參數 —— Anthropic SDK 會直接讀取 `ANTHROPIC_BASE_URL`。
|
||||
|
||||
**成本:** 每次 PR 執行約 $0.05–0.15,取決於 diff 大小和模型。建議僅在 `pull_request` 事件上執行。
|
||||
|
||||
## 路線圖
|
||||
@@ -472,7 +486,7 @@ MIT License——詳見 [LICENSE](LICENSE)。
|
||||
|
||||
## Star 歷史
|
||||
|
||||
[](https://star-history.com/#hyhmrright/brooks-lint&Date)
|
||||
[](https://github.com/hyhmrright/brooks-lint/stargazers)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Source
|
||||
|
||||
- Repo: https://github.com/hyhmrright/brooks-lint
|
||||
- Ref: 814174cd5b340bc0d8b0161b6d8288980428a44d
|
||||
- Ref: e9acf5c6100d2d786ed58f0fb7b9566a6218926a
|
||||
- Remove-Paths:
|
||||
- Snapshot: 2026-08-12
|
||||
- Snapshot: 2026-08-24
|
||||
- Sync-Mode: copy_skill_dirs
|
||||
- Notes: vendored into playbook branch thirdparty/skill
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="400" viewBox="0 0 800 400" role="img" aria-label="Star history for hyhmrright/brooks-lint: 1393 stars">
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.title { fill: #111827; font: 600 16px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
|
||||
.sub { fill: #6b7280; font: 400 12px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
|
||||
.tick { fill: #6b7280; font: 400 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
|
||||
.grid { stroke: #e5e7eb; stroke-width: 1; }
|
||||
.axis { stroke: #d1d5db; stroke-width: 1; }
|
||||
.total { fill: #3b82f6; font: 600 13px -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0d1117; }
|
||||
.title { fill: #e6edf3; }
|
||||
.sub, .tick { fill: #8b949e; }
|
||||
.grid { stroke: #21262d; }
|
||||
.axis { stroke: #30363d; }
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#3b82f6" stop-opacity="0.28"/>
|
||||
<stop offset="100%" stop-color="#3b82f6" stop-opacity="0.02"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="800" height="400" class="bg"/>
|
||||
<text x="64" y="26" class="title">Star History</text>
|
||||
<text x="776" y="26" class="sub" text-anchor="end">hyhmrright/brooks-lint</text>
|
||||
<g>
|
||||
<line x1="64" y1="352.0" x2="776" y2="352.0" class="grid"/><text x="54" y="356.0" class="tick" text-anchor="end">0</text>
|
||||
<line x1="64" y1="249.3" x2="776" y2="249.3" class="grid"/><text x="54" y="253.3" class="tick" text-anchor="end">500</text>
|
||||
<line x1="64" y1="146.7" x2="776" y2="146.7" class="grid"/><text x="54" y="150.7" class="tick" text-anchor="end">1,000</text>
|
||||
<line x1="64" y1="44.0" x2="776" y2="44.0" class="grid"/><text x="54" y="48.0" class="tick" text-anchor="end">1,500</text>
|
||||
</g>
|
||||
<line x1="64" y1="352" x2="776" y2="352" class="axis"/>
|
||||
<path d="M64.0 351.8L82.8 350.8L92.5 349.7L99.2 348.7L116.3 347.7L125.3 346.7L156.4 345.6L168.8 344.6L178.1 343.6L186.6 342.6L194.8 341.5L198.1 340.5L205.9 339.5L212.1 338.4L215.9 337.4L218.4 336.4L222.5 335.4L227.0 334.3L229.0 333.3L239.8 332.3L246.3 331.3L257.3 330.2L259.1 329.2L268.1 328.2L279.8 327.2L290.7 326.1L296.3 325.1L303.7 324.1L312.2 323.0L318.6 322.0L322.8 321.0L330.8 320.0L344.8 318.9L349.9 317.9L355.5 316.9L360.2 315.9L372.6 314.8L386.8 313.8L404.4 312.8L413.5 311.8L413.6 310.7L413.7 309.7L413.8 308.7L413.8 307.6L413.8 306.6L413.8 305.6L413.9 304.6L413.9 303.5L413.9 302.5L413.9 301.5L414.0 300.5L414.0 299.4L414.0 298.4L414.1 297.4L414.1 296.4L414.1 295.3L414.2 294.3L414.2 293.3L414.2 292.2L414.3 291.2L414.3 290.2L414.4 289.2L414.4 288.1L414.5 287.1L414.5 286.1L414.5 285.1L414.6 284.0L414.6 283.0L414.6 282.0L414.7 281.0L414.7 279.9L414.7 278.9L414.8 277.9L414.8 276.8L414.8 275.8L414.8 274.8L414.9 273.8L414.9 272.7L414.9 271.7L414.9 270.7L414.9 269.7L414.9 268.6L415.0 267.6L415.0 266.6L415.0 265.6L415.0 264.5L415.1 263.5L415.1 262.5L415.1 261.4L415.2 260.4L415.2 259.4L415.3 258.4L415.3 257.3L415.3 256.3L415.3 255.3L415.4 254.3L415.4 253.2L415.4 252.2L415.4 251.2L415.5 250.2L415.5 249.1L415.5 248.1L415.6 247.1L415.6 246.0L415.6 245.0L415.6 244.0L415.7 243.0L415.7 241.9L415.7 240.9L415.7 239.9L415.7 238.9L415.8 237.8L415.8 236.8L415.8 235.8L415.8 234.8L415.8 233.7L415.9 232.7L415.9 231.7L415.9 230.6L416.0 229.6L416.0 228.6L416.1 227.6L416.2 226.5L416.2 225.5L416.3 224.5L416.4 223.5L416.6 222.4L416.8 221.4L416.9 220.4L417.0 219.4L417.1 218.3L417.1 217.3L417.2 216.3L417.3 215.2L417.4 214.2L417.4 213.2L417.5 212.2L417.5 211.1L417.6 210.1L417.7 209.1L417.7 208.1L417.8 207.0L417.9 206.0L417.9 205.0L418.0 204.0L418.1 202.9L418.2 201.9L418.3 200.9L418.4 199.8L418.6 198.8L418.7 197.8L418.8 196.8L418.9 195.7L419.0 194.7L419.1 193.7L419.2 192.7L419.4 191.6L419.5 190.6L419.5 189.6L419.6 188.6L419.7 187.5L419.8 186.5L420.0 185.5L420.1 184.4L420.2 183.4L420.3 182.4L420.4 181.4L420.5 180.3L420.6 179.3L420.8 178.3L421.2 177.3L421.9 176.2L422.6 175.2L423.4 174.2L423.6 173.2L424.1 172.1L425.9 171.1L427.4 170.1L428.8 169.0L430.5 168.0L434.1 167.0L437.1 166.0L438.3 164.9L439.4 163.9L442.1 162.9L442.2 161.9L442.3 160.8L442.4 159.8L442.6 158.8L442.7 157.8L442.8 156.7L443.0 155.7L443.2 154.7L443.6 153.6L443.7 152.6L443.8 151.6L444.1 150.6L444.3 149.5L444.5 148.5L444.6 147.5L444.7 146.5L444.8 145.4L444.9 144.4L444.9 143.4L445.3 142.4L445.9 141.3L446.5 140.3L446.7 139.3L447.2 138.2L447.7 137.2L448.4 136.2L452.1 135.2L452.7 134.1L453.3 133.1L454.4 132.1L457.6 131.1L460.0 130.0L462.9 129.0L463.8 128.0L468.1 127.0L473.2 125.9L478.9 124.9L482.0 123.9L485.9 122.8L492.4 121.8L493.5 120.8L501.3 119.8L505.4 118.7L508.4 117.7L517.8 116.7L520.3 115.7L523.7 114.6L527.5 113.6L531.8 112.6L535.5 111.6L542.7 110.5L551.8 109.5L559.4 108.5L561.3 107.4L565.7 106.4L575.1 105.4L578.5 104.4L587.9 103.3L589.5 102.3L593.6 101.3L604.0 100.3L620.8 99.2L631.3 98.2L634.9 97.2L656.5 96.2L658.1 95.1L662.9 94.1L671.4 93.1L672.3 92.0L675.8 91.0L687.6 90.0L698.1 89.0L717.6 87.9L725.3 86.9L732.2 85.9L747.1 84.9L747.6 83.8L748.3 82.8L749.0 81.8L749.5 80.8L750.2 79.7L750.8 78.7L751.1 77.7L751.8 76.6L752.6 75.6L754.4 74.6L755.3 73.6L756.8 72.5L760.1 71.5L762.4 70.5L765.0 69.5L769.5 68.4L770.7 67.4L774.8 66.4L776.0 66.0L776.0 352.0L64.0 352.0Z" fill="url(#fade)"/>
|
||||
<path d="M64.0 351.8L82.8 350.8L92.5 349.7L99.2 348.7L116.3 347.7L125.3 346.7L156.4 345.6L168.8 344.6L178.1 343.6L186.6 342.6L194.8 341.5L198.1 340.5L205.9 339.5L212.1 338.4L215.9 337.4L218.4 336.4L222.5 335.4L227.0 334.3L229.0 333.3L239.8 332.3L246.3 331.3L257.3 330.2L259.1 329.2L268.1 328.2L279.8 327.2L290.7 326.1L296.3 325.1L303.7 324.1L312.2 323.0L318.6 322.0L322.8 321.0L330.8 320.0L344.8 318.9L349.9 317.9L355.5 316.9L360.2 315.9L372.6 314.8L386.8 313.8L404.4 312.8L413.5 311.8L413.6 310.7L413.7 309.7L413.8 308.7L413.8 307.6L413.8 306.6L413.8 305.6L413.9 304.6L413.9 303.5L413.9 302.5L413.9 301.5L414.0 300.5L414.0 299.4L414.0 298.4L414.1 297.4L414.1 296.4L414.1 295.3L414.2 294.3L414.2 293.3L414.2 292.2L414.3 291.2L414.3 290.2L414.4 289.2L414.4 288.1L414.5 287.1L414.5 286.1L414.5 285.1L414.6 284.0L414.6 283.0L414.6 282.0L414.7 281.0L414.7 279.9L414.7 278.9L414.8 277.9L414.8 276.8L414.8 275.8L414.8 274.8L414.9 273.8L414.9 272.7L414.9 271.7L414.9 270.7L414.9 269.7L414.9 268.6L415.0 267.6L415.0 266.6L415.0 265.6L415.0 264.5L415.1 263.5L415.1 262.5L415.1 261.4L415.2 260.4L415.2 259.4L415.3 258.4L415.3 257.3L415.3 256.3L415.3 255.3L415.4 254.3L415.4 253.2L415.4 252.2L415.4 251.2L415.5 250.2L415.5 249.1L415.5 248.1L415.6 247.1L415.6 246.0L415.6 245.0L415.6 244.0L415.7 243.0L415.7 241.9L415.7 240.9L415.7 239.9L415.7 238.9L415.8 237.8L415.8 236.8L415.8 235.8L415.8 234.8L415.8 233.7L415.9 232.7L415.9 231.7L415.9 230.6L416.0 229.6L416.0 228.6L416.1 227.6L416.2 226.5L416.2 225.5L416.3 224.5L416.4 223.5L416.6 222.4L416.8 221.4L416.9 220.4L417.0 219.4L417.1 218.3L417.1 217.3L417.2 216.3L417.3 215.2L417.4 214.2L417.4 213.2L417.5 212.2L417.5 211.1L417.6 210.1L417.7 209.1L417.7 208.1L417.8 207.0L417.9 206.0L417.9 205.0L418.0 204.0L418.1 202.9L418.2 201.9L418.3 200.9L418.4 199.8L418.6 198.8L418.7 197.8L418.8 196.8L418.9 195.7L419.0 194.7L419.1 193.7L419.2 192.7L419.4 191.6L419.5 190.6L419.5 189.6L419.6 188.6L419.7 187.5L419.8 186.5L420.0 185.5L420.1 184.4L420.2 183.4L420.3 182.4L420.4 181.4L420.5 180.3L420.6 179.3L420.8 178.3L421.2 177.3L421.9 176.2L422.6 175.2L423.4 174.2L423.6 173.2L424.1 172.1L425.9 171.1L427.4 170.1L428.8 169.0L430.5 168.0L434.1 167.0L437.1 166.0L438.3 164.9L439.4 163.9L442.1 162.9L442.2 161.9L442.3 160.8L442.4 159.8L442.6 158.8L442.7 157.8L442.8 156.7L443.0 155.7L443.2 154.7L443.6 153.6L443.7 152.6L443.8 151.6L444.1 150.6L444.3 149.5L444.5 148.5L444.6 147.5L444.7 146.5L444.8 145.4L444.9 144.4L444.9 143.4L445.3 142.4L445.9 141.3L446.5 140.3L446.7 139.3L447.2 138.2L447.7 137.2L448.4 136.2L452.1 135.2L452.7 134.1L453.3 133.1L454.4 132.1L457.6 131.1L460.0 130.0L462.9 129.0L463.8 128.0L468.1 127.0L473.2 125.9L478.9 124.9L482.0 123.9L485.9 122.8L492.4 121.8L493.5 120.8L501.3 119.8L505.4 118.7L508.4 117.7L517.8 116.7L520.3 115.7L523.7 114.6L527.5 113.6L531.8 112.6L535.5 111.6L542.7 110.5L551.8 109.5L559.4 108.5L561.3 107.4L565.7 106.4L575.1 105.4L578.5 104.4L587.9 103.3L589.5 102.3L593.6 101.3L604.0 100.3L620.8 99.2L631.3 98.2L634.9 97.2L656.5 96.2L658.1 95.1L662.9 94.1L671.4 93.1L672.3 92.0L675.8 91.0L687.6 90.0L698.1 89.0L717.6 87.9L725.3 86.9L732.2 85.9L747.1 84.9L747.6 83.8L748.3 82.8L749.0 81.8L749.5 80.8L750.2 79.7L750.8 78.7L751.1 77.7L751.8 76.6L752.6 75.6L754.4 74.6L755.3 73.6L756.8 72.5L760.1 71.5L762.4 70.5L765.0 69.5L769.5 68.4L770.7 67.4L774.8 66.4L776.0 66.0" fill="none" stroke="#3b82f6" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="776.0" cy="66.0" r="4" fill="#3b82f6"/>
|
||||
<text x="764.0" y="70.0" class="total" text-anchor="end">1,393</text>
|
||||
<g>
|
||||
<text x="85.7" y="374" class="tick" text-anchor="middle">Apr 2026</text>
|
||||
<text x="232.0" y="374" class="tick" text-anchor="middle">May</text>
|
||||
<text x="383.1" y="374" class="tick" text-anchor="middle">Jun</text>
|
||||
<text x="529.4" y="374" class="tick" text-anchor="middle">Jul</text>
|
||||
<text x="680.6" y="374" class="tick" text-anchor="middle">Aug</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
@@ -0,0 +1,60 @@
|
||||
# DeepSeek Harness (dsh) Setup
|
||||
|
||||
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`) is DeepSeek AI's
|
||||
open-source agent harness — an "everything is a plugin" architecture with a Web UI, started with
|
||||
`npx @deepseek-ai/dsh web`. It natively loads [Agent Skills](https://agentskills.io) and reads
|
||||
`AGENTS.md`, so all six brooks-lint modes run with no conversion.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# simplest — one command (global)
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- dsh
|
||||
|
||||
# from a clone
|
||||
./scripts/install.sh dsh # global: ~/.dsh/skills (or $DSH_HOME/skills)
|
||||
./scripts/install.sh dsh --project # this repo: ./.dsh/skills
|
||||
```
|
||||
|
||||
Prefer a manual copy? Clone the repo and `cp -r skills/* ~/.dsh/skills/` — the contents, not the
|
||||
`skills/` folder itself, so `_shared/` lands as a sibling of the `brooks-*` folders.
|
||||
|
||||
dsh scans these skill roots, highest priority first, so an existing vendor-neutral install is picked
|
||||
up automatically:
|
||||
|
||||
| Root | Notes |
|
||||
|---|---|
|
||||
| `<projectRoot>/.dsh/skills` | what `--project` writes |
|
||||
| `<projectRoot>/.agents/skills` | shared with Cursor, Copilot, pi |
|
||||
| `$DSH_HOME/skills` (default `~/.dsh/skills`) | what the global install writes |
|
||||
| `$DSH_AGENTS_HOME/skills` (default `~/.agents/skills`) | `./scripts/install.sh agents` also covers dsh |
|
||||
|
||||
The project root is the nearest ancestor containing `.git`; without one, dsh uses the current
|
||||
directory. When the same skill name appears in two roots, the higher one wins.
|
||||
|
||||
## Invoke
|
||||
|
||||
Just ask — dsh routes to a skill from its `description`:
|
||||
|
||||
- "review this PR" → `brooks-review`
|
||||
- "audit the architecture" → `brooks-audit`
|
||||
- "where's our worst tech debt?" → `brooks-debt`
|
||||
|
||||
For explicit invocation, type `/` in the Web UI prompt and pick the skill, or type the token by hand:
|
||||
`/brooks-review`, `/brooks-audit`, `/brooks-debt`, `/brooks-test`, `/brooks-health`, `/brooks-sweep`.
|
||||
dsh recognises a whitespace-bounded `/name` anywhere in a message and injects that skill's body
|
||||
deterministically. The repo's `AGENTS.md` carries the Iron Law (Symptom → Source → Consequence →
|
||||
Remedy) and the Health Score rules; dsh also loads `$DSH_HOME/AGENTS.md` plus every `AGENTS.md` from
|
||||
the project root down to your working directory.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Flat layout** is mandatory (the installer guarantees it): discovery is one level deep
|
||||
(`<root>/<name>/SKILL.md`), never recursive, and the skills read `../_shared/`, which only resolves
|
||||
when `_shared/` sits beside the `brooks-*` folders. `_shared/` itself has no `SKILL.md`, so dsh
|
||||
ignores it as a skill and the modes read it as ordinary files.
|
||||
- dsh is in developer preview and warns of compatibility-breaking changes; the skill discovery
|
||||
contract above reflects `packages/skill/skill-filesystem` as of August 2026.
|
||||
- 🧪 Documented per the official [skills subsystem](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/skills.md)
|
||||
docs; community end-to-end verification welcome —
|
||||
[open an issue](https://github.com/hyhmrright/brooks-lint/issues/new).
|
||||
@@ -33,10 +33,10 @@ layout wrong:
|
||||
curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
```
|
||||
|
||||
`<platform>` ∈ `opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · gemini ·
|
||||
codex · claude · agents`. Add `--project` to install into the current repo instead of your global
|
||||
config. `agents` targets the vendor-neutral `~/.agents/skills` folder that Cursor, Copilot, pi, Gemini,
|
||||
and Codex all read.
|
||||
`<platform>` ∈ `opencode · cursor · windsurf · antigravity · pi · kiro · copilot · droid · dsh ·
|
||||
gemini · codex · claude · agents`. Add `--project` to install into the current repo instead of your
|
||||
global config. `agents` targets the vendor-neutral `~/.agents/skills` folder that Cursor, Copilot, pi,
|
||||
Gemini, Codex, and DeepSeek Harness all read.
|
||||
|
||||
## Per-platform guides
|
||||
|
||||
@@ -50,6 +50,7 @@ and Codex all read.
|
||||
| GitHub Copilot | [copilot-setup.md](copilot-setup.md) | `.github/skills`, `.claude/skills`, `~/.copilot/skills` | ✅ |
|
||||
| Kiro | [kiro-setup.md](kiro-setup.md) | `.kiro/skills`, `~/.kiro/skills` | ✅ |
|
||||
| Factory Droid | [factory-droid-setup.md](factory-droid-setup.md) | `~/.factory/skills`, `.factory/skills`, `.agent/skills` | ✅ |
|
||||
| DeepSeek Harness | [dsh-setup.md](dsh-setup.md) | `.dsh/skills`, `.agents/skills`, `~/.dsh/skills`, `~/.agents/skills` | ✅ |
|
||||
|
||||
For Claude Code, Gemini CLI, and Codex CLI, see the [README install section](../README.md#installation).
|
||||
`./scripts/install.sh gemini` and `./scripts/install.sh codex` also work and use the flat layout these
|
||||
@@ -68,7 +69,7 @@ If your agent accepts a skills folder or an instruction file, brooks-lint works:
|
||||
## Verification status
|
||||
|
||||
The marketplace-installed platforms (Claude Code, Gemini CLI, Codex CLI) are maintainer-verified. The
|
||||
eight Agent-Skills platforms above are documented from each tool's official skill spec and verified at
|
||||
nine Agent-Skills platforms above are documented from each tool's official skill spec and verified at
|
||||
the file-layout level (the installer is tested), but not yet end-to-end run by the maintainer on every
|
||||
platform. **Tried one? Tell us** — [open an issue](https://github.com/hyhmrright/brooks-lint/issues/new)
|
||||
with the platform, version, and what you saw, working or broken.
|
||||
|
||||
@@ -28,3 +28,4 @@ jobs:
|
||||
# fail-on: critical # fail on any Critical finding (none | warning | critical)
|
||||
# fail-on-regression: true # fail if the Health Score dropped vs the last run
|
||||
# sarif-file: brooks-lint.sarif # also upload findings to GitHub Code Scanning
|
||||
# api-base-url: https://your-gateway.example.com # any Anthropic-compatible /v1/messages endpoint
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"description": "AI code reviews grounded in twelve classic software engineering books. Decay-risk diagnostics with book citations, severity labels, and six analysis modes including full-sweep auto-fix.",
|
||||
"url": "https://hyhmrright.github.io/brooks-lint/",
|
||||
"image": "https://hyhmrright.github.io/brooks-lint/hero.png",
|
||||
"softwareVersion": "1.4.3",
|
||||
"softwareVersion": "1.5.0",
|
||||
"license": "https://github.com/hyhmrright/brooks-lint/blob/main/LICENSE",
|
||||
"codeRepository": "https://github.com/hyhmrright/brooks-lint",
|
||||
"keywords": "AI code review, code quality, tech debt, architecture audit, test quality, Claude Code plugin, refactoring, clean architecture",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "brooks-lint",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"description": "AI code reviews grounded in twelve classic engineering books — decay risk diagnostics with book citations, severity labels, and six analysis modes (PR review, architecture audit, tech debt, test quality, health dashboard, full-sweep auto-fix)",
|
||||
"author": "hyhmrright",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "brooks-lint",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"bump": "node scripts/bump-version.mjs",
|
||||
|
||||
@@ -101,7 +101,9 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const report = message.content[0]?.text ?? "";
|
||||
// The first block is not always the text one — an endpoint that returns a
|
||||
// thinking block first would otherwise yield undefined.
|
||||
const report = message.content.find((block) => block.type === "text")?.text ?? "";
|
||||
|
||||
const scoreMatch = report.match(/Health\s+Score[:\s]+(\d+)/i);
|
||||
const score = scoreMatch ? parseInt(scoreMatch[1], 10) : null;
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
// Generates the README star-history chart from first-party GitHub data.
|
||||
//
|
||||
// GitHub restricted the public stargazers API to a repository's own admins and
|
||||
// collaborators (announced 2026-06-30), which broke every third-party chart
|
||||
// service — api.star-history.com now serves an error card instead of a chart.
|
||||
// We own this repo, so we read the star data ourselves and commit the result,
|
||||
// keeping the README free of any third-party image host.
|
||||
//
|
||||
// The committed dataset, not the drawing, is the source of truth: the SVG is a
|
||||
// pure function of assets/star-history.json. That keeps the chart re-renderable
|
||||
// with no credentials if the endpoint tightens further, keeps the weekly diff
|
||||
// readable (dates, not shifted path coordinates), and lets `npm run validate`
|
||||
// prove the two files agree.
|
||||
//
|
||||
// Run: node scripts/gen-star-history.mjs → refetch, rewrite both files
|
||||
// node scripts/gen-star-history.mjs --render-only → redraw the SVG offline
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync, writeFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
// README-only assets, so they live in assets/ alone — unlike the banners, the
|
||||
// GitHub Pages site under docs/ does not render them.
|
||||
const OUT = join(ROOT, "assets", "star-history.svg");
|
||||
const DATA = join(ROOT, "assets", "star-history.json");
|
||||
|
||||
const REPO = process.env.GITHUB_REPOSITORY ?? "hyhmrright/brooks-lint";
|
||||
|
||||
const W = 800, H = 400;
|
||||
const PAD = { top: 44, right: 24, bottom: 48, left: 64 };
|
||||
const PLOT_W = W - PAD.left - PAD.right;
|
||||
const PLOT_H = H - PAD.top - PAD.bottom;
|
||||
const ACCENT = "#3b82f6"; // matches the logo palette used by gen-banner.mjs
|
||||
const FONT = `-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif`;
|
||||
|
||||
const esc = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
|
||||
// The stargazers endpoint is no longer public: it needs credentials that can
|
||||
// read this repo. CI supplies GITHUB_TOKEN; locally we borrow the gh CLI's.
|
||||
function resolveToken() {
|
||||
if (process.env.GITHUB_TOKEN) return process.env.GITHUB_TOKEN;
|
||||
try {
|
||||
return execFileSync("gh", ["auth", "token"], { encoding: "utf8" }).trim();
|
||||
} catch {
|
||||
throw new Error(
|
||||
"No GITHUB_TOKEN set and `gh auth token` failed. Since GitHub restricted " +
|
||||
"the stargazers API, this script needs credentials for a repo admin or collaborator.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the raw starred_at strings, oldest first — exactly what we commit.
|
||||
async function fetchStarTimestamps(token) {
|
||||
const stamps = [];
|
||||
for (let page = 1; ; page++) {
|
||||
const url = `https://api.github.com/repos/${REPO}/stargazers?per_page=100&page=${page}`;
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
// The star+json media type is what adds starred_at to each entry.
|
||||
Accept: "application/vnd.github.star+json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
"User-Agent": "brooks-lint-gen-star-history",
|
||||
},
|
||||
});
|
||||
// Past 400 pages GitHub answers 422 rather than paginating, so a repo above
|
||||
// 40,000 stars can no longer be read in full — the committed dataset is what
|
||||
// preserves the history when that day comes.
|
||||
if (!res.ok) {
|
||||
throw new Error(`GitHub API ${res.status} on page ${page}: ${(await res.text()).slice(0, 200)}`);
|
||||
}
|
||||
const batch = await res.json();
|
||||
for (const entry of batch) {
|
||||
// A missing starred_at means the star+json media type stopped being
|
||||
// honoured. Fail loudly rather than plot NaN coordinates.
|
||||
if (Number.isNaN(Date.parse(entry.starred_at))) {
|
||||
throw new Error(`Stargazer without a usable starred_at on page ${page}.`);
|
||||
}
|
||||
stamps.push(entry.starred_at);
|
||||
}
|
||||
if (batch.length < 100) return stamps.sort((a, b) => Date.parse(a) - Date.parse(b));
|
||||
}
|
||||
}
|
||||
|
||||
export function readStamps() {
|
||||
return JSON.parse(readFileSync(DATA, "utf8")).starredAt;
|
||||
}
|
||||
|
||||
function writeStamps(stamps) {
|
||||
// Deliberately no generated-at field: the file has to stay byte-identical when
|
||||
// no star was added, or the workflow's "commit only when it moved" guard would
|
||||
// fire every single run. Git already records when it last changed.
|
||||
writeFileSync(DATA, `${JSON.stringify({ repo: REPO, starredAt: stamps }, null, 2)}\n`);
|
||||
}
|
||||
|
||||
// Round the axis maximum up to a 1/2/5 × 10ⁿ step so tick labels stay readable.
|
||||
function niceStep(max, targetTicks) {
|
||||
const raw = max / targetTicks;
|
||||
const mag = 10 ** Math.floor(Math.log10(raw));
|
||||
for (const m of [1, 2, 5]) if (raw <= m * mag) return m * mag;
|
||||
return 10 * mag;
|
||||
}
|
||||
|
||||
// One tick per month, thinned out so labels never collide on a long history.
|
||||
function monthTicks(from, to) {
|
||||
const all = [];
|
||||
const cursor = new Date(from);
|
||||
cursor.setUTCDate(1);
|
||||
cursor.setUTCHours(0, 0, 0, 0);
|
||||
if (cursor.getTime() < from) cursor.setUTCMonth(cursor.getUTCMonth() + 1);
|
||||
while (cursor.getTime() <= to) {
|
||||
all.push(cursor.getTime());
|
||||
cursor.setUTCMonth(cursor.getUTCMonth() + 1);
|
||||
}
|
||||
const stride = Math.ceil(all.length / 8) || 1;
|
||||
return all.filter((_, i) => i % stride === 0);
|
||||
}
|
||||
|
||||
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
|
||||
function monthLabel(ms, showYear) {
|
||||
const d = new Date(ms);
|
||||
const month = MONTHS[d.getUTCMonth()];
|
||||
return showYear ? `${month} ${d.getUTCFullYear()}` : month;
|
||||
}
|
||||
|
||||
// 1,392 points would bloat the committed SVG for no visible gain. Keep every
|
||||
// nth sample plus the exact final point, so the headline count stays truthful.
|
||||
function downsample(points, limit) {
|
||||
if (points.length <= limit) return points;
|
||||
const stride = Math.ceil(points.length / limit);
|
||||
const kept = points.filter((_, i) => i % stride === 0);
|
||||
if (kept.at(-1) !== points.at(-1)) kept.push(points.at(-1));
|
||||
return kept;
|
||||
}
|
||||
|
||||
export function render(stamps) {
|
||||
const times = stamps.map((iso) => Date.parse(iso));
|
||||
const total = times.length;
|
||||
const t0 = times[0];
|
||||
// The axis ends at the newest star rather than at "now". Anchoring it to the
|
||||
// clock would shift every x coordinate on every run, so the workflow could
|
||||
// never tell a real change from a redraw and would commit noise weekly.
|
||||
const t1 = times.at(-1);
|
||||
// Stars are whole numbers, so never let a tiny repo produce fractional ticks.
|
||||
const step = Math.max(1, niceStep(total, 5));
|
||||
const yMax = Math.ceil(total / step) * step;
|
||||
|
||||
const x = (ms) => PAD.left + ((ms - t0) / (t1 - t0)) * PLOT_W;
|
||||
const y = (n) => PAD.top + PLOT_H - (n / yMax) * PLOT_H;
|
||||
|
||||
const points = downsample(
|
||||
times.map((ms, i) => [ms, i + 1]),
|
||||
300,
|
||||
);
|
||||
const line = points.map(([ms, n], i) => `${i === 0 ? "M" : "L"}${x(ms).toFixed(1)} ${y(n).toFixed(1)}`).join("");
|
||||
const area = `${line}L${x(t1).toFixed(1)} ${y(0).toFixed(1)}L${x(t0).toFixed(1)} ${y(0).toFixed(1)}Z`;
|
||||
|
||||
const yTicks = [];
|
||||
for (let n = 0; n <= yMax; n += step) yTicks.push(n);
|
||||
|
||||
const xTicks = monthTicks(t0, t1);
|
||||
const spansYears = new Date(t0).getUTCFullYear() !== new Date(t1).getUTCFullYear();
|
||||
|
||||
const gridLines = yTicks
|
||||
.map(
|
||||
(n) =>
|
||||
`<line x1="${PAD.left}" y1="${y(n).toFixed(1)}" x2="${PAD.left + PLOT_W}" y2="${y(n).toFixed(1)}" class="grid"/>` +
|
||||
`<text x="${PAD.left - 10}" y="${(y(n) + 4).toFixed(1)}" class="tick" text-anchor="end">${n.toLocaleString("en-US")}</text>`,
|
||||
)
|
||||
.join("\n ");
|
||||
|
||||
const xLabels = xTicks
|
||||
.map(
|
||||
(ms, i) =>
|
||||
`<text x="${x(ms).toFixed(1)}" y="${PAD.top + PLOT_H + 22}" class="tick" text-anchor="middle">` +
|
||||
`${monthLabel(ms, spansYears || i === 0)}</text>`,
|
||||
)
|
||||
.join("\n ");
|
||||
|
||||
// Anchoring the axis to the newest star puts the final point exactly on the
|
||||
// right edge, so the callout always hangs back inside the plot.
|
||||
const lastX = PAD.left + PLOT_W;
|
||||
const lastY = y(total);
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" role="img" aria-label="Star history for ${esc(REPO)}: ${total} stars">
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.title { fill: #111827; font: 600 16px ${FONT}; }
|
||||
.sub { fill: #6b7280; font: 400 12px ${FONT}; }
|
||||
.tick { fill: #6b7280; font: 400 11px ${FONT}; }
|
||||
.grid { stroke: #e5e7eb; stroke-width: 1; }
|
||||
.axis { stroke: #d1d5db; stroke-width: 1; }
|
||||
.total { fill: ${ACCENT}; font: 600 13px ${FONT}; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0d1117; }
|
||||
.title { fill: #e6edf3; }
|
||||
.sub, .tick { fill: #8b949e; }
|
||||
.grid { stroke: #21262d; }
|
||||
.axis { stroke: #30363d; }
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="${ACCENT}" stop-opacity="0.28"/>
|
||||
<stop offset="100%" stop-color="${ACCENT}" stop-opacity="0.02"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="${W}" height="${H}" class="bg"/>
|
||||
<text x="${PAD.left}" y="26" class="title">Star History</text>
|
||||
<text x="${W - PAD.right}" y="26" class="sub" text-anchor="end">${esc(REPO)}</text>
|
||||
<g>
|
||||
${gridLines}
|
||||
</g>
|
||||
<line x1="${PAD.left}" y1="${PAD.top + PLOT_H}" x2="${PAD.left + PLOT_W}" y2="${PAD.top + PLOT_H}" class="axis"/>
|
||||
<path d="${area}" fill="url(#fade)"/>
|
||||
<path d="${line}" fill="none" stroke="${ACCENT}" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>
|
||||
<circle cx="${lastX.toFixed(1)}" cy="${lastY.toFixed(1)}" r="4" fill="${ACCENT}"/>
|
||||
<text x="${(lastX - 12).toFixed(1)}" y="${(lastY + 4).toFixed(1)}" class="total" text-anchor="end">${total.toLocaleString("en-US")}</text>
|
||||
<g>
|
||||
${xLabels}
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const renderOnly = process.argv.includes("--render-only");
|
||||
const stamps = renderOnly ? readStamps() : await fetchStarTimestamps(resolveToken());
|
||||
// Two points are the minimum a time axis can span; one would divide by zero
|
||||
// and silently write a chart full of NaN coordinates.
|
||||
if (stamps.length < 2) throw new Error(`Only ${stamps.length} stargazer(s) for ${REPO} — refusing to write a chart.`);
|
||||
if (!renderOnly) writeStamps(stamps);
|
||||
writeFileSync(OUT, render(stamps));
|
||||
console.log(`Wrote ${OUT} — ${stamps.length} stars through ${stamps.at(-1).slice(0, 10)}`);
|
||||
}
|
||||
|
||||
// Importable so `npm run validate` can re-render from the committed data and
|
||||
// prove the SVG is in sync; only a direct run touches the network or disk.
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) await main();
|
||||
@@ -13,8 +13,9 @@
|
||||
# ./scripts/install.sh <platform> [--project]
|
||||
# curl -fsSL https://raw.githubusercontent.com/hyhmrright/brooks-lint/main/scripts/install.sh | bash -s -- <platform>
|
||||
#
|
||||
# Platforms: opencode cursor windsurf antigravity pi kiro copilot droid gemini codex claude agents
|
||||
# agents = the vendor-neutral ~/.agents/skills folder (read by Cursor, Copilot, pi, Gemini, Codex)
|
||||
# Platforms: opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents
|
||||
# agents = the vendor-neutral ~/.agents/skills folder (read by Cursor, Copilot, pi, Gemini,
|
||||
# Codex, and DeepSeek Harness)
|
||||
#
|
||||
# Flags:
|
||||
# --project install into the current repo (./.<platform>/skills) instead of the global folder
|
||||
@@ -24,7 +25,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
REPO_URL="https://github.com/hyhmrright/brooks-lint.git"
|
||||
PLATFORMS="opencode cursor windsurf antigravity pi kiro copilot droid gemini codex claude agents"
|
||||
PLATFORMS="opencode cursor windsurf antigravity pi kiro copilot droid dsh gemini codex claude agents"
|
||||
|
||||
err() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; }
|
||||
info() { printf '\033[36m›\033[0m %s\n' "$*"; }
|
||||
@@ -59,6 +60,8 @@ global_dir() {
|
||||
kiro) printf '%s' "$HOME/.kiro/skills" ;;
|
||||
copilot) printf '%s' "$HOME/.copilot/skills" ;;
|
||||
droid) printf '%s' "$HOME/.factory/skills" ;;
|
||||
# DeepSeek Harness resolves its config root from $DSH_HOME, falling back to ~/.dsh.
|
||||
dsh) printf '%s' "${DSH_HOME:-$HOME/.dsh}/skills" ;;
|
||||
gemini) printf '%s' "$HOME/.gemini/skills" ;;
|
||||
codex) printf '%s' "$HOME/.codex/skills" ;;
|
||||
claude) printf '%s' "$HOME/.claude/skills" ;;
|
||||
@@ -77,6 +80,7 @@ project_dir() {
|
||||
kiro) printf '%s' "$PWD/.kiro/skills" ;;
|
||||
copilot) printf '%s' "$PWD/.github/skills" ;;
|
||||
droid) printf '%s' "$PWD/.factory/skills" ;;
|
||||
dsh) printf '%s' "$PWD/.dsh/skills" ;;
|
||||
gemini) printf '%s' "$PWD/.gemini/skills" ;;
|
||||
codex) printf '%s' "$PWD/.codex/skills" ;;
|
||||
claude) printf '%s' "$PWD/.claude/skills" ;;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Platform-inventory helpers shared by validate-repo.mjs and its tests.
|
||||
*
|
||||
* Nothing here hardcodes a platform or a translation. The platforms carrying an
|
||||
* install-table row are discovered from the docs/<name>-setup.md files on disk,
|
||||
* the documents that must show that table are discovered from README*.md, and
|
||||
* the installer's own list is parsed out of scripts/install.sh — so a new
|
||||
* platform, or a seventh language, is covered on arrival. Keeping a separate
|
||||
* hand-maintained list is what let the localized README badges go stale before
|
||||
* (see version-refs.mjs).
|
||||
*/
|
||||
|
||||
import { readdirSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
/**
|
||||
* Every document that carries the per-platform install table: all README
|
||||
* translations, plus the getting-started guide, which is the one non-README
|
||||
* page with a platform table of its own.
|
||||
*/
|
||||
export function platformDocs(root) {
|
||||
const readmes = readdirSync(root)
|
||||
.filter((file) => file.startsWith("README") && file.endsWith(".md"))
|
||||
.sort();
|
||||
return [...readmes, path.join("docs", "getting-started.md")];
|
||||
}
|
||||
|
||||
/** Every per-platform setup guide, as bare filenames. */
|
||||
export function setupGuides(root) {
|
||||
return readdirSync(path.join(root, "docs"))
|
||||
.filter((file) => file.endsWith("-setup.md"))
|
||||
.sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup-guide filenames linked from one document, normalized so a README's
|
||||
* `docs/kiro-setup.md` and getting-started's sibling `kiro-setup.md` compare
|
||||
* equal. Returns a sorted, de-duplicated array.
|
||||
*/
|
||||
export function linkedSetupGuides(text) {
|
||||
const links = text.matchAll(/\((?:docs\/)?([a-z0-9-]+-setup\.md)\)/g);
|
||||
return [...new Set([...links].map((match) => match[1]))].sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* The installer's platform list plus the platforms each directory-mapping
|
||||
* function actually handles. A platform in PLATFORMS with no case arm fails
|
||||
* `install.sh <platform>` with "unknown platform"; a case arm missing from
|
||||
* PLATFORMS is invisible in --list and the help text.
|
||||
*/
|
||||
export function parseInstallerPlatforms(text) {
|
||||
const declared = text.match(/^PLATFORMS="([^"]*)"/m)?.[1].trim();
|
||||
return {
|
||||
declared: declared ? declared.split(/\s+/) : [],
|
||||
global: caseArms(text, "global_dir"),
|
||||
project: caseArms(text, "project_dir"),
|
||||
};
|
||||
}
|
||||
|
||||
/** Platform names matched by the `case` arms of one shell function. */
|
||||
function caseArms(text, fnName) {
|
||||
const body = text.match(new RegExp(`^${fnName}\\(\\) \\{$([\\s\\S]*?)^\\}$`, "m"))?.[1] ?? "";
|
||||
return [...body.matchAll(/^\s+([a-z][a-z0-9-]*)\)/gm)].map((match) => match[1]);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ for (const scenario of scenarios) {
|
||||
system: systemPrompt,
|
||||
messages: [{ role: "user", content: userMessage }],
|
||||
});
|
||||
aiText = message.content[0]?.text ?? "";
|
||||
aiText = message.content.find((block) => block.type === "text")?.text ?? "";
|
||||
verdict = classify(scenario, aiText);
|
||||
} catch (err) {
|
||||
error = err.message;
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from "./frontmatter.mjs";
|
||||
import { GUIDE_BY_MODE, VALID_MODES } from "./assemble-prompt.mjs";
|
||||
import { versionRefs } from "./version-refs.mjs";
|
||||
import { platformDocs, setupGuides, linkedSetupGuides, parseInstallerPlatforms } from "./platforms.mjs";
|
||||
import { render as renderStarHistory, readStamps as readStarStamps } from "./gen-star-history.mjs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, "..");
|
||||
@@ -325,6 +327,49 @@ function checkAgentsDocs() {
|
||||
}
|
||||
}
|
||||
|
||||
// Every docs/<name>-setup.md must be linked from all six READMEs and the
|
||||
// getting-started table. A platform added to one language and forgotten in the
|
||||
// others was previously caught only by hand.
|
||||
function checkPlatformDocs() {
|
||||
const guides = setupGuides(root);
|
||||
check(guides.length > 0, "docs/ should contain at least one <platform>-setup.md guide");
|
||||
|
||||
for (const file of platformDocs(root)) {
|
||||
const linked = linkedSetupGuides(readText(file));
|
||||
for (const guide of guides) {
|
||||
check(linked.includes(guide), `${file} is missing an install-table link to docs/${guide}`);
|
||||
}
|
||||
for (const link of linked) {
|
||||
check(guides.includes(link), `${file} links to docs/${link}, which does not exist`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkInstallerPlatforms() {
|
||||
const { declared, global: globalArms, project } = parseInstallerPlatforms(readText("scripts/install.sh"));
|
||||
check(declared.length > 0, "scripts/install.sh should declare a PLATFORMS list");
|
||||
|
||||
for (const [arms, fn] of [[globalArms, "global_dir"], [project, "project_dir"]]) {
|
||||
for (const platform of declared) {
|
||||
check(arms.includes(platform), `scripts/install.sh ${fn}() has no path for PLATFORMS entry '${platform}'`);
|
||||
}
|
||||
for (const platform of arms) {
|
||||
check(declared.includes(platform), `scripts/install.sh ${fn}() maps '${platform}', which PLATFORMS omits`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assets/star-history.svg is a pure function of assets/star-history.json, so a
|
||||
// mismatch means the chart was hand-edited or the data moved without a redraw.
|
||||
// Re-rendering here needs no credentials, which is the point of committing the
|
||||
// data rather than the drawing alone.
|
||||
function checkStarHistory() {
|
||||
check(
|
||||
renderStarHistory(readStarStamps()) === readText("assets/star-history.svg"),
|
||||
"assets/star-history.svg does not match assets/star-history.json — rerun `node scripts/gen-star-history.mjs --render-only`",
|
||||
);
|
||||
}
|
||||
|
||||
function checkSecurity() {
|
||||
const security = readText("SECURITY.md");
|
||||
check(!security.includes("<!--"), "SECURITY.md still contains placeholder content");
|
||||
@@ -373,7 +418,10 @@ checkStepAlignment();
|
||||
checkEvalSuite();
|
||||
checkContributing();
|
||||
checkAgentsDocs();
|
||||
checkPlatformDocs();
|
||||
checkInstallerPlatforms();
|
||||
checkSecurity();
|
||||
checkStarHistory();
|
||||
checkHookOutput();
|
||||
|
||||
// ── Report ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -31,6 +31,7 @@ import { reportToSarif } from "./sarif.mjs";
|
||||
import { severityBreached, isRegression } from "./ci-gate.mjs";
|
||||
import { summarize } from "./benchmark.mjs";
|
||||
import { versionRefs } from "./version-refs.mjs";
|
||||
import { linkedSetupGuides, parseInstallerPlatforms } from "./platforms.mjs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -905,6 +906,70 @@ test("patterns match the real badge and JSON-LD shapes", () => {
|
||||
});
|
||||
});
|
||||
|
||||
console.log("\nlinkedSetupGuides");
|
||||
|
||||
test("collects guides from README-style and sibling-style links alike", () => {
|
||||
const text = "| Kiro | [setup](docs/kiro-setup.md) |\n| pi | [pi-setup.md](pi-setup.md) |";
|
||||
assert.deepEqual(linkedSetupGuides(text), ["kiro-setup.md", "pi-setup.md"]);
|
||||
});
|
||||
|
||||
test("de-duplicates a guide linked more than once", () => {
|
||||
const text = "[a](docs/dsh-setup.md) … [b](dsh-setup.md)";
|
||||
assert.deepEqual(linkedSetupGuides(text), ["dsh-setup.md"]);
|
||||
});
|
||||
|
||||
test("returns an empty array when no guide is linked", () => {
|
||||
assert.deepEqual(linkedSetupGuides("no links here"), []);
|
||||
});
|
||||
|
||||
console.log("\nparseInstallerPlatforms");
|
||||
|
||||
const INSTALLER_FIXTURE = [
|
||||
'PLATFORMS="kiro dsh"',
|
||||
"",
|
||||
"global_dir() {",
|
||||
" case $1 in",
|
||||
" kiro) printf '%s' \"$HOME/.kiro/skills\" ;;",
|
||||
" # DeepSeek Harness resolves its config root from $DSH_HOME.",
|
||||
" dsh) printf '%s' \"${DSH_HOME:-$HOME/.dsh}/skills\" ;;",
|
||||
" *) return 1 ;;",
|
||||
" esac",
|
||||
"}",
|
||||
"",
|
||||
"project_dir() {",
|
||||
" case $1 in",
|
||||
" kiro) printf '%s' \"$PWD/.kiro/skills\" ;;",
|
||||
" *) return 1 ;;",
|
||||
" esac",
|
||||
"}",
|
||||
].join("\n");
|
||||
|
||||
test("reads the declared list and both directory mappings", () => {
|
||||
const parsed = parseInstallerPlatforms(INSTALLER_FIXTURE);
|
||||
assert.deepEqual(parsed.declared, ["kiro", "dsh"]);
|
||||
assert.deepEqual(parsed.global, ["kiro", "dsh"]);
|
||||
});
|
||||
|
||||
test("omits a platform whose case arm is missing, so the validator can catch it", () => {
|
||||
// project_dir() in the fixture handles kiro but not dsh — running
|
||||
// `install.sh dsh --project` would die with "unknown platform".
|
||||
assert.deepEqual(parseInstallerPlatforms(INSTALLER_FIXTURE).project, ["kiro"]);
|
||||
});
|
||||
|
||||
test("skips comments and the catch-all arm", () => {
|
||||
const { global: arms } = parseInstallerPlatforms(INSTALLER_FIXTURE);
|
||||
assert.ok(!arms.includes("*"));
|
||||
assert.equal(arms.length, 2);
|
||||
});
|
||||
|
||||
test("parses the real installer, proving the patterns still match", () => {
|
||||
const installer = readFileSync(path.join(__dirname, "install.sh"), "utf8");
|
||||
const { declared, global: globalArms, project } = parseInstallerPlatforms(installer);
|
||||
assert.ok(declared.length >= 12, `expected the full platform list, got ${declared.length}`);
|
||||
assert.deepEqual(new Set(globalArms), new Set(declared));
|
||||
assert.deepEqual(new Set(project), new Set(declared));
|
||||
});
|
||||
|
||||
// ── Integration: validate-repo.mjs passes against current repo ─────────────
|
||||
|
||||
console.log("\nvalidate-repo integration");
|
||||
|
||||
Reference in New Issue
Block a user