This is the full developer documentation for gspot
# Introduction
> See what gspot checks and how it fits your Git work.
gspot is a command-line tool that lints AI-generated code and installs rules for AI coding agents.
* **Picks the checks.** `gspot init` reads your files and turns on the checks for the languages and frameworks it finds.
* **Pins the tools.** Every tool runs at one version on every machine and in CI, so a check that passes for you passes for everyone.
* **Stops bad commits.** Git hooks check the staged files, and a CI job checks every push.
* **Writes rules for your agent.** `AGENTS.md` lists rule files for your languages, frameworks, and level. They say how to write code that passes the checks.
## What it flags
* Bugs and type errors
* Security and accessibility problems, and leaked secrets
* Vulnerable dependencies and licenses that you do not allow
* Formatting, broken links, and spelling
* Lint rules turned off without a reason
* At [level](/start/concepts/#levels) `all`: naming, wording, structure, doc comments, duplication, and complexity
Continue with the [Quickstart](/start/quickstart/).
# Quickstart
> Set up gspot yourself or with your coding agent.
Set up gspot yourself, or give one prompt to your coding agent.
## Set up gspot
* Human
Run these commands in the root of your repository.
* npm
```shell
npm i -D -E @gspothq/cli
npx gspot init
npx gspot install
```
* pnpm
```shell
pnpm add -D -E @gspothq/cli
pnpm exec gspot init
pnpm exec gspot install
```
* Yarn
```shell
yarn add -D -E @gspothq/cli
yarn exec gspot init
yarn exec gspot install
```
* Bun
```shell
bun add -d -E @gspothq/cli
bunx gspot init
bunx gspot install
```
* mise
```shell
mise use --pin node@24 --tool-option allow_low_downloads=true npm:@gspothq/cli
mise exec -- gspot init
mise exec -- gspot install
```
1. The first command adds gspot to the project, pinned to one version.
2. `gspot init` shows a plan. When you accept it, gspot writes `gspot.toml`, the tool files, `AGENTS.md` and the rules, the Git hooks, and the CI workflow, and deletes the tool files it replaces. [Read the plan](/start/migrate/#read-the-plan) first.
3. `gspot install` installs the pinned tools and turns on the hooks. From then on, every commit runs the checks.
gspot starts at level `recommended`. For stricter checks, run `gspot set level all`, then `gspot install` again. [Levels](/start/concepts/#levels) says what each level checks.
* Agent
```shell
npm i -D -E @gspothq/cli
npx gspot init
npx gspot install
```
* npm
```shell
pnpm add -D -E @gspothq/cli
pnpm exec gspot init
pnpm exec gspot install
```
* pnpm
```shell
yarn add -D -E @gspothq/cli
yarn exec gspot init
yarn exec gspot install
```
* Yarn
```shell
bun add -d -E @gspothq/cli
bunx gspot init
bunx gspot install
```
* Bun
```shell
mise use --pin node@24 --tool-option allow_low_downloads=true npm:@gspothq/cli
mise exec -- gspot init
mise exec -- gspot install
```
* mise
Paste this prompt into Claude Code, Codex, Cursor, or another coding agent that can run commands.
```text
Set up gspot in a repository. Run curl -fsSL https://generativespotting.com/llms.txt and follow its steps.
```
The agent then:
1. Adds `@gspothq/cli` with the package manager of your repository, or pins it with mise.
2. Runs `gspot init --dry-run`, shows you the plan, and asks which [level](/start/concepts/#levels) you want. It writes the plan only after you agree.
3. Runs `gspot install` and `gspot check`, and shows you the result.
4. Reads `AGENTS.md` and the agent rules before it writes more code.
Note
Agents read the steps in [/llms.txt](/llms.txt). Read the whole documentation in [/llms-full.txt](/llms-full.txt).
## After you install
Commit `gspot.toml` and the files that gspot wrote; [Generated files](/reference/generated-files/) lists them. Then commit as usual. The pre-commit hook checks the staged files and stops the commit when a check fails. Run `gspot check` to check every file, including work that you did not commit.
## Next steps
* [Run checks](/use/run-checks/)
* [gspot.toml](/configure/gspot-toml/)
* [Coding agents](/use/coding-agents/)
# Concepts
> Understand configurations, checks, findings, settings, levels, and scopes.
| Term | Meaning |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Configuration | The checks, tool files, and rules for one language, framework, library, platform, or topic, such as `python`. The [configuration reference](/reference/configurations/) lists them. |
| Check | A tool check runs a tool, such as ESLint. A built-in check runs code in gspot. A [command check](/configure/command-checks/) runs your own command. |
| Check ID | `/`, such as `python/ruff`. `--only`, `gspot ignore`, and `gspot explain` take it. A command check takes its `name` as its ID. |
| Finding | One result of a check: a message, often with a file, line, column, lint rule, and help. |
| Lint rule | One instruction for a linter, such as `no-console`. |
| Rules | Markdown for coding agents, under `.gspot/configurations///rules/` and listed in `AGENTS.md`. See [Coding agents](/use/coding-agents/). |
| Settings | The values in `gspot.toml`. See [gspot.toml](/configure/gspot-toml/). |
| Level | `recommended` or `all`. See [Levels](#levels). |
| Stage | When a check runs. `commit` checks run in the pre-commit hook. `push` checks run with the commit checks in the pre-push hook, in CI, and in `gspot check`. `message` checks run in the commit-msg hook. `manual` checks run only when `--only` names them, and in the CI job on the default branch. |
| Scope | A folder with its own configurations and settings. See [Monorepos](/configure/monorepos/). |
| Generated file | A file that gspot writes from your settings. See [Generated files](/reference/generated-files/). |
## Levels
| Level | What it checks | Shipped checks | Tools it adds |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | --------------------------- |
| `recommended` (default) | Bugs, security and accessibility problems, type errors, formatting, and misuse of declared frameworks and libraries. Choose it for an existing codebase or a first run. | 104 | — |
| `all` | Also naming, wording, code structure, doc comments, declaration order, duplication, API style, and complexity. Choose it for a new repository or one that coding agents write. | 127 | commitlint, jscpd, syncpack |
The counts cover every shipped configuration. Your repository runs only its selected and applicable checks.
To change the level and install any extra tools:
```shell
gspot set level all
gspot install
```
Run `gspot set level recommended` to return to the default level.
# Installation
> Choose an installation method for your repository and prepare its checks.
## What you need
Git and Node.js 24.15 or newer. The tools that your checks run need these:
| Tools | What you need |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Tools from npm | Node.js and the npm, pnpm, Yarn, or Bun package manager of the project. gspot uses the package manager that `package.json` names in `packageManager` or `devEngines.packageManager`, else the one whose lockfile it finds, else npm. |
| Tools from PyPI | uv; gspot installs its pinned Python version when needed. |
| Other programs | mise, or the exact pinned tools on `PATH`. `gspot doctor` lists missing tools. |
| Your project’s own commands, such as its tests and build | Your project’s runtime, dependencies, and build tools. |
`gspot init --dry-run` shows the configurations that gspot picks. After init, `gspot doctor` lists the missing tools.
### Systems
gspot runs on macOS, Linux, and Windows. Some checks need more:
| Checks | Need |
| ----------------------------------------------------------------------- | -------------------------------------------- |
| Swift checks | The Swift toolchain |
| SwiftLint, SwiftFormat, and Swift package builds | macOS or Linux |
| `xcode/plutil`, Periphery, the SwiftLint analyzer, Xcode project builds | macOS, with Xcode selected by `xcode-select` |
| Some container checks | A running Docker daemon |
| Some scans | Network access |
Each [check page](/reference/checks/) lists its systems.
## Choose how to install
Install gspot with the package manager of your repository. A repository without `package.json`, such as a Python or Swift project, uses mise, which pins gspot in `mise.toml`. The guides show commands as `gspot ...`; start them as the last column shows.
| Package manager | Add gspot | Start later commands with |
| --------------- | -------------------------------------------------------------------------------- | ------------------------- |
| npm | `npm i -D -E @gspothq/cli` | `npx gspot` |
| pnpm | `pnpm add -D -E @gspothq/cli` | `pnpm exec gspot` |
| Yarn | `yarn add -D -E @gspothq/cli` | `yarn exec gspot` |
| Bun | `bun add -d -E @gspothq/cli` | `bunx gspot` |
| mise | `mise use --pin node@24 --tool-option allow_low_downloads=true npm:@gspothq/cli` | `mise exec -- gspot` |
Then run `gspot init` and `gspot install`, as the [Quickstart](/start/quickstart/) shows. With mise, `--pin` writes exact Node.js and gspot versions to `mise.toml`. mise requires approval for a new npm package or one with few downloads; `allow_low_downloads=true` approves gspot.
Caution
Install `@gspothq/cli` before you run `npx gspot`. npm has no package named `gspot`, so without the install, npx stops with a 404 error.
## Set up a clone
Install the dependencies from the committed lockfile first; it pins the gspot version of the repository. Without `package.json`, `mise.toml` pins it. When the versions differ, gspot stops with `This repository pins gspot ` and prints the install command. `gspot init` refuses to run where `gspot.toml` exists, so run `gspot install` instead.
* npm
```shell
npm ci
npx gspot install
npx gspot check
```
* pnpm
```shell
pnpm install --frozen-lockfile
pnpm exec gspot install
pnpm exec gspot check
```
* Yarn
```shell
yarn install --immutable
yarn exec gspot install
yarn exec gspot check
```
Yarn Classic installs with `yarn install --frozen-lockfile`.
* Bun
```shell
bun install --frozen-lockfile
bunx gspot install
bunx gspot check
```
* mise
```shell
mise exec -- gspot install
mise exec -- gspot check
```
`gspot install` installs the tools and turns on hooks in this clone. If it changes a lockfile under `.gspot/`, commit that file.
## Private registries
The npm, pnpm, Yarn, and Bun installs of gspot use the registry settings of your repository and your environment. uv reads Python package indexes from `uv.toml` or `[tool.uv]` in `pyproject.toml`.
Keep credentials in environment variables or your user settings, never in `gspot.toml` or a lockfile.
## Uninstall gspot
To stop checking one language or framework instead, see [gspot.toml](/configure/gspot-toml/#choose-configurations).
gspot has no uninstall command. Commit first, so you can go back.
1. Copy the list under “Generated files” in the gspot section of `AGENTS.md`. It names every file gspot writes outside `.gspot/` and each key it sets in your files.
2. Run `gspot set rules.enabled false`, `gspot set ci.enabled false`, and `gspot set hooks.enabled false`, then `gspot install`. gspot deletes the rules, its `AGENTS.md` section, the CI workflow, and the hook scripts, and `gspot install` removes `core.hooksPath`. If Husky, Lefthook, or your own hooks run gspot, delete those lines.
3. Restore your earlier tool files from Git, such as `git show :.eslintrc.json`, or set up the tools you want.
4. Delete the files and keys from step 1, the gspot sections in `.gitignore` and `.gitattributes`, `gspot.toml`, and `.gspot/`.
5. Remove the package: `npm uninstall @gspothq/cli`, the remove command of your package manager, or `mise unuse npm:@gspothq/cli`.
Review the diff. To undo, go back to the first commit and run `gspot install`.
# Migrate existing tools
> Review the init plan and move the settings you need.
## Read the plan
`gspot init --dry-run` prints the plan and writes nothing. Read it before you accept. The plan has these parts:
| Part | What gspot does | What you do |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `write` | Writes `gspot.toml`, `.gspot/`, and the generated files. | Commit them after `gspot install`. |
| `delete` | Deletes the tool files that gspot replaces, such as `.eslintrc.json` and `ruff.toml`. Git keeps them: `git show HEAD:`. | Move the settings you need with `gspot set` and `gspot ignore`. |
| `left in place` | Keeps tool sections in shared files, such as `[tool.ruff]` in `pyproject.toml`, which no longer apply, and keeps CI files and existing lint jobs. | Delete each tool section after you move its settings. |
| `change` | Adds its sections to `.gitignore` and `.gitattributes`, sets keys in your own files, such as the Stylelint fields of `package.json`, and names the lint tools that `.gspot/package.json` now pins. | Remove those tools from `devDependencies` when nothing else uses them. |
| `no longer runs; delete when ready` | Names folders of lint scripts, such as `lint/`. | Check that nothing uses them, then delete them. |
init copies the indent width, line width, and quote style from your Prettier JSON settings or `.editorconfig` into `[format]`. It copies no other settings. A tool that gspot does not run can run as a [command check](/configure/command-checks/).
If Husky, Lefthook, or your own hooks folder runs your Git hooks, gspot keeps it and prints three lines to add; see [Existing hook managers](/use/hooks/#existing-hook-managers).
# Upgrade gspot
> Install a new gspot version and update the generated files.
Commit first, so you can go back. Read the [release notes](https://github.com/stefanionescu/gspot/releases), pick a release, such as `1.2.0`, and put it in place of `` below.
* npm
```shell
npm i -D -E @gspothq/cli@
npx gspot apply
npx gspot install
npx gspot check
```
* pnpm
```shell
pnpm add -D -E @gspothq/cli@
pnpm exec gspot apply
pnpm exec gspot install
pnpm exec gspot check
```
* Yarn
```shell
yarn add -D -E @gspothq/cli@
yarn exec gspot apply
yarn exec gspot install
yarn exec gspot check
```
* Bun
```shell
bun add -d -E @gspothq/cli@
bunx gspot apply
bunx gspot install
bunx gspot check
```
* mise
```shell
mise use --pin --tool-option allow_low_downloads=true npm:@gspothq/cli@
mise exec -- gspot apply
mise exec -- gspot install
mise exec -- gspot check
```
`gspot apply` writes the new version to `.gspot/version` and updates the generated files. If it refuses a key of `gspot.toml` that the new version renamed or removed, the message names the close keys and links the release notes, which list each change. Edit `gspot.toml`, then run `gspot apply` again.
When `gspot apply` succeeds, review the diff and fix any findings. Then commit the generated files that changed, in `.gspot/` and outside it, and either `package.json` with its lockfile or `mise.toml`, together. Commit `gspot.toml` too if you changed it to fix findings. Every other clone then runs `gspot install`. See [Set up a clone](/start/install/#set-up-a-clone).
If `gspot apply` names a JSON file it did not overwrite, delete that file and run `gspot apply` again. A JSON file has no header that marks it as generated, so gspot 0.1 kept that mark in a log that newer versions do not read.
# Continuous integration
> Run checks in GitHub Actions, GitLab, or an existing pipeline.
`gspot init` plans a workflow for GitHub or GitLab. It picks the provider from `.github/workflows/` or `.gitlab-ci.yml`, or else from an `origin` remote on github.com or gitlab.com.
init plans no workflow when a GitHub or GitLab CI file already has a lint job, such as a job that runs `eslint`, `ruff check`, or `gspot check`, and it keeps that job. A Jenkins, CircleCI, Bitbucket, Azure Pipelines, or Buildkite file does not stop it. If that pipeline runs gspot, turn the gspot workflow off:
```shell
gspot set ci.enabled false
```
## Turn CI on
To turn CI on later, run the first command. For GitLab, run the second one too:
```shell
gspot set ci.enabled true
gspot set ci.provider gitlab
```
`gspot set` writes the workflow file. For GitHub, gspot writes `.github/workflows/gspot.yml`. For GitLab, it writes `.gitlab/ci/gspot.yml`. Include that file in your pipeline:
```yaml
include:
- local: .gitlab/ci/gspot.yml
```
## What the job checks
The job checks the files changed after the pull or merge request base, or after the previous push. The first push of a branch checks every file. On pushes to the default branch, a second job runs the manual checks that your configurations select.
## Systems
On GitHub, the job runs on Linux. When the repository selects the `swift` configuration, the workflow also runs on macOS. To add other systems:
```shell
gspot set ci.operating_systems linux macos windows
```
This setting works on GitHub only; gspot refuses it with `ci.provider = "gitlab"`. Pick only systems where your checks can run.
## Install steps
Each GitHub job installs mise, then runs `gspot install` with the gspot version pinned in the generated workflow. The GitLab job needs mise on the CI machine.
To prepare the project first, set one command. The job runs it before `gspot install` and stops if it fails:
```shell
gspot set ci.setup_command npm ci
```
gspot quotes each argument, so spaces, quotes, and `$` reach the command as plain text.
On GitHub, the install step gives mise the token of the job as `MISE_GITHUB_TOKEN`, so tool downloads skip the low rate limit for anonymous requests. In other pipelines, set `MISE_GITHUB_TOKEN` yourself.
## Your own pipeline
Install mise and the runtime of your project. Then install your dependencies and run gspot the way your repository starts it; see [Install gspot](/start/install/#choose-how-to-install). With npm:
```shell
npm ci
npm exec --no -- gspot install
npm exec --no -- gspot check
```
With mise:
```shell
mise exec -- gspot install
mise exec -- gspot check
```
Run manual checks by name, for example:
```shell
npm exec --no -- gspot check --only security/semgrep-registry
```
Let the pipeline fail on the gspot exit code: `1` means findings, and `2` means the run did not finish. A missing program gives exit 2, and the report names it. `gspot check --json` prints the report as JSON.
Do not cache or upload `.gspot/state/` or credentials. To cache downloads and installed tools, key the cache on `gspot.toml` and the files in `.gspot/`, as the GitHub job does.
## Turn CI off
Run `gspot set ci.enabled false`. gspot deletes the workflow file.
# Coding agents
> What gspot writes for coding agents, and how to change it.
gspot writes rules: Markdown files for your configurations and level, in the `rules/` folder of each configuration under `.gspot/configurations/`. The rules guide agents; the checks still block failing code.
## The `AGENTS.md` section
gspot writes one section between gspot markers and keeps your own text outside it. The section lists the rules and names `gspot check --staged`, the command to run before a commit.
It also names the commands that change settings, and the generated files that agents must not edit, with the keys gspot sets in your own files.
An agent that reads another file needs a pointer. For Claude Code, add the line `@AGENTS.md` to `CLAUDE.md`.
## Leave out some rules
`gspot set rules.exclude .gspot/configurations/language/javascript/rules/BUN.md` leaves out one file. A folder, such as `.gspot/configurations/language/bash`, leaves out every rule in it.
## Turn rules off
Run `gspot set rules.enabled false`. gspot deletes the rules and its section. If `AGENTS.md` holds nothing else, gspot deletes the file.
## These docs for agents
Each page of these docs has a Markdown copy at its address plus `index.md`. For example, the Quickstart copy lives at `https://generativespotting.com/start/quickstart/index.md`. A request with `Accept: text/markdown` gets the copy at the page address. `/llms.txt` holds the setup steps for agents, and `/.well-known/agent-skills/index.json` lists the same steps as an Agent Skill.
# Editors
> Make your editor run the same tools, with the same settings, as gspot.
Your editor reads the same tool files as gspot. A pointer file at the root of each scope leads each editor extension to its tool file under `.gspot/configurations/`, so the editor reports what `gspot check` reports.
## Before you open the editor
Run `gspot install` in each clone first. The tool files load their plugins from `.gspot/node_modules` and `.gspot/.venv`, so the ESLint extension, for example, fails until `gspot install` puts the tools there.
## How editors find the tool files
| Tool | Pointer |
| ------------ | -------------------------------------------- |
| ESLint | `eslint.config.mjs` |
| Prettier | `prettier.config.mjs` |
| Stylelint | `.stylelintrc.json` |
| markdownlint | `.markdownlint.jsonc` |
| Ruff | `ruff.toml` |
| basedpyright | `pyrightconfig.json` |
| SwiftLint | `.swiftlint.yml`, also in Swift test folders |
`.editorconfig` carries the `[format]` settings to every editor that reads it. Some tools have no pointer; gspot passes their tool file when it runs them, so give your editor the same path. [Generated files](/reference/generated-files/) lists every file gspot writes.
## Validate `gspot.toml`
The [JSON Schema](https://generativespotting.com/gspot.schema.json) describes every key of `gspot.toml`. Editors apply it on their own once SchemaStore lists gspot.
# Git hooks
> Run checks before commits and pushes and check commit messages.
The `gspot init` plan includes Git hooks. With `hooks.enabled = true`, gspot writes each hook below that runs at least one check, and `gspot install` turns them on. At level `recommended`, no check runs on commit messages, so gspot writes no `commit-msg` hook.
| Hook | Checks |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pre-commit` | The commit-stage checks, on the staged files. |
| `pre-push` | The commit- and push-stage checks, on each commit that the push sends. At level `all`, `commits/commitlint-pushed` also checks each pushed commit message. |
| `commit-msg` | The message-stage checks on the new commit message: `commits/commitlint`, at level `all`. |
The commit hook checks the staged files. The push hook checks every commit that it pushes, intermediate ones included. Some checks, such as the type checker, read the whole project and can report a finding in a file you did not change.
Each clone runs `gspot install` once to turn the hooks on; see [Set up a clone](/start/install/#set-up-a-clone).
## Turn hooks on or off
To turn hooks on, run:
```shell
gspot set hooks.enabled true
gspot install
```
To turn them off, run `gspot set hooks.enabled false`, then `gspot install`.
## Existing hook managers
A repository with Husky, Lefthook, or your own hook folder keeps its hooks. gspot prints one line for each hook instead; add each line to the matching hook. For npm, the lines look like this:
```shell
# pre-commit
npm exec --no -- gspot check --hook pre-commit
# pre-push: preserve Git's remote arguments and standard input
npm exec --no -- gspot check --hook pre-push -- "$@"
# commit-msg: preserve Git's message-file argument
npm exec --no -- gspot check --hook commit-msg -- "$1"
```
Use the exact lines that gspot prints for your package manager or mise. A hook without its line runs no gspot checks.
The lines start gspot this way:
```text
npm: npm exec --no -- gspot
pnpm: pnpm exec gspot
Yarn: yarn exec gspot
Bun: bun run --no-install gspot
```
When the root `package.json` does not list `@gspothq/cli`, they run `mise exec -- gspot`.
## Test a hook
```shell
gspot check --hook pre-commit
gspot check --hook commit-msg -- .git/COMMIT_EDITMSG
```
The pre-push hook needs the remote arguments and revision updates that Git passes on standard input, so test it with `git push`. After a failed push, the `run again:` line of the report runs the pre-push checks again with the input of Git. To compare with a branch outside a push:
```shell
gspot check --changed origin/main
```
## Skip the hooks once
`git commit --no-verify` and `git push --no-verify` bypass the local hooks once. CI still runs. To stop a finding on every run, add an ignore. See [Ignore findings](/use/ignore/).
# Ignore findings
> Record an ignore with a reason, then inspect or remove it.
```shell
gspot ignore javascript/eslint --rule no-console --paths "scripts/**" --reason "Scripts print their results to the terminal."
```
Every ignore needs a reason. Leave out `--rule` to ignore the whole check, and add `--paths` to select files.
Add `--until YYYY-MM-DD` for an ignore that ends at 00:00 UTC on that date. After that date, the record stays in `gspot.toml` for review.
To ignore a dependency advisory, give its ID as the rule:
```shell
gspot ignore dependencies/osv-scanner --rule --reason "..."
```
To remove the ignore:
```shell
gspot unignore javascript/eslint --rule no-console --paths "scripts/**"
```
`gspot check --verbose` prints each ignore in effect, its reason, and the number of findings it matched.
To change a limit, see [limits](/reference/gspot-toml/#limits). Use an ignore only when you can state why the check does not fit that code.
# Run checks
> Read a finding, explain it, fix it, and read the report.
A failed check prints one line with its check ID, `failed`, and the number of findings. In a nested scope, the line starts with the scope path. Under it, each finding prints its file, line and column, lint rule, and message.
`help:` says what to do, and `run again:` gives the command that runs that check alone. Without `--verbose`, the report prints the first 200 findings of each check.
```text
javascript/eslint failed 2 findings
src/checkout.ts:4:8 @typescript-eslint/require-await Async function 'pay' has no 'await' expression.
src/checkout.ts:8:13 no-empty Empty block statement.
help: Run `gspot check --fix` for the rules that fix themselves, then read each remaining line; `gspot explain ` links the rule documentation.
run again: gspot check --only javascript/eslint --staged
```
Fix the file, then run the check again.
## Explain a finding
| Command | Prints |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `gspot explain bash/shellcheck` | What the check looks for, why it matters, and what to do. |
| `gspot explain gspot/no-trivial-files` | A lint rule, as the report prints it, with a link to its documentation and the command that ignores it. |
| `gspot explain limits.file_lines` | The value of a setting, its default, and where the value comes from. |
| `gspot explain ./` | The checks that read a file and the ignores that match it. |
## Choose what to check
| Command | Checks |
| ----------------------------------------------------- | ------------------------------------------------------------------- |
| `gspot check` | Every file, including work that you did not commit. |
| `gspot check --staged` | The staged content, with the commit and push checks. |
| `gspot check --hook pre-commit` | What the pre-commit hook runs: the commit checks on staged files. |
| `gspot check --changed` | The files changed since your upstream branch or the default branch. |
| `gspot check --changed origin/main` | The files changed since another branch, tag, or commit. |
| `gspot check ` | Some files and folders, relative to the current folder. |
| `gspot check --only javascript/eslint typescript/tsc` | Some checks. |
A check that reads the whole project, such as `typescript/tsc`, still reports findings in every file.
## Fix findings automatically
Run the formatters and the other fixers, then check again. Add `--dry-run` to see the changes without writing them:
```shell
gspot check --fix
gspot check --fix --dry-run
```
The changes stay in your working tree, unstaged, so review them before you commit. To add a fixer for your own command, see [command checks](/configure/command-checks/#add-a-fix-command).
## Statuses and exit codes
| Status | Meaning | Exit code |
| --------- | ------------------------------------------------- | --------- |
| `failed` | The check reported findings. | 1 |
| `missing` | The tool needs installation: run `gspot install`. | 2 |
| `error` | The check could not run; the line says why. | 2 |
| `passed` | No findings. | 0 |
| `skipped` | The check did not run; the line says why. | 0 |
The report shows `failed`, `missing`, and `error` rows. Add `--verbose` for the `passed` and `skipped` rows, each command, and each ignore with its reason. `gspot check --json` prints the whole report as one JSON object.
When a lint rule does not fit your code, [ignore the finding](/use/ignore/). To change a limit, see [limits](/reference/gspot-toml/#limits).
# Troubleshooting
> Fix gspot.toml errors, missing tools, failed installs, version mismatches, and slow checks.
Start with `gspot doctor`. It lists missing tools, hook problems, and generated files that changed. When you [report a problem](https://github.com/stefanionescu/gspot/issues), include the output, the check ID, and the command that reproduces it. Leave out credentials.
## gspot.toml has an error
Fix the value or table named by the error and run the command again. A setting that loosens a check needs a reason beside its value.
## A tool cannot run
Run `gspot install`. It installs program tools with mise. Without mise, install the programs that `gspot doctor` lists. See [What you need](/start/install/#what-you-need). A missing tool makes the run incomplete with exit code 2, which also stops a commit in the hook.
## `gspot install` fails
If mise prints `GitHub rate limit exceeded`, set `MISE_GITHUB_TOKEN` to a GitHub token that can read public releases, then run `gspot install` again. See [mise: If something does not work](https://mise.jdx.dev/getting-started.html#if-something-doesn-t-work). To make new tool lockfiles, delete the lockfiles in `.gspot/`, run `gspot install`, and commit them.
## `This repository pins gspot `
The gspot that runs differs from `.gspot/version`. The error prints the command that installs the pinned version; run it. To move the repository to the version that runs, follow [Upgrade gspot](/start/upgrade/).
## A generated file has local edits
Move any change you need into `gspot.toml` before you run `gspot apply`. Apply replaces owned generated files, including local edits. See [Generated files](/reference/generated-files/).
## A check runs slowly
Run `gspot check --verbose`. Each passed check then shows its number of files and its time, and the command that it ran. Run one check alone with `gspot check --only `. A tool command that runs longer than `tool_timeout_seconds` stops with an error; the setting defaults to 600.
## `There is no terminal to ask in`
`gspot init` asks before it writes, and it cannot ask in CI or anywhere without a terminal. Pass `--yes` to accept the plan, or `--dry-run` to print it.
## npm exec cannot find gspot
Add `@gspothq/cli` with your package manager; init does not add it. See [Install gspot](/start/install/). In a fresh clone, run your package manager’s install, then `gspot install`.
## A commit stops with `npm is not installed`
The Git hook did not find the program that starts gspot: npm, pnpm, Yarn, Bun, or mise. The message names it. Install that program, run its install command, such as `npm install` or `mise install`, then commit again.
# Command checks
> Run your own command as a gspot check, with its findings in the same report.
A command check runs your own command on the files you select. gspot reads its output as findings, so they appear in the same report as every other check.
## Example: find unfinished notes
This check reports `FIXME` lines in the files under `notes/`. It needs Node.js on your `PATH`.
Save this checker under the example filename:
scripts/check-notes.mjs
```javascript
import { readFile } from 'node:fs/promises';
for (const path of process.argv.slice(2)) {
const lines = (await readFile(path, 'utf8')).split('\n');
for (const [index, line] of lines.entries()) {
if (!line.includes('FIXME')) continue;
console.log(`${path}:${index + 1}: ${line}`);
process.exitCode = 1;
}
}
```
Add the check to `gspot.toml` as a `[[command_check]]` record:
```toml
[[command_check]]
name = "notes"
command = ["node", "scripts/check-notes.mjs", "{files}"]
paths = ["notes/**"]
stage = "commit"
summary = "Finds unfinished FIXME notes."
help = "Replace each FIXME note with the finished instruction."
output = { format = "regex", pattern = '^(?[^:]+):(?\d+): (?.*)$' }
```
Save this note under the example filename:
notes/deploy.txt
```text
FIXME: document the deployment command.
```
Apply the settings and run the check:
```shell
gspot apply
gspot check --only notes
```
The check exits with `1` and reports a finding on the first line of the note. Replace that line with the finished instruction:
```text
Run the deployment command documented in the release guide.
```
Run the same check again. It exits with `0` and reports no findings.
## How the command runs
`command` lists arguments; gspot runs them without a shell. `{files}` expands to the selected files, one argument each. File names with spaces need no extra quoting. To run the command once for each file, use `{file}` instead. Then a regular expression match without a `file` group uses the file of that run.
By default, any nonzero exit fails the check. When your tool uses one exit code for findings, set `exit_codes = [1]`. Any other nonzero exit then counts as a failed run, exit code `2`, even when the tool printed findings first. An empty list makes every nonzero exit a failed run.
## Read the output
`output.format` selects the parser:
| Format | What it reads |
| -------------- | ---------------------------------------------------------------------------------------------- |
| `regex` | Lines matched by `pattern`, with named `file`, `line`, `column`, `rule`, and `message` groups. |
| `grouped` | A file heading matched by `file_pattern`, then findings matched by `pattern`. |
| `sarif` | SARIF results, rule metadata, source locations, and fixes. |
| `knip` | Knip JSON issue categories and source positions. |
| `semgrep` | Semgrep JSON results, locations, lint rule IDs, messages, and fixes. |
| `trufflehog` | TruffleHog JSON findings, with raw secret output withheld. |
| `typos` | typos JSON, with byte offsets converted to character columns. |
| `markdownlint` | markdownlint result objects and their lint rule and source locations. |
| `json` | JSON findings selected by `items`, `children`, and `fields`. |
| `lines` | Each nonempty output line as a finding without a source location. |
| `none` | Exit status only, without output findings. |
The regex groups have these meanings:
| Group | Meaning |
| --------- | -------------------------------------------------------- |
| `file` | Source path, resolved from the command’s working folder. |
| `line` | Source line number. |
| `column` | Source column number. |
| `rule` | Lint rule name. |
| `message` | Finding text. |
For JSON, map the report’s field names:
```toml
output = { format = "json", fields = { message = "message" } }
```
Malformed output makes the run incomplete with exit code `2`.
## Add a fix command
For `gspot check --fix`, set `fix` to a list of arguments. `--fix` runs every fixer, then runs them again on the files that changed, so a formatter reads what another fixer wrote. Then it runs the checks again. The fix commands change your working tree, not the staging area.
If the fix command exits with one of the `exit_codes`, gspot reports remaining findings. Other nonzero exits count as a failed run.
[Command check settings](/reference/gspot-toml/#command_check) lists the accepted inputs.
# Exclude files
> Keep generator output, vendored files, and other files out of the checks.
gspot reads the files that Git tracks, and untracked files that `.gitignore` does not hide. Keep build output and other files that do not belong in Git in `.gitignore`. Most checks read only your own source files. Secret scans read every file, and dependency checks read lockfiles.
## Files gspot leaves out
gspot leaves these out of most checks without a mark:
* generator output: files whose first lines carry a generator banner, such as `@generated` or `// Code generated by ... DO NOT EDIT.`
* vendored files: files under a folder named `vendor`, `third_party`, `third-party`, `node_modules`, `Pods`, `Carthage`, or `.build`, and license files
* binary files
Run `gspot explain ./` to see how gspot treats a file.
## Mark other files
In `.gitattributes`, mark other generator output as `linguist-generated`, and other vendored files as `linguist-vendored`:
```text
src/api/schema.ts linguist-generated
lib/upstream/** linguist-vendored
```
## Leave a file out of one check
Ignore the check for those paths, with a reason:
```shell
gspot ignore javascript/eslint --paths "scripts/**" --reason "Scripts print their results."
```
See [Ignore findings](/use/ignore/).
# gspot.toml
> Choose configurations and a level, and change settings in gspot.toml.
`gspot.toml` holds your configurations, level, settings, and ignores. Setting commands edit the file and apply each change.
## See the current choices
```shell
gspot list
gspot list settings
```
Add `--json` for complete values and inherited settings. The [settings reference](/reference/gspot-toml/) lists accepted values and defaults.
## Choose configurations
A configuration groups the checks, tool files, and rules for a language, framework, or concern. Select one, or drop a language or framework, by name:
```shell
gspot add vitest
gspot remove nextjs
```
`gspot remove nextjs` writes `nextjs` into `removed_configurations`, so detection does not select it again; `gspot add nextjs` reverses that. gspot does not remove a configuration that another selected configuration needs. When the tools change, gspot prints a reminder to run `gspot install`.
gspot always selects most general configurations, such as `secrets`, `spelling`, and `security`, even when you write the list yourself, and `gspot add` and `gspot remove` refuse them. To run fewer of their checks, change the level, or turn one check off with an [ignore](/use/ignore/).
In the file, the selection reads:
gspot.toml
```toml
configurations = ["bash"]
level = "recommended"
```
Framework lint rules follow your dependencies. For example, a NestJS project that uses `@nestjs/swagger` also gets the Swagger lint rules.
## The level
```shell
gspot set level all
```
[Levels](/start/concepts/#levels) define `recommended` and `all`. Neither enables experimental or preview lint rules.
## Change a setting
| Command | What it does |
| --------------------------------- | --------------------------------------------------------------------- |
| `gspot set ` | Writes your value. On a list, it adds items to the shipped ones. |
| `gspot unset ` | Removes your value and its reason, so the default takes effect again. |
| `gspot unset ` | Removes list items that you wrote. The shipped items stay. |
To change a setting in one folder only, see [Settings in a scope](/configure/monorepos/#settings-in-a-scope).
A value that loosens a check needs a reason beside it. This covers a raised limit, lowered coverage, and an item that the shipped list lacks added to an allow-list, such as `allowed_words` or `dependencies.allowed_registries`:
gspot.toml
```toml
[limits]
function_lines = { value = 80, reason = "The parser is one state machine." }
```
## Edit the file by hand
gspot.toml uses TOML 1.0. Keep each inline table on one line. The [JSON Schema](https://generativespotting.com/gspot.schema.json) describes every key. Editors apply it on their own once SchemaStore lists gspot, so `gspot.toml` needs no `#:schema` comment. See [Editors](/use/editors/).
After an edit, preview the changes, then write them and install the tools:
```shell
gspot apply --dry-run
gspot apply
gspot install
```
## When files of a language appear or go
When files of a language or framework appear, select its configuration with `gspot add`, then run `gspot install`.
When the files go away, its checks become inactive. Its configurations, settings, ignores, and command checks stay in gspot.toml. `gspot apply` writes generated files from your saved settings and does not change `configurations` or `removed_configurations`.
# Lint rule options
> Set lint rule options in gspot.toml instead of the tool files.
Set lint rule options in `gspot.toml`; gspot writes them into the tool file. Options take effect only for lint rules that the selected level turns on. To turn a lint rule off, use an [ignore](/use/ignore/) with a reason.
None of these settings turns a lint rule on or off, and none takes a severity; the level decides which rules run.
| Tool | Setting | Takes |
| --------------------- | ---------------------------------------------------- | ------------------------------------- |
| ESLint | `tools.eslint.rules` | An option array for each rule ID |
| ESLint for some files | `[[tools.eslint.override]]` with `paths` and `rules` | The same, for the files of `paths` |
| Stylelint | `tools.stylelint.rules` | The options of each rule |
| markdownlint | `tools.markdownlint.rules` | An option table for each rule ID |
| yamllint | `tools.yamllint.rules` | The options of each rule |
| commitlint | `tools.commitlint.rules`, `types`, and `scopes` | Rule options, and the accepted values |
## ESLint
For `export const matches = (value) => value == null;`, the `eqeqeq` lint rule can report:
```text
Expected '===' and instead saw '=='.
```
These settings accept the null comparison while requiring strict equality elsewhere:
```toml
configurations = ["javascript"]
[tools.eslint.rules]
eqeqeq = ["always", { null = "ignore" }]
```
```shell
gspot set tools.eslint.rules '{ eqeqeq = ["always", { null = "ignore" }] }'
```
This table holds options only; do not put a severity such as `"off"` in it. `gspot explain eslint/eqeqeq` links the documentation of the lint rule, which lists its options, and prints the commands that set its options or ignore it.
## Ruff
Ruff takes no lint rule options from `gspot.toml`. Its limits, such as `limits.cyclomatic_complexity` and `limits.function_parameters`, come from [limits](/reference/gspot-toml/#limits), and `tools.ruff.docstring_convention` sets the pydocstyle convention. To turn a Ruff lint rule off, ignore it by its code: `gspot ignore python/ruff --rule --reason ""`.
## Stylelint
Stylelint options go in `tools.stylelint.rules`:
```toml
[tools.stylelint.rules]
color-hex-length = "long"
number-max-precision = 0
```
This requires long hexadecimal colors at both levels. The precision option counts only at `all`, where that lint rule runs. To allow a WebKit property that your project needs, use the `ignoreProperties` option of `property-no-vendor-prefix`. To allow an at-rule, use the `ignoreAtRules` option of `at-rule-no-unknown`:
```toml
[tools.stylelint.rules]
at-rule-no-unknown = [true, { ignoreAtRules = ["container"] }]
```
## markdownlint
Each markdownlint entry takes an option table, without an on or off value or a `default` key:
```toml
[tools.markdownlint.rules]
MD024 = { siblings_only = false }
```
This reports repeated headings throughout a document, not only under the same parent heading.
See [tool options](/reference/gspot-toml/#tools) for the accepted fields.
# Monorepos
> Give each scope its own configurations and settings in gspot.toml.
A scope holds the configurations and settings for one folder. `gspot init` plans a scope for each folder with a tracked project file, such as `package.json`, `pyproject.toml`, or `Package.swift`. Check the path and configurations of each scope in the plan before you accept it.
## Declare scopes
This `gspot.toml` has two scopes and no configurations at the root:
```toml
[[scope]]
path = "api"
configurations = ["typescript", "express", "vitest"]
limits.function_lines = { value = 80, reason = "The API parser is one state machine." }
[[scope]]
path = "ios"
configurations = ["swift", "xcode"]
```
Each `[[scope]]` names its folder in `path`. Its settings cover that folder only.
After you change the scopes, apply the settings, install the tools, and check one scope:
```shell
gspot apply
gspot install
gspot check api
```
To add a configuration that init did not find: `gspot add pytest --scope services/api`.
## Which scope a file belongs to
A file belongs to the deepest scope that contains it. Files outside every scope belong to the root.
A whole-project check, such as a type check, can read other files of the same project when you name one file. Checks that read project files look inside the scope only, and each child scope checks its own files.
To see the scope and the checks of a file, run `gspot explain` with the path. Start the path with `./` when it also names a configuration or a check: `gspot explain bash` describes the configuration, and the `./` form describes the file.
## Settings in a scope
```shell
gspot set limits.function_lines 80 --scope api --reason "The parser is one state machine."
```
Root settings cover every scope. Write paths relative to the repository root. The default paths of a configuration resolve inside the scope that selects it.
A list setting adds up: a scope gets the values of its configurations, the root, and every scope that contains it, without repeats. A single-value setting takes the nearest value.
If two selected configurations give different defaults for the same setting, gspot names both. To pick one, set the value at the root or in a scope.
## gspot.toml in a subfolder
gspot uses the nearest `gspot.toml` in the current folder or a folder above it. To use the one in `api/`, run gspot from there: `cd api && gspot check`.
[Scope settings](/reference/gspot-toml/#scope) lists the accepted inputs.
# Share settings
> Export portable settings and set up another repository from them.
A template copies your gspot.toml, without scopes, for use in another repository.
## Export
```shell
gspot export gspot.template.toml
```
A template carries every setting and record that you wrote, including command checks and empty tables, but no defaults. In the destination, the `gspot/unmatched-patterns` check reports carried paths that match no tracked file or folder.
Export warns when a command check runs a file of this repository directly, such as `./