steerdev 1.0.37__tar.gz → 1.0.39__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.
Files changed (101) hide show
  1. {steerdev-1.0.37 → steerdev-1.0.39}/PKG-INFO +1 -1
  2. {steerdev-1.0.37 → steerdev-1.0.39}/pyproject.toml +1 -1
  3. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/agent_loop.py +61 -12
  4. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/runner.py +3 -0
  5. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workflow/executor.py +8 -0
  6. {steerdev-1.0.37 → steerdev-1.0.39}/.github/workflows/pre-commit.yml +0 -0
  7. {steerdev-1.0.37 → steerdev-1.0.39}/.github/workflows/publish.yml +0 -0
  8. {steerdev-1.0.37 → steerdev-1.0.39}/.gitignore +0 -0
  9. {steerdev-1.0.37 → steerdev-1.0.39}/.pre-commit-config.yaml +0 -0
  10. {steerdev-1.0.37 → steerdev-1.0.39}/AGENTS.md +0 -0
  11. {steerdev-1.0.37 → steerdev-1.0.39}/CLAUDE.md +0 -0
  12. {steerdev-1.0.37 → steerdev-1.0.39}/README.md +0 -0
  13. {steerdev-1.0.37 → steerdev-1.0.39}/scripts/pre-commit-version-bump.sh +0 -0
  14. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/__init__.py +0 -0
  15. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/__init__.py +0 -0
  16. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/activity.py +0 -0
  17. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/agents.py +0 -0
  18. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/canals.py +0 -0
  19. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/client.py +0 -0
  20. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/commands.py +0 -0
  21. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/configs.py +0 -0
  22. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/context.py +0 -0
  23. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/events.py +0 -0
  24. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/hooks.py +0 -0
  25. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/implementation_plan.py +0 -0
  26. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/messages.py +0 -0
  27. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/prd.py +0 -0
  28. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/runs.py +0 -0
  29. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/sessions.py +0 -0
  30. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/specs.py +0 -0
  31. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/tasks.py +0 -0
  32. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/workflow_runs.py +0 -0
  33. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/api/workflows.py +0 -0
  34. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/cli.py +0 -0
  35. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/config/__init__.py +0 -0
  36. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/config/models.py +0 -0
  37. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/config/platform.py +0 -0
  38. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/config/settings.py +0 -0
  39. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/executor/__init__.py +0 -0
  40. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/executor/base.py +0 -0
  41. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/executor/claude.py +0 -0
  42. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/executor/stream.py +0 -0
  43. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/handlers/__init__.py +0 -0
  44. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/handlers/prd.py +0 -0
  45. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/integration.py +0 -0
  46. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/prompt/__init__.py +0 -0
  47. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/prompt/builder.py +0 -0
  48. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/prompt/templates.py +0 -0
  49. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/prompt/workflow_template.py +0 -0
  50. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/py.typed +0 -0
  51. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/retry.py +0 -0
  52. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/__init__.py +0 -0
  53. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/claude_setup.py +0 -0
  54. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/repo_setup.py +0 -0
  55. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/ci/canal-integration.yml +0 -0
  56. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/claude_md_section.md +0 -0
  57. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/settings.json +0 -0
  58. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-activity-skill/SKILL.md +0 -0
  59. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-canal-workflow-skill/SKILL.md +0 -0
  60. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-context-skill/SKILL.md +0 -0
  61. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-git-workflow-skill/SKILL.md +0 -0
  62. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-merge-into-canal-skill/SKILL.md +0 -0
  63. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-progress-logging-skill/SKILL.md +0 -0
  64. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-single-task-merge-skill/SKILL.md +0 -0
  65. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-specs-management-skill/SKILL.md +0 -0
  66. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-task-management-skill/SKILL.md +0 -0
  67. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/skills/steerdev-wave-tasks-merge-skill/SKILL.md +0 -0
  68. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/setup/templates/steerdev.yaml +0 -0
  69. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/update_check.py +0 -0
  70. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/version.py +0 -0
  71. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workflow/__init__.py +0 -0
  72. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workflow/context.py +0 -0
  73. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workflow/memory.py +0 -0
  74. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workspace/__init__.py +0 -0
  75. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workspace/project_manager.py +0 -0
  76. {steerdev-1.0.37 → steerdev-1.0.39}/src/steerdev_agent/workspace/tool_detection.py +0 -0
  77. {steerdev-1.0.37 → steerdev-1.0.39}/tests/__init__.py +0 -0
  78. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_agent_loop.py +0 -0
  79. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_agent_loop_extended.py +0 -0
  80. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_agents_api.py +0 -0
  81. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_api_client.py +0 -0
  82. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_claude_executor.py +0 -0
  83. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_claude_setup.py +0 -0
  84. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_client_methods.py +0 -0
  85. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_commands_api.py +0 -0
  86. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_config.py +0 -0
  87. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_config_extended.py +0 -0
  88. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_context_search.py +0 -0
  89. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_executor.py +0 -0
  90. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_platform_config.py +0 -0
  91. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_prompt.py +0 -0
  92. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_retry.py +0 -0
  93. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_runner_merge_modes.py +0 -0
  94. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_stream_parser.py +0 -0
  95. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_tasks.py +0 -0
  96. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_version.py +0 -0
  97. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_workflow_context.py +0 -0
  98. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_workflow_memory.py +0 -0
  99. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_workflow_prompt_template.py +0 -0
  100. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_workspace.py +0 -0
  101. {steerdev-1.0.37 → steerdev-1.0.39}/tests/test_workspace_extended.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steerdev
3
- Version: 1.0.37
3
+ Version: 1.0.39
4
4
  Summary: Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting
5
5
  Project-URL: Homepage, https://github.com/pentoai/steerdev-agent
6
6
  Project-URL: Repository, https://github.com/pentoai/steerdev-agent
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "steerdev"
3
- version = "1.0.37"
3
+ version = "1.0.39"
4
4
  description = "Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -80,6 +80,8 @@ class CommandExecutor:
80
80
  _commands_client: CommandsClient | None
81
81
  _sessions_client: SessionsClient | None
82
82
  _shutdown_event: asyncio.Event
83
+ _current_task: asyncio.Task[bool] | None
84
+ _signal_count: int
83
85
  _commands_executed: int
84
86
  _commands_succeeded: int
85
87
  _commands_failed: int
@@ -172,6 +174,7 @@ class CommandExecutor:
172
174
  enable_worktrees=self._enable_worktrees,
173
175
  executor_config=self._executor_config,
174
176
  force_workflow_id=None,
177
+ shutdown_event=self._shutdown_event,
175
178
  )
176
179
 
177
180
  # Fetch task details
@@ -452,6 +455,8 @@ class AgentLoop(CommandExecutor):
452
455
  # State
453
456
  self._agent_id: str | None = None
454
457
  self._shutdown_event = asyncio.Event()
458
+ self._current_task: asyncio.Task[bool] | None = None
459
+ self._signal_count = 0
455
460
  self._is_busy = False
456
461
  self._consecutive_errors = 0
457
462
  self._commands_executed = 0
@@ -577,7 +582,14 @@ class AgentLoop(CommandExecutor):
577
582
 
578
583
  while not self._shutdown_event.is_set():
579
584
  try:
580
- executed = await self._poll_once()
585
+ task = asyncio.create_task(self._poll_once())
586
+ self._current_task = task
587
+ try:
588
+ executed = await task
589
+ except asyncio.CancelledError:
590
+ break
591
+ finally:
592
+ self._current_task = None
581
593
 
582
594
  if executed:
583
595
  self._consecutive_errors = 0
@@ -696,13 +708,27 @@ class AgentLoop(CommandExecutor):
696
708
  """Set up SIGINT/SIGTERM handlers for graceful shutdown."""
697
709
  loop = asyncio.get_running_loop()
698
710
 
699
- def _signal_handler() -> None:
700
- logger.info("Received shutdown signal")
701
- console.print("\n[yellow]Received shutdown signal, finishing current work...[/yellow]")
711
+ def _sigint_handler() -> None:
712
+ self._signal_count += 1
713
+ if self._signal_count == 1:
714
+ logger.info("Received shutdown signal")
715
+ console.print(
716
+ "\n[yellow]Received shutdown signal, finishing current work...[/yellow]"
717
+ )
718
+ console.print("[dim]Press Ctrl-C again to force stop[/dim]")
719
+ self._shutdown_event.set()
720
+ else:
721
+ logger.info("Force stop requested")
722
+ console.print("\n[red]Force stopping...[/red]")
723
+ if self._current_task and not self._current_task.done():
724
+ self._current_task.cancel()
725
+
726
+ def _sigterm_handler() -> None:
727
+ logger.info("Received SIGTERM")
702
728
  self._shutdown_event.set()
703
729
 
704
- for sig in (signal.SIGINT, signal.SIGTERM):
705
- loop.add_signal_handler(sig, _signal_handler)
730
+ loop.add_signal_handler(signal.SIGINT, _sigint_handler)
731
+ loop.add_signal_handler(signal.SIGTERM, _sigterm_handler)
706
732
 
707
733
  async def _shutdown(self) -> None:
708
734
  """Clean up resources and send offline heartbeat."""
@@ -790,6 +816,8 @@ class WorkspaceAgentLoop(CommandExecutor):
790
816
  # State
791
817
  self._agent_id: str | None = None
792
818
  self._shutdown_event = asyncio.Event()
819
+ self._current_task: asyncio.Task[bool] | None = None
820
+ self._signal_count = 0
793
821
  self._is_busy = False
794
822
  self._consecutive_errors = 0
795
823
  self._commands_executed = 0
@@ -956,7 +984,14 @@ class WorkspaceAgentLoop(CommandExecutor):
956
984
 
957
985
  while not self._shutdown_event.is_set():
958
986
  try:
959
- executed = await self._poll_once()
987
+ task = asyncio.create_task(self._poll_once())
988
+ self._current_task = task
989
+ try:
990
+ executed = await task
991
+ except asyncio.CancelledError:
992
+ break
993
+ finally:
994
+ self._current_task = None
960
995
 
961
996
  if executed:
962
997
  self._consecutive_errors = 0
@@ -1099,13 +1134,27 @@ class WorkspaceAgentLoop(CommandExecutor):
1099
1134
  """Set up SIGINT/SIGTERM handlers for graceful shutdown."""
1100
1135
  loop = asyncio.get_running_loop()
1101
1136
 
1102
- def _signal_handler() -> None:
1103
- logger.info("Received shutdown signal")
1104
- console.print("\n[yellow]Received shutdown signal, finishing current work...[/yellow]")
1137
+ def _sigint_handler() -> None:
1138
+ self._signal_count += 1
1139
+ if self._signal_count == 1:
1140
+ logger.info("Received shutdown signal")
1141
+ console.print(
1142
+ "\n[yellow]Received shutdown signal, finishing current work...[/yellow]"
1143
+ )
1144
+ console.print("[dim]Press Ctrl-C again to force stop[/dim]")
1145
+ self._shutdown_event.set()
1146
+ else:
1147
+ logger.info("Force stop requested")
1148
+ console.print("\n[red]Force stopping...[/red]")
1149
+ if self._current_task and not self._current_task.done():
1150
+ self._current_task.cancel()
1151
+
1152
+ def _sigterm_handler() -> None:
1153
+ logger.info("Received SIGTERM")
1105
1154
  self._shutdown_event.set()
1106
1155
 
1107
- for sig in (signal.SIGINT, signal.SIGTERM):
1108
- loop.add_signal_handler(sig, _signal_handler)
1156
+ loop.add_signal_handler(signal.SIGINT, _sigint_handler)
1157
+ loop.add_signal_handler(signal.SIGTERM, _sigterm_handler)
1109
1158
 
1110
1159
  async def _shutdown(self) -> None:
1111
1160
  """Clean up resources and send offline heartbeat."""
@@ -235,6 +235,7 @@ class Runner:
235
235
  force_workflow_id: str | None = None,
236
236
  dry_run: bool = False,
237
237
  retry_config: RetryConfig | None = None,
238
+ shutdown_event: asyncio.Event | None = None,
238
239
  ) -> None:
239
240
  """Initialize the runner.
240
241
 
@@ -268,6 +269,7 @@ class Runner:
268
269
  self.workflow_id = force_workflow_id
269
270
  self.dry_run = dry_run
270
271
  self._retry_config = retry_config or RetryConfig()
272
+ self._shutdown_event = shutdown_event
271
273
 
272
274
  # Executor configuration
273
275
  self._executor_config = executor_config or ExecutorConfig()
@@ -673,6 +675,7 @@ class Runner:
673
675
  project_id=self.project_id,
674
676
  agent_type=self.agent_type,
675
677
  agent_name=self.agent_name,
678
+ shutdown_event=self._shutdown_event,
676
679
  )
677
680
 
678
681
  try:
@@ -50,6 +50,7 @@ class WorkflowExecutor:
50
50
  project_id: str | None = None,
51
51
  agent_type: str = "claude",
52
52
  agent_name: str | None = None,
53
+ shutdown_event: asyncio.Event | None = None,
53
54
  ) -> None:
54
55
  """Initialize the workflow executor.
55
56
 
@@ -63,6 +64,7 @@ class WorkflowExecutor:
63
64
  project_id: Project ID for session tracking.
64
65
  agent_type: Agent type for session tracking.
65
66
  agent_name: Agent name for session tracking (resolves to agentId).
67
+ shutdown_event: Optional event to signal graceful shutdown between phases.
66
68
  """
67
69
  self.working_directory = Path(working_directory)
68
70
  self._api_key = api_key
@@ -73,6 +75,7 @@ class WorkflowExecutor:
73
75
  self.project_id = project_id
74
76
  self.agent_type = agent_type
75
77
  self.agent_name = agent_name
78
+ self._shutdown_event = shutdown_event
76
79
 
77
80
  self._memory_manager = WorkflowMemoryManager(working_directory)
78
81
  self._workflows_client: WorkflowsClient | None = None
@@ -492,6 +495,11 @@ Complete this phase according to its type: {phase.phase_type}
492
495
  phases_failed = 0
493
496
 
494
497
  for phase in phases:
498
+ if self._shutdown_event and self._shutdown_event.is_set():
499
+ console.print("[yellow]Shutdown requested, stopping workflow[/yellow]")
500
+ phases_failed += 1
501
+ break
502
+
495
503
  result = await self.execute_phase(
496
504
  workflow_run=workflow_run,
497
505
  phase=phase,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes