steerdev 0.4.27__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.
- steerdev-0.4.27.dist-info/METADATA +224 -0
- steerdev-0.4.27.dist-info/RECORD +57 -0
- steerdev-0.4.27.dist-info/WHEEL +4 -0
- steerdev-0.4.27.dist-info/entry_points.txt +2 -0
- steerdev_agent/__init__.py +10 -0
- steerdev_agent/api/__init__.py +32 -0
- steerdev_agent/api/activity.py +278 -0
- steerdev_agent/api/agents.py +145 -0
- steerdev_agent/api/client.py +158 -0
- steerdev_agent/api/commands.py +399 -0
- steerdev_agent/api/configs.py +238 -0
- steerdev_agent/api/context.py +306 -0
- steerdev_agent/api/events.py +294 -0
- steerdev_agent/api/hooks.py +178 -0
- steerdev_agent/api/implementation_plan.py +408 -0
- steerdev_agent/api/messages.py +231 -0
- steerdev_agent/api/prd.py +281 -0
- steerdev_agent/api/runs.py +526 -0
- steerdev_agent/api/sessions.py +403 -0
- steerdev_agent/api/specs.py +321 -0
- steerdev_agent/api/tasks.py +659 -0
- steerdev_agent/api/workflow_runs.py +351 -0
- steerdev_agent/api/workflows.py +191 -0
- steerdev_agent/cli.py +2254 -0
- steerdev_agent/config/__init__.py +19 -0
- steerdev_agent/config/models.py +236 -0
- steerdev_agent/config/platform.py +272 -0
- steerdev_agent/config/settings.py +62 -0
- steerdev_agent/daemon.py +675 -0
- steerdev_agent/executor/__init__.py +64 -0
- steerdev_agent/executor/base.py +121 -0
- steerdev_agent/executor/claude.py +328 -0
- steerdev_agent/executor/stream.py +163 -0
- steerdev_agent/git/__init__.py +1 -0
- steerdev_agent/handlers/__init__.py +5 -0
- steerdev_agent/handlers/prd.py +533 -0
- steerdev_agent/integration.py +334 -0
- steerdev_agent/prompt/__init__.py +10 -0
- steerdev_agent/prompt/builder.py +263 -0
- steerdev_agent/prompt/templates.py +422 -0
- steerdev_agent/py.typed +0 -0
- steerdev_agent/runner.py +829 -0
- steerdev_agent/setup/__init__.py +5 -0
- steerdev_agent/setup/claude_setup.py +560 -0
- steerdev_agent/setup/templates/claude_md_section.md +140 -0
- steerdev_agent/setup/templates/settings.json +69 -0
- steerdev_agent/setup/templates/skills/activity/SKILL.md +160 -0
- steerdev_agent/setup/templates/skills/context/SKILL.md +122 -0
- steerdev_agent/setup/templates/skills/git-workflow/SKILL.md +218 -0
- steerdev_agent/setup/templates/skills/progress-logging/SKILL.md +211 -0
- steerdev_agent/setup/templates/skills/specs-management/SKILL.md +161 -0
- steerdev_agent/setup/templates/skills/task-management/SKILL.md +343 -0
- steerdev_agent/setup/templates/steerdev.yaml +51 -0
- steerdev_agent/version.py +149 -0
- steerdev_agent/workflow/__init__.py +10 -0
- steerdev_agent/workflow/executor.py +494 -0
- steerdev_agent/workflow/memory.py +185 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Phase memory storage for workflow execution."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from loguru import logger
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PhaseMemory(BaseModel):
|
|
12
|
+
"""Memory stored for a completed phase."""
|
|
13
|
+
|
|
14
|
+
phase_id: str
|
|
15
|
+
phase_name: str
|
|
16
|
+
phase_type: str
|
|
17
|
+
input_context: dict[str, Any] = Field(default_factory=dict)
|
|
18
|
+
output_context: dict[str, Any] = Field(default_factory=dict)
|
|
19
|
+
result_summary: str | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkflowMemoryManager:
|
|
23
|
+
"""Manages local storage for phase context between executions.
|
|
24
|
+
|
|
25
|
+
Stores phase memory in `.steerdev/phases/{workflow_id}/{run_id}/{phase_id}.json`
|
|
26
|
+
to enable context propagation between phases. The two-level directory structure
|
|
27
|
+
ensures that multiple runs of the same workflow don't overwrite each other,
|
|
28
|
+
and allows debugging of failed runs by preserving their memory.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, working_directory: str | Path) -> None:
|
|
32
|
+
"""Initialize the memory manager.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
working_directory: Base directory for the project.
|
|
36
|
+
"""
|
|
37
|
+
self.working_directory = Path(working_directory)
|
|
38
|
+
self.base_path = self.working_directory / ".steerdev" / "phases"
|
|
39
|
+
|
|
40
|
+
def _get_workflow_path(self, workflow_id: str) -> Path:
|
|
41
|
+
"""Get the path for a workflow's memory directory."""
|
|
42
|
+
return self.base_path / workflow_id
|
|
43
|
+
|
|
44
|
+
def _get_run_path(self, workflow_id: str, run_id: str) -> Path:
|
|
45
|
+
"""Get the path for a workflow run's memory files."""
|
|
46
|
+
return self._get_workflow_path(workflow_id) / run_id
|
|
47
|
+
|
|
48
|
+
def _get_phase_path(self, workflow_id: str, run_id: str, phase_id: str) -> Path:
|
|
49
|
+
"""Get the path for a specific phase's memory file."""
|
|
50
|
+
return self._get_run_path(workflow_id, run_id) / f"{phase_id}.json"
|
|
51
|
+
|
|
52
|
+
def save_phase_memory(
|
|
53
|
+
self,
|
|
54
|
+
workflow_id: str,
|
|
55
|
+
run_id: str,
|
|
56
|
+
memory: PhaseMemory,
|
|
57
|
+
) -> Path:
|
|
58
|
+
"""Save phase memory after completion.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
workflow_id: The workflow definition ID.
|
|
62
|
+
run_id: The workflow run ID.
|
|
63
|
+
memory: Phase memory to save.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Path to the saved memory file.
|
|
67
|
+
"""
|
|
68
|
+
run_path = self._get_run_path(workflow_id, run_id)
|
|
69
|
+
run_path.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
|
|
71
|
+
phase_path = self._get_phase_path(workflow_id, run_id, memory.phase_id)
|
|
72
|
+
phase_path.write_text(memory.model_dump_json(indent=2))
|
|
73
|
+
|
|
74
|
+
logger.debug(f"Saved phase memory: {phase_path}")
|
|
75
|
+
return phase_path
|
|
76
|
+
|
|
77
|
+
def load_phase_memory(
|
|
78
|
+
self,
|
|
79
|
+
workflow_id: str,
|
|
80
|
+
run_id: str,
|
|
81
|
+
phase_id: str,
|
|
82
|
+
) -> PhaseMemory | None:
|
|
83
|
+
"""Load memory for a specific phase.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
workflow_id: The workflow definition ID.
|
|
87
|
+
run_id: The workflow run ID.
|
|
88
|
+
phase_id: The phase ID to load.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Phase memory or None if not found.
|
|
92
|
+
"""
|
|
93
|
+
phase_path = self._get_phase_path(workflow_id, run_id, phase_id)
|
|
94
|
+
|
|
95
|
+
if not phase_path.exists():
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
data = json.loads(phase_path.read_text())
|
|
100
|
+
return PhaseMemory(**data)
|
|
101
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
102
|
+
logger.error(f"Failed to load phase memory: {e}")
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def load_all_phase_memories(
|
|
106
|
+
self,
|
|
107
|
+
workflow_id: str,
|
|
108
|
+
run_id: str,
|
|
109
|
+
) -> list[PhaseMemory]:
|
|
110
|
+
"""Load all phase memories for a workflow run.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
workflow_id: The workflow definition ID.
|
|
114
|
+
run_id: The workflow run ID.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
List of phase memories, ordered by file modification time.
|
|
118
|
+
"""
|
|
119
|
+
run_path = self._get_run_path(workflow_id, run_id)
|
|
120
|
+
|
|
121
|
+
if not run_path.exists():
|
|
122
|
+
return []
|
|
123
|
+
|
|
124
|
+
memories = []
|
|
125
|
+
for phase_file in sorted(run_path.glob("*.json"), key=lambda p: p.stat().st_mtime):
|
|
126
|
+
try:
|
|
127
|
+
data = json.loads(phase_file.read_text())
|
|
128
|
+
memories.append(PhaseMemory(**data))
|
|
129
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
130
|
+
logger.warning(f"Skipping invalid phase memory {phase_file}: {e}")
|
|
131
|
+
|
|
132
|
+
return memories
|
|
133
|
+
|
|
134
|
+
def build_accumulated_context(
|
|
135
|
+
self,
|
|
136
|
+
workflow_id: str,
|
|
137
|
+
run_id: str,
|
|
138
|
+
) -> dict[str, Any]:
|
|
139
|
+
"""Build accumulated context from all completed phases.
|
|
140
|
+
|
|
141
|
+
Merges output contexts from all phases, with later phases
|
|
142
|
+
taking precedence for duplicate keys.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
workflow_id: The workflow definition ID.
|
|
146
|
+
run_id: The workflow run ID.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Merged context dictionary.
|
|
150
|
+
"""
|
|
151
|
+
memories = self.load_all_phase_memories(workflow_id, run_id)
|
|
152
|
+
|
|
153
|
+
accumulated: dict[str, Any] = {}
|
|
154
|
+
for memory in memories:
|
|
155
|
+
accumulated.update(memory.output_context)
|
|
156
|
+
|
|
157
|
+
return accumulated
|
|
158
|
+
|
|
159
|
+
def cleanup_run(self, workflow_id: str, run_id: str) -> bool:
|
|
160
|
+
"""Remove memory files for a completed workflow run.
|
|
161
|
+
|
|
162
|
+
Note: This should only be called for successful workflow runs.
|
|
163
|
+
Failed runs should preserve their memory for debugging.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
workflow_id: The workflow definition ID.
|
|
167
|
+
run_id: The workflow run ID to clean up.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
True if cleanup was successful.
|
|
171
|
+
"""
|
|
172
|
+
run_path = self._get_run_path(workflow_id, run_id)
|
|
173
|
+
|
|
174
|
+
if not run_path.exists():
|
|
175
|
+
return True
|
|
176
|
+
|
|
177
|
+
try:
|
|
178
|
+
import shutil
|
|
179
|
+
|
|
180
|
+
shutil.rmtree(run_path)
|
|
181
|
+
logger.debug(f"Cleaned up workflow memory: {run_path}")
|
|
182
|
+
return True
|
|
183
|
+
except OSError as e:
|
|
184
|
+
logger.error(f"Failed to cleanup workflow memory: {e}")
|
|
185
|
+
return False
|