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
25 changes: 25 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/claude-code-marketplace.json",
"name": "code-index",
"owner": {
"name": "dvcdsys",
"email": "dvcdsys@gmail.com"
},
"description": "Marketplace for cix — semantic code search and navigation tooling for Claude Code",
"plugins": [
{
"name": "cix",
"source": "./plugins/cix",
"description": "Semantic code search and navigation. Bundles the cix CLI and nudges Claude to prefer cix over Grep for semantic queries.",
"author": {
"name": "dvcdsys"
},
"homepage": "https://github.com/dvcdsys/code-index",
"repository": "https://github.com/dvcdsys/code-index",
"license": "MIT",
"keywords": ["search", "code-search", "semantic", "navigation", "indexing", "embeddings"],
"category": "developer-tools",
"tags": ["search", "indexing", "ai", "embeddings"]
}
]
}
75 changes: 75 additions & 0 deletions .github/workflows/ci-plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Plugin Tests

# Trigger only when plugin files change — server/CLI/dashboard work
# is unaffected and shouldn't run plugin tests.
on:
push:
branches: [main, 'feat/*', 'fix/*']
paths:
- 'plugins/cix/**'
- '.claude-plugin/**'
- '.github/workflows/ci-plugin.yml'
pull_request:
paths:
- 'plugins/cix/**'
- '.claude-plugin/**'
- '.github/workflows/ci-plugin.yml'

# Minimum permissions required by the workflow (CodeQL workflow-permissions advisory).
# Read-only on repo contents is enough — we don't push code, comments, or releases.
permissions:
contents: read

jobs:
test:
name: bats + shellcheck on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]

steps:
- uses: actions/checkout@v4

- name: Install bats, jq, shellcheck (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y bats jq shellcheck

- name: Install bats, jq, shellcheck (macOS)
if: runner.os == 'macOS'
run: |
brew install bats-core jq shellcheck

- name: Verify bats version
run: bats --version

- name: Run bats test suites
run: bats --tap plugins/cix/tests/*.bats

- name: ShellCheck on hook scripts
run: |
# `--severity=warning` filters out style nags; `-x` follows
# sourced files (we don't source any in v0.1, but defensive).
shellcheck --severity=warning plugins/cix/scripts/*.sh

- name: Validate JSON manifests with jq
run: |
jq . .claude-plugin/marketplace.json
jq . plugins/cix/.claude-plugin/plugin.json
jq . plugins/cix/hooks/hooks.json

- name: Verify symlink integrity
run: |
# The bin/cix symlink MUST point at scripts/cix-wrapper.sh.
if [[ ! -L plugins/cix/bin/cix ]]; then
echo "::error::plugins/cix/bin/cix is not a symlink"
exit 1
fi
target=$(readlink plugins/cix/bin/cix)
if [[ "$target" != "../scripts/cix-wrapper.sh" ]]; then
echo "::error::bin/cix points to '$target' (expected '../scripts/cix-wrapper.sh')"
exit 1
fi
14 changes: 14 additions & 0 deletions plugins/cix/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "cix",
"version": "0.1.0",
"description": "Semantic code search and navigation for Claude Code via the cix index. Bundles the cix CLI (auto-installs if missing) and nudges Claude to prefer cix over Grep for semantic queries.",
"author": {
"name": "dvcdsys",
"email": "dvcdsys@gmail.com"
},
"homepage": "https://github.com/dvcdsys/code-index",
"repository": "https://github.com/dvcdsys/code-index",
"license": "MIT",
"keywords": ["search", "code-search", "semantic", "navigation", "indexing", "embeddings", "ai"]
}
171 changes: 171 additions & 0 deletions plugins/cix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# cix — Claude Code plugin

Semantic code search and navigation for Claude Code, powered by the
[cix](https://github.com/dvcdsys/code-index) index.

## What you get

- **`/cix:search`, `/cix:def`, `/cix:refs`, `/cix:init`, `/cix:status`,
`/cix:summary`** — slash commands wrapping the most-used `cix` CLI
operations.
- **Bundled cix CLI** — the plugin auto-installs `cix` on first use if
it isn't already in your `PATH` (no sudo, installs to `~/.local/bin`).
If you already have `cix` installed via the official `install.sh`, the
plugin just uses it.
- **`cix` skill (SKILL.md)** — lazy-loaded full instruction sheet
covering when to use cix vs Grep, query patterns, scoring landscape,
and CLI flags. Loads into the conversation only when Claude or you
invoke it (`/cix:search`, `/cix-skill`, or auto-trigger on a relevant
prompt). Stays in context for the rest of the session — never
duplicated.
- **Behavioral nudges (5 hooks):**
- **SessionStart** — calls `cix status` (2 s timeout). Caches the
yes/no verdict in `$CLAUDE_PLUGIN_DATA/cix-aware-$SESSION_ID-$DIR_HASH`,
injects a one-line reminder on success.
- **CwdChanged** — when Claude `cd`s into another directory mid-session,
re-runs `cix status` for the new dir and caches the verdict. Silent
(no reminder); PreToolUse handles the first-Grep-in-new-project
nudge through its per-project backoff.
- **PreToolUse(Grep|Glob)** — reads the cache for the current
`(session, project_dir)` pair; no inline `cix` calls. If the
verdict is "yes" (`1`), suggests `cix search` with exponential
backoff per project (fires on call #1, 2, 4, 8, …). Missing cache
or "no" (`0`) → silent for the rest of the session in that project.
- **PostCompact** — after auto-compaction in long sessions, re-injects
the SessionStart reminder if the current project is cix-aware
(skill body itself survives compaction natively; the SessionStart
one-liner does not).
- **SessionEnd** — glob-deletes every per-(session, dir) cache file
when the session terminates. Best-effort; the 30-day GC inside
SessionStart catches markers left over from forced kills.

The cache key includes a project-dir hash (`shasum -a 256` first 8
chars), so per-session, per-project state is isolated — Claude can
move between projects mid-session and each one keeps its own verdict
and backoff counter.

## Install

From an existing Claude Code marketplace:

```
/plugin marketplace add dvcdsys/code-index
/plugin install cix@code-index
/reload-plugins # or restart Claude Code
```

Or for local development against this repo:

```
/plugin marketplace add /path/to/code-index
/plugin install cix@code-index --scope local
```

## Requirements

- **Claude Code v2.1.0+** (uses `hookSpecificOutput.additionalContext`
for hook-driven nudges).
- **`curl`** — only needed the first time, for the auto-bootstrap of
the `cix` CLI.
- **A reachable `cix-server`** — the CLI is a thin client. If you don't
yet have a server, see the project README for Docker setup
instructions.

## How adoption works (the design)

The plugin uses a 4-layer approach so SKILL.md loads at most once and
nudges don't spam the context:

| Layer | Mechanism | Cost over a 100-prompt session |
|---|---|---|
| 1. Skill description | Native Claude Code (always-in-context, ~200 B) | ~200 B once |
| 2. SessionStart hook | One-time reminder in indexed projects | ~200 B once |
| 3. PreToolUse(Grep\|Glob) hook | Exponential-backoff nudge | ~80 B × ~7 calls = ~560 B |
| 4. SKILL.md body | Native lazy-load (skill mechanism) | ~7 KB **once** if invoked |

Total plugin context overhead in a session that uses cix heavily:
~8 KB. In a session that doesn't touch cix at all: ~400 B (skill
description + slash command metadata).

The SKILL.md body is **never duplicated** — Claude Code's skill
mechanism guarantees a single insertion that stays in context for the
session. See the [skill content lifecycle](https://code.claude.com/docs/en/skills#skill-content-lifecycle)
docs.

## Configuration

### Where the bundled CLI is installed

The wrapper installs `cix` to `~/.local/bin/cix` by default. To override
the install location, set `CIX_PLUGIN_BIN_DIR` in your environment:

```bash
export CIX_PLUGIN_BIN_DIR=/usr/local/bin # if you want sudo-installed
```

If you've already installed `cix` system-wide (e.g. via the project's
`install.sh`), the wrapper detects it and uses that binary — no second
copy is downloaded.

### Skipping the auto-install

Set `CIX_PLUGIN_BIN_DIR` to a directory that already contains a working
`cix` binary, or simply make sure `cix` is in your `$PATH` before
enabling the plugin.

### Hook state cleanup

Two per-session marker files live in `$CLAUDE_PLUGIN_DATA`
(resolves to `~/.claude/plugins/data/cix-code-index/`):
- `cix-aware-$SESSION_ID` — written by SessionStart, read by
PreToolUse. Single-byte file (`0` or `1`).
- `cix-grep-count-$SESSION_ID` — counter for the exponential backoff.

This directory is plugin-managed and **not** cleaned by the OS
(unlike `/tmp`, which macOS purges daily). The plugin manages cleanup
in two tiers:
1. **SessionEnd hook** — deletes both markers when the session
terminates normally. Covers the common case.
2. **30-day GC in SessionStart** — opportunistically deletes markers
older than 30 days at every session start. Catches markers left
over from sessions that exited forcibly (kill -9, OOM).

## Files

| Path | Purpose |
|---|---|
| `.claude-plugin/plugin.json` | Plugin manifest |
| `skills/cix/SKILL.md` | Lazy-loaded usage skill (~7 KB) |
| `commands/*.md` | Six slash commands |
| `hooks/hooks.json` | SessionStart + PreToolUse(Grep\|Glob\|Bash) registration |
| `scripts/cix-wrapper.sh` | "Use system or auto-install" CLI wrapper |
| `scripts/session-start.sh` | One-time session reminder |
| `scripts/grep-nudge.sh` | Exponential-backoff Grep nudge |
| `bin/cix` | Symlink to wrapper, exposed on `$PATH` while plugin enabled |

## Troubleshooting

- **"cix: command not found" inside Claude Code Bash tool** — the
plugin isn't enabled or `bin/cix` isn't on `$PATH`. Run
`/plugin list` and `which cix` from inside a Claude Code session.
- **Hooks not firing** — run Claude Code with `--debug` and look for
hook registration messages. Check `/Users/dvcdsys/.claude/...` (or
your local cache path) for the hook scripts and verify they're
executable: `ls -la $(claude plugin list ... | path)/scripts/`.
- **Nudges feel too frequent / too rare** — edit the power-of-2 check
in `scripts/grep-nudge.sh` to your taste. The current schedule
(1, 2, 4, 8, 16, …) was chosen to balance "loud at start" with
"fade away".
- **"This project has a cix semantic code index" never appears** —
the project must contain a `.cix/` directory. Run `/cix:init` first.
- **Nudge does not fire on `grep` invoked via Bash** — the `PreToolUse`
matcher works on `tool_name`, not on the command string. The plugin
matches `Bash` explicitly and filters grep/rg from
`tool_input.command` inside `grep-nudge.sh`. Confirm
`hooks/hooks.json` contains both `"matcher": "Grep|Glob"` and
`"matcher": "Bash"` entries; the regression in
`tests/manifest.bats` enforces this.

## License

MIT — same as the parent project.
1 change: 1 addition & 0 deletions plugins/cix/bin/cix
15 changes: 15 additions & 0 deletions plugins/cix/commands/def.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Find symbol definition(s) via cix — go-to-definition across the indexed codebase
argument-hint: <symbol> [--kind function|class|method|type] [--file <path>]
allowed-tools: Bash(cix *)
---

Look up the definition of the symbol **$ARGUMENTS** in the cix index:

```!
cix definitions $ARGUMENTS
```

If multiple matches are returned, point out the most likely one based on
context. If nothing is found, suggest `cix symbols $ARGUMENTS` for a
broader name search.
17 changes: 17 additions & 0 deletions plugins/cix/commands/init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
description: Initialize the cix index for the current project (registers, indexes, starts file watcher)
allowed-tools: Bash(cix *)
---

Initialize the cix index for the current project. This registers the
project with the cix server, performs a full initial index, and starts
the file-watcher daemon for auto-reindex on changes.

```!
cix init
```

If the indexing run is in-progress, you can monitor it with `/cix:status`.
If it fails, common causes are: cix-server not reachable, missing
`CIX_API_KEY` env var, or `~/.cix/data` permission issues. Check
`cix status` for details.
14 changes: 14 additions & 0 deletions plugins/cix/commands/refs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
description: Find symbol references via cix — locate every usage of a symbol across the codebase
argument-hint: <symbol> [--file <path>] [--limit <n>]
allowed-tools: Bash(cix *)
---

Find references to the symbol **$ARGUMENTS** in the cix index:

```!
cix references $ARGUMENTS
```

Group the references by file and call out any high-traffic call sites or
suspicious usage patterns. If you need fewer results, add `--limit 20`.
18 changes: 18 additions & 0 deletions plugins/cix/commands/search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
description: Semantic code search via cix — find code by meaning, not by exact strings
argument-hint: <query>
allowed-tools: Bash(cix *)
---

Run a semantic search through the cix index for the query: **$ARGUMENTS**

```!
cix search "$ARGUMENTS"
```

Summarize the most relevant matches above. If results look weak, try:
- A more specific phrasing that names the area or symbol
- `cix search "$ARGUMENTS" --min-score 0.2` to lower the relevance floor
- `cix search "$ARGUMENTS" --in <subdir>` to narrow scope

If `cix` is not yet initialized in this project, run `/cix:init` first.
15 changes: 15 additions & 0 deletions plugins/cix/commands/status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
description: Show cix indexing status and file-watcher state for the current project
allowed-tools: Bash(cix *)
---

Show the current cix indexing status — last sync, number of indexed
files, and whether the file watcher is active.

```!
cix status
```

If `Watcher: ✗ not running`, search results may be stale. Run
`cix watch` to restart the auto-reindex daemon, or `cix reindex` for a
one-off refresh.
16 changes: 16 additions & 0 deletions plugins/cix/commands/summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
description: Show project overview from the cix index — languages, top directories, key symbols
allowed-tools: Bash(cix *)
---

Print a project overview from the cix index — languages, file counts,
top directories, and most-referenced symbols. Useful when starting work
on an unfamiliar codebase.

```!
cix summary
```

Use this output to orient yourself before diving into specific
subsystems. For deeper exploration, follow up with `cix search` on the
top-level concepts you see here.
Loading
Loading