claxcode 0.1.1__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 (60) hide show
  1. claxcode/__init__.py +3 -0
  2. claxcode/api/__init__.py +1 -0
  3. claxcode/api/chat.py +51 -0
  4. claxcode/api/session.py +66 -0
  5. claxcode/api/subagents.py +82 -0
  6. claxcode/api/tools.py +38 -0
  7. claxcode/cli.py +1214 -0
  8. claxcode/cli_completion.py +174 -0
  9. claxcode/config.py +54 -0
  10. claxcode/core/__init__.py +1 -0
  11. claxcode/core/agent.py +327 -0
  12. claxcode/core/checkpoint.py +100 -0
  13. claxcode/core/context.py +114 -0
  14. claxcode/core/models.py +446 -0
  15. claxcode/extensions/__init__.py +1 -0
  16. claxcode/extensions/hooks.py +131 -0
  17. claxcode/extensions/mcp.py +13 -0
  18. claxcode/extensions/plugins.py +10 -0
  19. claxcode/extensions/skills.py +205 -0
  20. claxcode/main.py +55 -0
  21. claxcode/memory/__init__.py +23 -0
  22. claxcode/memory/auto_memory.py +68 -0
  23. claxcode/memory/long_term.py +196 -0
  24. claxcode/memory/project_context.py +20 -0
  25. claxcode/memory/project_memory.py +194 -0
  26. claxcode/memory/session_store.py +206 -0
  27. claxcode/models/__init__.py +1 -0
  28. claxcode/models/session.py +25 -0
  29. claxcode/models/subagent.py +25 -0
  30. claxcode/models/tool.py +24 -0
  31. claxcode/security/__init__.py +1 -0
  32. claxcode/security/audit.py +54 -0
  33. claxcode/security/permissions.py +29 -0
  34. claxcode/skills/bug-fix/SKILL.md +33 -0
  35. claxcode/skills/code-review/SKILL.md +35 -0
  36. claxcode/skills/python-refactor/SKILL.md +22 -0
  37. claxcode/skills/task-workflow/SKILL.md +38 -0
  38. claxcode/subagents/__init__.py +1 -0
  39. claxcode/subagents/base.py +108 -0
  40. claxcode/subagents/executor.py +16 -0
  41. claxcode/subagents/explore.py +25 -0
  42. claxcode/subagents/planner.py +25 -0
  43. claxcode/tools/__init__.py +14 -0
  44. claxcode/tools/admin.py +170 -0
  45. claxcode/tools/base.py +19 -0
  46. claxcode/tools/browser.py +499 -0
  47. claxcode/tools/code_intelligence.py +68 -0
  48. claxcode/tools/custom_example.py +76 -0
  49. claxcode/tools/execution.py +114 -0
  50. claxcode/tools/file_operations.py +287 -0
  51. claxcode/tools/memory_tool.py +29 -0
  52. claxcode/tools/registry.py +35 -0
  53. claxcode/tools/search.py +157 -0
  54. claxcode/tools/ssh.py +534 -0
  55. claxcode/tools/web.py +83 -0
  56. claxcode-0.1.1.dist-info/METADATA +359 -0
  57. claxcode-0.1.1.dist-info/RECORD +60 -0
  58. claxcode-0.1.1.dist-info/WHEEL +4 -0
  59. claxcode-0.1.1.dist-info/entry_points.txt +3 -0
  60. claxcode-0.1.1.dist-info/licenses/LICENSE +201 -0
claxcode/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """ClaxCode — An agentic coding assistant powered by FastAPI."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1 @@
1
+ """API routes for ClaxCode."""
claxcode/api/chat.py ADDED
@@ -0,0 +1,51 @@
1
+ """Chat and streaming API."""
2
+
3
+ import json
4
+ from uuid import UUID
5
+
6
+ from fastapi import APIRouter, Depends, HTTPException
7
+ from fastapi.responses import StreamingResponse
8
+ from sqlalchemy import select
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+
11
+ from claxcode.core.agent import Agent
12
+ from claxcode.memory.session_store import (
13
+ SessionORM,
14
+ get_db_session,
15
+ get_session_messages,
16
+ save_session_messages,
17
+ )
18
+ from claxcode.models.session import ChatRequest
19
+
20
+ router = APIRouter()
21
+
22
+
23
+ @router.post("/{session_id}/chat")
24
+ async def chat(
25
+ session_id: UUID,
26
+ request: ChatRequest,
27
+ db: AsyncSession = Depends(get_db_session),
28
+ ):
29
+ """Stream chat events via SSE."""
30
+ result = await db.execute(select(SessionORM).where(SessionORM.id == str(session_id)))
31
+ row = result.scalar_one_or_none()
32
+ if row is None:
33
+ raise HTTPException(status_code=404, detail="会话不存在")
34
+
35
+ agent = Agent(
36
+ session_id=session_id,
37
+ project_path=row.project_path,
38
+ permission_mode=row.permission_mode,
39
+ )
40
+ agent.context.restore(await get_session_messages(session_id))
41
+
42
+ async def event_stream():
43
+ async for event in agent.run(request.message):
44
+ yield f"data: {json.dumps(event)}\n\n"
45
+ # Persist the conversation (minus the system prompt) for the next request
46
+ await save_session_messages(session_id, agent.context.messages[1:])
47
+
48
+ return StreamingResponse(
49
+ event_stream(),
50
+ media_type="text/event-stream",
51
+ )
@@ -0,0 +1,66 @@
1
+ """Session management API."""
2
+
3
+ from uuid import UUID, uuid4
4
+
5
+ from fastapi import APIRouter, Depends, HTTPException
6
+ from sqlalchemy import select
7
+ from sqlalchemy.ext.asyncio import AsyncSession
8
+
9
+ from claxcode.memory.session_store import SessionORM, get_db_session
10
+ from claxcode.models.session import Session, SessionCreate
11
+
12
+ router = APIRouter()
13
+
14
+
15
+ def _orm_to_model(orm: SessionORM) -> Session:
16
+ return Session(
17
+ id=UUID(orm.id),
18
+ name=orm.name,
19
+ project_path=orm.project_path,
20
+ permission_mode=orm.permission_mode,
21
+ created_at=orm.created_at,
22
+ updated_at=orm.updated_at,
23
+ )
24
+
25
+
26
+ @router.post("", response_model=Session)
27
+ async def create_session(
28
+ data: SessionCreate,
29
+ db: AsyncSession = Depends(get_db_session),
30
+ ):
31
+ session_orm = SessionORM(
32
+ id=str(uuid4()),
33
+ name=data.name,
34
+ project_path=data.project_path,
35
+ permission_mode=data.permission_mode,
36
+ )
37
+ db.add(session_orm)
38
+ await db.commit()
39
+ await db.refresh(session_orm)
40
+ return _orm_to_model(session_orm)
41
+
42
+
43
+ @router.get("", response_model=list[Session])
44
+ async def list_sessions(db: AsyncSession = Depends(get_db_session)):
45
+ result = await db.execute(select(SessionORM).order_by(SessionORM.created_at.desc()))
46
+ return [_orm_to_model(row) for row in result.scalars().all()]
47
+
48
+
49
+ @router.get("/{session_id}", response_model=Session)
50
+ async def get_session(session_id: UUID, db: AsyncSession = Depends(get_db_session)):
51
+ result = await db.execute(select(SessionORM).where(SessionORM.id == str(session_id)))
52
+ row = result.scalar_one_or_none()
53
+ if row is None:
54
+ raise HTTPException(status_code=404, detail="会话不存在")
55
+ return _orm_to_model(row)
56
+
57
+
58
+ @router.delete("/{session_id}", status_code=204)
59
+ async def delete_session(session_id: UUID, db: AsyncSession = Depends(get_db_session)):
60
+ result = await db.execute(select(SessionORM).where(SessionORM.id == str(session_id)))
61
+ row = result.scalar_one_or_none()
62
+ if row is None:
63
+ raise HTTPException(status_code=404, detail="会话不存在")
64
+ await db.delete(row)
65
+ await db.commit()
66
+ return None
@@ -0,0 +1,82 @@
1
+ """Subagent management API."""
2
+
3
+ import asyncio
4
+ import json
5
+ from uuid import UUID
6
+
7
+ from fastapi import APIRouter, HTTPException
8
+ from fastapi.responses import StreamingResponse
9
+
10
+ from claxcode.memory.session_store import (
11
+ create_subagent_task,
12
+ finish_subagent_task,
13
+ get_subagent_task,
14
+ )
15
+ from claxcode.models.subagent import SubagentStatus, SubagentTask
16
+ from claxcode.subagents.executor import ExecutorSubagent
17
+ from claxcode.subagents.explore import ExploreSubagent
18
+ from claxcode.subagents.planner import PlannerSubagent
19
+
20
+ router = APIRouter()
21
+
22
+
23
+ def _spawn_subagent(session_id: UUID, agent_type: str, instruction: str):
24
+ if agent_type == "explore":
25
+ return ExploreSubagent(session_id, instruction)
26
+ if agent_type == "planner":
27
+ return PlannerSubagent(session_id, instruction)
28
+ if agent_type == "executor":
29
+ return ExecutorSubagent(session_id, instruction)
30
+ raise ValueError(f"未知的代理类型: {agent_type}")
31
+
32
+
33
+ @router.post("", response_model=SubagentTask)
34
+ async def spawn_subagent(
35
+ session_id: UUID,
36
+ instruction: str,
37
+ agent_type: str = "explore",
38
+ ):
39
+ task = SubagentTask(
40
+ session_id=session_id,
41
+ agent_type=agent_type,
42
+ instruction=instruction,
43
+ status=SubagentStatus.RUNNING,
44
+ )
45
+ await create_subagent_task(task)
46
+
47
+ async def run_task():
48
+ results = []
49
+ try:
50
+ agent = _spawn_subagent(session_id, agent_type, instruction)
51
+ async for event in agent.run():
52
+ if event.get("type") == "result":
53
+ results.append(event.get("content", ""))
54
+ await finish_subagent_task(task.id, SubagentStatus.COMPLETED, "\n".join(results))
55
+ except Exception as exc:
56
+ await finish_subagent_task(task.id, SubagentStatus.FAILED, str(exc))
57
+
58
+ asyncio.create_task(run_task())
59
+ return task
60
+
61
+
62
+ @router.get("/{task_id}", response_model=SubagentTask)
63
+ async def get_subagent(task_id: UUID):
64
+ task = await get_subagent_task(task_id)
65
+ if task is None:
66
+ raise HTTPException(status_code=404, detail="子代理任务不存在")
67
+ return task
68
+
69
+
70
+ @router.post("/{task_id}/stream")
71
+ async def stream_subagent(task_id: UUID):
72
+ task = await get_subagent_task(task_id)
73
+ if task is None:
74
+ raise HTTPException(status_code=404, detail="子代理任务不存在")
75
+
76
+ async def event_stream():
77
+ agent = _spawn_subagent(task.session_id, task.agent_type, task.instruction)
78
+ async for event in agent.run():
79
+ yield f"data: {json.dumps(event)}\n\n"
80
+ yield "data: {\"type\": \"done\"}\n\n"
81
+
82
+ return StreamingResponse(event_stream(), media_type="text/event-stream")
claxcode/api/tools.py ADDED
@@ -0,0 +1,38 @@
1
+ """Tool listing and invocation API."""
2
+
3
+ from fastapi import APIRouter
4
+
5
+ from claxcode.models.tool import ToolInfo, ToolInvokeRequest, ToolInvokeResponse
6
+ from claxcode.tools.registry import registry
7
+
8
+ router = APIRouter()
9
+
10
+
11
+ @router.get("", response_model=list[ToolInfo])
12
+ async def list_tools():
13
+ return [
14
+ ToolInfo(
15
+ name=tool.name,
16
+ description=tool.description,
17
+ input_schema=tool.input_schema,
18
+ category=tool.category,
19
+ required_permissions=tool.required_permissions,
20
+ )
21
+ for tool in registry.list_tools()
22
+ ]
23
+
24
+
25
+ @router.post("/invoke", response_model=ToolInvokeResponse)
26
+ async def invoke_tool(request: ToolInvokeRequest):
27
+ tool = registry.get(request.name)
28
+ if tool is None:
29
+ return ToolInvokeResponse(
30
+ name=request.name,
31
+ output=f"工具 '{request.name}' 不存在",
32
+ error=True,
33
+ )
34
+ try:
35
+ output = await tool.execute(**request.arguments)
36
+ return ToolInvokeResponse(name=tool.name, output=str(output))
37
+ except Exception as exc:
38
+ return ToolInvokeResponse(name=tool.name, output=str(exc), error=True)