Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e8ac4fa
feat: add dev publish workflow and simplify stable publish
github-actions[bot] Feb 22, 2026
bf1a16b
feat: consolidate dev + stable publish into single workflow
github-actions[bot] Feb 22, 2026
5394078
fix: normalize native engine import paths to remove . and .. segments
github-actions[bot] Feb 22, 2026
d0f3e97
fix: auto-prune stale registry entries and skip temp dir registration
github-actions[bot] Feb 22, 2026
1435803
docs: add parallel sessions rules to CLAUDE.md
github-actions[bot] Feb 22, 2026
e1222df
fix: add JS-side path.normalize() defense-in-depth for native resolve
github-actions[bot] Feb 22, 2026
5e8c41b
feat: add TTL-based registry pruning for idle entries
github-actions[bot] Feb 22, 2026
92b2d23
refactor: split parser.js extractors into per-language files
github-actions[bot] Feb 22, 2026
6de23f2
docs: update improvement plan to reflect completed fixes
github-actions[bot] Feb 22, 2026
39f4452
docs: mark parser.js split (#3) as completed in improvement plan
github-actions[bot] Feb 22, 2026
dea0c3a
fix: isolate CLI tests from real registry and add CODEGRAPH_REGISTRY_…
github-actions[bot] Feb 23, 2026
02b931d
fix: throw on explicit --engine native when addon is unavailable
github-actions[bot] Feb 23, 2026
6ed1f59
refactor: rename generic walk to language-specific names in all extra…
github-actions[bot] Feb 23, 2026
12f89fa
feat: add `codegraph stats` command for graph health overview
github-actions[bot] Feb 23, 2026
0166103
chore: remove completed improvement plan
github-actions[bot] Feb 23, 2026
e16dfeb
feat: add worktree workflow hooks for parallel session safety
github-actions[bot] Feb 23, 2026
5730a65
feat: add embedding regression test with real ML model validation
github-actions[bot] Feb 23, 2026
0a679aa
docs: expand competitive analysis from 21 to 135+ tools
github-actions[bot] Feb 23, 2026
ac0b198
fix: replace jq with node in hooks for Windows compatibility
github-actions[bot] Feb 23, 2026
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
1 change: 1 addition & 0 deletions .claude/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
session-edits.log
26 changes: 18 additions & 8 deletions .claude/hooks/check-readme.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
# Runs as a PreToolUse hook on Bash tool calls.

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
COMMAND=$(echo "$INPUT" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const p=JSON.parse(d).tool_input?.command||'';
if(p)process.stdout.write(p);
});
" 2>/dev/null) || true

# Only act on git commit commands
if ! echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
Expand All @@ -30,13 +37,16 @@ if [ "$NEEDS_CHECK" -gt 0 ]; then
[ "$CLAUDE_STAGED" -eq 0 ] && MISSING="${MISSING:+$MISSING, }CLAUDE.md"
[ "$ROADMAP_STAGED" -eq 0 ] && MISSING="${MISSING:+$MISSING, }ROADMAP.md"

jq -n --arg missing "$MISSING" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: ($missing + " not staged but source files were changed. Review whether these docs need updating — README.md (language support table, feature list, command docs), CLAUDE.md (architecture table, supported languages, key design decisions), and ROADMAP.md (phase status, new features, deliverables). If they truly do not need changes, re-run the commit with docs check acknowledged.")
}
}'
node -e "
const missing = process.argv[1];
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: missing + ' not staged but source files were changed. Review whether these docs need updating — README.md (language support table, feature list, command docs), CLAUDE.md (architecture table, supported languages, key design decisions), and ROADMAP.md (phase status, new features, deliverables). If they truly do not need changes, re-run the commit with docs check acknowledged.'
}
}));
" "$MISSING"
exit 0
fi

Expand Down
40 changes: 29 additions & 11 deletions .claude/hooks/enrich-context.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ INPUT=$(cat)

# Extract file path based on tool type
# Read tool uses tool_input.file_path, Grep uses tool_input.path
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
FILE_PATH=$(echo "$INPUT" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const o=JSON.parse(d).tool_input||{};
const p=o.file_path||o.path||'';
if(p)process.stdout.write(p);
});
" 2>/dev/null) || true

# Guard: no file path found
if [ -z "$FILE_PATH" ]; then
Expand All @@ -30,8 +38,9 @@ fi

# Convert absolute path to relative (strip project dir prefix)
REL_PATH="$FILE_PATH"
if [[ "$FILE_PATH" == "${CLAUDE_PROJECT_DIR}"* ]]; then
REL_PATH="${FILE_PATH#"${CLAUDE_PROJECT_DIR}"/}"
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
if [[ "$FILE_PATH" == "${PROJECT_DIR}"* ]]; then
REL_PATH="${FILE_PATH#"${PROJECT_DIR}"/}"
fi
# Normalize backslashes to forward slashes (Windows compatibility)
REL_PATH="${REL_PATH//\\//}"
Expand All @@ -50,13 +59,22 @@ if [ -z "$DEPS" ] || [ "$DEPS" = "null" ]; then
fi

# Output as informational context (never deny)
echo "$DEPS" | jq -c '{
hookSpecificOutput: (
"Codegraph context for " + (.file // "unknown") + ":\n" +
" Imports: " + ((.results[0].imports // []) | length | tostring) + " files\n" +
" Imported by: " + ((.results[0].importedBy // []) | length | tostring) + " files\n" +
" Definitions: " + ((.results[0].definitions // []) | length | tostring) + " symbols"
)
}' 2>/dev/null || true
echo "$DEPS" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
try {
const o=JSON.parse(d);
const r=o.results?.[0]||{};
const imports=(r.imports||[]).length;
const importedBy=(r.importedBy||[]).length;
const defs=(r.definitions||[]).length;
const file=o.file||'unknown';
console.log(JSON.stringify({
hookSpecificOutput: 'Codegraph context for '+file+':\\n Imports: '+imports+' files\\n Imported by: '+importedBy+' files\\n Definitions: '+defs+' symbols'
}));
} catch(e) {}
});
" 2>/dev/null || true

exit 0
111 changes: 111 additions & 0 deletions .claude/hooks/guard-git.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
# guard-git.sh — PreToolUse hook for Bash tool calls
# Blocks dangerous git commands that interfere with parallel sessions
# and validates commits against the session edit log.

set -euo pipefail

INPUT=$(cat)

# Extract the command from tool_input JSON
COMMAND=$(echo "$INPUT" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const p=JSON.parse(d).tool_input?.command||'';
if(p)process.stdout.write(p);
});
" 2>/dev/null) || true

if [ -z "$COMMAND" ]; then
exit 0
fi

# Only act on git commands
if ! echo "$COMMAND" | grep -qE '^\s*git\s+'; then
exit 0
fi

deny() {
local reason="$1"
node -e "
console.log(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'PreToolUse',
permissionDecision: 'deny',
permissionDecisionReason: process.argv[1]
}
}));
" "$reason"
exit 0
}

# --- Block dangerous commands ---

# git add . / git add -A / git add --all (broad staging)
if echo "$COMMAND" | grep -qE '^\s*git\s+add\s+(\.\s*$|-A|--all)'; then
deny "BLOCKED: 'git add .' / 'git add -A' stages ALL changes including other sessions' work. Stage specific files instead: git add <file1> <file2>"
fi

# git reset (unstaging / hard reset)
if echo "$COMMAND" | grep -qE '^\s*git\s+reset'; then
deny "BLOCKED: 'git reset' can unstage or destroy other sessions' work. To unstage your own files, use: git restore --staged <file>"
fi

# git checkout -- <file> (reverting files)
if echo "$COMMAND" | grep -qE '^\s*git\s+checkout\s+--'; then
deny "BLOCKED: 'git checkout -- <file>' reverts working tree changes and may destroy other sessions' edits. If you need to discard your own changes, be explicit about which files."
fi

# git restore (reverting) — EXCEPT git restore --staged (safe unstaging)
if echo "$COMMAND" | grep -qE '^\s*git\s+restore'; then
if ! echo "$COMMAND" | grep -qE '^\s*git\s+restore\s+--staged'; then
deny "BLOCKED: 'git restore <file>' reverts working tree changes and may destroy other sessions' edits. To unstage files safely, use: git restore --staged <file>"
fi
fi

# git clean (delete untracked files)
if echo "$COMMAND" | grep -qE '^\s*git\s+clean'; then
deny "BLOCKED: 'git clean' deletes untracked files that may belong to other sessions."
fi

# git stash (hides all changes)
if echo "$COMMAND" | grep -qE '^\s*git\s+stash'; then
deny "BLOCKED: 'git stash' hides all working tree changes including other sessions' work. In worktree mode, commit your changes directly instead."
fi

# --- Commit validation against edit log ---

if echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
LOG_FILE="$PROJECT_DIR/.claude/session-edits.log"

# If no edit log exists, allow (backward compat for sessions without tracking)
if [ ! -f "$LOG_FILE" ] || [ ! -s "$LOG_FILE" ]; then
exit 0
fi

# Get unique edited files from log
EDITED_FILES=$(awk '{print $2}' "$LOG_FILE" | sort -u)

# Get staged files
STAGED_FILES=$(git diff --cached --name-only 2>/dev/null) || true

if [ -z "$STAGED_FILES" ]; then
exit 0
fi

# Find staged files that weren't edited in this session
UNEXPECTED=""
while IFS= read -r staged_file; do
if ! echo "$EDITED_FILES" | grep -qxF "$staged_file"; then
UNEXPECTED="${UNEXPECTED:+$UNEXPECTED, }$staged_file"
fi
done <<< "$STAGED_FILES"

if [ -n "$UNEXPECTED" ]; then
deny "BLOCKED: These staged files were NOT edited in this session: $UNEXPECTED. They may belong to another session. Commit only your files: git commit <your-files> -m \"msg\""
fi
fi

exit 0
52 changes: 52 additions & 0 deletions .claude/hooks/rebuild-graph.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# rebuild-graph.sh — PostToolUse hook for Edit and Write tools
# Incrementally rebuilds the codegraph after source file edits.
# Always exits 0 (informational only, never blocks).

set -euo pipefail

INPUT=$(cat)

# Extract file path using node (jq may not be available on Windows)
FILE_PATH=$(echo "$INPUT" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const p=JSON.parse(d).tool_input?.file_path||'';
if(p)process.stdout.write(p);
});
" 2>/dev/null) || true

if [ -z "$FILE_PATH" ]; then
exit 0
fi

# Only rebuild for source files codegraph tracks
# Skip docs, configs, test fixtures, and non-code files
case "$FILE_PATH" in
*.js|*.ts|*.tsx|*.jsx|*.py|*.go|*.rs|*.java|*.cs|*.php|*.rb|*.tf|*.hcl)
;;
*)
exit 0
;;
esac

# Skip test fixtures — they're copied to tmp dirs anyway
if echo "$FILE_PATH" | grep -qE '(fixtures|__fixtures__|testdata)/'; then
exit 0
fi

# Guard: codegraph DB must exist (project has been built at least once)
DB_PATH="${CLAUDE_PROJECT_DIR:-.}/.codegraph/graph.db"
if [ ! -f "$DB_PATH" ]; then
exit 0
fi

# Run incremental build (skips unchanged files via hash check)
if command -v codegraph &>/dev/null; then
codegraph build "${CLAUDE_PROJECT_DIR:-.}" -d "$DB_PATH" 2>/dev/null || true
else
node "${CLAUDE_PROJECT_DIR}/src/cli.js" build "${CLAUDE_PROJECT_DIR:-.}" -d "$DB_PATH" 2>/dev/null || true
fi

exit 0
46 changes: 46 additions & 0 deletions .claude/hooks/track-edits.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# track-edits.sh — PostToolUse hook for Edit and Write tools
# Logs each edited file path to .claude/session-edits.log so that
# guard-git.sh can validate commits against actually-edited files.
# In worktrees each session gets its own log automatically.
# Always exits 0 (informational only, never blocks).

set -euo pipefail

INPUT=$(cat)

# Extract file_path from tool_input JSON
FILE_PATH=$(echo "$INPUT" | node -e "
let d='';
process.stdin.on('data',c=>d+=c);
process.stdin.on('end',()=>{
const p=JSON.parse(d).tool_input?.file_path||'';
if(p)process.stdout.write(p);
});
" 2>/dev/null) || true

if [ -z "$FILE_PATH" ]; then
exit 0
fi

PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
LOG_FILE="$PROJECT_DIR/.claude/session-edits.log"

# Normalize to relative path with forward slashes
REL_PATH=$(node -e "
const path = require('path');
const abs = path.resolve(process.argv[1]);
const base = path.resolve(process.argv[2]);
const rel = path.relative(base, abs).split(path.sep).join('/');
process.stdout.write(rel);
" "$FILE_PATH" "$PROJECT_DIR" 2>/dev/null) || true

if [ -z "$REL_PATH" ]; then
exit 0
fi

# Append timestamped entry
mkdir -p "$(dirname "$LOG_FILE")"
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) $REL_PATH" >> "$LOG_FILE"

exit 0
22 changes: 22 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/check-readme.sh\"",
"timeout": 10
},
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-git.sh\"",
"timeout": 10
}
]
},
Expand All @@ -31,6 +36,23 @@
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/rebuild-graph.sh\"",
"timeout": 30
},
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/track-edits.sh\"",
"timeout": 5
}
]
}
]
}
}
41 changes: 41 additions & 0 deletions .github/workflows/embedding-regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Embedding Regression

on:
schedule:
- cron: '0 6 * * 1' # Monday 6am UTC
workflow_dispatch:
pull_request:
paths:
- 'src/embedder.js'
- 'tests/search/**'
- 'package.json'

concurrency:
group: embedding-regression-${{ github.ref }}
cancel-in-progress: true

jobs:
embedding-regression:
runs-on: ubuntu-latest
name: Embedding regression tests
timeout-minutes: 15

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22

- name: Install dependencies
run: npm install

- name: Cache HuggingFace models
uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: hf-models-minilm-v1

- name: Run embedding regression tests
run: npx vitest run tests/search/embedding-regression.test.js
Loading
Loading