diff --git a/app/core/file_watcher.py b/app/core/file_watcher.py index c7a6cf5..0a2e397 100644 --- a/app/core/file_watcher.py +++ b/app/core/file_watcher.py @@ -178,6 +178,9 @@ class GlobalFileWatcher: watcher = ProjectWatcher(project_id) watcher.start() self.watchers[project_id] = watcher + # Enable if we have active watchers + if self.watchers: + self.enabled = True def stop_project(self, project_id: int): """Stop watching a project.""" @@ -187,6 +190,9 @@ class GlobalFileWatcher: watcher = self.watchers.pop(project_id) watcher.stop() + # Disable if no more watchers + if not self.watchers: + self.enabled = False def restart_project(self, project_id: int): """Restart watcher for a project.""" diff --git a/docs/assets/og-image.png b/docs/assets/og-image.png new file mode 100644 index 0000000..c989d0b Binary files /dev/null and b/docs/assets/og-image.png differ diff --git a/docs/index.html b/docs/index.html index 5399b7d..4e9ebef 100644 --- a/docs/index.html +++ b/docs/index.html @@ -5,6 +5,26 @@ Claude OS - The AI Operating System That Never Forgets + + + + + + + + + + + + + + + + + + + + diff --git a/mcp_server/server.py b/mcp_server/server.py index c1d8405..9f64e8f 100644 --- a/mcp_server/server.py +++ b/mcp_server/server.py @@ -3324,6 +3324,35 @@ async def startup_event(): except Exception as e: logger.error(f"โŒ Failed to start spec watchers: {e}") + # Auto-start file watchers for all projects with hooks enabled + try: + from app.core.hooks import ProjectHook + + db_manager = get_sqlite_manager() + projects = db_manager.list_projects() + file_watcher = get_global_watcher() + started_count = 0 + + for project in projects: + try: + # Check if project has any enabled hooks + hook = ProjectHook(project['id']) + hook_status = hook.get_hook_status() + + if hook_status.get('enabled_hooks', 0) > 0: + file_watcher.start_project(project['id']) + started_count += 1 + logger.info(f"๐Ÿ“‚ File watcher started for project {project['name']}") + except Exception as e: + logger.warning(f"Could not start file watcher for project {project['id']}: {e}") + + if started_count > 0: + logger.info(f"โœ… File watchers started for {started_count} project(s)") + else: + logger.info("โ„น๏ธ No projects with hooks enabled - file watchers will start when hooks are configured") + except Exception as e: + logger.error(f"โŒ Failed to start file watchers: {e}") + @app.on_event("shutdown") async def shutdown_event(): @@ -3340,6 +3369,14 @@ async def shutdown_event(): except Exception as e: logger.error(f"โŒ Failed to stop spec watchers: {e}") + # Stop all file watchers + try: + file_watcher = get_global_watcher() + file_watcher.stop_all() + logger.info("โœ… File watchers stopped") + except Exception as e: + logger.error(f"โŒ Failed to stop file watchers: {e}") + def main(): """Start the MCP server.""" diff --git a/start_all_services.sh b/start_all_services.sh index 6b836b8..68b6e84 100755 --- a/start_all_services.sh +++ b/start_all_services.sh @@ -154,7 +154,7 @@ echo "" # ===== 5. Start RQ Workers (Real-Time Learning System) ===== echo -e "${YELLOW}5. Starting RQ Workers (Real-Time Learning)...${NC}" if check_port 6379; then - nohup "$PROJECT_DIR/venv/bin/python3" -m rq worker claude-os:learning claude-os:prompts claude-os:ingest --with-scheduler > "$PROJECT_DIR/logs/rq_workers.log" 2>&1 & + nohup "$PROJECT_DIR/venv/bin/rq" worker claude-os:learning claude-os:prompts claude-os:ingest --with-scheduler > "$PROJECT_DIR/logs/rq_workers.log" 2>&1 & RQ_PID=$! echo " RQ Workers PID: $RQ_PID (logging to logs/rq_workers.log)" echo -e " ${GREEN}โœ“ RQ workers started${NC}" diff --git a/templates/commands/claude-os-init.md b/templates/commands/claude-os-init.md index e1caf3d..f8747f9 100644 --- a/templates/commands/claude-os-init.md +++ b/templates/commands/claude-os-init.md @@ -73,8 +73,8 @@ Let's get your project configured for AI-powered development. - Default: `http://localhost:8051` - Ask: `Claude OS server URL? [default: http://localhost:8051]` -9. **Agent-OS Setup** (Optional): - - Ask: `Enable Agent-OS for spec-driven development? [Y/n]` +9. **Agent-OS Setup** (Optional - for advanced users): + - Ask: `Enable Agent-OS for spec-driven development? [y/N]` - Explain: ``` Agent-OS provides 8 specialized agents for structured feature development: @@ -83,13 +83,14 @@ Let's get your project configured for AI-powered development. - Generate task breakdowns - Implement and verify features + This is recommended for complex projects with formal spec workflows. Note: Requires Ollama (local) or OpenAI API key for advanced features. ``` - If YES: - Set `ENABLE_AGENT_OS=true` - Will create `agent-os/` directory structure - Will symlink agents to `.claude/agents/agent-os/` - - If NO: + - If NO (default): - Set `ENABLE_AGENT_OS=false` - Can enable later by manually copying templates @@ -184,6 +185,18 @@ mkdir -p .claude-os - Copy from: `{claude_os_dir}/templates/project-files/.claude-os/.gitignore` - Write to: `./.claude-os/.gitignore` +**Install TDD Skill (always - best practice for all projects):** +- Create directory: `mkdir -p ./.claude/skills/test-driven-development` +- Copy from: `{claude_os_dir}/templates/skills/test-driven-development/SKILL.md` +- Write to: `./.claude/skills/test-driven-development/SKILL.md` +- Show: + ``` + โœ… Installed TDD skill (test-driven-development) + - Guides RED-GREEN-REFACTOR workflow + - Ensures tests fail first, then pass + - Use: "I'm using the TDD skill to implement this feature" + ``` + **If Agent-OS is enabled (`ENABLE_AGENT_OS=true`):** 1. **Create agent-os/ directory structure:** @@ -450,6 +463,11 @@ Your project is now connected to Claude OS. /claude-os-session - Manage development sessions /claude-os-remember - Quick save to memories +๐Ÿงช TDD Skill Installed: + - RED-GREEN-REFACTOR workflow built-in + - Say "I'm using the TDD skill" to activate + - Write tests first, watch them fail, then implement + ๐Ÿ“‚ Project Structure: ./CLAUDE.md - Project context (auto-loaded every session) ./.claude/ - Commands, skills, agents diff --git a/templates/skills/test-driven-development/SKILL.md b/templates/skills/test-driven-development/SKILL.md new file mode 100644 index 0000000..bd8bbf6 --- /dev/null +++ b/templates/skills/test-driven-development/SKILL.md @@ -0,0 +1,298 @@ +--- +name: test-driven-development +description: Use when implementing any feature or bugfix, before writing implementation code - write the test first, watch it fail, write minimal code to pass; ensures tests actually verify behavior by requiring failure first +--- + +# Test-Driven Development (TDD) + +## Overview + +Write the test first. Watch it fail. Write minimal code to pass. + +**Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing. + +**Violating the letter of the rules is violating the spirit of the rules.** + +## When to Use + +**Always:** +- New features +- Bug fixes +- Refactoring +- Behavior changes + +**Exceptions (ask your human partner):** +- Throwaway prototypes +- Generated code +- Configuration files + +Thinking "skip TDD just this once"? Stop. That's rationalization. + +## The Iron Law + +``` +NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST +``` + +Write code before the test? Delete it. Start over. + +**No exceptions:** +- Don't keep it as "reference" +- Don't "adapt" it while writing tests +- Don't look at it +- Delete means delete + +Implement fresh from tests. Period. + +## Red-Green-Refactor + +### RED - Write Failing Test + +Write one minimal test showing what should happen. + +**Good:** +```typescript +test('retries failed operations 3 times', async () => { + let attempts = 0; + const operation = () => { + attempts++; + if (attempts < 3) throw new Error('fail'); + return 'success'; + }; + + const result = await retryOperation(operation); + + expect(result).toBe('success'); + expect(attempts).toBe(3); +}); +``` +Clear name, tests real behavior, one thing + +**Bad:** +```typescript +test('retry works', async () => { + const mock = jest.fn() + .mockRejectedValueOnce(new Error()) + .mockRejectedValueOnce(new Error()) + .mockResolvedValueOnce('success'); + await retryOperation(mock); + expect(mock).toHaveBeenCalledTimes(3); +}); +``` +Vague name, tests mock not code + +**Requirements:** +- One behavior +- Clear name +- Real code (no mocks unless unavoidable) + +### Verify RED - Watch It Fail + +**MANDATORY. Never skip.** + +```bash +npm test path/to/test.test.ts +``` + +Confirm: +- Test fails (not errors) +- Failure message is expected +- Fails because feature missing (not typos) + +**Test passes?** You're testing existing behavior. Fix test. + +**Test errors?** Fix error, re-run until it fails correctly. + +### GREEN - Minimal Code + +Write simplest code to pass the test. + +**Good:** +```typescript +async function retryOperation(fn: () => Promise): Promise { + for (let i = 0; i < 3; i++) { + try { + return await fn(); + } catch (e) { + if (i === 2) throw e; + } + } + throw new Error('unreachable'); +} +``` +Just enough to pass + +**Bad:** +```typescript +async function retryOperation( + fn: () => Promise, + options?: { + maxRetries?: number; + backoff?: 'linear' | 'exponential'; + onRetry?: (attempt: number) => void; + } +): Promise { + // YAGNI +} +``` +Over-engineered + +Don't add features, refactor other code, or "improve" beyond the test. + +### Verify GREEN - Watch It Pass + +**MANDATORY.** + +```bash +npm test path/to/test.test.ts +``` + +Confirm: +- Test passes +- Other tests still pass +- Output pristine (no errors, warnings) + +**Test fails?** Fix code, not test. + +**Other tests fail?** Fix now. + +### REFACTOR - Clean Up + +After green only: +- Remove duplication +- Improve names +- Extract helpers + +Keep tests green. Don't add behavior. + +### Repeat + +Next failing test for next feature. + +## Good Tests + +| Quality | Good | Bad | +|---------|------|-----| +| **Minimal** | One thing. "and" in name? Split it. | `test('validates email and domain and whitespace')` | +| **Clear** | Name describes behavior | `test('test1')` | +| **Shows intent** | Demonstrates desired API | Obscures what code should do | + +## Why Order Matters + +**"I'll write tests after to verify it works"** + +Tests written after code pass immediately. Passing immediately proves nothing: +- Might test wrong thing +- Might test implementation, not behavior +- Might miss edge cases you forgot +- You never saw it catch the bug + +Test-first forces you to see the test fail, proving it actually tests something. + +**"I already manually tested all the edge cases"** + +Manual testing is ad-hoc. You think you tested everything but: +- No record of what you tested +- Can't re-run when code changes +- Easy to forget cases under pressure +- "It worked when I tried it" โ‰  comprehensive + +Automated tests are systematic. They run the same way every time. + +## Common Rationalizations + +| Excuse | Reality | +|--------|---------| +| "Too simple to test" | Simple code breaks. Test takes 30 seconds. | +| "I'll test after" | Tests passing immediately prove nothing. | +| "Already manually tested" | Ad-hoc โ‰  systematic. No record, can't re-run. | +| "Need to explore first" | Fine. Throw away exploration, start with TDD. | +| "Test hard = design unclear" | Listen to test. Hard to test = hard to use. | +| "TDD will slow me down" | TDD faster than debugging. Pragmatic = test-first. | + +## Red Flags - STOP and Start Over + +- Code before test +- Test after implementation +- Test passes immediately +- Can't explain why test failed +- Tests added "later" +- Rationalizing "just this once" + +**All of these mean: Delete code. Start over with TDD.** + +## Example: Bug Fix + +**Bug:** Empty email accepted + +**RED** +```typescript +test('rejects empty email', async () => { + const result = await submitForm({ email: '' }); + expect(result.error).toBe('Email required'); +}); +``` + +**Verify RED** +```bash +$ npm test +FAIL: expected 'Email required', got undefined +``` + +**GREEN** +```typescript +function submitForm(data: FormData) { + if (!data.email?.trim()) { + return { error: 'Email required' }; + } + // ... +} +``` + +**Verify GREEN** +```bash +$ npm test +PASS +``` + +**REFACTOR** +Extract validation for multiple fields if needed. + +## Verification Checklist + +Before marking work complete: + +- [ ] Every new function/method has a test +- [ ] Watched each test fail before implementing +- [ ] Each test failed for expected reason (feature missing, not typo) +- [ ] Wrote minimal code to pass each test +- [ ] All tests pass +- [ ] Output pristine (no errors, warnings) +- [ ] Tests use real code (mocks only if unavoidable) +- [ ] Edge cases and errors covered + +Can't check all boxes? You skipped TDD. Start over. + +## When Stuck + +| Problem | Solution | +|---------|----------| +| Don't know how to test | Write wished-for API. Write assertion first. Ask your human partner. | +| Test too complicated | Design too complicated. Simplify interface. | +| Must mock everything | Code too coupled. Use dependency injection. | +| Test setup huge | Extract helpers. Still complex? Simplify design. | + +## Debugging Integration + +Bug found? Write failing test reproducing it. Follow TDD cycle. Test proves fix and prevents regression. + +Never fix bugs without a test. + +## Final Rule + +``` +Production code โ†’ test exists and failed first +Otherwise โ†’ not TDD +``` + +No exceptions without your human partner's permission.