steerdev 1.1.25__tar.gz → 1.1.26__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 (125) hide show
  1. {steerdev-1.1.25 → steerdev-1.1.26}/PKG-INFO +1 -1
  2. {steerdev-1.1.25 → steerdev-1.1.26}/pyproject.toml +1 -1
  3. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/claude_setup.py +46 -4
  4. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workspace/project_manager.py +17 -0
  5. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_claude_setup.py +70 -0
  6. {steerdev-1.1.25 → steerdev-1.1.26}/.github/workflows/pre-commit.yml +0 -0
  7. {steerdev-1.1.25 → steerdev-1.1.26}/.github/workflows/publish.yml +0 -0
  8. {steerdev-1.1.25 → steerdev-1.1.26}/.gitignore +0 -0
  9. {steerdev-1.1.25 → steerdev-1.1.26}/.pre-commit-config.yaml +0 -0
  10. {steerdev-1.1.25 → steerdev-1.1.26}/AGENTS.md +0 -0
  11. {steerdev-1.1.25 → steerdev-1.1.26}/CLAUDE.md +0 -0
  12. {steerdev-1.1.25 → steerdev-1.1.26}/README.md +0 -0
  13. {steerdev-1.1.25 → steerdev-1.1.26}/docs/superpowers/plans/2026-06-05-wave-id-implies-unlimited-max-tasks.md +0 -0
  14. {steerdev-1.1.25 → steerdev-1.1.26}/docs/superpowers/specs/2026-06-05-wave-id-implies-unlimited-max-tasks-design.md +0 -0
  15. {steerdev-1.1.25 → steerdev-1.1.26}/scripts/pre-commit-version-bump.sh +0 -0
  16. {steerdev-1.1.25 → steerdev-1.1.26}/snapshots/steerdev-agent-v1/Dockerfile +0 -0
  17. {steerdev-1.1.25 → steerdev-1.1.26}/snapshots/steerdev-agent-v1/start-agent.sh +0 -0
  18. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/__init__.py +0 -0
  19. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/agent_loop.py +0 -0
  20. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/__init__.py +0 -0
  21. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/activity.py +0 -0
  22. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/agents.py +0 -0
  23. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/canals.py +0 -0
  24. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/client.py +0 -0
  25. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/commands.py +0 -0
  26. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/configs.py +0 -0
  27. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/context.py +0 -0
  28. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/events.py +0 -0
  29. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/hooks.py +0 -0
  30. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/implementation_plan.py +0 -0
  31. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/merger.py +0 -0
  32. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/messages.py +0 -0
  33. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/prd.py +0 -0
  34. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/reports.py +0 -0
  35. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/runs.py +0 -0
  36. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/sessions.py +0 -0
  37. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/specs.py +0 -0
  38. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/tasks.py +0 -0
  39. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/workflow_runs.py +0 -0
  40. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/api/workflows.py +0 -0
  41. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/cli.py +0 -0
  42. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/config/__init__.py +0 -0
  43. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/config/models.py +0 -0
  44. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/config/platform.py +0 -0
  45. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/config/settings.py +0 -0
  46. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/evidence.py +0 -0
  47. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/evidence_assets.py +0 -0
  48. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/executor/__init__.py +0 -0
  49. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/executor/base.py +0 -0
  50. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/executor/claude.py +0 -0
  51. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/executor/stream.py +0 -0
  52. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/git_ops.py +0 -0
  53. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/handlers/__init__.py +0 -0
  54. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/handlers/prd.py +0 -0
  55. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/integration.py +0 -0
  56. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/prompt/__init__.py +0 -0
  57. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/prompt/builder.py +0 -0
  58. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/prompt/templates.py +0 -0
  59. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/prompt/workflow_template.py +0 -0
  60. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/py.typed +0 -0
  61. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/retry.py +0 -0
  62. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/runner.py +0 -0
  63. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/__init__.py +0 -0
  64. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/repo_setup.py +0 -0
  65. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/ci/canal-integration.yml +0 -0
  66. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/claude_md_section.md +0 -0
  67. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/settings.json +0 -0
  68. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-activity-skill/SKILL.md +0 -0
  69. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-canal-workflow-skill/SKILL.md +0 -0
  70. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-context-skill/SKILL.md +0 -0
  71. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-git-workflow-skill/SKILL.md +0 -0
  72. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-merge-into-canal-skill/SKILL.md +0 -0
  73. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-progress-logging-skill/SKILL.md +0 -0
  74. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-single-task-merge-skill/SKILL.md +0 -0
  75. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-specs-management-skill/SKILL.md +0 -0
  76. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-task-management-skill/SKILL.md +0 -0
  77. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/skills/steerdev-wave-tasks-merge-skill/SKILL.md +0 -0
  78. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/steerdev.yaml +0 -0
  79. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/setup/templates/worktrunk.config.toml +0 -0
  80. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/update_check.py +0 -0
  81. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/version.py +0 -0
  82. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workflow/__init__.py +0 -0
  83. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workflow/context.py +0 -0
  84. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workflow/executor.py +0 -0
  85. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workflow/memory.py +0 -0
  86. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workspace/__init__.py +0 -0
  87. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workspace/preparation.py +0 -0
  88. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/workspace/tool_detection.py +0 -0
  89. {steerdev-1.1.25 → steerdev-1.1.26}/src/steerdev_agent/worktree.py +0 -0
  90. {steerdev-1.1.25 → steerdev-1.1.26}/tests/__init__.py +0 -0
  91. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_agent_loop.py +0 -0
  92. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_agent_loop_extended.py +0 -0
  93. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_agents_api.py +0 -0
  94. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_api_client.py +0 -0
  95. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_claude_executor.py +0 -0
  96. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_client_methods.py +0 -0
  97. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_commands_api.py +0 -0
  98. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_config.py +0 -0
  99. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_config_extended.py +0 -0
  100. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_conflict_mitigation.py +0 -0
  101. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_context_search.py +0 -0
  102. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_executor.py +0 -0
  103. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_git_ops.py +0 -0
  104. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_hooks_post_tool_use.py +0 -0
  105. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_platform_config.py +0 -0
  106. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_preparation.py +0 -0
  107. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_preparation_provenance.py +0 -0
  108. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_prompt.py +0 -0
  109. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_reports_client.py +0 -0
  110. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_resolve_max_tasks.py +0 -0
  111. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_retry.py +0 -0
  112. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_runner_merge_modes.py +0 -0
  113. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_runner_worktrees.py +0 -0
  114. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_stream_parser.py +0 -0
  115. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_tasks.py +0 -0
  116. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_tasks_update_branch.py +0 -0
  117. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_version.py +0 -0
  118. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_wave_scoped_fetch.py +0 -0
  119. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workflow_context.py +0 -0
  120. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workflow_memory.py +0 -0
  121. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workflow_prompt_template.py +0 -0
  122. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workflow_runs_api.py +0 -0
  123. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workspace.py +0 -0
  124. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_workspace_extended.py +0 -0
  125. {steerdev-1.1.25 → steerdev-1.1.26}/tests/test_worktree.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steerdev
3
- Version: 1.1.25
3
+ Version: 1.1.26
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.1.25"
3
+ version = "1.1.26"
4
4
  description = "Backend task runner for steerdev.com - orchestrates CLI coding agents with activity reporting"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -14,6 +14,43 @@ from loguru import logger
14
14
 
15
15
  from steerdev_agent.config.platform import PlatformConfig
16
16
 
17
+ # Commands owned by steerdev are reconciled to the template; anything else in
18
+ # an existing settings file is treated as user-added and left untouched.
19
+ _STEERDEV_HOOK_PREFIX = "steerdev hooks"
20
+
21
+
22
+ def _is_steerdev_hook_entry(entry: dict) -> bool:
23
+ """Return True if a hook matcher-group entry is steerdev-owned.
24
+
25
+ An entry is steerdev-owned when any of its commands invoke
26
+ `steerdev hooks ...`. Used to reconcile only our own hooks while
27
+ preserving user-added hooks on the same event.
28
+ """
29
+ for hook in entry.get("hooks", []):
30
+ if isinstance(hook, dict) and str(hook.get("command", "")).strip().startswith(
31
+ _STEERDEV_HOOK_PREFIX
32
+ ):
33
+ return True
34
+ return False
35
+
36
+
37
+ def _merge_steerdev_hooks(existing_hooks: dict, template_hooks: dict) -> None:
38
+ """Reconcile steerdev-owned hooks into *existing_hooks* in place.
39
+
40
+ For each event in the template, drop any existing steerdev-owned entries
41
+ (so stale/missing definitions are replaced) while keeping user-added
42
+ entries, then append the template's entries. Events not in the template
43
+ are left untouched.
44
+ """
45
+ for event, template_entries in template_hooks.items():
46
+ user_entries = [
47
+ entry
48
+ for entry in existing_hooks.get(event, [])
49
+ if isinstance(entry, dict) and not _is_steerdev_hook_entry(entry)
50
+ ]
51
+ existing_hooks[event] = user_entries + template_entries
52
+
53
+
17
54
  # ---------------------------------------------------------------------------
18
55
  # CLI dependency checking
19
56
  # ---------------------------------------------------------------------------
@@ -319,13 +356,18 @@ class ClaudeSetup:
319
356
  if rule not in existing["permissions"]["allow"]:
320
357
  existing["permissions"]["allow"].append(rule)
321
358
 
322
- # Merge hooks configuration if include_hooks=True
359
+ # Reconcile hooks configuration if include_hooks=True.
360
+ #
361
+ # The template hooks are steerdev-owned (their commands all start
362
+ # with `steerdev hooks`). We reconcile them by command identity so
363
+ # that pre-existing settings files self-heal when new steerdev
364
+ # hooks are added (e.g. the PostToolUse checkpoint-push hook) or
365
+ # when an existing steerdev hook's definition changes. Any
366
+ # user-added hooks on the same event are preserved untouched.
323
367
  if include_hooks and "hooks" in new_settings:
324
368
  if "hooks" not in existing:
325
369
  existing["hooks"] = {}
326
- for hook_event, hook_configs in new_settings["hooks"].items():
327
- if hook_event not in existing["hooks"]:
328
- existing["hooks"][hook_event] = hook_configs
370
+ _merge_steerdev_hooks(existing["hooks"], new_settings["hooks"])
329
371
 
330
372
  settings_path.write_text(json.dumps(existing, indent=2) + "\n")
331
373
  logger.info(f"Updated existing settings: {settings_path}")
@@ -45,6 +45,10 @@ class ProjectManager:
45
45
  local_path = self._resolve_path(assignment)
46
46
  if local_path and local_path.is_dir():
47
47
  self._path_cache[assignment.project_id] = local_path
48
+ # Existing checkout: reconcile .claude config so settings files
49
+ # that predate newer steerdev hooks (e.g. the PostToolUse
50
+ # checkpoint-push hook) self-heal on every dispatch.
51
+ self._reconcile_settings(local_path)
48
52
  continue
49
53
 
50
54
  # Project not found locally — bootstrap it
@@ -55,6 +59,19 @@ class ProjectManager:
55
59
  except Exception:
56
60
  logger.exception(f"Failed to setup project {assignment.project_id}")
57
61
 
62
+ def _reconcile_settings(self, project_dir: Path) -> None:
63
+ """Re-apply steerdev-owned .claude/settings.json hooks for an existing
64
+ checkout. Idempotent and best-effort: a setup failure must never block
65
+ an otherwise-usable project.
66
+ """
67
+ from steerdev_agent.setup.claude_setup import ClaudeSetup
68
+
69
+ try:
70
+ setup = ClaudeSetup(project_dir, install_target="project")
71
+ setup.setup_settings(force=False, include_hooks=True)
72
+ except Exception:
73
+ logger.exception(f"Failed to reconcile .claude settings for {project_dir}")
74
+
58
75
  async def setup_project(self, assignment: ProjectAssignment) -> Path | None:
59
76
  """Bootstrap a project using existing setup primitives.
60
77
 
@@ -51,6 +51,76 @@ class TestClaudeSetup:
51
51
  ) # skill now references merge skills by name
52
52
 
53
53
 
54
+ class TestSettingsHookReconciliation:
55
+ """Tests for steerdev-owned hook reconciliation in setup_settings."""
56
+
57
+ @staticmethod
58
+ def _commands(settings: dict, event: str) -> list[str]:
59
+ commands = []
60
+ for entry in settings.get("hooks", {}).get(event, []):
61
+ for hook in entry.get("hooks", []):
62
+ commands.append(hook.get("command"))
63
+ return commands
64
+
65
+ def test_fresh_create_includes_post_tool_use(self, tmp_path):
66
+ import json
67
+
68
+ setup = ClaudeSetup(project_dir=tmp_path)
69
+
70
+ path = setup.setup_settings(force=False, include_hooks=True)
71
+ settings = json.loads(path.read_text())
72
+
73
+ assert "steerdev hooks post-tool-use" in self._commands(settings, "PostToolUse")
74
+
75
+ def test_stale_file_self_heals_post_tool_use(self, tmp_path):
76
+ """A settings file predating the PostToolUse hook gains it on re-run."""
77
+ import json
78
+
79
+ stale = {
80
+ "permissions": {"allow": ["Bash(git commit:*)"]},
81
+ "hooks": {
82
+ "SessionStart": [
83
+ {"hooks": [{"type": "command", "command": "steerdev hooks session-start"}]}
84
+ ]
85
+ },
86
+ }
87
+ claude_dir = tmp_path / ".claude"
88
+ claude_dir.mkdir(parents=True)
89
+ (claude_dir / "settings.json").write_text(json.dumps(stale))
90
+
91
+ setup = ClaudeSetup(project_dir=tmp_path)
92
+ path = setup.setup_settings(force=False, include_hooks=True)
93
+ settings = json.loads(path.read_text())
94
+
95
+ assert "steerdev hooks post-tool-use" in self._commands(settings, "PostToolUse")
96
+ # Pre-existing steerdev hook is reconciled, not duplicated.
97
+ assert self._commands(settings, "SessionStart").count("steerdev hooks session-start") == 1
98
+
99
+ def test_user_added_hooks_preserved(self, tmp_path):
100
+ import json
101
+
102
+ existing = {
103
+ "hooks": {
104
+ "PostToolUse": [
105
+ {
106
+ "matcher": "Write",
107
+ "hooks": [{"type": "command", "command": "my-custom-linter"}],
108
+ }
109
+ ]
110
+ },
111
+ }
112
+ claude_dir = tmp_path / ".claude"
113
+ claude_dir.mkdir(parents=True)
114
+ (claude_dir / "settings.json").write_text(json.dumps(existing))
115
+
116
+ setup = ClaudeSetup(project_dir=tmp_path)
117
+ path = setup.setup_settings(force=False, include_hooks=True)
118
+ commands = self._commands(json.loads(path.read_text()), "PostToolUse")
119
+
120
+ assert "my-custom-linter" in commands
121
+ assert "steerdev hooks post-tool-use" in commands
122
+
123
+
54
124
  class TestCheckCliDependencies:
55
125
  """Tests for CLI dependency checking."""
56
126
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes