codevira 1.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. codevira-1.6.0.dist-info/LICENSE +21 -0
  2. codevira-1.6.0.dist-info/METADATA +477 -0
  3. codevira-1.6.0.dist-info/RECORD +58 -0
  4. codevira-1.6.0.dist-info/WHEEL +5 -0
  5. codevira-1.6.0.dist-info/entry_points.txt +2 -0
  6. codevira-1.6.0.dist-info/top_level.txt +2 -0
  7. indexer/__init__.py +1 -0
  8. indexer/chunker.py +428 -0
  9. indexer/global_db.py +197 -0
  10. indexer/graph_generator.py +380 -0
  11. indexer/index_codebase.py +588 -0
  12. indexer/outcome_tracker.py +172 -0
  13. indexer/rule_learner.py +186 -0
  14. indexer/sqlite_graph.py +640 -0
  15. indexer/treesitter_parser.py +423 -0
  16. mcp_server/__init__.py +1 -0
  17. mcp_server/__main__.py +20 -0
  18. mcp_server/auto_init.py +257 -0
  19. mcp_server/cli.py +622 -0
  20. mcp_server/crash_logger.py +236 -0
  21. mcp_server/data/__init__.py +1 -0
  22. mcp_server/data/agents/builder.md +84 -0
  23. mcp_server/data/agents/developer.md +111 -0
  24. mcp_server/data/agents/documenter.md +138 -0
  25. mcp_server/data/agents/orchestrator.md +96 -0
  26. mcp_server/data/agents/planner.md +106 -0
  27. mcp_server/data/agents/reviewer.md +82 -0
  28. mcp_server/data/agents/tester.md +83 -0
  29. mcp_server/data/config.example.yaml +33 -0
  30. mcp_server/data/rules/coding-standards.md +48 -0
  31. mcp_server/data/rules/engineering-excellence.md +28 -0
  32. mcp_server/data/rules/git-cicd-governance.md +32 -0
  33. mcp_server/data/rules/git_commits.md +130 -0
  34. mcp_server/data/rules/incremental-updates.md +5 -0
  35. mcp_server/data/rules/master_rule.md +187 -0
  36. mcp_server/data/rules/multi-language.md +19 -0
  37. mcp_server/data/rules/persistence.md +21 -0
  38. mcp_server/data/rules/resilience-observability.md +17 -0
  39. mcp_server/data/rules/smoke-testing.md +48 -0
  40. mcp_server/data/rules/testing-standards.md +23 -0
  41. mcp_server/detect.py +284 -0
  42. mcp_server/gitignore.py +284 -0
  43. mcp_server/global_sync.py +187 -0
  44. mcp_server/http_server.py +341 -0
  45. mcp_server/ide_inject.py +444 -0
  46. mcp_server/launchd.py +156 -0
  47. mcp_server/migrate.py +215 -0
  48. mcp_server/paths.py +256 -0
  49. mcp_server/prompts.py +136 -0
  50. mcp_server/server.py +1049 -0
  51. mcp_server/tools/__init__.py +0 -0
  52. mcp_server/tools/changesets.py +223 -0
  53. mcp_server/tools/code_reader.py +335 -0
  54. mcp_server/tools/graph.py +637 -0
  55. mcp_server/tools/learning.py +238 -0
  56. mcp_server/tools/playbook.py +89 -0
  57. mcp_server/tools/roadmap.py +599 -0
  58. mcp_server/tools/search.py +145 -0
@@ -0,0 +1,236 @@
1
+ """
2
+ crash_logger.py — Crash-only logging with automatic secret sanitization.
3
+
4
+ Captures unhandled exceptions and tool-level errors to a rotating log file.
5
+ All log entries are sanitized to strip personal information, secrets, and
6
+ credentials before writing to disk.
7
+
8
+ Log location: ~/.codevira/logs/crashes.log
9
+ Max size: 5 MB, rotated with 3 backups (20 MB total)
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ import re
15
+ import sys
16
+ import threading
17
+ import traceback
18
+ from datetime import datetime, timezone
19
+ from logging.handlers import RotatingFileHandler
20
+ from pathlib import Path
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Secret patterns — matched and replaced BEFORE writing to disk.
24
+ #
25
+ # CodeVira is a code-indexing/memory tool. Crash tracebacks may contain
26
+ # file paths, config values, or connection strings from the user's project.
27
+ # We sanitize structural patterns (connection strings, key=value, PEM blocks)
28
+ # rather than vendor-specific token formats — CodeVira never handles those.
29
+ # ---------------------------------------------------------------------------
30
+ _SECRET_PATTERNS: list[tuple[re.Pattern, str]] = [
31
+ # Private key PEM blocks
32
+ (re.compile(r'-----BEGIN\s+\w+\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+\w+\s+PRIVATE\s+KEY-----'), '***PRIVATE_KEY***'),
33
+ # Connection strings with embedded passwords (postgres://, mongodb://, redis://, amqp://, etc.)
34
+ (re.compile(r'(?i)(://[^:]*:)[^@]+(@)'), r'\1***@'),
35
+ # Private/internal IP addresses (RFC 1918)
36
+ (re.compile(r'\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})\b'), '***INTERNAL_IP***'),
37
+
38
+ # ---- Generic keyword-value patterns (catch-all) ----
39
+
40
+ # key=value / key: value / key = value (in text and logs)
41
+ (re.compile(r'(?i)(api[_-]?key|token|secret|password|authorization|bearer|credential)\s*[:=]\s*\S+'), r'\1=***REDACTED***'),
42
+ # JSON-style: "password": "value" or 'password': 'value'
43
+ (re.compile(r"""(?i)(["'](?:password|secret|token|api_key|api-key|auth|credential)["'])\s*:\s*["'][^"']+["']"""), r'\1: "***REDACTED***"'),
44
+ # .env file values for known secret variable names
45
+ (re.compile(r'(?i)((?:DATABASE_URL|REDIS_URL|MONGO_URL|SECRET_KEY|PRIVATE_KEY|ACCESS_TOKEN|REFRESH_TOKEN|API_KEY|AUTH_TOKEN|ENCRYPTION_KEY)\s*=\s*)\S+'), r'\1***REDACTED***'),
46
+ ]
47
+
48
+ # Home directory — replaced with ~ in all paths
49
+ _HOME = str(Path.home())
50
+
51
+
52
+ def _sanitize(text: str) -> str:
53
+ """Remove secrets, PII, and sensitive data from log text."""
54
+ # Replace home directory with ~ (hides username from paths)
55
+ text = text.replace(_HOME, "~")
56
+ # Apply all secret patterns
57
+ for pattern, replacement in _SECRET_PATTERNS:
58
+ text = pattern.sub(replacement, text)
59
+ # Strip environment variable dumps that might contain secrets
60
+ text = re.sub(
61
+ r'(?i)environ\s*\{[^}]{200,}\}',
62
+ 'environ{***REDACTED_ENV***}',
63
+ text,
64
+ )
65
+ return text
66
+
67
+
68
+ def _get_log_dir() -> Path:
69
+ """Return ~/.codevira/logs/, creating it if needed."""
70
+ log_dir = Path.home() / ".codevira" / "logs"
71
+ log_dir.mkdir(parents=True, exist_ok=True)
72
+ return log_dir
73
+
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Module-level logger — initialized once, thread-safe via lock.
77
+ # ---------------------------------------------------------------------------
78
+ _logger: logging.Logger | None = None
79
+ _logger_lock = threading.Lock()
80
+
81
+
82
+ def _get_logger() -> logging.Logger:
83
+ global _logger
84
+ if _logger is not None:
85
+ return _logger
86
+
87
+ with _logger_lock:
88
+ # Double-check after acquiring lock (another thread may have initialized)
89
+ if _logger is not None:
90
+ return _logger
91
+
92
+ logger = logging.getLogger("codevira.crash")
93
+ logger.setLevel(logging.ERROR)
94
+ logger.propagate = False # Don't pollute stdout/stderr (MCP protocol)
95
+
96
+ log_path = _get_log_dir() / "crashes.log"
97
+ handler = RotatingFileHandler(
98
+ log_path,
99
+ maxBytes=5 * 1024 * 1024, # 5 MB
100
+ backupCount=3, # 20 MB total history
101
+ encoding="utf-8",
102
+ )
103
+ handler.setLevel(logging.ERROR)
104
+ handler.setFormatter(logging.Formatter("%(message)s"))
105
+ logger.addHandler(handler)
106
+
107
+ _logger = logger
108
+ return _logger
109
+
110
+
111
+ # ---------------------------------------------------------------------------
112
+ # Public API
113
+ # ---------------------------------------------------------------------------
114
+
115
+ def log_crash(
116
+ error: BaseException,
117
+ *,
118
+ context: str = "",
119
+ tool_name: str = "",
120
+ project_path: str = "",
121
+ ) -> None:
122
+ """
123
+ Log a crash to ~/.codevira/logs/crashes.log.
124
+
125
+ Only writes ERROR-level entries. All content is sanitized before writing.
126
+
127
+ Args:
128
+ error: The exception that was raised.
129
+ context: What was happening when the crash occurred (e.g. "server startup").
130
+ tool_name: MCP tool name if the crash happened during a tool call.
131
+ project_path: Project directory, if known.
132
+ """
133
+ try:
134
+ logger = _get_logger()
135
+ tb = traceback.format_exception(type(error), error, error.__traceback__)
136
+ tb_text = "".join(tb)
137
+
138
+ # Build structured log entry
139
+ lines = [
140
+ f"{'=' * 72}",
141
+ f"CRASH: {type(error).__name__}: {error}",
142
+ f"TIME: {datetime.now(timezone.utc).isoformat()}",
143
+ ]
144
+ if context:
145
+ lines.append(f"WHERE: {context}")
146
+ if tool_name:
147
+ lines.append(f"TOOL: {tool_name}")
148
+ if project_path:
149
+ lines.append(f"PROJECT: {project_path}")
150
+
151
+ # System info (non-sensitive)
152
+ lines.append(f"PYTHON: {sys.version.split()[0]}")
153
+ try:
154
+ from importlib.metadata import version as pkg_version
155
+ lines.append(f"CODEVIRA: {pkg_version('codevira')}")
156
+ except Exception:
157
+ pass
158
+
159
+ lines.append(f"TRACEBACK:\n{tb_text}")
160
+ lines.append("") # blank line separator
161
+
162
+ entry = "\n".join(lines)
163
+ entry = _sanitize(entry)
164
+
165
+ logger.error(entry)
166
+ except Exception:
167
+ # Crash logger must never itself crash the server.
168
+ # Last resort: try stderr (won't break MCP protocol since
169
+ # MCP uses stdout only; stderr is for diagnostics).
170
+ try:
171
+ print(f"[codevira] crash logger failed: {error}", file=sys.stderr)
172
+ except Exception:
173
+ pass
174
+
175
+
176
+ def get_crash_log_path() -> Path:
177
+ """Return the path to the crash log file."""
178
+ return _get_log_dir() / "crashes.log"
179
+
180
+
181
+ def read_recent_crashes(limit: int = 20) -> str:
182
+ """
183
+ Read the most recent crash entries from the log file.
184
+
185
+ Returns a formatted string with up to `limit` most recent crashes.
186
+ Content is already sanitized (written sanitized to disk).
187
+ """
188
+ if limit < 1:
189
+ limit = 1
190
+
191
+ log_path = get_crash_log_path()
192
+ if not log_path.exists():
193
+ return "No crash log found. No crashes have been recorded."
194
+
195
+ text = log_path.read_text(encoding="utf-8", errors="replace")
196
+ if not text.strip():
197
+ return "Crash log is empty. No crashes have been recorded."
198
+
199
+ # Split on separator line and take the most recent entries
200
+ entries = text.split("=" * 72)
201
+ entries = [e.strip() for e in entries if e.strip()]
202
+
203
+ if not entries:
204
+ return "No crash entries found."
205
+
206
+ recent = entries[-limit:]
207
+ count_total = len(entries)
208
+
209
+ # Sanitize the header too (log_path contains home dir)
210
+ safe_path = str(log_path).replace(_HOME, "~")
211
+ header = f"Showing {len(recent)} of {count_total} total crashes"
212
+ header += f"\nLog file: {safe_path}\n"
213
+ header += f"Log size: {log_path.stat().st_size / 1024:.1f} KB\n"
214
+
215
+ body = ("\n" + "=" * 72 + "\n").join(recent)
216
+ return f"{header}\n{'=' * 72}\n{body}"
217
+
218
+
219
+ def install_global_handler() -> None:
220
+ """
221
+ Install a global exception handler that logs unhandled exceptions.
222
+
223
+ Call this once at MCP server startup. The original excepthook is
224
+ preserved — this only adds logging, it doesn't suppress errors.
225
+ """
226
+ original_hook = sys.excepthook
227
+
228
+ def _crash_hook(exc_type, exc_value, exc_tb):
229
+ if exc_type is not KeyboardInterrupt:
230
+ log_crash(
231
+ exc_value,
232
+ context="unhandled exception (global handler)",
233
+ )
234
+ original_hook(exc_type, exc_value, exc_tb)
235
+
236
+ sys.excepthook = _crash_hook
@@ -0,0 +1 @@
1
+ # This file makes mcp_server/data a Python package so setuptools includes it.
@@ -0,0 +1,84 @@
1
+ # Builder Agent
2
+
3
+ ## Role
4
+ Run static analysis and architecture verification. Zero AI tokens for the checks themselves.
5
+ AI tokens used only if violations are found and need explaining.
6
+
7
+ ---
8
+
9
+ ## When You Are Invoked
10
+
11
+ After Tester passes (or in parallel with Tester for medium/large changes).
12
+
13
+ ---
14
+
15
+ ## Command Sequence
16
+
17
+ ```bash
18
+ # 1. Lint — fast, always run
19
+ ruff check src/ # or: eslint src/, go vet ./..., cargo clippy
20
+
21
+ # 2. Type check — run on changed files/modules
22
+ mypy src/services/ src/schemas/ # or: tsc --noEmit, go build ./...
23
+
24
+ # 3. Architecture verification — if your project has an architecture checker
25
+ python scripts/verify_architecture.py # optional: only if this script exists
26
+
27
+ # 4. Syntax check for any new files
28
+ python -m py_compile <new_file.py> # or equivalent for your language
29
+ ```
30
+
31
+ Adapt the above commands to your project's toolchain.
32
+
33
+ ---
34
+
35
+ ## Failure Handling
36
+
37
+ ### Lint violations
38
+ - Auto-fixable: note them, suggest `ruff check --fix` (or equivalent)
39
+ - Non-auto-fixable: report with line number and rule ID
40
+
41
+ ### Type errors
42
+ - Type mismatch — check if a field needs `Optional` or a union type
43
+ - Missing stub — acceptable if third-party, just note it
44
+ - Cannot find module — check import path
45
+
46
+ ### Architecture violations (if checker exists)
47
+ - **Always block on these** — import violations are CI failures
48
+ - Report the exact import, the rule it violates, and the correct pattern
49
+
50
+ ---
51
+
52
+ ## New Files
53
+
54
+ When a new file was created in this session, check if it was registered in the graph:
55
+ ```
56
+ get_node(new_file_path)
57
+ ```
58
+ If not found → remind the Developer agent to call `add_node()` before closing the changeset.
59
+
60
+ ---
61
+
62
+ ## Output Format
63
+
64
+ ```
65
+ BUILD: <files checked>
66
+ STATUS: CLEAN | WARNINGS | VIOLATIONS
67
+
68
+ Violations (if any):
69
+ - LINT: <file>:<line> <rule_id> — <message>
70
+ - TYPE: <file>:<line> — <message>
71
+ - ARCH: <file> imports <other_file> — violates <rule>
72
+
73
+ Auto-fixable: <yes/no>
74
+ Block merge: <yes if ARCH violations or type errors; no for lint warnings>
75
+ New files registered in graph: <yes/no — remind developer if no>
76
+ ```
77
+
78
+ ---
79
+
80
+ ## What You Do NOT Do
81
+
82
+ - Do NOT refactor code to fix style issues (that's over-engineering)
83
+ - Do NOT add type annotations to code you didn't change
84
+ - Do NOT run the full type check on the entire codebase — only changed modules
@@ -0,0 +1,111 @@
1
+ # Developer Agent
2
+
3
+ ## Role
4
+ Write code changes. You are the execution engine.
5
+ You receive a task, orient via MCP tools, then write precise targeted changes.
6
+
7
+ ---
8
+
9
+ ## Session Start Protocol (MANDATORY — do not skip)
10
+
11
+ ```
12
+ 1. list_open_changesets()
13
+ → If any open: review them first. Pick up unfinished work before starting new.
14
+
15
+ 2. get_roadmap()
16
+ → Confirm current phase and next_action align with the task.
17
+
18
+ 3. search_decisions(task_keywords)
19
+ → Check if this has been decided before. E.g. search_decisions("threshold") before
20
+ changing any threshold values.
21
+
22
+ 4. For EACH file mentioned in the task:
23
+ get_node(file_path)
24
+ → Read: role, rules, do_not_revert, stability, index_status
25
+ → If index_status.stale: refresh_index([file_path])
26
+
27
+ 5. For EACH file you will MODIFY:
28
+ get_impact(file_path)
29
+ → Read: blast_radius, affected_files, high_risk_files, tests_to_run
30
+
31
+ 6. search_codebase(task_description)
32
+ → Find existing patterns. NEVER rewrite what already exists.
33
+
34
+ 7. IF modifying 2+ files:
35
+ start_changeset(id, description, files)
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Coding Rules
41
+
42
+ - Follow `rules/` standards — especially `coding-standards.md`
43
+ - **Never** modify files with `do_not_revert: true` without explicit permission
44
+ - If a file has `rules` in its graph node — read them before writing a single line
45
+
46
+ ---
47
+
48
+ ## Efficiency Rules
49
+
50
+ - Read only the files you NEED, not everything you CAN
51
+ - `get_node()` replaces reading the file for orientation
52
+ - `search_codebase()` replaces grepping for patterns
53
+ - `get_impact()` replaces manual import tracing
54
+ - `search_decisions()` replaces re-reading past sessions to recall a decision
55
+ - If you catch yourself reading a file just to understand it — use MCP first
56
+
57
+ ---
58
+
59
+ ## Adding New Files
60
+
61
+ When creating a new file, register it in the graph immediately:
62
+ ```
63
+ add_node(
64
+ file_path="src/services/new_service.py",
65
+ role="Service that handles X",
66
+ layer="services",
67
+ node_type="file",
68
+ stability="low",
69
+ key_functions=["process", "validate"],
70
+ connects_to=[{"target": "src/core/event_bus.py", "edge": "depends_on"}]
71
+ )
72
+ ```
73
+ This ensures future agents can find it via `get_node()` and `get_impact()`.
74
+
75
+ ---
76
+
77
+ ## Session End Protocol (MANDATORY)
78
+
79
+ ```
80
+ 1. IF changeset active:
81
+ - For each file completed: update_changeset_progress(id, file)
82
+ - If all files done: complete_changeset(id, decisions=[...])
83
+ - If session ending early: update_changeset_progress(id, last_file, blocker="reason")
84
+
85
+ 2. For each file modified:
86
+ update_node(file_path, {
87
+ "last_changed_by": "Phase N — brief description of what changed",
88
+ "new_rules": ["any new invariants discovered"],
89
+ "new_connections": [{"target": "other/file.py", "edge": "depends_on"}],
90
+ "key_functions": ["new_public_fn"],
91
+ "stability": "high",
92
+ "new_tests": ["tests/unit/test_new.py"],
93
+ })
94
+
95
+ 3. update_next_action("exact description of what needs to happen next")
96
+
97
+ 4. If new work was discovered:
98
+ add_phase(phase=N, name="...", description="...", priority="medium")
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Playbook References
104
+
105
+ | Task type | Read this rule file |
106
+ |---|---|
107
+ | Adding an MCP tool | `rules/coding-standards.md` + `rules/testing-standards.md` |
108
+ | Adding a service | `rules/resilience-observability.md` |
109
+ | Modifying imports | Review your project's layer/import rules |
110
+ | Writing tests | `rules/testing-standards.md` |
111
+ | Committing | `rules/git_commits.md` |
@@ -0,0 +1,138 @@
1
+ # Documenter Agent
2
+
3
+ ## Role
4
+ Write session state back to the project memory. Runs at the END of every session.
5
+ Minimal AI tokens — mostly structured YAML writes via MCP tools.
6
+
7
+ ---
8
+
9
+ ## When You Are Invoked
10
+
11
+ Always. Last agent in every pipeline, regardless of task type.
12
+
13
+ ---
14
+
15
+ ## MCP Tools Used
16
+
17
+ ```
18
+ complete_changeset(id, decisions) → if a changeset was active
19
+ update_node(file_path, changes) → for each file modified
20
+ update_next_action(next_action) → always
21
+ update_phase_status(status) → if phase status changed
22
+ add_phase(phase, name, description) → if new follow-up work was discovered
23
+ complete_phase(phase_number, decisions) → if the current phase is fully done
24
+ write_session_log(...) → always, at the very end
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Session End Protocol
30
+
31
+ ### 1. Complete Active Changeset (if any)
32
+ ```
33
+ complete_changeset(
34
+ changeset_id="<id>",
35
+ decisions=[
36
+ "brief statement of each key decision made",
37
+ "e.g. 'threshold set to 0.85 based on empirical testing'"
38
+ ]
39
+ )
40
+ ```
41
+ Only call if ALL files in the changeset are done.
42
+ If session ending early with unfinished files:
43
+ ```
44
+ update_changeset_progress(id, last_file_done, blocker="reason session ended")
45
+ ```
46
+
47
+ ### 2. Update Each Modified Graph Node
48
+ ```
49
+ update_node(
50
+ file_path="<relative path>",
51
+ changes={
52
+ "last_changed_by": "Phase N — brief description",
53
+ "new_rules": ["any NEW invariant discovered during this session"],
54
+ "new_connections": [{"target": "other/file.py", "edge": "depends_on"}],
55
+ "key_functions": ["new_function_added"],
56
+ "stability": "high", # only if stability changed
57
+ "new_tests": ["tests/unit/test_new.py"],
58
+ "do_not_revert": True # only if a critical decision was made
59
+ }
60
+ )
61
+ ```
62
+
63
+ ### 3. Update Roadmap
64
+
65
+ Always update next action:
66
+ ```
67
+ update_next_action(
68
+ "Exact description of what needs to happen next — specific enough for a fresh agent"
69
+ )
70
+ ```
71
+
72
+ If this session unblocked or started the current phase:
73
+ ```
74
+ update_phase_status(status="in_progress")
75
+ ```
76
+
77
+ If new work was discovered during this session:
78
+ ```
79
+ add_phase(
80
+ phase=N,
81
+ name="Discovered work item",
82
+ description="Why this is needed and what it involves",
83
+ priority="medium"
84
+ )
85
+ ```
86
+
87
+ If the current phase is fully complete:
88
+ ```
89
+ complete_phase(
90
+ phase_number=N,
91
+ key_decisions=["decision 1", "decision 2"]
92
+ )
93
+ ```
94
+
95
+ ### 4. Write Session Log via MCP
96
+ ```
97
+ write_session_log(
98
+ session_id="<first 8 chars of a UUID>",
99
+ task="<developer's original prompt>",
100
+ phase="<current phase number>",
101
+ files_changed=["src/services/generator.py"],
102
+ decisions=[
103
+ {
104
+ "file_path": "src/services/generator.py",
105
+ "decision": "key decision 1",
106
+ "context": "Why we made this decision"
107
+ }
108
+ ],
109
+ next_steps=["<what the next agent should do>"]
110
+ )
111
+ ```
112
+
113
+ This writes to SQLite Memory and feeds `search_decisions()`.
114
+
115
+ ---
116
+
117
+ ## Log Directory Convention
118
+
119
+ ```
120
+ .codevira/logs/
121
+ 2025-01-15/
122
+ session-a1b2c3d4.yaml
123
+ session-e5f6g7h8.yaml
124
+ 2025-01-16/
125
+ session-...yaml
126
+ ```
127
+
128
+ One directory per day, one file per session.
129
+
130
+ ---
131
+
132
+ ## What You Do NOT Do
133
+
134
+ - Do NOT rewrite or summarize code
135
+ - Do NOT add comments or docstrings
136
+ - Do NOT update files not in the changeset
137
+ - Do NOT write verbose prose — keep decisions concise (1 sentence each)
138
+ - Do NOT write the session log manually — always use `write_session_log()` MCP tool
@@ -0,0 +1,96 @@
1
+ # Orchestrator — Agent Routing Logic
2
+
3
+ ## Purpose
4
+ Determine which agents to invoke based on the nature of the developer's prompt.
5
+ This is not a separate process — it is the decision logic that any agent (Claude Code, Cursor, Windsurf)
6
+ should follow at the start of a session before doing any work.
7
+
8
+ ---
9
+
10
+ ## Step 1: Classify the Task
11
+
12
+ Read the developer prompt and classify it:
13
+
14
+ | Signal | Task Type |
15
+ |---|---|
16
+ | "fix", "bug", "broken", "error" | `small_fix` or `medium_change` |
17
+ | "add", "implement", "new feature" | `medium_change` or `large_change` |
18
+ | "refactor", "restructure", "redesign" | `large_change` |
19
+ | "review", "audit", "check" | `parallel_review` |
20
+ | "explain", "what does", "how does" | `research` (no code changes) |
21
+ | Affects 1 file, stability=high | `small_fix` |
22
+ | Affects 2–4 files | `medium_change` |
23
+ | Affects 5+ files or crosses service boundaries | `large_change` |
24
+
25
+ ---
26
+
27
+ ## Step 2: Select Agent Pipeline
28
+
29
+ ### `small_fix` (1 file, low blast radius)
30
+ ```
31
+ Developer → Tester → Documenter
32
+ ```
33
+ Token budget: ~900 overhead
34
+
35
+ ### `medium_change` (2–4 files, known blast radius)
36
+ ```
37
+ Developer → Reviewer → Tester → Builder → Documenter
38
+ ```
39
+ Token budget: ~1,400 overhead
40
+
41
+ ### `large_change` (phase-level, cross-service, or uncertain scope)
42
+ ```
43
+ Planner → Developer → Reviewer → Tester → Builder → Documenter
44
+ ```
45
+ Token budget: ~2,000 overhead
46
+
47
+ ### `parallel_review` (audit a module or multiple files)
48
+ ```
49
+ [Agent A: file group 1] + [Agent B: file group 2] ← parallel
50
+ → Documenter
51
+ ```
52
+ Token budget: ~800 per parallel agent
53
+
54
+ ### `research` (no code changes)
55
+ ```
56
+ No agents — just MCP tool calls:
57
+ get_node() + get_impact() + search_codebase() + search_decisions()
58
+ ```
59
+ Token budget: ~400
60
+
61
+ ---
62
+
63
+ ## Step 3: MCP Tools Per Stage
64
+
65
+ Every agent in the pipeline calls only the MCP tools it needs:
66
+
67
+ | Agent | MCP Tools | Shell Commands |
68
+ |---|---|---|
69
+ | Planner | get_full_roadmap, get_impact, list_nodes, search_codebase, search_decisions, add_phase | — |
70
+ | Developer | list_open_changesets, get_roadmap, get_node, get_impact, search_codebase, search_decisions, start_changeset, refresh_index, add_node | — |
71
+ | Reviewer | get_node, get_playbook | — |
72
+ | Tester | get_node (for tests field), list_nodes | project test command |
73
+ | Builder | — | linter, type checker, optional architecture verifier |
74
+ | Documenter | complete_changeset, update_node, update_next_action, update_phase_status, add_phase, write_session_log | — |
75
+
76
+ ---
77
+
78
+ ## Step 4: Reviewer Trigger Conditions
79
+
80
+ Reviewer is NOT always needed. Trigger it when ANY of:
81
+ - The file's graph node has `stability: high`
82
+ - The file's graph node has `do_not_revert: true`
83
+ - The file's graph node has non-empty `rules`
84
+ - The change affects a schema or event payload
85
+ - The change touches `.codevira/graph/` or `.codevira/roadmap.yaml`
86
+
87
+ Skip reviewer for: scaffolding, new test files, config-only changes, docs.
88
+
89
+ ---
90
+
91
+ ## Escalation Rule
92
+
93
+ If at any point the blast radius (`get_impact` result) returns more files than expected for the task type, escalate:
94
+ - `small_fix` → blast_radius > 3 → escalate to `medium_change`
95
+ - `medium_change` → blast_radius > 7 → escalate to `large_change`
96
+ - `large_change` → blast_radius > 15 → stop, call `get_roadmap` and document the scope before proceeding