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
8 changes: 4 additions & 4 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,21 @@
{
"name": "cix",
"source": "./plugins/cix",
"description": "Semantic code search and navigation for Claude Code. Bundles the cix CLI, slash commands, behavioral hooks, and the cix + cix-workspace skills (CLI-based) plus the cix-workspace-investigator sub-agent for cross-project research across cix workspaces.",
"description": "Semantic code search and navigation for Claude Code. Requires a self-hosted cix (CodeIndeX) server — free, MIT, ~10-minute install (https://codeindex.app); the plugin is the client. Bundles the cix CLI, slash commands, behavioral hooks, and the cix + cix-workspace skills (CLI-based) plus the cix-workspace-investigator sub-agent for cross-project research across cix workspaces.",
"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", "workspace", "cross-project", "sub-agent"],
"keywords": ["search", "self-hosted", "code-search", "semantic", "navigation", "indexing", "embeddings", "workspace", "cross-project", "sub-agent"],
"category": "developer-tools",
"tags": ["search", "indexing", "ai", "embeddings", "workspace", "cross-project"]
"tags": ["search", "self-hosted", "indexing", "ai", "embeddings", "workspace", "cross-project"]
},
{
"name": "cix-cowork",
"source": "./plugins/cix-cowork",
"description": "cix skills for Claude Desktop & Cowork: the cix and cix-workspace guidance adapted to the cix_* MCP tools. Install AFTER registering the cix MCP server (run `cix mcp install claude-desktop`). Skills only — no MCP server, no CLI.",
"description": "cix skills for Claude Desktop & Cowork: the cix and cix-workspace guidance adapted to the cix_* MCP tools. Requires a self-hosted cix (CodeIndeX) server (https://codeindex.app). Install AFTER registering the cix MCP server (run `cix mcp install claude-desktop`). Skills only — no MCP server, no CLI.",
"author": {
"name": "dvcdsys"
},
Expand Down
5 changes: 3 additions & 2 deletions plugins/cix-cowork/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "cix-cowork",
"version": "0.1.0",
"description": "cix skills for Claude Desktop & Cowork — the cix (single-repo) and cix-workspace (cross-project) guidance adapted to the cix_* MCP tools. Skills only: no MCP server, no CLI, no hooks. Register the cix MCP server first with `cix mcp install claude-desktop`, then install this for the richer guidance.",
"version": "0.1.1",
"description": "cix skills for Claude Desktop & Cowork — the cix (single-repo) and cix-workspace (cross-project) guidance adapted to the cix_* MCP tools. Requires a self-hosted cix (CodeIndeX) server (https://codeindex.app). Skills only: no MCP server, no CLI, no hooks. Register the cix MCP server first with `cix mcp install claude-desktop`, then install this for the richer guidance.",
"author": {
"name": "dvcdsys",
"email": "dvcdsys@gmail.com"
Expand All @@ -12,6 +12,7 @@
"license": "MIT",
"keywords": [
"search",
"self-hosted",
"code-search",
"semantic",
"navigation",
Expand Down
5 changes: 4 additions & 1 deletion plugins/cix-cowork/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ This plugin is **skills only**: no MCP server, no CLI, no hooks. It teaches the
agent *how* to use the cix tools well (cix-vs-grep judgment, choosing the
cheapest tool, query writing, the cross-project workflow, and the workspace
trust rules). The tools themselves come from the cix MCP server, which you
register separately.
register separately — and everything ultimately talks to a
**self-hosted [cix (CodeIndeX) server](https://codeindex.app)** that you
deploy yourself (free, MIT, ~10-minute install). No server → nothing to
search.

## Two steps

Expand Down
5 changes: 3 additions & 2 deletions plugins/cix/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "cix",
"version": "0.3.0",
"description": "Semantic code search and navigation for Claude Code via the cix index. Single-repo cix skill + commands (incl. cix file / cix tree for external projects), plus the experimental cix-workspace skill and cix-workspace-investigator sub-agent for cross-project research across cix workspaces.",
"version": "0.4.0",
"description": "Semantic code search for Claude Code. Requires a self-hosted cix (CodeIndeX) server — free, MIT, ~10-minute install (https://codeindex.app) on your machine, LAN box, or GPU server; the plugin is the client. Ships the cix CLI, slash commands (incl. cix file / cix tree for external projects), behavioral hooks, the cix skill, plus the experimental cix-workspace skill and cix-workspace-investigator sub-agent for cross-project research.",
"author": {
"name": "dvcdsys",
"email": "dvcdsys@gmail.com"
Expand All @@ -12,6 +12,7 @@
"license": "MIT",
"keywords": [
"search",
"self-hosted",
"code-search",
"semantic",
"navigation",
Expand Down
18 changes: 18 additions & 0 deletions plugins/cix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
Semantic code search and navigation for Claude Code, powered by the
[cix](https://github.com/dvcdsys/code-index) index.

## Requires a self-hosted cix server

This plugin is a **client**. All indexing and search happen on a
[cix (CodeIndeX)](https://codeindex.app) server that **you deploy
yourself** — on your laptop, a LAN box, or a GPU server. Free, MIT,
~10 minutes with the interactive installer:

```bash
curl -fsSL https://raw.githubusercontent.com/dvcdsys/code-index/main/install-server.sh | bash
```

Already have a server on your network? Point the CLI at it instead:
`cix config set server.main.url <url>` and
`cix config set server.main.key <key>` (the first server added becomes
the default) — or paste the ready-made connect command from the
dashboard's API-keys dialog. Without a reachable server, the slash
commands and skills have nothing to talk to.

## What you get

- **`/cix:search`, `/cix:def`, `/cix:refs`, `/cix:init`, `/cix:status`,
Expand Down
8 changes: 1 addition & 7 deletions plugins/cix/scripts/grep-nudge.sh
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,6 @@ fi
# ── Emit nudge ────────────────────────────────────────────────────────────────
MESSAGE="💡 You just ran a file/text search in this project (call #$COUNT this session). This project has a cix semantic index — next time, for queries by meaning (find by concept, cross-file lookups, symbol navigation, locating files by symbol name), the CLI commands \`cix search\` / \`cix def\` / \`cix refs\` outperform grep/find. grep and find are best for exact strings or filename patterns (error messages, config keys, import paths, glob extensions). Recommended to activate /cix SKILL to use cix effectively"

if command -v jq >/dev/null 2>&1; then
jq -n --arg msg "$MESSAGE" \
'{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
else
ESC=$(printf '%s' "$MESSAGE" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
printf '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"%s"}}\n' "$ESC"
fi
cix_emit_context "PostToolUse" "$MESSAGE"

exit 0
33 changes: 33 additions & 0 deletions plugins/cix/scripts/lib-cix-probe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,39 @@
# which silenced the nudge for the ENTIRE session even if the cix server
# came up seconds later. "unknown" lets the next Grep re-probe and recover.

# cix_server_configured — exit 0 when this machine has any cix server
# configured (env override or a url in ~/.cix/config.yaml), nonzero
# otherwise. Used by session-start.sh to tell "no server at all" (worth a
# setup hint) apart from "server exists, project not indexed" (stay
# silent). A heuristic on purpose: any `url:` key counts, whether a named
# server entry or a legacy api.url — false positives just suppress the
# hint, which is the safe direction. CIX_API_KEY alone also counts: the
# CLI falls back to the implicit localhost default server, so a key with
# no config file is a working setup.
cix_server_configured() {
[ -n "${CIX_API_URL:-}" ] && return 0
[ -n "${CIX_API_KEY:-}" ] && return 0
local cfg="${HOME:-}/.cix/config.yaml"
[ -f "$cfg" ] && grep -qE 'url:[[:space:]]*[^[:space:]#]' "$cfg" && return 0
return 1
}

# cix_emit_context <hookEventName> <message>
# Emit the hookSpecificOutput JSON that feeds additionalContext back to
# Claude. Single implementation for every hook (SessionStart, PostToolUse,
# PostCompact); prefers jq, falls back to a sed escape for jq-less hosts.
cix_emit_context() {
local event="$1" msg="$2"
if command -v jq >/dev/null 2>&1; then
jq -n --arg ev "$event" --arg msg "$msg" \
'{hookSpecificOutput: {hookEventName: $ev, additionalContext: $msg}}'
else
local esc
esc=$(printf '%s' "$msg" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
printf '{"hookSpecificOutput":{"hookEventName":"%s","additionalContext":"%s"}}\n' "$event" "$esc"
fi
}

# cix_resolve_bin — echo a usable cix binary path, or empty string.
# Prefers the plugin-bundled wrapper so behavior matches the slash commands.
cix_resolve_bin() {
Expand Down
12 changes: 5 additions & 7 deletions plugins/cix/scripts/post-compact.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@

set -euo pipefail

# Shared helpers (cix_emit_context).
# shellcheck source=lib-cix-probe.sh
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib-cix-probe.sh"

INPUT=$(cat 2>/dev/null || echo "{}")
if command -v jq >/dev/null 2>&1; then
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null || echo "")
Expand Down Expand Up @@ -51,12 +55,6 @@ fi
# ── Re-inject the SessionStart reminder ───────────────────────────────────────
MESSAGE='💡 (Post-compact reminder) This project has a cix semantic code index. For semantic queries — finding code by meaning, cross-file lookups, symbol navigation, "where is X used", "how does Y work" — use the CLI: `cix search`, `cix def`, `cix refs` (via Bash). Activate the /cix SKILL for guidance. Use Grep only for exact strings (error messages, config keys, import paths).'

if command -v jq >/dev/null 2>&1; then
jq -n --arg msg "$MESSAGE" \
'{hookSpecificOutput: {hookEventName: "PostCompact", additionalContext: $msg}}'
else
ESC=$(printf '%s' "$MESSAGE" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
printf '{"hookSpecificOutput":{"hookEventName":"PostCompact","additionalContext":"%s"}}\n' "$ESC"
fi
cix_emit_context "PostCompact" "$MESSAGE"

exit 0
39 changes: 30 additions & 9 deletions plugins/cix/scripts/session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,39 @@ CACHE_FILE="$CACHE_DIR/cix-aware-$SESSION_ID-$DIR_HASH"
# A file outside this prefix is invisible to find — it's never even
# considered for deletion, regardless of how the cache dir is configured.
find "$CACHE_DIR" -maxdepth 1 -type f \
\( -name 'cix-aware-*' -o -name 'cix-grep-count-*' \) \
\( -name 'cix-aware-*' -o -name 'cix-grep-count-*' -o -name 'cix-setup-hint' \) \
-mtime +30 -delete 2>/dev/null || true

# Setup hint for the "plugin installed but no server anywhere" state.
# Shown only when NO cix server is configured on this machine — a
# configured-but-unreachable server or a merely-unindexed project stays
# silent as before. Without this, the plugin fails with cryptic errors on
# first use and the user never learns the missing piece is the server.
# Rate-limited to once per 7 days per machine via the cix-setup-hint
# marker's mtime (see maybe_emit_no_server_hint), so it is a pointer,
# not a nag; the 30-day GC above may reap the marker, which merely lets
# the hint reappear — same order of magnitude, no harm.
NO_SERVER_MESSAGE='cix plugin: no cix server is configured on this machine. The plugin is a client for a self-hosted cix (CodeIndeX) server — without one, /cix commands and skills have nothing to talk to. Setup takes ~10 minutes: https://codeindex.app — or connect to an existing server with `cix config set server.main.url <url>` + `cix config set server.main.key <key>`. If the user asks about cix or tries /cix commands, surface this. Otherwise do not bring it up on your own.'

# maybe_emit_no_server_hint — emit NO_SERVER_MESSAGE when no server is
# configured anywhere AND the hint has not fired in the last 7 days.
maybe_emit_no_server_hint() {
cix_server_configured && return 0
local marker="$CACHE_DIR/cix-setup-hint"
if [ -n "$(find "$CACHE_DIR" -maxdepth 1 -type f -name 'cix-setup-hint' -mtime -7 2>/dev/null)" ]; then
return 0
fi
touch "$marker" 2>/dev/null || true
cix_emit_context "SessionStart" "$NO_SERVER_MESSAGE"
}

# ── Resolve a working `cix` binary ────────────────────────────────────────────
CIX_BIN="$(cix_resolve_bin)"

if [ -z "$CIX_BIN" ]; then
# CLI not yet installed (would auto-bootstrap on first call). Mark off.
printf '0' > "$CACHE_FILE"
maybe_emit_no_server_hint
exit 0
fi

Expand All @@ -112,8 +136,11 @@ if [ "$VERDICT" = "unknown" ]; then
fi

if [ "$VERDICT" != "1" ]; then
# Definitive "not indexed". Stay silent for the session in this project.
# Definitive "not indexed". Stay silent for the session in this project —
# unless there is no server configured at all, which deserves the
# rate-limited setup hint (the plugin is unusable in every project then).
printf '0' > "$CACHE_FILE"
maybe_emit_no_server_hint
exit 0
fi

Expand All @@ -122,12 +149,6 @@ printf '1' > "$CACHE_FILE"

MESSAGE='💡 This project has a cix semantic code index. For semantic queries — finding code by meaning, cross-file lookups, symbol navigation, "where is X used", "how does Y work" — use the CLI: `cix search`, `cix def`, `cix refs` (via Bash). Activate the /cix SKILL for guidance. Use Grep only for exact strings (error messages, config keys, import paths). Run `cix status` if results seem stale.'

if command -v jq >/dev/null 2>&1; then
jq -n --arg msg "$MESSAGE" \
'{hookSpecificOutput: {hookEventName: "SessionStart", additionalContext: $msg}}'
else
ESC=$(printf '%s' "$MESSAGE" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"%s"}}\n' "$ESC"
fi
cix_emit_context "SessionStart" "$MESSAGE"

exit 0
21 changes: 20 additions & 1 deletion plugins/cix/tests/helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ TEST_CACHE_DIR=""
TEST_MOCK_BIN=""
TEST_LOG_FILE=""
TEST_PROJECT_DIR=""
TEST_HOME=""

setup_test_env() {
# Plugin root = repo's plugins/cix directory, so scripts find their
Expand All @@ -32,6 +33,12 @@ setup_test_env() {
# Per-test scratch project dir.
TEST_PROJECT_DIR="$(mktemp -d "${BATS_TMPDIR}/cix-proj-XXXXXX")"

# Per-test scratch HOME so hook behavior never depends on the real
# user's ~/.cix/config.yaml (session-start.sh consults it via
# cix_server_configured). Tests that need "a server is configured"
# call make_server_config to opt in.
TEST_HOME="$(mktemp -d "${BATS_TMPDIR}/cix-home-XXXXXX")"

# Per-test cix invocation log.
TEST_LOG_FILE="$(mktemp "${BATS_TMPDIR}/cix-log-XXXXXX")"

Expand All @@ -49,8 +56,17 @@ teardown_test_env() {
[ -n "$TEST_CACHE_DIR" ] && [ -d "$TEST_CACHE_DIR" ] && rm -rf "$TEST_CACHE_DIR"
[ -n "$TEST_PROJECT_DIR" ] && [ -d "$TEST_PROJECT_DIR" ] && rm -rf "$TEST_PROJECT_DIR"
[ -n "$TEST_LOG_FILE" ] && [ -f "$TEST_LOG_FILE" ] && rm -f "$TEST_LOG_FILE"
[ -n "$TEST_HOME" ] && [ -d "$TEST_HOME" ] && rm -rf "$TEST_HOME"
unset MOCK_CIX_EXIT MOCK_CIX_DELAY MOCK_CIX_LOG_FILE MOCK_CIX_STDOUT_LINES
unset TEST_PLUGIN_ROOT TEST_CACHE_DIR TEST_MOCK_BIN TEST_LOG_FILE TEST_PROJECT_DIR
unset TEST_PLUGIN_ROOT TEST_CACHE_DIR TEST_MOCK_BIN TEST_LOG_FILE TEST_PROJECT_DIR TEST_HOME
}

# Write a minimal ~/.cix/config.yaml with a server url into TEST_HOME.
# Tests call this to simulate "a cix server IS configured on this machine".
make_server_config() {
mkdir -p "$TEST_HOME/.cix"
printf 'servers:\n local:\n url: http://localhost:21847\n' \
> "$TEST_HOME/.cix/config.yaml"
}

# Run a hook script with controlled env. Usage:
Expand Down Expand Up @@ -85,8 +101,11 @@ run_hook() {
# We deliberately do NOT pre-empt CLAUDE_PLUGIN_ROOT/bin/cix here —
# the wrapper has its own resolution logic that prefers it; for hook
# tests we want the mock to win.
# HOME points at the per-test scratch home (see setup_test_env) so the
# real user's ~/.cix/config.yaml never leaks into hook behavior.
run env \
PATH="$TEST_MOCK_BIN:$PATH" \
HOME="$TEST_HOME" \
CLAUDE_PLUGIN_DATA="$TEST_CACHE_DIR" \
CLAUDE_PROJECT_DIR="$project_dir" \
bash "$TEST_PLUGIN_ROOT/scripts/$script" <<<"$payload"
Expand Down
Loading
Loading