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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 35 additions & 45 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,43 @@
# kagi

## Goal

Unofficial Unix-style CLIs for Kagi: `kagi-search`, `kagi-maps`, `kagi-summarize`.
Three Unix-style CLIs for Kagi: `kagi-search`, `kagi-maps`, and `kagi-summarize`. `README.md`
covers install, the token, and usage.

## Gates

- `task ci` runs `task check`, the release-profile check, `nix build`,
cargo-machete, and cargo-deny. GitHub Actions runs all but `task test:nix`, so
run `task ci` yourself after you touch `flake.nix`, `flake.lock`, or
`Cargo.toml`. A lock-only input refresh still ships a Nix build nothing else
checks.
- `task lint` calls `zizmor`. The `nix develop` shell does not ship it, so put
`zizmor` on `PATH` before you run lint or check.
- `task test:live` hits the real Kagi service. Run it when you change the
request path or a parser: `src/cli.rs`, `src/client.rs`, or `src/parse.rs`. It
needs `KAGI_SESSION_TOKEN` or `~/.config/kagi/session-token` and fails without
one.

## Decisions

- Three separate binaries. Do not reintroduce a combined command.
- Never hardcode a session token. The client reads `KAGI_SESSION_TOKEN` (empty
counts as unset), then `$XDG_CONFIG_HOME/kagi/session-token`, falling back to
`~/.config/kagi/session-token`. It refuses a token file that is group- or
world-readable.
- `--sort` means two different things. `kagi-search` sends it to Kagi as the
`order` parameter. `kagi-maps` fetches the whole page, sorts locally, then
truncates to `--limit`.
- `install.sh` is the one installer. `task install` and the release smoke test
run it on local archives. `task install` packs the local build like a
release archive, so it runs only on the two release platforms.
- The repo ships no agent skill; an agent that uses these CLIs brings its own.
On the upgrade from 0.5 or earlier, `install.sh` removes the skill that
release installed; later runs leave any `kagi` skill alone.
- A version bump is the release trigger: on each push to `main`, `release.yml`
reads `version` from `Cargo.toml`. When the tag `v<version>` does not exist,
it tags and publishes. The tag decides, not the parent commit, so a
multi-commit push or a cancelled run does not lose a release. A tag deleted by
hand comes back on the next push. Recover a failed build or release through
`workflow_dispatch` on `release.yml`.
- `task ci` runs `task check`, the release-profile check, `nix build`, cargo-machete, and
cargo-deny. GitHub Actions runs all of it except `task test:nix`. Run `task ci` yourself after
you touch `flake.nix`, `flake.lock`, or `Cargo.toml`, because nothing else builds the Nix
package. This includes a lock-only input refresh.
- `task lint`, and with it `task check` and `task ci`, runs `zizmor`. The `nix develop` shell does
not include it, so put `zizmor` on `PATH` first.
- `task test:live` calls the real Kagi service. Run it when you change the request path or a
parser: `src/cli.rs`, `src/client.rs`, or `src/parse.rs`. It needs a session token (see
`README.md`) and fails without one.
Comment thread
rubas marked this conversation as resolved.
Resolved

## Rules

- Keep three separate binaries. Do not add a combined command again.
- Never hardcode a session token.
- `--sort` means two things. `kagi-search` sends it to Kagi as the `order` parameter. `kagi-maps`
gets the whole result page, sorts it locally, then cuts it to `--limit`.
- `install.sh` is the one installer. `task install` and the release smoke test run it on local
archives. `task install` packs the local build like a release archive, so it runs only on the
two release platforms.
- The repo ships no agent skill. An agent that uses these CLIs brings its own. On the upgrade from
0.5 or earlier, `install.sh` removes the skill that release installed. Later runs leave any
`kagi` skill alone.
- A version bump is the release trigger. On each push to `main`, `release.yml` reads `version`
from `Cargo.toml`. When the tag `v<version>` does not exist, it tags and publishes. The tag
decides, not the parent commit, so a push of many commits or a cancelled run does not lose a
release. A tag that someone deletes comes back on the next push. To recover a failed build or
release, run `release.yml` through `workflow_dispatch`.

## Pitfalls

- `flake.nix` repeats the package version as a literal. Bump it in the same
commit as `Cargo.toml`, or `nix build` produces a package with the old
version.
- `install.sh` runs under `sh` on Linux with GNU tools and on macOS with BSD
tools. Use POSIX sh and flags both sets have: no `realpath --relative-to`, no
`ln -T`.
- `ci.yml` skips a pull request whose author is not the repository owner. A
contributor PR shows no checks; that is the gate, not a broken run.
- `flake.nix` repeats the package version as a literal. Bump it in the same commit as
`Cargo.toml`, or `nix build` makes a package with the old version.
- `install.sh` runs under `sh` on Linux with GNU tools and on macOS with BSD tools. Use POSIX sh
and only flags that both sets have: no `realpath --relative-to`, no `ln -T`.
- `ci.yml` skips a pull request whose author is not the repository owner. A contributor PR shows
no checks. That is the gate, not a broken run.
106 changes: 31 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# kagi

Unofficial Unix-style CLI tools for [Kagi Search](https://kagi.com). Not affiliated with or endorsed by Kagi Inc.
Unofficial Unix-style command-line tools for [Kagi Search](https://kagi.com). Kagi Inc. does not
make or endorse them.

Each binary does one job:

- `kagi-search` finds sources
- `kagi-maps` finds places and addresses
- `kagi-summarize` summarizes one explicit URL

Output is plain text by default and compact JSON with `--json`, so it works well for terminal use and agentic pipelines.

Requires a Kagi account with an active session token.
Output is plain text by default and compact JSON with `--json`, for scripts and agents. You need a
Kagi account and its session token.

## Install

Expand All @@ -20,8 +20,8 @@ Requires a Kagi account with an active session token.
curl -fsSL https://github.com/rubas/kagi/releases/latest/download/install.sh | sh
```

The installer puts `kagi-search`, `kagi-maps`, and `kagi-summarize` in `~/.local/bin`. On the upgrade
from 0.5 or earlier, it removes the `kagi` agent skill and the links that release installed.
The installer puts `kagi-search`, `kagi-maps`, and `kagi-summarize` in `~/.local/bin`. On the
upgrade from 0.5 or earlier, it removes the `kagi` agent skill and the links that release installed.

Run the same command again to update. When `~/.local/bin/kagi-search --version` already shows the
target version, the installer says so and changes nothing.
Expand All @@ -42,8 +42,8 @@ curl -fsSL https://github.com/rubas/kagi/releases/latest/download/install.sh | s
Supported platforms: Linux x86_64 and macOS aarch64.

When the GitHub CLI (`gh`) is available, the installer verifies the build provenance attestation of
the release archive before it changes a file; without `gh` it warns and continues. For a fully
verifiable install path, prefer the Nix flake below: `flake.lock` pins every input by hash.
the release archive before it changes a file. Without `gh`, it warns and continues. The Nix flake
below is the fully verifiable path: `flake.lock` pins every input by hash.

The release also attests `install.sh`. To verify the installer before you run it:

Expand All @@ -66,17 +66,6 @@ curl -fsSLO https://github.com/rubas/kagi/releases/latest/download/install.sh &&
cargo install --git https://github.com/rubas/kagi.git
```

For local development with the required native build tools:

```bash
nix develop
task check
```

`task lint` also runs [zizmor](https://github.com/zizmorcore/zizmor) over the
workflows, and the dev shell does not include it. Install `zizmor` separately or
that step fails.

### With Nix flakes

Install the CLIs directly:
Expand Down Expand Up @@ -108,11 +97,11 @@ Or enable the Home Manager module to install the CLIs:

You need a Kagi session token. The binaries check these sources in order:

1. `KAGI_SESSION_TOKEN` environment variable (ignored when empty)
2. `$XDG_CONFIG_HOME/kagi/session-token`, falling back to
`~/.config/kagi/session-token` when `XDG_CONFIG_HOME` is unset, empty, or
relative
3. Fail with an error if neither is set
1. The `KAGI_SESSION_TOKEN` environment variable, when it is not empty.
2. `$XDG_CONFIG_HOME/kagi/session-token`, or `~/.config/kagi/session-token` when `XDG_CONFIG_HOME`
is unset, empty, or relative.

Without either, they stop with an error.

Create the token file with owner-only permissions (paste the token, then
Ctrl-D):
Expand All @@ -122,26 +111,18 @@ install -d -m 700 ~/.config/kagi
(umask 077; cat > ~/.config/kagi/session-token)
```

The token is a full kagi.com session cookie, so the binaries refuse a token
file that is group- or world-readable (`chmod 600` fixes it). The permission
check does not apply to `KAGI_SESSION_TOKEN`. The binaries never embed or
store secrets.
The token is a full kagi.com session cookie, so the binaries refuse a token file that is group- or
world-readable (`chmod 600` fixes it). This check does not apply to `KAGI_SESSION_TOKEN`. The
binaries never embed or store secrets.

## Binaries

### `kagi-search`

Search the web with Kagi.

Usage:
Each binary lists all its flags with `--help`.

```bash
kagi-search [OPTIONS] <QUERY>...
```

Run the binary with `--help` for all flags.
### `kagi-search`

Examples:
Searches the web. The text output is numbered results and, when Kagi has them, `Related:` terms.
The JSON is `{ "results": [...], "related": [...] }`.

```bash
kagi-search 'rust async runtime' --lens programming --limit 5
Expand All @@ -151,17 +132,8 @@ kagi-search 'memory leak' --site github.com --filetype rs --json

### `kagi-maps`

Search Kagi Maps for places, businesses, points of interest, and addresses.

Usage:

```bash
kagi-maps [OPTIONS] <QUERY>...
```

Run the binary with `--help` for all flags.

Examples:
Searches Kagi Maps for places, businesses, and addresses. The text output is numbered places with
the address, coordinates, rating, phone, and URL that Kagi has. The JSON is `{ "results": [...] }`.

```bash
kagi-maps 'coffee zurich' --ll 47.3769,8.5417 --zoom 13
Expand All @@ -170,40 +142,24 @@ kagi-maps 'bookstore near bern' --sort rating --json

### `kagi-summarize`

Summarize one explicit URL with Kagi.

Usage:

```bash
kagi-summarize [OPTIONS] <URL>
```

Run the binary with `--help` for all flags.

Examples:
Summarizes one explicit URL. The text output is the raw Markdown summary. The JSON is
`{ "summary": "..." }`.

```bash
kagi-summarize 'https://www.rust-lang.org/learn'
kagi-summarize 'https://www.rust-lang.org/learn' --type takeaway
kagi-summarize 'https://www.rust-lang.org/learn' --lang DE --json
```

## Output

`kagi-search` returns:

- text: numbered results plus optional `Related:` terms
- json: `{ "results": [...], "related": [...] }`

`kagi-maps` returns:

- text: numbered places with address, coordinates, rating, phone, and URL when present
- json: `{ "results": [...] }`
## Development

`kagi-summarize` returns:
```bash
nix develop
task check
```

- text: raw markdown summary
- json: `{ "summary": "..." }`
`task lint` also runs [zizmor](https://github.com/zizmorcore/zizmor) over the workflows. The dev
shell does not include it, so install `zizmor` yourself or that step fails.

## License

Expand Down