bharatcode 0.1.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.
- bharatcode/__init__.py +13 -0
- bharatcode/agent.py +2088 -0
- bharatcode/commands.py +1072 -0
- bharatcode/config.py +93 -0
- bharatcode/coordinator.py +670 -0
- bharatcode/cost.py +77 -0
- bharatcode/diff.py +113 -0
- bharatcode/hooks.py +75 -0
- bharatcode/index.py +155 -0
- bharatcode/main.py +846 -0
- bharatcode/memory.py +286 -0
- bharatcode/permissions.py +99 -0
- bharatcode/project.py +179 -0
- bharatcode/session_storage.py +108 -0
- bharatcode/skills.py +1746 -0
- bharatcode/subagent.py +363 -0
- bharatcode/tools.py +1021 -0
- bharatcode/ui.py +72 -0
- bharatcode-0.1.0.dist-info/METADATA +150 -0
- bharatcode-0.1.0.dist-info/RECORD +23 -0
- bharatcode-0.1.0.dist-info/WHEEL +5 -0
- bharatcode-0.1.0.dist-info/entry_points.txt +3 -0
- bharatcode-0.1.0.dist-info/top_level.txt +1 -0
bharatcode/subagent.py
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sylithe Code Subagent System — parallel specialist agents that work while you work.
|
|
3
|
+
|
|
4
|
+
Each agent type has a dedicated role, restricted toolset, and purpose-built system prompt.
|
|
5
|
+
The main agent spawns them via the `spawn_agent` tool; they run, report back, and disappear.
|
|
6
|
+
|
|
7
|
+
Why this beats generic agent loops:
|
|
8
|
+
✦ Tool-level enforcement — Explorer literally cannot write files (tool not in its list)
|
|
9
|
+
✦ Role-specific prompts — each agent is primed to excel at one thing
|
|
10
|
+
✦ Shared file cache — subagents never re-read what the parent already read
|
|
11
|
+
✦ No recursion — subagents don't get spawn_agent, so no runaway chains
|
|
12
|
+
✦ Parallel capable — run_subagents_parallel() fires multiple agents in threads
|
|
13
|
+
|
|
14
|
+
Agent types:
|
|
15
|
+
explore — silent analyst, reads everything, writes nothing, reports findings
|
|
16
|
+
coder — dedicated implementer, full system access, ships complete code
|
|
17
|
+
verifier — ruthless auditor, finds bugs and security holes before users do
|
|
18
|
+
researcher — live web researcher, fetches real docs and real examples
|
|
19
|
+
general — all tools, no restrictions, deploy for anything
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import time
|
|
23
|
+
import threading
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from io import StringIO
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
from rich.console import Console
|
|
29
|
+
from rich.panel import Panel
|
|
30
|
+
from rich.text import Text
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
AGENT_TYPES: dict[str, dict] = {
|
|
34
|
+
"explore": {
|
|
35
|
+
"label": "Explorer",
|
|
36
|
+
"icon": "🔍",
|
|
37
|
+
"color": "cyan",
|
|
38
|
+
"tagline": "Reads everything, writes nothing — maps your codebase with precision",
|
|
39
|
+
"description": (
|
|
40
|
+
"Silently reads your entire codebase — every route, every model, every config. "
|
|
41
|
+
"Zero writes. Pure intelligence. Use before you build anything."
|
|
42
|
+
),
|
|
43
|
+
"allowed_tools": {"read_file", "glob", "grep", "list_dir", "web_fetch"},
|
|
44
|
+
"system_suffix": (
|
|
45
|
+
"\n\n## SUBAGENT ROLE: EXPLORER (READ-ONLY)\n"
|
|
46
|
+
"You are a silent intelligence agent. Your only job is to read, understand, and report.\n"
|
|
47
|
+
"Map the codebase with precision: file structure, key functions, API routes, data models, dependencies.\n"
|
|
48
|
+
"FORBIDDEN: write_file, edit_file, bash — these tools do not exist in your context.\n"
|
|
49
|
+
"ALLOWED: read_file, glob, grep, list_dir, web_fetch.\n"
|
|
50
|
+
"Output format: structured report with headings, bullet points, exact file paths and line numbers.\n"
|
|
51
|
+
"Be thorough. The main agent is counting on your findings to build correctly."
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
"coder": {
|
|
55
|
+
"label": "Coder",
|
|
56
|
+
"icon": "💻",
|
|
57
|
+
"color": "green",
|
|
58
|
+
"tagline": "Full system access — writes and ships complete production code, no stubs ever",
|
|
59
|
+
"description": (
|
|
60
|
+
"Dedicated implementer with full system access. Writes complete production code, "
|
|
61
|
+
"runs commands to verify it works. No stubs. No TODOs. Ships the real thing."
|
|
62
|
+
),
|
|
63
|
+
"allowed_tools": None, # all standard tools, spawn_agent excluded
|
|
64
|
+
"system_suffix": (
|
|
65
|
+
"\n\n## SUBAGENT ROLE: CODER\n"
|
|
66
|
+
"You are a specialist implementer. You have one job: build it completely and correctly.\n"
|
|
67
|
+
"Standards:\n"
|
|
68
|
+
" - Every function has a real implementation — no stubs, no TODOs, no 'add your code here'\n"
|
|
69
|
+
" - Every file is complete — not a skeleton, not a template, the actual working thing\n"
|
|
70
|
+
" - After writing, run bash to verify it compiles/starts/passes basic checks\n"
|
|
71
|
+
" - Use <<<FILE:>>> markers for files over 100 lines (avoids JSON truncation)\n"
|
|
72
|
+
"Shared-project coordination (other agents may build sibling parts):\n"
|
|
73
|
+
" - BEFORE writing code: read API_CONTRACT.md and BUILD_LOG.md in the project root\n"
|
|
74
|
+
" if they exist. Follow API_CONTRACT.md EXACTLY — same endpoint paths, same JSON\n"
|
|
75
|
+
" keys, same ports, same env var names. Never change the contract; report\n"
|
|
76
|
+
" mismatches in your final summary instead.\n"
|
|
77
|
+
" - AFTER changing files: APPEND a short summary to BUILD_LOG.md (write_file\n"
|
|
78
|
+
" mode='a', never overwrite): files you created + endpoints/exports/ports defined.\n"
|
|
79
|
+
"End your work with: exactly what you built, which files changed, and how to test it."
|
|
80
|
+
),
|
|
81
|
+
},
|
|
82
|
+
"verifier": {
|
|
83
|
+
"label": "Verifier",
|
|
84
|
+
"icon": "🛡️",
|
|
85
|
+
"color": "yellow",
|
|
86
|
+
"tagline": "Ruthless auditor — catches every bug and security hole before users do",
|
|
87
|
+
"description": (
|
|
88
|
+
"Ruthless code auditor — hunts down bugs, security holes, and broken logic "
|
|
89
|
+
"line by line. Your last line of defence before users find the problems."
|
|
90
|
+
),
|
|
91
|
+
"allowed_tools": {"read_file", "glob", "grep", "list_dir", "bash",
|
|
92
|
+
"web_fetch", "process_output", "process_kill"},
|
|
93
|
+
"system_suffix": (
|
|
94
|
+
"\n\n## SUBAGENT ROLE: VERIFIER\n"
|
|
95
|
+
"You are a ruthless code auditor. Read everything. Assume nothing is correct.\n"
|
|
96
|
+
"VERIFY BY RUNNING, not just by reading: start servers with "
|
|
97
|
+
"bash(run_in_background=true), check boot logs with process_output, hit the "
|
|
98
|
+
"health endpoint with web_fetch, then process_kill what you started.\n"
|
|
99
|
+
"Hunt for every category of problem:\n"
|
|
100
|
+
" BUGS — off-by-one, null dereference, wrong logic, edge cases not handled\n"
|
|
101
|
+
" SECURITY — SQL injection, XSS, missing auth checks, exposed secrets, no rate limiting\n"
|
|
102
|
+
" INCOMPLETE — stub functions, empty handlers, missing error handling, unvalidated input\n"
|
|
103
|
+
" BROKEN API — CORS misconfigured, wrong port, hardcoded URL, missing /api/ prefix\n"
|
|
104
|
+
" BAD SETUP — missing .env values, wrong package names, incorrect import paths\n"
|
|
105
|
+
"For every issue: file name + line number + what is wrong + exactly how to fix it.\n"
|
|
106
|
+
"Do not be polite. Be precise. The developer needs to know every problem before shipping."
|
|
107
|
+
),
|
|
108
|
+
},
|
|
109
|
+
"researcher": {
|
|
110
|
+
"label": "Researcher",
|
|
111
|
+
"icon": "📡",
|
|
112
|
+
"color": "magenta",
|
|
113
|
+
"tagline": "Fetches live docs and real API examples from the web — never guess again",
|
|
114
|
+
"description": (
|
|
115
|
+
"Fetches live documentation, real API specs, and working code examples straight "
|
|
116
|
+
"from the web. Never guess an API again — get the exact answer in seconds."
|
|
117
|
+
),
|
|
118
|
+
"allowed_tools": {"web_fetch", "read_file"},
|
|
119
|
+
"system_suffix": (
|
|
120
|
+
"\n\n## SUBAGENT ROLE: RESEARCHER\n"
|
|
121
|
+
"You are a live documentation agent. Your job is to find accurate, current information fast.\n"
|
|
122
|
+
"For each topic deliver:\n"
|
|
123
|
+
" 1. What it is and what problem it solves (2 sentences max)\n"
|
|
124
|
+
" 2. Exact installation command (pip install / npm install / etc.)\n"
|
|
125
|
+
" 3. Minimal working code example — real code, not pseudocode\n"
|
|
126
|
+
" 4. The most common pitfalls and how to avoid them\n"
|
|
127
|
+
" 5. Source URL (official docs preferred over Stack Overflow)\n"
|
|
128
|
+
"Fetch the official documentation. Don't rely on training data for version-sensitive APIs."
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
"general": {
|
|
132
|
+
"label": "Agent",
|
|
133
|
+
"icon": "🤖",
|
|
134
|
+
"color": "blue",
|
|
135
|
+
"tagline": "Full-capability autonomous agent — every tool, no restrictions, any task",
|
|
136
|
+
"description": (
|
|
137
|
+
"Full-capability autonomous agent with every tool available. "
|
|
138
|
+
"Deploy when the task spans multiple roles or doesn't fit a single specialist."
|
|
139
|
+
),
|
|
140
|
+
"allowed_tools": None,
|
|
141
|
+
"system_suffix": (
|
|
142
|
+
"\n\n## SUBAGENT ROLE: GENERAL PURPOSE\n"
|
|
143
|
+
"You are a full-capability agent. Use whatever tools the task needs.\n"
|
|
144
|
+
"Complete the task thoroughly and report back with clear results."
|
|
145
|
+
),
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class AgentResult:
|
|
152
|
+
agent_type: str
|
|
153
|
+
label: str
|
|
154
|
+
task: str
|
|
155
|
+
output: str
|
|
156
|
+
success: bool
|
|
157
|
+
duration: float = 0.0
|
|
158
|
+
error: Optional[str] = None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def run_subagent(
|
|
162
|
+
task: str,
|
|
163
|
+
agent_type: str = "general",
|
|
164
|
+
project_path: str = ".",
|
|
165
|
+
parent_system: str = None,
|
|
166
|
+
parent_file_cache: dict = None,
|
|
167
|
+
context_history: list = None,
|
|
168
|
+
) -> AgentResult:
|
|
169
|
+
"""
|
|
170
|
+
Run a specialized subagent synchronously.
|
|
171
|
+
Shows a clear header/footer around its output so the user can distinguish
|
|
172
|
+
the subagent's work from the main agent's work.
|
|
173
|
+
Shares parent's file_cache so files already read are never re-fetched.
|
|
174
|
+
Returns the agent's final text response.
|
|
175
|
+
"""
|
|
176
|
+
from .agent import run_agent, _build_system, console
|
|
177
|
+
|
|
178
|
+
info = AGENT_TYPES.get(agent_type, AGENT_TYPES["general"])
|
|
179
|
+
label = info["label"]
|
|
180
|
+
icon = info["icon"]
|
|
181
|
+
color = info["color"]
|
|
182
|
+
allowed = info["allowed_tools"]
|
|
183
|
+
|
|
184
|
+
base_system = parent_system or _build_system(project_path)
|
|
185
|
+
agent_system = base_system + info["system_suffix"]
|
|
186
|
+
|
|
187
|
+
agent_history = list(context_history or [])
|
|
188
|
+
|
|
189
|
+
task_preview = task[:72] + "..." if len(task) > 72 else task
|
|
190
|
+
console.print()
|
|
191
|
+
console.print(f" ┌{'─'*64}┐")
|
|
192
|
+
console.print(f" │ {icon} [bold {color}]Subagent: {label}[/bold {color}]"
|
|
193
|
+
+ " " * max(0, 53 - len(label)) + "│")
|
|
194
|
+
console.print(f" │ [dim]{task_preview:<62}[/dim] │")
|
|
195
|
+
console.print(f" └{'─'*64}┘")
|
|
196
|
+
console.print()
|
|
197
|
+
|
|
198
|
+
t0 = time.time()
|
|
199
|
+
output = ""
|
|
200
|
+
error = None
|
|
201
|
+
|
|
202
|
+
try:
|
|
203
|
+
output = run_agent(
|
|
204
|
+
task=task,
|
|
205
|
+
project_path=project_path,
|
|
206
|
+
auto_approve=True,
|
|
207
|
+
history=agent_history,
|
|
208
|
+
system_content=agent_system,
|
|
209
|
+
file_cache=parent_file_cache if parent_file_cache is not None else {},
|
|
210
|
+
allowed_tools=allowed,
|
|
211
|
+
silent=True,
|
|
212
|
+
) or ""
|
|
213
|
+
except Exception as exc:
|
|
214
|
+
error = str(exc)
|
|
215
|
+
output = f"[Subagent error: {error}]"
|
|
216
|
+
console.print(f" [red]Subagent {label} failed:[/red] {error}")
|
|
217
|
+
|
|
218
|
+
duration = time.time() - t0
|
|
219
|
+
ok_icon = "✅" if error is None else "❌"
|
|
220
|
+
|
|
221
|
+
console.print()
|
|
222
|
+
console.print(f" ┌{'─'*64}┐")
|
|
223
|
+
console.print(f" │ {ok_icon} [bold {color}]{label} done[/bold {color}] "
|
|
224
|
+
f"[dim]{duration:.1f}s[/dim]"
|
|
225
|
+
+ " " * max(0, 50 - len(label)) + "│")
|
|
226
|
+
console.print(f" └{'─'*64}┘")
|
|
227
|
+
console.print()
|
|
228
|
+
|
|
229
|
+
return AgentResult(
|
|
230
|
+
agent_type=agent_type,
|
|
231
|
+
label=label,
|
|
232
|
+
task=task,
|
|
233
|
+
output=output,
|
|
234
|
+
success=error is None,
|
|
235
|
+
duration=duration,
|
|
236
|
+
error=error,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def run_subagents_parallel(
|
|
241
|
+
tasks: list[dict],
|
|
242
|
+
project_path: str = ".",
|
|
243
|
+
parent_system: str = None,
|
|
244
|
+
parent_file_cache: dict = None,
|
|
245
|
+
) -> list[AgentResult]:
|
|
246
|
+
"""
|
|
247
|
+
Run multiple subagents in parallel threads.
|
|
248
|
+
Each agent gets its own StringIO console so output doesn't interleave.
|
|
249
|
+
After all complete, each agent's output is printed sequentially.
|
|
250
|
+
Returns results in the same order as input tasks.
|
|
251
|
+
|
|
252
|
+
Usage:
|
|
253
|
+
results = run_subagents_parallel([
|
|
254
|
+
{"task": "Analyze the backend routes", "agent_type": "explore"},
|
|
255
|
+
{"task": "Check frontend components", "agent_type": "explore"},
|
|
256
|
+
])
|
|
257
|
+
"""
|
|
258
|
+
from .agent import _build_system, run_agent, console
|
|
259
|
+
|
|
260
|
+
n = len(tasks)
|
|
261
|
+
results: list[Optional[AgentResult]] = [None] * n
|
|
262
|
+
buffers: list[StringIO] = [StringIO() for _ in range(n)]
|
|
263
|
+
threads: list[threading.Thread] = []
|
|
264
|
+
|
|
265
|
+
def worker(idx: int, spec: dict):
|
|
266
|
+
agent_type = spec.get("agent_type", "general")
|
|
267
|
+
task = spec.get("task", "")
|
|
268
|
+
info = AGENT_TYPES.get(agent_type, AGENT_TYPES["general"])
|
|
269
|
+
allowed = info["allowed_tools"]
|
|
270
|
+
base_system = parent_system or _build_system(project_path)
|
|
271
|
+
agent_sys = base_system + info["system_suffix"]
|
|
272
|
+
buf_console = Console(file=buffers[idx], width=100, highlight=False, markup=True)
|
|
273
|
+
|
|
274
|
+
t0 = time.time()
|
|
275
|
+
error = None
|
|
276
|
+
output = ""
|
|
277
|
+
try:
|
|
278
|
+
output = run_agent(
|
|
279
|
+
task=task,
|
|
280
|
+
project_path=project_path,
|
|
281
|
+
auto_approve=True,
|
|
282
|
+
history=[],
|
|
283
|
+
system_content=agent_sys,
|
|
284
|
+
file_cache=parent_file_cache if parent_file_cache is not None else {},
|
|
285
|
+
allowed_tools=allowed,
|
|
286
|
+
silent=True,
|
|
287
|
+
) or ""
|
|
288
|
+
except Exception as exc:
|
|
289
|
+
error = str(exc)
|
|
290
|
+
output = f"[Error: {error}]"
|
|
291
|
+
|
|
292
|
+
results[idx] = AgentResult(
|
|
293
|
+
agent_type=agent_type,
|
|
294
|
+
label=info["label"],
|
|
295
|
+
task=task,
|
|
296
|
+
output=output,
|
|
297
|
+
success=error is None,
|
|
298
|
+
duration=time.time() - t0,
|
|
299
|
+
error=error,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Print launch banner
|
|
303
|
+
console.print(f"\n [bold]Launching {n} parallel agents[/bold]")
|
|
304
|
+
for i, spec in enumerate(tasks):
|
|
305
|
+
info = AGENT_TYPES.get(spec.get("agent_type", "general"), AGENT_TYPES["general"])
|
|
306
|
+
console.print(
|
|
307
|
+
f" [{i+1}] {info['icon']} [{info['color']}]{info['label']}[/{info['color']}] "
|
|
308
|
+
f"[dim]{spec.get('task', '')[:60]}[/dim]"
|
|
309
|
+
)
|
|
310
|
+
console.print()
|
|
311
|
+
|
|
312
|
+
for i, spec in enumerate(tasks):
|
|
313
|
+
t = threading.Thread(target=worker, args=(i, spec), daemon=True)
|
|
314
|
+
threads.append(t)
|
|
315
|
+
t.start()
|
|
316
|
+
|
|
317
|
+
# Live status while waiting
|
|
318
|
+
start = time.time()
|
|
319
|
+
while any(t.is_alive() for t in threads):
|
|
320
|
+
done = sum(1 for r in results if r is not None)
|
|
321
|
+
elapsed = time.time() - start
|
|
322
|
+
time.sleep(0.5)
|
|
323
|
+
|
|
324
|
+
for t in threads:
|
|
325
|
+
t.join()
|
|
326
|
+
|
|
327
|
+
# Display each agent's buffered output
|
|
328
|
+
for i, result in enumerate(results):
|
|
329
|
+
if result is None:
|
|
330
|
+
continue
|
|
331
|
+
info = AGENT_TYPES.get(result.agent_type, AGENT_TYPES["general"])
|
|
332
|
+
color = info["color"]
|
|
333
|
+
icon = info["icon"]
|
|
334
|
+
ok = "✅" if result.success else "❌"
|
|
335
|
+
|
|
336
|
+
console.print(f"\n {'═'*64}")
|
|
337
|
+
console.print(
|
|
338
|
+
f" {ok} {icon} [bold {color}]{result.label}[/bold {color}] "
|
|
339
|
+
f"[dim]completed in {result.duration:.1f}s[/dim]"
|
|
340
|
+
)
|
|
341
|
+
console.print(f" {'═'*64}\n")
|
|
342
|
+
|
|
343
|
+
# Print the buffered output
|
|
344
|
+
buf_text = buffers[i].getvalue()
|
|
345
|
+
if buf_text.strip():
|
|
346
|
+
console.print(buf_text)
|
|
347
|
+
|
|
348
|
+
console.print(f"\n [bold]All {n} agents done.[/bold] "
|
|
349
|
+
f"[dim]Total: {time.time() - start:.1f}s[/dim]\n")
|
|
350
|
+
|
|
351
|
+
return [r for r in results if r is not None]
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def format_results_for_model(results: list[AgentResult]) -> str:
|
|
355
|
+
"""Format multiple agent results as a string for the main model's context."""
|
|
356
|
+
parts = []
|
|
357
|
+
for r in results:
|
|
358
|
+
parts.append(
|
|
359
|
+
f"=== {r.label} ({r.agent_type}) — {r.duration:.1f}s ===\n"
|
|
360
|
+
f"Task: {r.task}\n\n"
|
|
361
|
+
f"{r.output}\n"
|
|
362
|
+
)
|
|
363
|
+
return "\n".join(parts)
|