god-code 0.5.0__tar.gz → 0.5.1__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.1/CLAUDE.md +196 -0
- {god_code-0.5.0 → god_code-0.5.1}/PKG-INFO +1 -1
- {god_code-0.5.0 → god_code-0.5.1}/README.md +13 -8
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/agents/dispatcher.py +2 -1
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/cli.py +135 -88
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/assembler.py +5 -0
- god_code-0.5.1/godot_agent/prompts/skill_library.py +130 -0
- god_code-0.5.1/godot_agent/prompts/skill_selector.py +80 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/engine.py +28 -6
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tui/input_handler.py +10 -3
- {god_code-0.5.0 → god_code-0.5.1}/pyproject.toml +1 -1
- {god_code-0.5.0 → god_code-0.5.1}/tests/prompts/test_prompt_assembler.py +14 -0
- god_code-0.5.1/tests/prompts/test_skill_selector.py +30 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_engine.py +51 -0
- god_code-0.5.1/tests/test_runtime_switch_commands.py +153 -0
- god_code-0.5.1/tests/tui/test_input_handler.py +125 -0
- god_code-0.5.0/CLAUDE.md +0 -215
- god_code-0.5.0/tests/test_runtime_switch_commands.py +0 -26
- god_code-0.5.0/tests/tui/test_input_handler.py +0 -19
- {god_code-0.5.0 → god_code-0.5.1}/.github/workflows/publish.yml +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/.gitignore +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/AGENTS.md +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/CHANGELOG.md +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/CONTRIBUTING.md +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/LICENSE +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/agents/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/agents/configs.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/agents/results.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/collision_planner.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/consistency_checker.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/dependency_graph.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/gdscript_linter.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/impact_analysis.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/pattern_advisor.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/project.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/resource_validator.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/scene_parser.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/scene_writer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/godot/tscn_validator.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/adapters/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/adapters/anthropic.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/adapters/base.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/adapters/openai.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/client.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/streaming.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/types.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/llm/vision.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/build_discipline.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/godot_playbook.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/image_templates.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/knowledge_selector.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/prompts/system.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/py.typed +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/auth.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/config.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/context_manager.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/design_memory.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/error_loop.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/events.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/gameplay_reviewer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/modes.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/oauth.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/playtest_harness.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/providers.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/quality_gate.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/reviewer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/runtime_bridge.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/scenario_specs/hud_feedback.json +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/scenario_specs/player_movement.json +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/scenario_specs/scene_transition.json +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/runtime/session.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/classifier.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/hooks.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/policies.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/protected_paths.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/security/tool_pipeline.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/testing/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/testing/scenario_runner.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/analysis_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/base.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/editor_bridge.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/file_ops.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/git.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/godot_cli.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/image_gen.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/list_dir.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/memory_tool.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/registry.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/scene_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/screenshot.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/script_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/search.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/shell.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tools/web_search.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tui/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/godot_agent/tui/display.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/agents/test_dispatcher.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/agents/test_playtest_analyst.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/e2e/test_planner_worker_reviewer_flow.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/e2e/test_policy_enforcement.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/e2e/test_scenario_runner.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_collision_planner.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_consistency.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_dependency_graph.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_impact_analysis.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_linter.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_pattern_advisor.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_project.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_resource_validator.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_scene_parser.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_scene_writer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/godot/test_tscn_validator.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/llm/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/llm/test_adapters.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/llm/test_client.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/llm/test_vision.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/prompts/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/prompts/test_knowledge_selector.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/prompts/test_system_prompt.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_config.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_context_manager.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_design_memory.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_error_loop.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_gameplay_reviewer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_mode_restrictions.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_multi_agent_flow.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_playtest_harness.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_quality_gate.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_reviewer.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_runtime_bridge.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/runtime/test_session.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/security/test_classifier.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/security/test_hooks.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/security/test_permissions.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/security/test_tool_pipeline.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/test_e2e.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/__init__.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_analysis_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_editor_bridge.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_file_ops.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_git.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_godot_cli.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_list_dir.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_memory_tool.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_registry.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_scene_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_script_tools.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_search.py +0 -0
- {god_code-0.5.0 → god_code-0.5.1}/tests/tools/test_shell.py +0 -0
god_code-0.5.1/CLAUDE.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# CLAUDE.md — God Code Development Guide
|
|
2
|
+
|
|
3
|
+
> AI coding agent specialized for Godot 4.4 game development. This file guides Claude Code 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. Multi-provider LLM support with 29 Godot-specific tools, AI sprite generation, and 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.9+ (uses `from __future__ import annotations` for 3.10+ syntax)
|
|
16
|
+
- **CLI**: click
|
|
17
|
+
- **HTTP**: httpx (async)
|
|
18
|
+
- **Models**: pydantic v2 (tool schemas, config, structured outputs)
|
|
19
|
+
- **Image**: Pillow (screenshot + sprite post-processing)
|
|
20
|
+
- **TUI**: rich (panels, markdown, diff, tables, spinner) + prompt_toolkit (history, autocomplete)
|
|
21
|
+
- **Test**: pytest + pytest-asyncio (383 tests)
|
|
22
|
+
- **Build**: hatchling
|
|
23
|
+
- **CI**: GitHub Actions (auto-publish to PyPI on tag)
|
|
24
|
+
|
|
25
|
+
## Current Version: 0.5.1
|
|
26
|
+
|
|
27
|
+
**Stats**: 70 source files, 55 test files, ~11K lines, 383 tests, 29 tools.
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
godot_agent/
|
|
33
|
+
├── cli.py # Click CLI + setup wizard + chat loop
|
|
34
|
+
├── agents/ # Multi-agent system
|
|
35
|
+
│ ├── configs.py # Agent role configurations
|
|
36
|
+
│ ├── dispatcher.py # Planner/worker/reviewer dispatch
|
|
37
|
+
│ └── results.py # Agent result types
|
|
38
|
+
├── runtime/
|
|
39
|
+
│ ├── engine.py # Conversation loop (tools, streaming, quality gates, review)
|
|
40
|
+
│ ├── config.py # AgentConfig (pydantic) + env overrides
|
|
41
|
+
│ ├── session.py # Session persistence with metadata
|
|
42
|
+
│ ├── oauth.py # Codex refresh token flow
|
|
43
|
+
│ ├── error_loop.py # Godot output parsing + fix suggestions
|
|
44
|
+
│ ├── context_manager.py # Smart compression with working memory (1.05M context)
|
|
45
|
+
│ ├── events.py # Engine event system for TUI
|
|
46
|
+
│ ├── modes.py # Interaction modes (apply/plan/explain/review/fix)
|
|
47
|
+
│ ├── providers.py # Provider detection (openai/anthropic/gemini/xai/openrouter)
|
|
48
|
+
│ ├── quality_gate.py # Post-tool validation pipeline
|
|
49
|
+
│ ├── reviewer.py # Automated code review
|
|
50
|
+
│ ├── playtest_harness.py # Automated gameplay testing
|
|
51
|
+
│ ├── gameplay_reviewer.py # Gameplay quality analysis
|
|
52
|
+
│ ├── runtime_bridge.py # Runtime state snapshots
|
|
53
|
+
│ ├── design_memory.py # Persistent design decisions
|
|
54
|
+
│ └── auth.py # Auth context
|
|
55
|
+
├── llm/
|
|
56
|
+
│ ├── client.py # LLMClient with retry, content filter handling
|
|
57
|
+
│ ├── types.py # Message, ToolCall, TokenUsage, ChatResponse, LLMConfig
|
|
58
|
+
│ ├── streaming.py # SSE streaming with tool call assembly
|
|
59
|
+
│ ├── vision.py # Image encoding
|
|
60
|
+
│ └── adapters/ # Provider-specific adapters
|
|
61
|
+
│ ├── base.py # Adapter interface
|
|
62
|
+
│ ├── openai.py # OpenAI/OpenRouter adapter
|
|
63
|
+
│ └── anthropic.py # Anthropic adapter
|
|
64
|
+
├── tools/ # 29 function-calling tools
|
|
65
|
+
│ ├── base.py # BaseTool ABC (strict mode support)
|
|
66
|
+
│ ├── registry.py # ToolRegistry with security pipeline
|
|
67
|
+
│ ├── file_ops.py # read_file, write_file, edit_file (path-contained)
|
|
68
|
+
│ ├── script_tools.py # read_script, edit_script, lint_script
|
|
69
|
+
│ ├── scene_tools.py # read_scene, scene_tree, add/write/remove scene nodes
|
|
70
|
+
│ ├── analysis_tools.py # validate_project, check_consistency, dependency_graph, impact
|
|
71
|
+
│ ├── search.py # grep, glob
|
|
72
|
+
│ ├── list_dir.py # list_dir
|
|
73
|
+
│ ├── git.py # git (shlex-parsed)
|
|
74
|
+
│ ├── shell.py # run_shell (3 safety levels)
|
|
75
|
+
│ ├── godot_cli.py # run_godot (GUT, validate, output parser)
|
|
76
|
+
│ ├── screenshot.py # screenshot_scene (headless)
|
|
77
|
+
│ ├── image_gen.py # generate_sprite (AI pixel art + post-processing)
|
|
78
|
+
│ ├── web_search.py # web_search (Godot docs, web)
|
|
79
|
+
│ ├── memory_tool.py # design memory read/write
|
|
80
|
+
│ └── editor_bridge.py # runtime snapshot, playtest
|
|
81
|
+
├── godot/ # Godot-specific analysis
|
|
82
|
+
│ ├── project.py # project.godot parser
|
|
83
|
+
│ ├── scene_parser.py # .tscn → TscnScene
|
|
84
|
+
│ ├── scene_writer.py # Structured .tscn modification
|
|
85
|
+
│ ├── tscn_validator.py # Format validation + auto-fix
|
|
86
|
+
│ ├── gdscript_linter.py # Style, naming, type annotations
|
|
87
|
+
│ ├── collision_planner.py # Standard 8-layer scheme
|
|
88
|
+
│ ├── consistency_checker.py # Cross-file checks
|
|
89
|
+
│ ├── dependency_graph.py # Project-wide file graph
|
|
90
|
+
│ ├── pattern_advisor.py # Design pattern suggestions
|
|
91
|
+
│ ├── impact_analysis.py # Change impact analysis
|
|
92
|
+
│ └── resource_validator.py # res:// path checks
|
|
93
|
+
├── prompts/
|
|
94
|
+
│ ├── system.py # Compatibility wrapper
|
|
95
|
+
│ ├── assembler.py # Full prompt assembly pipeline
|
|
96
|
+
│ ├── godot_playbook.py # 17 knowledge sections
|
|
97
|
+
│ ├── knowledge_selector.py # Context-aware section scoring
|
|
98
|
+
│ ├── skill_library.py # Skill definitions
|
|
99
|
+
│ ├── skill_selector.py # Dynamic skill activation
|
|
100
|
+
│ ├── build_discipline.py # Build-and-verify rules
|
|
101
|
+
│ └── image_templates.py # Pixel art prompt templates
|
|
102
|
+
├── security/
|
|
103
|
+
│ ├── classifier.py # Tool risk classification
|
|
104
|
+
│ ├── hooks.py # Pre/post execution hooks
|
|
105
|
+
│ ├── policies.py # Execution context + policies
|
|
106
|
+
│ ├── protected_paths.py # Path protection rules
|
|
107
|
+
│ └── tool_pipeline.py # Tool execution pipeline
|
|
108
|
+
├── testing/
|
|
109
|
+
│ └── scenario_runner.py # Automated test scenarios
|
|
110
|
+
└── tui/
|
|
111
|
+
├── display.py # Rich TUI components
|
|
112
|
+
└── input_handler.py # prompt_toolkit input + autocomplete
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Key Patterns
|
|
116
|
+
|
|
117
|
+
### Tool System
|
|
118
|
+
Every tool inherits `BaseTool`. Supports `strict` mode for GPT-5+ structured outputs. Security pipeline validates path containment and safety level before execution.
|
|
119
|
+
|
|
120
|
+
### Provider Adapters
|
|
121
|
+
`llm/adapters/` handles provider-specific request/response formats:
|
|
122
|
+
- OpenAI: `max_completion_tokens` for gpt-5+, `max_tokens` for others
|
|
123
|
+
- Anthropic: `thinking` budget for reasoning models
|
|
124
|
+
- Gemini: `reasoning_effort` parameter
|
|
125
|
+
|
|
126
|
+
### Engine Loop Phases
|
|
127
|
+
```
|
|
128
|
+
PREPARE_CONTEXT → CALL_MODEL → EXECUTE_TOOLS → RUN_QUALITY_GATE → RUN_REVIEWER → NEXT_ROUND → DONE
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Context Management (1.05M window)
|
|
132
|
+
Smart compression at 75% (787K tokens):
|
|
133
|
+
1. Extract working memory (modified files, decisions, errors)
|
|
134
|
+
2. Keep 20 recent messages intact
|
|
135
|
+
3. Replace old turns with memory summary
|
|
136
|
+
4. Tell LLM to re-read files if needed
|
|
137
|
+
|
|
138
|
+
### Interaction Modes
|
|
139
|
+
- `apply`: Full tool access, write code
|
|
140
|
+
- `plan`: Read-only tools, design first
|
|
141
|
+
- `explain`: Read-only, educational
|
|
142
|
+
- `review`: Read-only, quality analysis
|
|
143
|
+
- `fix`: Full tools, error-focused
|
|
144
|
+
|
|
145
|
+
## Development Rules
|
|
146
|
+
|
|
147
|
+
### Adding a New Tool
|
|
148
|
+
1. Create in `godot_agent/tools/your_tool.py` inheriting `BaseTool`
|
|
149
|
+
2. Define `Input`/`Output` as pydantic `BaseModel` (all fields must have defaults for strict mode)
|
|
150
|
+
3. Implement `async execute()`, `is_read_only()`, `is_destructive()`
|
|
151
|
+
4. Register in `cli.py:build_registry()`
|
|
152
|
+
5. Add to `prompts/system.py` active_tools list
|
|
153
|
+
6. Add tests in `tests/tools/`
|
|
154
|
+
|
|
155
|
+
### Adding a Provider
|
|
156
|
+
1. Create adapter in `godot_agent/llm/adapters/`
|
|
157
|
+
2. Register in `runtime/providers.py`
|
|
158
|
+
3. Add detection rules (model prefix, base_url pattern)
|
|
159
|
+
|
|
160
|
+
## Config Reference
|
|
161
|
+
|
|
162
|
+
`~/.config/god-code/config.json`:
|
|
163
|
+
|
|
164
|
+
| Field | Default | Description |
|
|
165
|
+
|-------|---------|-------------|
|
|
166
|
+
| api_key | "" | LLM API key |
|
|
167
|
+
| provider | "openai" | openai, anthropic, gemini, xai, openrouter |
|
|
168
|
+
| model | "gpt-5.4" | Model ID |
|
|
169
|
+
| reasoning_effort | "high" | low, medium, high |
|
|
170
|
+
| mode | "apply" | apply, plan, explain, review, fix |
|
|
171
|
+
| language | "en" | en, zh-TW, ja, ko |
|
|
172
|
+
| verbosity | "normal" | concise, normal, detailed |
|
|
173
|
+
| auto_validate | true | Run Godot after file changes |
|
|
174
|
+
| auto_commit | false | Suggest git commit |
|
|
175
|
+
| token_budget | 0 | Max tokens (0=unlimited) |
|
|
176
|
+
| safety | "normal" | strict, normal, permissive |
|
|
177
|
+
| streaming | true | Stream responses |
|
|
178
|
+
| extra_prompt | "" | Custom instructions |
|
|
179
|
+
|
|
180
|
+
## Testing
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
python -m pytest tests/ -v # Full suite (383 tests)
|
|
184
|
+
python -m pytest tests/tools/ -v # Tool tests only
|
|
185
|
+
python -m pytest tests/e2e/ -v # E2E integration tests
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Release
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# Bump version in pyproject.toml + cli.py _VERSION
|
|
192
|
+
git commit -am "release: v0.5.1"
|
|
193
|
+
git tag v0.5.1
|
|
194
|
+
git push && git push --tags
|
|
195
|
+
# → GitHub Actions auto-publishes to PyPI
|
|
196
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: god-code
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1
|
|
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
|
|
@@ -4,15 +4,20 @@ AI coding agent specialized for Godot 4.4 game development. Unlike generic codin
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
7
|
+
- **29 tools**: file ops, scene manipulation, script editing, search, git, shell, Godot headless runner, screenshot, AI sprite generation, web search
|
|
8
|
+
- **AI sprite generation**: pixel art pipeline with chroma key removal, auto-crop, nearest-neighbor resize, style presets
|
|
9
|
+
- **Structured Outputs**: strict JSON schemas for gpt-5+ models — zero tool call parse errors
|
|
10
|
+
- **Web search**: query Godot docs and web when built-in knowledge isn't enough
|
|
11
|
+
- **Workspace-style chat TUI**: session snapshot, activity timeline, live tool feedback, streaming output
|
|
12
|
+
- **Interaction modes**: `apply`, `plan`, `explain`, `review`, `fix` with mode-aware prompts and tool access
|
|
13
|
+
- **Multi-provider**: OpenAI (gpt-5.4), Anthropic (claude-sonnet-4.6), Google (gemini), xAI (grok), OpenRouter, local models
|
|
14
|
+
- **Session recovery**: autosave, `/sessions`, `/resume`, project-aware session metadata
|
|
11
15
|
- **Godot-native understanding**: project.godot parser, .tscn scene parser/writer/validator, collision layer planner
|
|
12
|
-
- **Code quality**: GDScript linter
|
|
13
|
-
- **Smart knowledge injection**: 17 Godot Playbook sections auto-selected by task context
|
|
14
|
-
- **Build discipline**: incremental build-and-verify
|
|
15
|
-
- **
|
|
16
|
+
- **Code quality**: GDScript linter, cross-file consistency checker, design pattern advisor, impact analysis
|
|
17
|
+
- **Smart knowledge injection**: 17 Godot Playbook sections auto-selected by task context + skill system
|
|
18
|
+
- **Build discipline**: incremental build-and-verify with quality gates and automated review
|
|
19
|
+
- **Security**: path containment, shell command blocking (3 safety levels), tool execution pipeline
|
|
20
|
+
- **1.05M context window**: smart compression with working memory extraction at 75% threshold
|
|
16
21
|
|
|
17
22
|
## Install
|
|
18
23
|
|
|
@@ -85,7 +85,8 @@ class AgentDispatcher:
|
|
|
85
85
|
prompt_assembler=prompt_assembler,
|
|
86
86
|
mode=config.mode,
|
|
87
87
|
)
|
|
88
|
-
engine.
|
|
88
|
+
engine.base_allowed_tools = set(allowed_tools)
|
|
89
|
+
engine.allowed_tools = set(allowed_tools)
|
|
89
90
|
return engine
|
|
90
91
|
|
|
91
92
|
async def run_planner(self, task: str) -> AgentTaskResult:
|
|
@@ -104,6 +104,50 @@ def _has_meaningful_input(value: str) -> bool:
|
|
|
104
104
|
return any(ch.isprintable() and not ch.isspace() for ch in value)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
def _command_argument(value: str, command: str) -> str | None:
|
|
108
|
+
stripped = value.strip()
|
|
109
|
+
if stripped == command:
|
|
110
|
+
return ""
|
|
111
|
+
prefix = f"{command} "
|
|
112
|
+
if not stripped.startswith(prefix):
|
|
113
|
+
return None
|
|
114
|
+
parts = stripped.split(None, 1)
|
|
115
|
+
return parts[1].strip() if len(parts) > 1 else ""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _set_arguments(value: str) -> tuple[str, str] | None:
|
|
119
|
+
stripped = value.strip()
|
|
120
|
+
if stripped == "/set":
|
|
121
|
+
return None
|
|
122
|
+
if not stripped.startswith("/set "):
|
|
123
|
+
return None
|
|
124
|
+
parts = stripped.split(None, 2)
|
|
125
|
+
if len(parts) != 3:
|
|
126
|
+
return None
|
|
127
|
+
return parts[1], parts[2]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _cd_argument(value: str) -> str | None:
|
|
131
|
+
for command in ("/cd", "cd"):
|
|
132
|
+
arg = _command_argument(value, command)
|
|
133
|
+
if arg is not None:
|
|
134
|
+
return arg
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _starts_multiline_input(value: str) -> bool:
|
|
139
|
+
return value.strip().startswith('"""')
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _multiline_initial_fragment(value: str) -> str:
|
|
143
|
+
stripped = value.strip()
|
|
144
|
+
return stripped[3:] if stripped.startswith('"""') else ""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _is_multiline_terminator(value: str | None) -> bool:
|
|
148
|
+
return value is None or value.strip() == '"""'
|
|
149
|
+
|
|
150
|
+
|
|
107
151
|
def build_registry() -> ToolRegistry:
|
|
108
152
|
registry = ToolRegistry()
|
|
109
153
|
for tool_cls in [
|
|
@@ -174,7 +218,8 @@ def build_engine(config: AgentConfig, project_root: Path) -> ConversationEngine:
|
|
|
174
218
|
mode=config.mode,
|
|
175
219
|
dispatcher=dispatcher,
|
|
176
220
|
)
|
|
177
|
-
engine.
|
|
221
|
+
engine.base_allowed_tools = set(allowed_tools)
|
|
222
|
+
engine.allowed_tools = set(allowed_tools)
|
|
178
223
|
return engine
|
|
179
224
|
|
|
180
225
|
|
|
@@ -407,7 +452,7 @@ def _run_setup_wizard() -> None:
|
|
|
407
452
|
click.echo()
|
|
408
453
|
|
|
409
454
|
|
|
410
|
-
_VERSION = "0.5.
|
|
455
|
+
_VERSION = "0.5.1"
|
|
411
456
|
|
|
412
457
|
|
|
413
458
|
def _check_update() -> None:
|
|
@@ -690,7 +735,7 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
690
735
|
if in_multiline:
|
|
691
736
|
from godot_agent.tui.input_handler import get_multiline_continuation_async
|
|
692
737
|
line = await get_multiline_continuation_async(input_session)
|
|
693
|
-
if line
|
|
738
|
+
if _is_multiline_terminator(line):
|
|
694
739
|
in_multiline = False
|
|
695
740
|
user_input = "\n".join(multiline_buffer)
|
|
696
741
|
multiline_buffer = []
|
|
@@ -702,9 +747,9 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
702
747
|
user_input = await get_input_async(input_session, completer, bottom_toolbar=_toolbar())
|
|
703
748
|
if user_input is None:
|
|
704
749
|
break
|
|
705
|
-
if user_input
|
|
750
|
+
if _starts_multiline_input(user_input):
|
|
706
751
|
in_multiline = True
|
|
707
|
-
rest = user_input
|
|
752
|
+
rest = _multiline_initial_fragment(user_input)
|
|
708
753
|
if rest:
|
|
709
754
|
multiline_buffer.append(rest)
|
|
710
755
|
continue
|
|
@@ -727,10 +772,9 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
727
772
|
|
|
728
773
|
if cmd in ("/load", "/resume", "resume"):
|
|
729
774
|
target = "latest"
|
|
730
|
-
elif stripped.startswith("/resume "):
|
|
731
|
-
target = stripped.split(None, 1)[1].strip()
|
|
732
775
|
else:
|
|
733
|
-
|
|
776
|
+
resume_arg = _command_argument(user_input, "/resume")
|
|
777
|
+
target = resume_arg or "latest" if resume_arg is not None else None
|
|
734
778
|
|
|
735
779
|
if target is not None:
|
|
736
780
|
record = (
|
|
@@ -803,13 +847,14 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
803
847
|
display.error(f"No project.godot in {project_root}")
|
|
804
848
|
continue
|
|
805
849
|
|
|
806
|
-
|
|
850
|
+
mode_arg = _command_argument(user_input, "/mode")
|
|
851
|
+
if cmd == "/mode" or mode_arg == "":
|
|
807
852
|
display.mode_panel(cfg.mode)
|
|
808
853
|
continue
|
|
809
854
|
|
|
810
|
-
if
|
|
855
|
+
if mode_arg:
|
|
811
856
|
try:
|
|
812
|
-
cfg.mode = normalize_mode(
|
|
857
|
+
cfg.mode = normalize_mode(mode_arg)
|
|
813
858
|
except ValueError as e:
|
|
814
859
|
display.error(str(e))
|
|
815
860
|
continue
|
|
@@ -819,7 +864,8 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
819
864
|
_refresh_workspace()
|
|
820
865
|
continue
|
|
821
866
|
|
|
822
|
-
|
|
867
|
+
provider_arg = _command_argument(user_input, "/provider")
|
|
868
|
+
if cmd == "/provider" or provider_arg == "":
|
|
823
869
|
display.info_panel({
|
|
824
870
|
"Provider": cfg.provider,
|
|
825
871
|
"Base URL": cfg.base_url,
|
|
@@ -828,9 +874,9 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
828
874
|
})
|
|
829
875
|
continue
|
|
830
876
|
|
|
831
|
-
if
|
|
877
|
+
if provider_arg:
|
|
832
878
|
try:
|
|
833
|
-
provider = _apply_provider_preset(cfg,
|
|
879
|
+
provider = _apply_provider_preset(cfg, provider_arg)
|
|
834
880
|
except ValueError as e:
|
|
835
881
|
display.error(str(e))
|
|
836
882
|
continue
|
|
@@ -839,7 +885,8 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
839
885
|
_refresh_workspace()
|
|
840
886
|
continue
|
|
841
887
|
|
|
842
|
-
|
|
888
|
+
model_arg = _command_argument(user_input, "/model")
|
|
889
|
+
if cmd == "/model" or model_arg == "":
|
|
843
890
|
display.info_panel({
|
|
844
891
|
"Provider": cfg.provider,
|
|
845
892
|
"Model": cfg.model,
|
|
@@ -847,10 +894,10 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
847
894
|
})
|
|
848
895
|
continue
|
|
849
896
|
|
|
850
|
-
if
|
|
897
|
+
if model_arg is not None:
|
|
851
898
|
previous_provider = cfg.provider
|
|
852
899
|
previous_base_url = cfg.base_url
|
|
853
|
-
cfg.model =
|
|
900
|
+
cfg.model = model_arg
|
|
854
901
|
if not cfg.model:
|
|
855
902
|
display.error("Usage: /model <name>")
|
|
856
903
|
continue
|
|
@@ -860,7 +907,8 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
860
907
|
_refresh_workspace()
|
|
861
908
|
continue
|
|
862
909
|
|
|
863
|
-
|
|
910
|
+
effort_arg = _command_argument(user_input, "/effort")
|
|
911
|
+
if cmd == "/effort" or effort_arg == "":
|
|
864
912
|
display.info_panel({
|
|
865
913
|
"Effort": cfg.reasoning_effort,
|
|
866
914
|
"Allowed": ", ".join(REASONING_EFFORT_LEVELS),
|
|
@@ -868,9 +916,11 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
868
916
|
})
|
|
869
917
|
continue
|
|
870
918
|
|
|
871
|
-
if
|
|
919
|
+
if effort_arg is not None:
|
|
872
920
|
try:
|
|
873
|
-
|
|
921
|
+
if not effort_arg:
|
|
922
|
+
raise ValueError("Usage: /effort <level>")
|
|
923
|
+
cfg.reasoning_effort = _normalize_reasoning_effort(effort_arg)
|
|
874
924
|
except ValueError as e:
|
|
875
925
|
display.error(str(e))
|
|
876
926
|
continue
|
|
@@ -910,82 +960,79 @@ def chat(project: str = ".", config: str | None = None):
|
|
|
910
960
|
display.settings_panel(cfg)
|
|
911
961
|
continue
|
|
912
962
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
if
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
except ValueError as e:
|
|
940
|
-
display.error(str(e))
|
|
941
|
-
continue
|
|
942
|
-
|
|
943
|
-
display.success(f"{key} = {getattr(cfg, key)}")
|
|
944
|
-
if key == "base_url":
|
|
945
|
-
cfg.provider = infer_provider(
|
|
946
|
-
base_url=cfg.base_url,
|
|
947
|
-
model=cfg.model,
|
|
948
|
-
provider="",
|
|
949
|
-
)
|
|
950
|
-
|
|
951
|
-
if key in (
|
|
952
|
-
"language",
|
|
953
|
-
"verbosity",
|
|
954
|
-
"extra_prompt",
|
|
955
|
-
"auto_validate",
|
|
956
|
-
"mode",
|
|
957
|
-
"safety",
|
|
958
|
-
"godot_path",
|
|
959
|
-
"provider",
|
|
960
|
-
"model",
|
|
961
|
-
"base_url",
|
|
962
|
-
"reasoning_effort",
|
|
963
|
-
"api_key",
|
|
964
|
-
"oauth_token",
|
|
965
|
-
"max_tokens",
|
|
966
|
-
"temperature",
|
|
967
|
-
):
|
|
968
|
-
await _replace_engine(project_root, preserve_messages=True, rescan_project=False)
|
|
969
|
-
display.info("Engine rebuilt with updated settings")
|
|
963
|
+
set_args = _set_arguments(user_input)
|
|
964
|
+
if stripped == "/set" or set_args is not None:
|
|
965
|
+
if set_args is None:
|
|
966
|
+
display.error("Usage: /set <key> <value>")
|
|
967
|
+
continue
|
|
968
|
+
key, val = set_args
|
|
969
|
+
if hasattr(cfg, key):
|
|
970
|
+
old_val = getattr(cfg, key)
|
|
971
|
+
try:
|
|
972
|
+
if key == "mode":
|
|
973
|
+
setattr(cfg, key, normalize_mode(val))
|
|
974
|
+
elif key == "provider":
|
|
975
|
+
_apply_provider_preset(cfg, val)
|
|
976
|
+
elif key == "reasoning_effort":
|
|
977
|
+
setattr(cfg, key, _normalize_reasoning_effort(val))
|
|
978
|
+
elif key == "model":
|
|
979
|
+
previous_provider = cfg.provider
|
|
980
|
+
previous_base_url = cfg.base_url
|
|
981
|
+
setattr(cfg, key, val)
|
|
982
|
+
_sync_provider_from_model(cfg, previous_provider, previous_base_url)
|
|
983
|
+
elif isinstance(old_val, bool):
|
|
984
|
+
setattr(cfg, key, val.lower() in ("true", "1", "yes", "on"))
|
|
985
|
+
elif isinstance(old_val, int):
|
|
986
|
+
setattr(cfg, key, int(val))
|
|
987
|
+
elif isinstance(old_val, float):
|
|
988
|
+
setattr(cfg, key, float(val))
|
|
970
989
|
else:
|
|
971
|
-
|
|
972
|
-
|
|
990
|
+
setattr(cfg, key, val)
|
|
991
|
+
except ValueError as e:
|
|
992
|
+
display.error(str(e))
|
|
993
|
+
continue
|
|
994
|
+
|
|
995
|
+
display.success(f"{key} = {getattr(cfg, key)}")
|
|
996
|
+
if key == "base_url":
|
|
997
|
+
cfg.provider = infer_provider(
|
|
998
|
+
base_url=cfg.base_url,
|
|
999
|
+
model=cfg.model,
|
|
1000
|
+
provider="",
|
|
1001
|
+
)
|
|
1002
|
+
|
|
1003
|
+
if key in (
|
|
1004
|
+
"language",
|
|
1005
|
+
"verbosity",
|
|
1006
|
+
"extra_prompt",
|
|
1007
|
+
"auto_validate",
|
|
1008
|
+
"mode",
|
|
1009
|
+
"safety",
|
|
1010
|
+
"godot_path",
|
|
1011
|
+
"provider",
|
|
1012
|
+
"model",
|
|
1013
|
+
"base_url",
|
|
1014
|
+
"reasoning_effort",
|
|
1015
|
+
"api_key",
|
|
1016
|
+
"oauth_token",
|
|
1017
|
+
"max_tokens",
|
|
1018
|
+
"temperature",
|
|
1019
|
+
):
|
|
1020
|
+
await _replace_engine(project_root, preserve_messages=True, rescan_project=False)
|
|
1021
|
+
display.info("Engine rebuilt with updated settings")
|
|
973
1022
|
else:
|
|
974
|
-
|
|
1023
|
+
_wire_engine_callbacks(engine, display, cfg)
|
|
1024
|
+
_refresh_workspace()
|
|
975
1025
|
else:
|
|
976
|
-
display.error("
|
|
1026
|
+
display.error(f"Unknown setting: {key}")
|
|
977
1027
|
continue
|
|
978
1028
|
|
|
979
1029
|
# Support both /cd and cd
|
|
980
|
-
cd_input =
|
|
981
|
-
if cd_input.startswith("/cd "):
|
|
982
|
-
cd_input = cd_input[4:]
|
|
983
|
-
elif cd_input.startswith("cd "):
|
|
984
|
-
cd_input = cd_input[3:]
|
|
985
|
-
else:
|
|
986
|
-
cd_input = None
|
|
1030
|
+
cd_input = _cd_argument(user_input)
|
|
987
1031
|
|
|
988
1032
|
if cd_input is not None:
|
|
1033
|
+
if not cd_input:
|
|
1034
|
+
display.error("Usage: /cd <path>")
|
|
1035
|
+
continue
|
|
989
1036
|
new_path = Path(cd_input).expanduser().resolve()
|
|
990
1037
|
if not new_path.exists():
|
|
991
1038
|
display.error(f"Path not found: {new_path}")
|
|
@@ -9,6 +9,7 @@ from godot_agent.godot.impact_analysis import ImpactAnalysisReport, format_impac
|
|
|
9
9
|
from godot_agent.godot.project import parse_project_godot
|
|
10
10
|
from godot_agent.prompts.build_discipline import BUILD_DISCIPLINE_PROMPT
|
|
11
11
|
from godot_agent.prompts.knowledge_selector import format_knowledge_injection, select_sections
|
|
12
|
+
from godot_agent.prompts.skill_selector import format_skill_injection, select_skills
|
|
12
13
|
from godot_agent.runtime.design_memory import DesignMemory, format_design_memory
|
|
13
14
|
from godot_agent.runtime.modes import mode_prompt
|
|
14
15
|
from godot_agent.runtime.playtest_harness import PlaytestReport, format_playtest_report
|
|
@@ -75,6 +76,10 @@ class PromptAssembler:
|
|
|
75
76
|
) -> str:
|
|
76
77
|
sections = [self._static_prompt]
|
|
77
78
|
|
|
79
|
+
skills = select_skills(user_hint, file_paths, max_skills=2)
|
|
80
|
+
if skills:
|
|
81
|
+
sections.append(format_skill_injection(skills))
|
|
82
|
+
|
|
78
83
|
knowledge_sections = select_sections(user_hint, file_paths, max_sections=4)
|
|
79
84
|
if knowledge_sections:
|
|
80
85
|
sections.append(format_knowledge_injection(knowledge_sections))
|