The previous fix (403b4e8) filtered doc-file selection through
should_skip_path(), assuming it matched tree_sitter_indexer.py's
exclusion behavior. It doesn't: ingestion.py's own SKIP_DIRECTORIES
never included 'venv'/'.venv' at all - only tree_sitter_indexer.py's
separate SKIP_DIRS did. Two inconsistent exclusion lists in the
codebase, and the previous fix happened to reuse the incomplete one.
Confirmed live: a selective-indexing run after the previous fix still
pulled in 61 .venv/lib/python3.11/site-packages/... files into
Ody-project_index.
_run_semantic_indexing_background and _run_semantic_indexing_sync both
select doc files with Path(project_path).rglob("*.md"/"*.txt"/"*.rst")
and no directory exclusion at all - unlike the tree-sitter code-file
selection right next to it, which already filters via
TreeSitterIndexer.SKIP_DIRS.
For a project with a .venv checked into its own directory, this pulled
in hundreds of unrelated site-packages files (LICENSE.txt, dist-info
top_level.txt, vendored library .rst docs) as "important" project
knowledge, drowning out the actual project's docs and code in every
KB built with selective indexing. Confirmed live: Ody-project_index
had .venv/lib/python3.11/site-packages/... entries making up most of
its 67 documents.
Filter with the already-existing should_skip_path() from
app.core.ingestion (same SKIP_DIRECTORIES used by ingest_directory)
instead of writing a second exclusion list.
code-forge (the stdio MCP server) calls the REST API directly and has
no login flow of its own. With auth disabled it already passes
through as guest, same as before - but the moment CLAUDE_OS_EMAIL/
PASSWORD get set, it would start getting 401'd with no way to
authenticate.
get_current_user now accepts a CLAUDE_OS_SERVICE_TOKEN as a bearer
token, checked with a constant-time compare, alongside normal user
JWTs. claude_code_mcp.py sends it (via a shared _auth_headers() used
by all 5 of its httpx.AsyncClient() call sites) whenever
CLAUDE_OS_SERVICE_TOKEN is set, and sends nothing when it isn't - a
no-op change for anyone not using auth.
Since this process can be launched directly (e.g. over SSH as a stdio
server) without claude-os's own .env sourced into its shell or its cwd
set to the project root, it now loads .env explicitly by path relative
to the script's own location rather than relying on load_dotenv()'s
cwd-relative default search.
Two gaps found while wiring auth into the data routes:
1. mcp_server/auth.py: HTTPBearer() defaults to auto_error=True, which
rejects any request with no Authorization header before
get_current_user's own "auth not configured -> allow as guest"
check ever runs. That meant turning on Depends(get_current_user)
anywhere 401'd every request even with auth fully disabled.
Switched to auto_error=False and handle the missing-credentials
case explicitly inside get_current_user instead.
2. Frontend never actually sent the stored token on data requests.
auth.tsx only attached it to its own /api/auth/me check; api.ts's
fetch() calls (chat, stats, documents, upload, delete) and every
axios call elsewhere in the app (projects, kanban, hooks) had no
Authorization header at all. Added an authHeaders() helper in
api.ts, and a single axios request interceptor in auth.tsx that
attaches the token app-wide since every component imports the same
default axios instance.
Known gap not addressed here: mcp_server/claude_code_mcp.py (the
code-forge stdio MCP server) calls the REST API with no token at all.
Harmless while auth is disabled (current state), but it will start
getting 401'd the moment CLAUDE_OS_EMAIL/PASSWORD are actually set -
needs a service-token bypass if that's ever turned on.
get_current_user was only wired into one of 75 routes. Everything
else - chat, kb crud, uploads, projects, jobs, mcp management - had
no auth dependency at all, so setting CLAUDE_OS_EMAIL/PASSWORD only
gated the frontend's login screen while the API itself stayed fully
open to anyone who could reach it directly.
Added Depends(get_current_user) to all 72 remaining data routes,
left /api/auth/login, /api/auth/status, and /health public (has to
be reachable pre-login / for health checks). get_current_user already
returns a guest user when auth isn't configured, so this is a no-op
for anyone who hasn't set credentials.
Moved the mcp_server.auth import to the top of the file - it was
previously imported ~3250 lines down, after most of these routes'
def statements would have already tried to evaluate
Depends(get_current_user) as a default argument.
_query_base/_query_hybrid passed raw TextNodes to
get_response_synthesizer().synthesize(), which requires NodeWithScore
(it reads node.node internally). Every real query hit
AttributeError: 'TextNode' object has no attribute 'node', got caught
by a bare except Exception, and silently fell back to
_generate_fallback_answer - the "Based on the available
documentation: 1. From X... 2. From Y..." template users were
actually seeing instead of a real LLM-synthesized answer.
This was a pre-existing bug independent of the Groq change - Ollama
hit the same exception, it just wasn't noticed because the fallback
looks superficially like an answer.
api.ts and auth.tsx hardcoded API_BASE to http://localhost:8051 with
no override, unlike every other component which already checked
VITE_API_URL first. That's fine when the browser and server are on
the same machine, but breaks auth/chat/stats/documents entirely when
accessing the UI from a different machine than the one running
claude-os - "localhost" resolves to the browser's own machine, not
the server.
Also pins mcp<2.0.0 in requirements.txt: mcp 2.0.0 removed the
low-level Server.list_tools() API that mcp_server/claude_code_mcp.py
(the code-forge stdio MCP server) depends on, so an unbounded
mcp>=1.0.0 install silently breaks that integration.
Config.PROVIDER was never actually consulted by the LLM call sites -
rag_engine.py, insight_extractor.py, and knowledge_lifecycle.py all
hardcoded Ollama directly. This wires up a real "groq" provider:
- config.py: GROQ_API_KEY/GROQ_MODEL settings, is_groq_provider(),
provider-aware get_active_llm_model() and validate_provider_config()
- rag_engine.py: uses llama_index's Groq LLM when provider is groq
- insight_extractor.py / knowledge_lifecycle.py: branch to Groq's
OpenAI-compatible chat completions API instead of Ollama's /api/generate
- requirements.txt: add llama-index-llms-groq
Embeddings are untouched and stay on Ollama - Groq has no embeddings API.
Default model is openai/gpt-oss-120b.
Ensures new projects initialized with /claude-os-init include
the lifecycle slash command and MCP tools in their CLAUDE.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add KB lifecycle management system with deduplication, consolidation,
archival, and health analytics. Also fix pre-existing routing bug in
upload_document MCP tool (was hitting /import instead of new /documents/content).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Skill:
- Merged remember-this into unified memory skill
- Now uses Claude OS KBs (not separate memory MCP)
- Supports natural language: "remember this:", "save to memory:", etc.
- Deleted redundant remember-this skill
README:
- New tagline: "Give Your AI a Memory"
- Added "The Magic" section showing natural language UX
- Rewrote "What is Claude OS" with before/after comparison
- Added killer line: "Claude is a tool → Claude is a partner"
- Compact features table replacing wall of checkmarks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Claude Code stores MCP configs in ~/.claude.json per-project, NOT in
~/.claude/settings.json. The previous approach of writing to settings.json
didn't work - projects showed "No MCP servers configured".
Changes:
- Remove broken MCP setup from setup-claude-os.sh (was writing to wrong file)
- Add Step 2.5 to /claude-os-init that runs `claude mcp add` for each project
- Update final summary to remind users to restart Claude Code after init
Now the workflow is:
1. ./setup-claude-os.sh - Install Claude OS once
2. /claude-os-init - Run in each project (creates KBs + configures MCP)
3. Restart Claude Code - MCP connects
4. mcp__code-forge__* tools available!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Discord badge and invite link at top of README
- Add 5 new PRs from @illAssad (#18-#22) to contributions table
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace context manager pattern with direct cursor assignment
(SQLite cursors don't support context manager protocol)
- Use conn.close() instead of db_manager.return_connection()
(SQLiteManager doesn't have return_connection method)
Fixes 500 error when deleting documents via REST API.
The ingest_directory function was recursively indexing ALL files including
node_modules, which caused:
- Massive index bloat (thousands of irrelevant library files)
- Polluted search results with third-party code
- Very slow indexing times
Added SKIP_DIRECTORIES set with common directories to exclude:
- Package managers: node_modules, vendor, Pods
- Build outputs: dist, build, target, .next, .nuxt
- VCS: .git, .svn, .hg
- Caches: __pycache__, .pytest_cache, .mypy_cache, .cache
- IDE: .vscode, .idea
- Our own: .claude-os
Also added should_skip_path() helper function with wildcard support
for patterns like *.egg-info.
Pin tree-sitter to 0.20.x because tree-sitter-languages 1.10.x is
incompatible with tree-sitter 0.21+. The 0.21+ API changed Parser()
and Language() constructors, causing '__init__() takes exactly 1
argument (2 given)' errors on all file parsing operations.
This fix restores selective indexing functionality which relies on
TreeSitter for code parsing and symbol extraction.
- Fix server blocking issue during semantic indexing by using FastAPI BackgroundTasks
- Add job progress tracking with real-time status updates
- Add /api/jobs and /api/jobs/{job_id} endpoints for monitoring
- Add JobsDashboard component with live progress bars and status indicators
- Add Jobs tab to MainApp for interactive monitoring
- Maintain backward compatibility with background=false option for sync mode
- Include automatic job cleanup to prevent memory leaks
The start script was running `npm run dev` without checking if
node_modules existed, causing "vite: command not found" errors
for new users who hadn't manually run npm install.
Now checks for node_modules and runs npm install automatically
if dependencies are missing.
New command line options:
- --demo: Interactive demo mode (no changes made)
- --dry-run: Preview what would be done without doing it
- --help/-h: Show usage information
- --version/-v: Show version number
- --force/-f: Skip confirmation prompts
Safety improvements:
- Automatic backup of .env and config.json before overwriting
- Backups saved as filename.backup.YYYYMMDD_HHMMSS
- Dry-run shows all actions that would be taken
- Demo mode lets users see the beautiful UI safely
Version bumped to 2.2.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Detect gum and offer to install for enhanced UX
- Add wrapper functions with graceful bash fallback:
- gum_choose: Interactive option selection
- gum_confirm: Yes/no prompts
- gum_input: Text input
- gum_password: Hidden input
- gum_spin: Animated spinners
- gum_style_box: Styled boxes
- Works perfectly without gum, beautiful with it
- Supports macOS (Homebrew), Debian/Ubuntu (apt), and Go install
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
✨ New Features:
- Beautiful CLI installer (setup-claude-os.sh) with ASCII art,
progress bars, spinners, and styled prompts
- Choose between Local (Ollama) or Cloud (OpenAI) providers
- Lite model (llama3.2:3b) as default - faster download, works on more machines
- Full model (llama3.1:8b) option for users who want better quality
- OpenAI API support for users who prefer cloud
🔧 Changes:
- setup.sh and install.sh now redirect to setup-claude-os.sh
- config.py updated with provider support and new defaults
- Works on both macOS and Linux
📦 Default Model Change:
- Before: llama3.1:latest (8B, 4.7GB download)
- After: llama3.2:3b (3B, 2GB download)
This makes Claude OS accessible to more users with smaller machines
while still offering the full experience for power users.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, if ~/.claude already existed from a prior Claude Code
installation, the install script would skip creating the commands/
and skills/ subdirectories. This caused symlink creation to fail
silently, leaving users without access to /claude-os-init and other
commands.
This fix ensures the subdirectories are always created, regardless
of whether ~/.claude already exists.
Fixes issue where /claude-os-init command was not available after
running install.sh on systems with existing ~/.claude directory.