god-code 0.4.1__tar.gz → 0.5.0__tar.gz
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.
- god_code-0.5.0/AGENTS.md +215 -0
- {god_code-0.4.1 → god_code-0.5.0}/CHANGELOG.md +12 -0
- {god_code-0.4.1 → god_code-0.5.0}/PKG-INFO +1 -1
- {god_code-0.4.1 → god_code-0.5.0}/README.md +21 -0
- god_code-0.5.0/godot_agent/agents/__init__.py +12 -0
- god_code-0.5.0/godot_agent/agents/configs.py +80 -0
- god_code-0.5.0/godot_agent/agents/dispatcher.py +152 -0
- god_code-0.5.0/godot_agent/agents/results.py +15 -0
- god_code-0.5.0/godot_agent/cli.py +1068 -0
- god_code-0.5.0/godot_agent/godot/impact_analysis.py +148 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/scene_writer.py +3 -0
- god_code-0.5.0/godot_agent/llm/adapters/__init__.py +21 -0
- god_code-0.5.0/godot_agent/llm/adapters/anthropic.py +22 -0
- god_code-0.5.0/godot_agent/llm/adapters/base.py +31 -0
- god_code-0.5.0/godot_agent/llm/adapters/openai.py +32 -0
- god_code-0.5.0/godot_agent/llm/client.py +98 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/llm/streaming.py +2 -2
- god_code-0.5.0/godot_agent/llm/types.py +175 -0
- god_code-0.5.0/godot_agent/prompts/assembler.py +184 -0
- god_code-0.5.0/godot_agent/prompts/image_templates.py +102 -0
- god_code-0.5.0/godot_agent/prompts/system.py +59 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/runtime/config.py +15 -2
- god_code-0.5.0/godot_agent/runtime/context_manager.py +231 -0
- god_code-0.5.0/godot_agent/runtime/design_memory.py +140 -0
- god_code-0.5.0/godot_agent/runtime/engine.py +710 -0
- god_code-0.5.0/godot_agent/runtime/events.py +11 -0
- god_code-0.5.0/godot_agent/runtime/gameplay_reviewer.py +132 -0
- god_code-0.5.0/godot_agent/runtime/modes.py +132 -0
- god_code-0.5.0/godot_agent/runtime/playtest_harness.py +142 -0
- god_code-0.5.0/godot_agent/runtime/providers.py +165 -0
- god_code-0.5.0/godot_agent/runtime/quality_gate.py +316 -0
- god_code-0.5.0/godot_agent/runtime/reviewer.py +228 -0
- god_code-0.5.0/godot_agent/runtime/runtime_bridge.py +74 -0
- god_code-0.5.0/godot_agent/runtime/scenario_specs/hud_feedback.json +7 -0
- god_code-0.5.0/godot_agent/runtime/scenario_specs/player_movement.json +8 -0
- god_code-0.5.0/godot_agent/runtime/scenario_specs/scene_transition.json +7 -0
- god_code-0.5.0/godot_agent/runtime/session.py +132 -0
- god_code-0.5.0/godot_agent/security/__init__.py +1 -0
- god_code-0.5.0/godot_agent/security/classifier.py +87 -0
- god_code-0.5.0/godot_agent/security/hooks.py +140 -0
- god_code-0.5.0/godot_agent/security/policies.py +89 -0
- god_code-0.5.0/godot_agent/security/protected_paths.py +47 -0
- god_code-0.5.0/godot_agent/security/tool_pipeline.py +89 -0
- god_code-0.5.0/godot_agent/testing/__init__.py +1 -0
- god_code-0.5.0/godot_agent/testing/scenario_runner.py +41 -0
- god_code-0.5.0/godot_agent/tools/analysis_tools.py +157 -0
- god_code-0.5.0/godot_agent/tools/base.py +54 -0
- god_code-0.5.0/godot_agent/tools/editor_bridge.py +79 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/file_ops.py +18 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/git.py +10 -1
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/godot_cli.py +12 -1
- god_code-0.5.0/godot_agent/tools/image_gen.py +180 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/list_dir.py +10 -3
- god_code-0.5.0/godot_agent/tools/memory_tool.py +78 -0
- god_code-0.5.0/godot_agent/tools/registry.py +61 -0
- god_code-0.5.0/godot_agent/tools/scene_tools.py +271 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/screenshot.py +11 -1
- god_code-0.5.0/godot_agent/tools/script_tools.py +136 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/search.py +19 -2
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/shell.py +6 -1
- god_code-0.5.0/godot_agent/tools/web_search.py +89 -0
- god_code-0.5.0/godot_agent/tui/display.py +426 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tui/input_handler.py +65 -11
- {god_code-0.4.1 → god_code-0.5.0}/pyproject.toml +1 -1
- god_code-0.5.0/tests/agents/test_dispatcher.py +97 -0
- god_code-0.5.0/tests/agents/test_playtest_analyst.py +37 -0
- god_code-0.5.0/tests/e2e/test_planner_worker_reviewer_flow.py +136 -0
- god_code-0.5.0/tests/e2e/test_policy_enforcement.py +112 -0
- god_code-0.5.0/tests/e2e/test_scenario_runner.py +16 -0
- god_code-0.5.0/tests/godot/test_impact_analysis.py +39 -0
- god_code-0.5.0/tests/llm/test_adapters.py +37 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/llm/test_client.py +67 -2
- god_code-0.5.0/tests/prompts/test_prompt_assembler.py +66 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/prompts/test_system_prompt.py +5 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/runtime/test_config.py +26 -4
- {god_code-0.4.1 → god_code-0.5.0}/tests/runtime/test_context_manager.py +6 -3
- god_code-0.5.0/tests/runtime/test_design_memory.py +48 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/runtime/test_engine.py +44 -0
- god_code-0.5.0/tests/runtime/test_gameplay_reviewer.py +32 -0
- god_code-0.5.0/tests/runtime/test_mode_restrictions.py +105 -0
- god_code-0.5.0/tests/runtime/test_multi_agent_flow.py +152 -0
- god_code-0.5.0/tests/runtime/test_playtest_harness.py +28 -0
- god_code-0.5.0/tests/runtime/test_quality_gate.py +37 -0
- god_code-0.5.0/tests/runtime/test_reviewer.py +50 -0
- god_code-0.5.0/tests/runtime/test_runtime_bridge.py +30 -0
- god_code-0.5.0/tests/runtime/test_session.py +63 -0
- god_code-0.5.0/tests/security/test_classifier.py +42 -0
- god_code-0.5.0/tests/security/test_hooks.py +122 -0
- god_code-0.5.0/tests/security/test_permissions.py +104 -0
- god_code-0.5.0/tests/security/test_tool_pipeline.py +121 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/test_e2e.py +2 -0
- god_code-0.5.0/tests/test_runtime_switch_commands.py +26 -0
- god_code-0.5.0/tests/tools/test_analysis_tools.py +47 -0
- god_code-0.5.0/tests/tools/test_editor_bridge.py +46 -0
- god_code-0.5.0/tests/tools/test_memory_tool.py +19 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_registry.py +5 -0
- god_code-0.5.0/tests/tools/test_scene_tools.py +66 -0
- god_code-0.5.0/tests/tools/test_script_tools.py +47 -0
- god_code-0.5.0/tests/tui/test_input_handler.py +19 -0
- god_code-0.4.1/godot_agent/cli.py +0 -602
- god_code-0.4.1/godot_agent/llm/client.py +0 -189
- god_code-0.4.1/godot_agent/prompts/system.py +0 -114
- god_code-0.4.1/godot_agent/runtime/context_manager.py +0 -135
- god_code-0.4.1/godot_agent/runtime/engine.py +0 -234
- god_code-0.4.1/godot_agent/runtime/session.py +0 -58
- god_code-0.4.1/godot_agent/tools/base.py +0 -30
- god_code-0.4.1/godot_agent/tools/registry.py +0 -29
- god_code-0.4.1/godot_agent/tui/display.py +0 -190
- {god_code-0.4.1 → god_code-0.5.0}/.github/workflows/publish.yml +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/.gitignore +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/CLAUDE.md +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/CONTRIBUTING.md +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/LICENSE +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/collision_planner.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/consistency_checker.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/dependency_graph.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/gdscript_linter.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/pattern_advisor.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/project.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/resource_validator.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/scene_parser.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/godot/tscn_validator.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/llm/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/llm/vision.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/prompts/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/prompts/build_discipline.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/prompts/godot_playbook.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/prompts/knowledge_selector.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/py.typed +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/runtime/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/runtime/auth.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/runtime/error_loop.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/runtime/oauth.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tools/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/godot_agent/tui/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_collision_planner.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_consistency.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_dependency_graph.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_linter.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_pattern_advisor.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_project.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_resource_validator.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_scene_parser.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_scene_writer.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/godot/test_tscn_validator.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/llm/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/llm/test_vision.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/prompts/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/prompts/test_knowledge_selector.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/runtime/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/runtime/test_error_loop.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/__init__.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_file_ops.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_git.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_godot_cli.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_list_dir.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_search.py +0 -0
- {god_code-0.4.1 → god_code-0.5.0}/tests/tools/test_shell.py +0 -0
god_code-0.5.0/AGENTS.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# AGENTS.md — God Code Development Guide
|
|
2
|
+
|
|
3
|
+
> AI coding agent specialized for Godot 4.4 game development. This file guides Codex when working on the god-code codebase itself.
|
|
4
|
+
|
|
5
|
+
## Project Identity
|
|
6
|
+
|
|
7
|
+
**god-code** — a Python CLI agent that understands GDScript, .tscn scenes, collision layers, and Godot architecture patterns. It calls LLM APIs with Godot-specific tools and enforces incremental build-and-verify discipline.
|
|
8
|
+
|
|
9
|
+
**PyPI**: `pip install god-code`
|
|
10
|
+
**GitHub**: https://github.com/888wing/god-code
|
|
11
|
+
**License**: GPL-3.0
|
|
12
|
+
|
|
13
|
+
## Tech Stack
|
|
14
|
+
|
|
15
|
+
- **Language**: Python 3.12+
|
|
16
|
+
- **CLI**: click
|
|
17
|
+
- **HTTP**: httpx (async)
|
|
18
|
+
- **Models**: pydantic v2 (tool schemas, config)
|
|
19
|
+
- **Image**: Pillow (screenshot encoding)
|
|
20
|
+
- **Test**: pytest + pytest-asyncio
|
|
21
|
+
- **Build**: hatchling
|
|
22
|
+
- **CI**: GitHub Actions (auto-publish to PyPI on tag)
|
|
23
|
+
|
|
24
|
+
## Current Version: 0.1.0
|
|
25
|
+
|
|
26
|
+
**Completed**: CLI (ask/chat/info/setup/login/logout/status), 10 tools, LLM client with OpenAI-compatible API, Godot Playbook knowledge system (17 sections), .tscn parser/writer/validator, GDScript linter, collision planner, dependency graph, pattern advisor, consistency checker, error loop, context manager, first-run setup wizard, PyPI published.
|
|
27
|
+
|
|
28
|
+
**157 tests**, ~4,000 lines production code.
|
|
29
|
+
|
|
30
|
+
## Architecture
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
godot_agent/
|
|
34
|
+
├── cli.py # Click CLI entry point + setup wizard
|
|
35
|
+
├── runtime/
|
|
36
|
+
│ ├── engine.py # Conversation loop (tool calling + context compaction + error loop)
|
|
37
|
+
│ ├── config.py # AgentConfig (pydantic) + load from file/env
|
|
38
|
+
│ ├── session.py # Session persistence (.agent_sessions/*.json)
|
|
39
|
+
│ ├── oauth.py # Codex refresh token flow
|
|
40
|
+
│ ├── error_loop.py # Godot output parsing + fix suggestions
|
|
41
|
+
│ ├── context_manager.py # Message compaction + file relevance scoring
|
|
42
|
+
│ └── auth.py # AuthContext dataclass
|
|
43
|
+
├── llm/
|
|
44
|
+
│ ├── client.py # LLMClient (OpenAI-compatible, retry, content filter handling)
|
|
45
|
+
│ ├── streaming.py # SSE streaming
|
|
46
|
+
│ └── vision.py # Image → base64 encoding
|
|
47
|
+
├── tools/ # 10 function-calling tools
|
|
48
|
+
│ ├── base.py # BaseTool ABC + ToolResult
|
|
49
|
+
│ ├── registry.py # ToolRegistry (register, execute, to_openai_tools)
|
|
50
|
+
│ ├── file_ops.py # read_file, write_file, edit_file (path-contained)
|
|
51
|
+
│ ├── search.py # grep, glob
|
|
52
|
+
│ ├── list_dir.py # list_dir
|
|
53
|
+
│ ├── git.py # git (shlex-parsed)
|
|
54
|
+
│ ├── shell.py # run_shell (blocked patterns)
|
|
55
|
+
│ ├── godot_cli.py # run_godot (GUT, validate, output parser)
|
|
56
|
+
│ └── screenshot.py # screenshot_scene (headless)
|
|
57
|
+
├── godot/ # Godot-specific analysis
|
|
58
|
+
│ ├── project.py # project.godot parser → GodotProject
|
|
59
|
+
│ ├── scene_parser.py # .tscn → TscnScene (nodes, resources, connections)
|
|
60
|
+
│ ├── scene_writer.py # add_node, set_property, remove_node, add_connection
|
|
61
|
+
│ ├── tscn_validator.py # Format validation + auto-fix ordering
|
|
62
|
+
│ ├── gdscript_linter.py # Naming, ordering, type annotations, anti-patterns
|
|
63
|
+
│ ├── collision_planner.py # Standard 8-layer scheme
|
|
64
|
+
│ ├── consistency_checker.py # Cross-file collision/signal/resource/group checks
|
|
65
|
+
│ ├── dependency_graph.py # Project-wide scene→script→resource graph
|
|
66
|
+
│ ├── pattern_advisor.py # Object pool, component, state machine suggestions
|
|
67
|
+
│ └── resource_validator.py # res:// path existence check
|
|
68
|
+
└── prompts/
|
|
69
|
+
├── system.py # Builds system prompt (identity + knowledge + discipline + context)
|
|
70
|
+
├── godot_playbook.py # 17 indexed knowledge sections
|
|
71
|
+
├── knowledge_selector.py # Context-aware section scoring + injection
|
|
72
|
+
└── build_discipline.py # Incremental build-and-verify rules
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Key Patterns
|
|
76
|
+
|
|
77
|
+
### Tool System
|
|
78
|
+
Every tool inherits `BaseTool` with pydantic `Input`/`Output` models. The registry auto-generates OpenAI function calling schemas.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
class MyTool(BaseTool):
|
|
82
|
+
name = "my_tool"
|
|
83
|
+
description = "Does something"
|
|
84
|
+
class Input(BaseModel):
|
|
85
|
+
param: str = Field(description="...")
|
|
86
|
+
class Output(BaseModel):
|
|
87
|
+
result: str
|
|
88
|
+
async def execute(self, input: Input) -> ToolResult:
|
|
89
|
+
return ToolResult(output=self.Output(result="done"))
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Register in `cli.py:build_registry()`.
|
|
93
|
+
|
|
94
|
+
### Path Containment
|
|
95
|
+
`file_ops.py` has a module-level `_project_root` set by CLI on startup. All read/write/edit operations validate paths are within project root. `set_project_root()` is called in `build_engine()`.
|
|
96
|
+
|
|
97
|
+
### Engine Flow
|
|
98
|
+
```
|
|
99
|
+
user message → _maybe_compact() → client.chat() → tool_calls?
|
|
100
|
+
→ yes: execute tools → _post_tool_validate() → loop
|
|
101
|
+
→ no: return content
|
|
102
|
+
```
|
|
103
|
+
- Compaction triggers at ~80K estimated tokens
|
|
104
|
+
- Post-tool validation runs `godot --headless --quit` after write_file/edit_file
|
|
105
|
+
- Max tool rounds default: 20
|
|
106
|
+
|
|
107
|
+
### Knowledge Injection
|
|
108
|
+
`knowledge_selector.py` scores Playbook sections by keyword overlap with user prompt + file extensions. Top 4 sections injected (~2K tokens instead of full ~15K).
|
|
109
|
+
|
|
110
|
+
### Error Loop Integration
|
|
111
|
+
After any file-mutating tool call, engine runs Godot headless validation. If errors found, injects a system message telling the LLM to fix them before proceeding.
|
|
112
|
+
|
|
113
|
+
## Development Rules
|
|
114
|
+
|
|
115
|
+
### CRITICAL: Read Before Modify
|
|
116
|
+
Never modify files without reading them first. This applies to both god-code source AND Godot project files the agent operates on.
|
|
117
|
+
|
|
118
|
+
### Adding a New Tool
|
|
119
|
+
1. Create `godot_agent/tools/your_tool.py` inheriting `BaseTool`
|
|
120
|
+
2. Define `Input` and `Output` as pydantic `BaseModel`
|
|
121
|
+
3. Implement `async execute()`
|
|
122
|
+
4. Register in `cli.py:build_registry()`
|
|
123
|
+
5. Add tests in `tests/tools/test_your_tool.py`
|
|
124
|
+
6. Update README tool list
|
|
125
|
+
|
|
126
|
+
### Adding Godot Knowledge
|
|
127
|
+
Edit `godot_agent/prompts/godot_playbook.py`:
|
|
128
|
+
- Each section: `(title, [keywords], content_string)`
|
|
129
|
+
- Keywords drive auto-selection in `knowledge_selector.py`
|
|
130
|
+
- Keep sections concise (~200 tokens each)
|
|
131
|
+
|
|
132
|
+
### Adding Godot Analysis
|
|
133
|
+
New analyzers go in `godot_agent/godot/`. Follow existing patterns:
|
|
134
|
+
- Pure functions, no side effects
|
|
135
|
+
- Return structured dataclasses
|
|
136
|
+
- Include `format_*()` for LLM-readable output
|
|
137
|
+
|
|
138
|
+
### Config
|
|
139
|
+
`~/.config/god-code/config.json` with `GODOT_AGENT_*` env overrides:
|
|
140
|
+
|
|
141
|
+
| Field | Default | Env Var |
|
|
142
|
+
|-------|---------|---------|
|
|
143
|
+
| api_key | "" | GODOT_AGENT_API_KEY |
|
|
144
|
+
| base_url | https://api.openai.com/v1 | GODOT_AGENT_BASE_URL |
|
|
145
|
+
| model | gpt-5.4 | GODOT_AGENT_MODEL |
|
|
146
|
+
| godot_path | godot | GODOT_AGENT_GODOT_PATH |
|
|
147
|
+
| oauth_token | null | GODOT_AGENT_OAUTH_TOKEN |
|
|
148
|
+
|
|
149
|
+
OAuth client_id configurable via `GODOT_AGENT_OAUTH_CLIENT_ID`.
|
|
150
|
+
|
|
151
|
+
## Testing
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Full suite (157 tests)
|
|
155
|
+
python -m pytest tests/ -v
|
|
156
|
+
|
|
157
|
+
# Specific module
|
|
158
|
+
python -m pytest tests/godot/test_tscn_validator.py -v
|
|
159
|
+
|
|
160
|
+
# E2E with mocked LLM
|
|
161
|
+
python -m pytest tests/test_e2e.py -v
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
All tests use `tmp_path` fixture for isolation. LLM tests mock `httpx` responses. No real API calls in tests.
|
|
165
|
+
|
|
166
|
+
## Release Process
|
|
167
|
+
|
|
168
|
+
Automated via GitHub Actions:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# 1. Bump version in pyproject.toml
|
|
172
|
+
# 2. Commit + tag
|
|
173
|
+
git commit -am "release: v0.2.0"
|
|
174
|
+
git tag v0.2.0
|
|
175
|
+
git push && git push --tags
|
|
176
|
+
# → GitHub Actions runs tests → publishes to PyPI
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Requires `PYPI_API_TOKEN` in GitHub repo secrets.
|
|
180
|
+
|
|
181
|
+
## Security Model
|
|
182
|
+
|
|
183
|
+
- **File ops**: Restricted to project root (symlink-aware)
|
|
184
|
+
- **Shell**: Blocked patterns (rm -rf /, sudo, curl|sh, etc.)
|
|
185
|
+
- **Git**: shlex.split() for proper argument parsing
|
|
186
|
+
- **API keys**: Stored with 600 permissions, masked in CLI output
|
|
187
|
+
- **Content filter**: 400 errors retried, graceful fallback message
|
|
188
|
+
|
|
189
|
+
## Roadmap
|
|
190
|
+
|
|
191
|
+
### Phase 2: Editor Plugin Bridge
|
|
192
|
+
- Godot EditorPlugin with WebSocket server
|
|
193
|
+
- `editor_bridge.py` Python WebSocket client
|
|
194
|
+
- Tier 1 operations: scene tree, properties, signals, run/stop game
|
|
195
|
+
- Live viewport screenshots from editor
|
|
196
|
+
|
|
197
|
+
### Phase 3: Hosted API
|
|
198
|
+
- Cloudflare Worker proxy with Google OAuth
|
|
199
|
+
- Free tier (50 req/day, gpt-4o-mini)
|
|
200
|
+
- Pro tier ($12/mo, gpt-5.4 + vision)
|
|
201
|
+
- Small model routing for simple tasks
|
|
202
|
+
- Prompt caching for session continuity
|
|
203
|
+
|
|
204
|
+
## Key Files
|
|
205
|
+
|
|
206
|
+
| File | Purpose |
|
|
207
|
+
|------|---------|
|
|
208
|
+
| `pyproject.toml` | Package metadata, version, dependencies |
|
|
209
|
+
| `CHANGELOG.md` | Version history |
|
|
210
|
+
| `README.md` | User-facing documentation |
|
|
211
|
+
| `CONTRIBUTING.md` | Contributor guide |
|
|
212
|
+
| `.github/workflows/publish.yml` | Auto-publish to PyPI on tag |
|
|
213
|
+
| `godot_agent/cli.py` | CLI commands + setup wizard |
|
|
214
|
+
| `godot_agent/runtime/engine.py` | Core conversation loop |
|
|
215
|
+
| `godot_agent/prompts/godot_playbook.py` | Godot knowledge base |
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to God Code will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Workspace-style chat TUI with session snapshot, recent activity, and live streaming panels
|
|
9
|
+
- Interaction modes (`apply`, `plan`, `explain`, `review`, `fix`) with mode-aware tool availability
|
|
10
|
+
- Autosaved session metadata with `/sessions`, `/resume`, `/new`, and project-aware restore flow
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Unified `ask` and `chat` rendering pipeline, including tool progress and validation feedback
|
|
14
|
+
- Improved post-tool validation visibility and tool result summaries in interactive sessions
|
|
15
|
+
- Session persistence now preserves assistant tool calls and richer metadata for restore
|
|
16
|
+
|
|
5
17
|
## [0.1.0] - 2026-04-02
|
|
6
18
|
|
|
7
19
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: god-code
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: AI coding agent specialized for Godot game development
|
|
5
5
|
Project-URL: Homepage, https://github.com/chuisiufai/god-code
|
|
6
6
|
Project-URL: Repository, https://github.com/chuisiufai/god-code
|
|
@@ -5,6 +5,9 @@ AI coding agent specialized for Godot 4.4 game development. Unlike generic codin
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **10 tools**: read/write/edit files, grep/glob search, git, shell, Godot headless runner, screenshot capture
|
|
8
|
+
- **Workspace-style chat TUI**: session snapshot, recent activity timeline, live tool feedback, and validation visibility
|
|
9
|
+
- **Interaction modes**: `apply`, `plan`, `explain`, `review`, and `fix` with mode-aware prompts and tool access
|
|
10
|
+
- **Session recovery**: autosave, `/sessions`, `/resume`, and project-aware session metadata
|
|
8
11
|
- **Godot-native understanding**: project.godot parser, .tscn scene parser/writer/validator, collision layer planner
|
|
9
12
|
- **Code quality**: GDScript linter (naming, ordering, type annotations), cross-file consistency checker, design pattern advisor
|
|
10
13
|
- **Smart knowledge injection**: 17 Godot Playbook sections auto-selected by task context
|
|
@@ -52,6 +55,9 @@ EOF
|
|
|
52
55
|
# Single prompt
|
|
53
56
|
god-code ask "Add a health bar to the player scene" --project ./my-game
|
|
54
57
|
|
|
58
|
+
# Script-friendly plain output
|
|
59
|
+
god-code ask "Summarize this project" --project ./my-game --plain
|
|
60
|
+
|
|
55
61
|
# Interactive chat
|
|
56
62
|
god-code chat --project ./my-game
|
|
57
63
|
|
|
@@ -108,9 +114,14 @@ god-code logout # Removes stored credentials
|
|
|
108
114
|
"base_url": "https://api.openai.com/v1",
|
|
109
115
|
"model": "gpt-5.4",
|
|
110
116
|
"oauth_token": null,
|
|
117
|
+
"mode": "apply",
|
|
111
118
|
"max_turns": 20,
|
|
112
119
|
"max_tokens": 4096,
|
|
113
120
|
"temperature": 0.0,
|
|
121
|
+
"auto_validate": true,
|
|
122
|
+
"auto_commit": false,
|
|
123
|
+
"streaming": true,
|
|
124
|
+
"autosave_session": true,
|
|
114
125
|
"screenshot_max_iterations": 5,
|
|
115
126
|
"godot_path": "godot",
|
|
116
127
|
"session_dir": ".agent_sessions"
|
|
@@ -119,6 +130,16 @@ god-code logout # Removes stored credentials
|
|
|
119
130
|
|
|
120
131
|
All fields can be overridden with `GODOT_AGENT_` prefixed environment variables.
|
|
121
132
|
|
|
133
|
+
## Chat Commands
|
|
134
|
+
|
|
135
|
+
Interactive chat supports:
|
|
136
|
+
|
|
137
|
+
- `/mode [apply|plan|explain|review|fix]`
|
|
138
|
+
- `/sessions` and `/resume [session-id]`
|
|
139
|
+
- `/new` to start a fresh session
|
|
140
|
+
- `/workspace` to re-render the session snapshot
|
|
141
|
+
- `/set <key> <value>` for live configuration changes
|
|
142
|
+
|
|
122
143
|
## System Prompt & Output Quality
|
|
123
144
|
|
|
124
145
|
God Code's system prompt is dynamically assembled from:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Multi-agent orchestration primitives for God Code."""
|
|
2
|
+
|
|
3
|
+
from godot_agent.agents.configs import AGENT_CONFIGS, AgentConfig
|
|
4
|
+
from godot_agent.agents.dispatcher import AgentDispatcher
|
|
5
|
+
from godot_agent.agents.results import AgentTaskResult
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AGENT_CONFIGS",
|
|
9
|
+
"AgentConfig",
|
|
10
|
+
"AgentDispatcher",
|
|
11
|
+
"AgentTaskResult",
|
|
12
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Built-in role configurations for multi-agent orchestration."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from godot_agent.runtime.modes import allowed_tools_for_mode
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class AgentConfig:
|
|
12
|
+
name: str
|
|
13
|
+
mode: str
|
|
14
|
+
prompt: str
|
|
15
|
+
allowed_tools: set[str]
|
|
16
|
+
auto_validate: bool = False
|
|
17
|
+
max_tool_rounds: int = 8
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_READ_ONLY_TOOLS = allowed_tools_for_mode("plan")
|
|
21
|
+
_WORKER_TOOLS = allowed_tools_for_mode("apply")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
AGENT_CONFIGS: dict[str, AgentConfig] = {
|
|
25
|
+
"planner": AgentConfig(
|
|
26
|
+
name="planner",
|
|
27
|
+
mode="plan",
|
|
28
|
+
prompt=(
|
|
29
|
+
"You are the Planner agent. Inspect the project and produce a concrete implementation plan, "
|
|
30
|
+
"risks, and validation strategy. Do not edit files."
|
|
31
|
+
),
|
|
32
|
+
allowed_tools=set(_READ_ONLY_TOOLS),
|
|
33
|
+
auto_validate=False,
|
|
34
|
+
max_tool_rounds=6,
|
|
35
|
+
),
|
|
36
|
+
"explorer": AgentConfig(
|
|
37
|
+
name="explorer",
|
|
38
|
+
mode="review",
|
|
39
|
+
prompt=(
|
|
40
|
+
"You are the Explorer agent. Read the project, map the relevant scripts/scenes/resources, "
|
|
41
|
+
"and return concise findings. Do not edit files."
|
|
42
|
+
),
|
|
43
|
+
allowed_tools=set(_READ_ONLY_TOOLS),
|
|
44
|
+
auto_validate=False,
|
|
45
|
+
max_tool_rounds=6,
|
|
46
|
+
),
|
|
47
|
+
"worker": AgentConfig(
|
|
48
|
+
name="worker",
|
|
49
|
+
mode="apply",
|
|
50
|
+
prompt=(
|
|
51
|
+
"You are the Worker agent. Implement the requested change with the smallest viable edit, "
|
|
52
|
+
"then rely on validation and reviewer feedback to converge."
|
|
53
|
+
),
|
|
54
|
+
allowed_tools=set(_WORKER_TOOLS),
|
|
55
|
+
auto_validate=True,
|
|
56
|
+
max_tool_rounds=12,
|
|
57
|
+
),
|
|
58
|
+
"reviewer": AgentConfig(
|
|
59
|
+
name="reviewer",
|
|
60
|
+
mode="review",
|
|
61
|
+
prompt=(
|
|
62
|
+
"You are the Reviewer agent. Validate the changes adversarially and report concrete findings. "
|
|
63
|
+
"Do not edit files."
|
|
64
|
+
),
|
|
65
|
+
allowed_tools=set(_READ_ONLY_TOOLS),
|
|
66
|
+
auto_validate=False,
|
|
67
|
+
max_tool_rounds=4,
|
|
68
|
+
),
|
|
69
|
+
"playtest_analyst": AgentConfig(
|
|
70
|
+
name="playtest_analyst",
|
|
71
|
+
mode="review",
|
|
72
|
+
prompt=(
|
|
73
|
+
"You are the Playtest Analyst agent. Evaluate runtime evidence and scenario-based playtest output, "
|
|
74
|
+
"then explain whether the gameplay intent still holds."
|
|
75
|
+
),
|
|
76
|
+
allowed_tools=set(_READ_ONLY_TOOLS),
|
|
77
|
+
auto_validate=False,
|
|
78
|
+
max_tool_rounds=4,
|
|
79
|
+
),
|
|
80
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Synchronous multi-agent dispatcher."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import replace
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from godot_agent.godot.impact_analysis import format_impact_report, infer_request_impact
|
|
9
|
+
from godot_agent.agents.configs import AGENT_CONFIGS, AgentConfig
|
|
10
|
+
from godot_agent.agents.results import AgentTaskResult
|
|
11
|
+
from godot_agent.llm.client import LLMClient
|
|
12
|
+
from godot_agent.prompts.assembler import PromptAssembler, PromptContext
|
|
13
|
+
from godot_agent.runtime.engine import ConversationEngine
|
|
14
|
+
from godot_agent.runtime.playtest_harness import format_playtest_report, run_playtest_harness
|
|
15
|
+
from godot_agent.runtime.quality_gate import QualityGateReport
|
|
16
|
+
from godot_agent.runtime.runtime_bridge import get_runtime_snapshot
|
|
17
|
+
from godot_agent.runtime.reviewer import format_review_report, review_changes
|
|
18
|
+
from godot_agent.tools.registry import ToolRegistry
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentDispatcher:
|
|
22
|
+
"""Creates role-scoped engines and deterministic review passes."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
*,
|
|
27
|
+
client: LLMClient,
|
|
28
|
+
registry: ToolRegistry,
|
|
29
|
+
prompt_context: PromptContext | None,
|
|
30
|
+
project_path: str | None,
|
|
31
|
+
godot_path: str = "godot",
|
|
32
|
+
base_allowed_tools: set[str] | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
self.client = client
|
|
35
|
+
self.registry = registry
|
|
36
|
+
self.prompt_context = prompt_context
|
|
37
|
+
self.project_path = project_path
|
|
38
|
+
self.godot_path = godot_path
|
|
39
|
+
self.base_allowed_tools = set(base_allowed_tools) if base_allowed_tools is not None else None
|
|
40
|
+
|
|
41
|
+
def _clone_registry(self) -> ToolRegistry:
|
|
42
|
+
cloned = ToolRegistry()
|
|
43
|
+
for tool in self.registry.list_tools():
|
|
44
|
+
cloned.register(tool)
|
|
45
|
+
return cloned
|
|
46
|
+
|
|
47
|
+
def resolve_allowed_tools(self, role: str) -> set[str]:
|
|
48
|
+
config = AGENT_CONFIGS[role]
|
|
49
|
+
if self.base_allowed_tools is None:
|
|
50
|
+
return set(config.allowed_tools)
|
|
51
|
+
return set(config.allowed_tools & self.base_allowed_tools)
|
|
52
|
+
|
|
53
|
+
def _build_prompt_assembler(self, config: AgentConfig) -> PromptAssembler | None:
|
|
54
|
+
if self.prompt_context is None:
|
|
55
|
+
return None
|
|
56
|
+
context = replace(
|
|
57
|
+
self.prompt_context,
|
|
58
|
+
mode=config.mode,
|
|
59
|
+
extra_prompt="\n\n".join(
|
|
60
|
+
section for section in [self.prompt_context.extra_prompt, config.prompt] if section
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
return PromptAssembler(context)
|
|
64
|
+
|
|
65
|
+
def _build_engine(self, config: AgentConfig, *, user_hint: str) -> ConversationEngine:
|
|
66
|
+
registry = self._clone_registry()
|
|
67
|
+
prompt_assembler = self._build_prompt_assembler(config)
|
|
68
|
+
allowed_tools = self.resolve_allowed_tools(config.name)
|
|
69
|
+
if prompt_assembler is not None:
|
|
70
|
+
system_prompt = prompt_assembler.build(
|
|
71
|
+
user_hint=user_hint,
|
|
72
|
+
active_tools=[tool.name for tool in registry.list_tools() if tool.name in allowed_tools],
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
system_prompt = config.prompt
|
|
76
|
+
|
|
77
|
+
engine = ConversationEngine(
|
|
78
|
+
client=self.client,
|
|
79
|
+
registry=registry,
|
|
80
|
+
system_prompt=system_prompt,
|
|
81
|
+
max_tool_rounds=config.max_tool_rounds,
|
|
82
|
+
project_path=self.project_path,
|
|
83
|
+
godot_path=self.godot_path,
|
|
84
|
+
auto_validate=config.auto_validate,
|
|
85
|
+
prompt_assembler=prompt_assembler,
|
|
86
|
+
mode=config.mode,
|
|
87
|
+
)
|
|
88
|
+
engine.allowed_tools = allowed_tools
|
|
89
|
+
return engine
|
|
90
|
+
|
|
91
|
+
async def run_planner(self, task: str) -> AgentTaskResult:
|
|
92
|
+
config = AGENT_CONFIGS["planner"]
|
|
93
|
+
engine = self._build_engine(config, user_hint=task)
|
|
94
|
+
planning_prompt = task
|
|
95
|
+
if self.project_path:
|
|
96
|
+
impact_report = infer_request_impact(Path(self.project_path), task)
|
|
97
|
+
planning_prompt = f"{task}\n\nLikely impact before implementation:\n{format_impact_report(impact_report)}"
|
|
98
|
+
content = await engine.submit(planning_prompt)
|
|
99
|
+
return AgentTaskResult(
|
|
100
|
+
role=config.name,
|
|
101
|
+
content=content,
|
|
102
|
+
used_tools=engine.last_turn.tools_called if engine.last_turn else [],
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
async def run_worker(self, task: str, *, plan: str = "") -> AgentTaskResult:
|
|
106
|
+
config = AGENT_CONFIGS["worker"]
|
|
107
|
+
prompt = task if not plan else f"{task}\n\nApproved plan:\n{plan}"
|
|
108
|
+
engine = self._build_engine(config, user_hint=task)
|
|
109
|
+
content = await engine.submit(prompt)
|
|
110
|
+
verdict = "FAIL" if engine.last_review_report and engine.last_review_report.requires_fix else "PASS"
|
|
111
|
+
return AgentTaskResult(
|
|
112
|
+
role=config.name,
|
|
113
|
+
content=content,
|
|
114
|
+
verdict=verdict,
|
|
115
|
+
used_tools=engine.last_turn.tools_called if engine.last_turn else [],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
async def run_reviewer(
|
|
119
|
+
self,
|
|
120
|
+
*,
|
|
121
|
+
changed_files: set[str],
|
|
122
|
+
quality_report: QualityGateReport | None = None,
|
|
123
|
+
) -> AgentTaskResult:
|
|
124
|
+
if not self.project_path:
|
|
125
|
+
return AgentTaskResult(role="reviewer", verdict="PASS", content="No project path configured.")
|
|
126
|
+
report = await review_changes(
|
|
127
|
+
project_root=Path(self.project_path),
|
|
128
|
+
changed_files=changed_files,
|
|
129
|
+
godot_path=self.godot_path,
|
|
130
|
+
quality_report=quality_report,
|
|
131
|
+
)
|
|
132
|
+
return AgentTaskResult(
|
|
133
|
+
role="reviewer",
|
|
134
|
+
content=format_review_report(report),
|
|
135
|
+
verdict=report.verdict,
|
|
136
|
+
raw=report,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
async def run_playtest_analyst(self, *, changed_files: set[str]) -> AgentTaskResult:
|
|
140
|
+
if not self.project_path:
|
|
141
|
+
return AgentTaskResult(role="playtest_analyst", verdict="PASS", content="No project path configured.")
|
|
142
|
+
report = run_playtest_harness(
|
|
143
|
+
project_root=Path(self.project_path),
|
|
144
|
+
changed_files=changed_files,
|
|
145
|
+
runtime_snapshot=get_runtime_snapshot(),
|
|
146
|
+
)
|
|
147
|
+
return AgentTaskResult(
|
|
148
|
+
role="playtest_analyst",
|
|
149
|
+
content=format_playtest_report(report),
|
|
150
|
+
verdict=report.verdict,
|
|
151
|
+
raw=report,
|
|
152
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Structured results for sub-agent execution."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class AgentTaskResult:
|
|
10
|
+
role: str
|
|
11
|
+
content: str = ""
|
|
12
|
+
verdict: str = "PASS"
|
|
13
|
+
notes: list[str] = field(default_factory=list)
|
|
14
|
+
used_tools: list[str] = field(default_factory=list)
|
|
15
|
+
raw: object | None = None
|