stravinsky 0.4.41__tar.gz → 0.4.42__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.

Potentially problematic release.


This version of stravinsky might be problematic. Click here for more details.

Files changed (147) hide show
  1. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/tool_messaging.py +7 -0
  2. stravinsky-0.4.42/.claude/task_dependencies.json +39 -0
  3. {stravinsky-0.4.41 → stravinsky-0.4.42}/PKG-INFO +1 -1
  4. stravinsky-0.4.42/mcp_bridge/__init__.py +1 -0
  5. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/server.py +35 -20
  6. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/server_tools.py +27 -3
  7. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/agent_manager.py +121 -4
  8. {stravinsky-0.4.41 → stravinsky-0.4.42}/pyproject.toml +1 -1
  9. stravinsky-0.4.41/.claude/task_dependencies.json +0 -9
  10. stravinsky-0.4.41/mcp_bridge/__init__.py +0 -1
  11. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/HOOKS_INTEGRATION.md +0 -0
  12. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/HOOKS.md +0 -0
  13. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/code-reviewer.md +0 -0
  14. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/debugger.md +0 -0
  15. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/delphi.md +0 -0
  16. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/dewey.md +0 -0
  17. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/explore.md +0 -0
  18. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/frontend.md +0 -0
  19. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/implementation-lead.md +0 -0
  20. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/research-lead.md +0 -0
  21. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/agents/stravinsky.md +0 -0
  22. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/delphi.md +0 -0
  23. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/dewey.md +0 -0
  24. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/git-master.md +0 -0
  25. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/index.md +0 -0
  26. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/publish.md +0 -0
  27. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/review.md +0 -0
  28. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/cancel.md +0 -0
  29. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/clean.md +0 -0
  30. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/index.md +0 -0
  31. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/list_watchers.md +0 -0
  32. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/search.md +0 -0
  33. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/start_filewatch.md +0 -0
  34. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/stats.md +0 -0
  35. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/stop_filewatch.md +0 -0
  36. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/unwatch.md +0 -0
  37. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/str/watch.md +0 -0
  38. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/strav.md +0 -0
  39. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/verify.md +0 -0
  40. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/commands/version.md +0 -0
  41. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/README.md +0 -0
  42. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/comment_checker.py +0 -0
  43. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/context.py +0 -0
  44. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/context_monitor.py +0 -0
  45. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/dependency_tracker.py +0 -0
  46. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/edit_recovery.py +0 -0
  47. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/execution_state_tracker.py +0 -0
  48. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/notification_hook.py +0 -0
  49. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/notification_hook_v2.py +0 -0
  50. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/parallel_execution.py +0 -0
  51. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/parallel_reinforcement.py +0 -0
  52. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/parallel_reinforcement_v2.py +0 -0
  53. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/pre_compact.py +0 -0
  54. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/session_recovery.py +0 -0
  55. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/stravinsky_mode.py +0 -0
  56. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/subagent_stop.py +0 -0
  57. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/todo_continuation.py +0 -0
  58. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/todo_delegation.py +0 -0
  59. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/hooks/truncator.py +0 -0
  60. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/rules/deployment_safety.md +0 -0
  61. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/rules/pypi_deployment.md +0 -0
  62. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/settings.json +0 -0
  63. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/skills/chrome-devtools/SKILL.md +0 -0
  64. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/skills/sqlite/SKILL.md +0 -0
  65. {stravinsky-0.4.41 → stravinsky-0.4.42}/.claude/skills/supabase/SKILL.md +0 -0
  66. {stravinsky-0.4.41 → stravinsky-0.4.42}/.gitignore +0 -0
  67. {stravinsky-0.4.41 → stravinsky-0.4.42}/README.md +0 -0
  68. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/__init__.py +0 -0
  69. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/cli.py +0 -0
  70. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/oauth.py +0 -0
  71. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/openai_oauth.py +0 -0
  72. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/token_refresh.py +0 -0
  73. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/auth/token_store.py +0 -0
  74. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/cli/__init__.py +0 -0
  75. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/cli/install_hooks.py +0 -0
  76. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/cli/session_report.py +0 -0
  77. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/MANIFEST_SCHEMA.md +0 -0
  78. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/README.md +0 -0
  79. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/__init__.py +0 -0
  80. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/hook_config.py +0 -0
  81. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/hooks.py +0 -0
  82. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/hooks_manifest.json +0 -0
  83. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/rate_limits.py +0 -0
  84. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/config/skills_manifest.json +0 -0
  85. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/HOOKS_SETTINGS.json +0 -0
  86. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/README.md +0 -0
  87. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/__init__.py +0 -0
  88. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/agent_reminder.py +0 -0
  89. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/auto_slash_command.py +0 -0
  90. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/budget_optimizer.py +0 -0
  91. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/comment_checker.py +0 -0
  92. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/compaction.py +0 -0
  93. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/context.py +0 -0
  94. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/context_monitor.py +0 -0
  95. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/directory_context.py +0 -0
  96. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/edit_recovery.py +0 -0
  97. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/empty_message_sanitizer.py +0 -0
  98. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/git_noninteractive.py +0 -0
  99. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/keyword_detector.py +0 -0
  100. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/manager.py +0 -0
  101. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/notification_hook.py +0 -0
  102. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/parallel_enforcer.py +0 -0
  103. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/parallel_execution.py +0 -0
  104. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/pre_compact.py +0 -0
  105. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/preemptive_compaction.py +0 -0
  106. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/rules_injector.py +0 -0
  107. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/session_idle.py +0 -0
  108. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/session_notifier.py +0 -0
  109. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/session_recovery.py +0 -0
  110. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/stravinsky_mode.py +0 -0
  111. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/subagent_stop.py +0 -0
  112. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/task_validator.py +0 -0
  113. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/tmux_manager.py +0 -0
  114. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/todo_continuation.py +0 -0
  115. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/todo_delegation.py +0 -0
  116. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/todo_enforcer.py +0 -0
  117. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/tool_messaging.py +0 -0
  118. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/hooks/truncator.py +0 -0
  119. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/notifications.py +0 -0
  120. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/__init__.py +0 -0
  121. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/delphi.py +0 -0
  122. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/dewey.py +0 -0
  123. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/document_writer.py +0 -0
  124. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/explore.py +0 -0
  125. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/frontend.py +0 -0
  126. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/multimodal.py +0 -0
  127. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/planner.py +0 -0
  128. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/prompts/stravinsky.py +0 -0
  129. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/__init__.py +0 -0
  130. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/background_tasks.py +0 -0
  131. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/code_search.py +0 -0
  132. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/continuous_loop.py +0 -0
  133. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/init.py +0 -0
  134. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/lsp/__init__.py +0 -0
  135. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/lsp/manager.py +0 -0
  136. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/lsp/tools.py +0 -0
  137. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/model_invoke.py +0 -0
  138. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/project_context.py +0 -0
  139. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/query_classifier.py +0 -0
  140. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/semantic_search.py +0 -0
  141. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/session_manager.py +0 -0
  142. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/skill_loader.py +0 -0
  143. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/task_runner.py +0 -0
  144. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/tools/templates.py +0 -0
  145. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/update_manager.py +0 -0
  146. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/update_manager_pypi.py +0 -0
  147. {stravinsky-0.4.41 → stravinsky-0.4.42}/mcp_bridge/utils/__init__.py +0 -0
@@ -216,6 +216,13 @@ def extract_description(tool_name: str, params: dict) -> str:
216
216
  repo = params.get("repo", "")
217
217
  return f"Fetching {path} from {repo}"
218
218
 
219
+ # Agent spawn (MCP tool)
220
+ if "agent_spawn" in tool_name:
221
+ agent_type = params.get("agent_type", "unknown")
222
+ description = params.get("description", "")
223
+ model = AGENT_MODELS.get(agent_type, "gemini-3-flash")
224
+ return f"{agent_type}({model})"
225
+
219
226
  # Task delegation
220
227
  if tool_name == "Task":
221
228
  subagent_type = params.get("subagent_type", "unknown")
@@ -0,0 +1,39 @@
1
+ {
2
+ "dependencies": {
3
+ "1": {
4
+ "deps": [],
5
+ "independent": true,
6
+ "parallel_safe": true
7
+ },
8
+ "2": {
9
+ "deps": [],
10
+ "independent": true,
11
+ "parallel_safe": true
12
+ },
13
+ "3": {
14
+ "deps": [],
15
+ "independent": false,
16
+ "parallel_safe": false
17
+ },
18
+ "4": {
19
+ "deps": [],
20
+ "independent": true,
21
+ "parallel_safe": true
22
+ },
23
+ "5": {
24
+ "deps": [],
25
+ "independent": true,
26
+ "parallel_safe": true
27
+ },
28
+ "6": {
29
+ "deps": [],
30
+ "independent": true,
31
+ "parallel_safe": true
32
+ },
33
+ "7": {
34
+ "deps": [],
35
+ "independent": false,
36
+ "parallel_safe": true
37
+ }
38
+ }
39
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stravinsky
3
- Version: 0.4.41
3
+ Version: 0.4.42
4
4
  Summary: MCP Bridge for Claude Code with Multi-Model Support. Install globally: claude mcp add --scope user stravinsky -- uvx stravinsky. Add to CLAUDE.md: See https://pypi.org/project/stravinsky/
5
5
  Project-URL: Repository, https://github.com/GratefulDave/stravinsky
6
6
  Project-URL: Issues, https://github.com/GratefulDave/stravinsky/issues
@@ -0,0 +1 @@
1
+ __version__ = "0.4.42"
@@ -314,7 +314,15 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
314
314
  elif name == "agent_list":
315
315
  from .tools.agent_manager import agent_list
316
316
 
317
- result_content = await agent_list()
317
+ result_content = await agent_list(show_all=arguments.get("show_all", True))
318
+
319
+ elif name == "agent_cleanup":
320
+ from .tools.agent_manager import agent_cleanup
321
+
322
+ result_content = await agent_cleanup(
323
+ max_age_minutes=arguments.get("max_age_minutes", 30),
324
+ statuses=arguments.get("statuses"),
325
+ )
318
326
 
319
327
  elif name == "agent_progress":
320
328
  from .tools.agent_manager import agent_progress
@@ -507,27 +515,34 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
507
515
  debounce_seconds=arguments.get("debounce_seconds", 2.0),
508
516
  )
509
517
 
510
- result_content = json.dumps({
511
- "status": "started",
512
- "project_path": str(watcher.project_path),
513
- "debounce_seconds": watcher.debounce_seconds,
514
- "provider": watcher.store.provider_name,
515
- "is_running": watcher.is_running()
516
- }, indent=2)
518
+ result_content = json.dumps(
519
+ {
520
+ "status": "started",
521
+ "project_path": str(watcher.project_path),
522
+ "debounce_seconds": watcher.debounce_seconds,
523
+ "provider": watcher.store.provider_name,
524
+ "is_running": watcher.is_running(),
525
+ },
526
+ indent=2,
527
+ )
517
528
  except ValueError as e:
518
529
  # No index exists
519
- result_content = json.dumps({
520
- "error": str(e),
521
- "hint": "Run semantic_index() before starting file watcher"
522
- }, indent=2)
530
+ result_content = json.dumps(
531
+ {"error": str(e), "hint": "Run semantic_index() before starting file watcher"},
532
+ indent=2,
533
+ )
523
534
  print(f"⚠️ start_file_watcher ValueError: {e}", file=sys.stderr)
524
535
  except Exception as e:
525
536
  # Unexpected error
526
537
  import traceback
527
- result_content = json.dumps({
528
- "error": f"{type(e).__name__}: {str(e)}",
529
- "hint": "Check MCP server logs for details"
530
- }, indent=2)
538
+
539
+ result_content = json.dumps(
540
+ {
541
+ "error": f"{type(e).__name__}: {str(e)}",
542
+ "hint": "Check MCP server logs for details",
543
+ },
544
+ indent=2,
545
+ )
531
546
  print(f"❌ start_file_watcher error: {e}", file=sys.stderr)
532
547
  traceback.print_exc(file=sys.stderr)
533
548
 
@@ -540,10 +555,9 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
540
555
  project_path=arguments.get("project_path", "."),
541
556
  )
542
557
 
543
- result_content = json.dumps({
544
- "stopped": stopped,
545
- "project_path": arguments.get("project_path", ".")
546
- }, indent=2)
558
+ result_content = json.dumps(
559
+ {"stopped": stopped, "project_path": arguments.get("project_path", ".")}, indent=2
560
+ )
547
561
 
548
562
  elif name == "cancel_indexing":
549
563
  from .tools.semantic_search import cancel_indexing
@@ -724,6 +738,7 @@ def sync_user_assets():
724
738
  # Try importlib.resources as last resort (Python 3.9+)
725
739
  try:
726
740
  import importlib.resources as resources
741
+
727
742
  # Check if stravinsky_claude_assets is a package
728
743
  with resources.files("stravinsky_claude_assets") as assets_path:
729
744
  if assets_path.is_dir():
@@ -1,4 +1,3 @@
1
-
2
1
  from mcp.types import Prompt, Tool
3
2
 
4
3
 
@@ -466,10 +465,35 @@ def get_tool_definitions() -> list[Tool]:
466
465
  ),
467
466
  Tool(
468
467
  name="agent_list",
469
- description="List all background agent tasks with their status.",
468
+ description="List all background agent tasks with their status. By default shows all agents; use show_all=false to see only running/pending.",
470
469
  inputSchema={
471
470
  "type": "object",
472
- "properties": {},
471
+ "properties": {
472
+ "show_all": {
473
+ "type": "boolean",
474
+ "description": "If false, only show running/pending agents. If true (default), show all.",
475
+ "default": True,
476
+ },
477
+ },
478
+ },
479
+ ),
480
+ Tool(
481
+ name="agent_cleanup",
482
+ description="Clean up old completed/failed/cancelled agents to reduce clutter in agent_list. Removes agents older than max_age_minutes.",
483
+ inputSchema={
484
+ "type": "object",
485
+ "properties": {
486
+ "max_age_minutes": {
487
+ "type": "integer",
488
+ "description": "Remove agents older than this many minutes (default: 30)",
489
+ "default": 30,
490
+ },
491
+ "statuses": {
492
+ "type": "array",
493
+ "items": {"type": "string"},
494
+ "description": "List of statuses to remove (default: ['completed', 'failed', 'cancelled'])",
495
+ },
496
+ },
473
497
  },
474
498
  ),
475
499
  Tool(
@@ -258,14 +258,29 @@ class AgentManager:
258
258
  tasks = self._load_tasks()
259
259
  return tasks.get(task_id)
260
260
 
261
- def list_tasks(self, parent_session_id: str | None = None) -> list[dict[str, Any]]:
262
- """List all tasks, optionally filtered by parent session."""
261
+ def list_tasks(
262
+ self, parent_session_id: str | None = None, show_all: bool = True
263
+ ) -> list[dict[str, Any]]:
264
+ """
265
+ List tasks, optionally filtered by parent session and status.
266
+
267
+ Args:
268
+ parent_session_id: Optional filter by parent session
269
+ show_all: If False, only show running/pending agents. If True (default), show all.
270
+
271
+ Returns:
272
+ List of task dictionaries
273
+ """
263
274
  tasks = self._load_tasks()
264
275
  task_list = list(tasks.values())
265
276
 
266
277
  if parent_session_id:
267
278
  task_list = [t for t in task_list if t.get("parent_session_id") == parent_session_id]
268
279
 
280
+ # Filter by status if not showing all
281
+ if not show_all:
282
+ task_list = [t for t in task_list if t.get("status") in ["running", "pending"]]
283
+
269
284
  return task_list
270
285
 
271
286
  def spawn(
@@ -453,6 +468,8 @@ class AgentManager:
453
468
 
454
469
  except FileNotFoundError:
455
470
  error_msg = f"Claude CLI not found at {self.CLAUDE_CLI}. Install with: npm install -g @anthropic-ai/claude-code"
471
+ # Ensure agents directory exists before writing log files
472
+ self.agents_dir.mkdir(parents=True, exist_ok=True)
456
473
  log_file.write_text(error_msg)
457
474
  # Write error to .out file for debugging
458
475
  output_file.write_text(f"❌ FILE NOT FOUND: {error_msg}")
@@ -466,6 +483,8 @@ class AgentManager:
466
483
 
467
484
  except Exception as e:
468
485
  error_msg = str(e)
486
+ # Ensure agents directory exists before writing log files
487
+ self.agents_dir.mkdir(parents=True, exist_ok=True)
469
488
  log_file.write_text(error_msg)
470
489
  # Write error to .out file for debugging
471
490
  output_file.write_text(f"❌ EXCEPTION: {error_msg}")
@@ -569,6 +588,76 @@ class AgentManager:
569
588
 
570
589
  return stopped_count
571
590
 
591
+ def cleanup(
592
+ self,
593
+ max_age_minutes: int = 30,
594
+ statuses: list[str] | None = None,
595
+ ) -> dict[str, Any]:
596
+ """
597
+ Clean up old completed/failed/cancelled agents.
598
+
599
+ Args:
600
+ max_age_minutes: Remove tasks older than this (default: 30 minutes)
601
+ statuses: List of statuses to remove (default: ['completed', 'failed', 'cancelled'])
602
+
603
+ Returns:
604
+ {
605
+ "removed": int,
606
+ "task_ids": list[str],
607
+ "summary": str
608
+ }
609
+ """
610
+ if statuses is None:
611
+ statuses = ["completed", "failed", "cancelled"]
612
+
613
+ tasks = self._load_tasks()
614
+ now = datetime.now()
615
+ removed_ids = []
616
+
617
+ for task_id, task in list(tasks.items()):
618
+ # Skip if status not in cleanup list
619
+ if task.get("status") not in statuses:
620
+ continue
621
+
622
+ # Check age
623
+ completed_at = task.get("completed_at")
624
+ if not completed_at:
625
+ continue
626
+
627
+ try:
628
+ completed_time = datetime.fromisoformat(completed_at)
629
+ age_minutes = (now - completed_time).total_seconds() / 60
630
+
631
+ if age_minutes > max_age_minutes:
632
+ removed_ids.append(task_id)
633
+ del tasks[task_id]
634
+
635
+ # Clean up associated files
636
+ task_files = [
637
+ self.agents_dir / f"{task_id}.log",
638
+ self.agents_dir / f"{task_id}.out",
639
+ self.agents_dir / f"{task_id}.system",
640
+ ]
641
+ for f in task_files:
642
+ if f.exists():
643
+ try:
644
+ f.unlink()
645
+ except Exception:
646
+ pass
647
+ except (ValueError, AttributeError):
648
+ # Invalid timestamp, skip
649
+ continue
650
+
651
+ # Save updated tasks
652
+ if removed_ids:
653
+ self._save_tasks(tasks)
654
+
655
+ return {
656
+ "removed": len(removed_ids),
657
+ "task_ids": removed_ids,
658
+ "summary": f"Removed {len(removed_ids)} agent(s) older than {max_age_minutes} minutes",
659
+ }
660
+
572
661
  def get_output(self, task_id: str, block: bool = False, timeout: float = 30.0) -> str:
573
662
  """
574
663
  Get output from an agent task.
@@ -1086,15 +1175,43 @@ async def agent_cancel(task_id: str) -> str:
1086
1175
  return f"⚠️ Task {task_id} is not running (status: {task['status']}). Cannot cancel."
1087
1176
 
1088
1177
 
1089
- async def agent_list() -> str:
1178
+ async def agent_cleanup(max_age_minutes: int = 30, statuses: list[str] | None = None) -> str:
1179
+ """
1180
+ Clean up old completed/failed/cancelled agents.
1181
+
1182
+ Args:
1183
+ max_age_minutes: Remove agents older than this many minutes (default: 30)
1184
+ statuses: List of statuses to remove (default: ['completed', 'failed', 'cancelled'])
1185
+
1186
+ Returns:
1187
+ Formatted cleanup summary
1188
+ """
1189
+ manager = get_manager()
1190
+ result = manager.cleanup(max_age_minutes=max_age_minutes, statuses=statuses)
1191
+
1192
+ if result["removed"] == 0:
1193
+ return f"🧹 No agents older than {max_age_minutes} minutes to clean up."
1194
+
1195
+ return (
1196
+ f"🧹 {Colors.BOLD}Cleanup Complete{Colors.RESET}\n\n"
1197
+ f"{result['summary']}\n\n"
1198
+ f"Removed agents:\n"
1199
+ + "\n".join(f" • {Colors.BRIGHT_BLACK}{tid}{Colors.RESET}" for tid in result["task_ids"])
1200
+ )
1201
+
1202
+
1203
+ async def agent_list(show_all: bool = True) -> str:
1090
1204
  """
1091
1205
  List all background agent tasks.
1092
1206
 
1207
+ Args:
1208
+ show_all: If False, only show running/pending agents. If True (default), show all.
1209
+
1093
1210
  Returns:
1094
1211
  Formatted list of tasks
1095
1212
  """
1096
1213
  manager = get_manager()
1097
- tasks = manager.list_tasks()
1214
+ tasks = manager.list_tasks(show_all=show_all)
1098
1215
 
1099
1216
  if not tasks:
1100
1217
  return "No background agent tasks found."
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stravinsky"
3
- version = "0.4.41"
3
+ version = "0.4.42"
4
4
  description = "MCP Bridge for Claude Code with Multi-Model Support. Install globally: claude mcp add --scope user stravinsky -- uvx stravinsky. Add to CLAUDE.md: See https://pypi.org/project/stravinsky/"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11,<3.14"
@@ -1,9 +0,0 @@
1
- {
2
- "dependencies": {
3
- "null": {
4
- "deps": [],
5
- "independent": false,
6
- "parallel_safe": false
7
- }
8
- }
9
- }
@@ -1 +0,0 @@
1
- __version__ = "0.4.41"
File without changes
File without changes