overcode 0.1.5__tar.gz → 0.1.7__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 (92) hide show
  1. {overcode-0.1.5/src/overcode.egg-info → overcode-0.1.7}/PKG-INFO +1 -1
  2. {overcode-0.1.5 → overcode-0.1.7}/pyproject.toml +1 -1
  3. overcode-0.1.7/src/overcode/__init__.py +14 -0
  4. overcode-0.1.7/src/overcode/claude_config.py +139 -0
  5. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/cli.py +550 -17
  6. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/config.py +23 -0
  7. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_utils.py +9 -0
  8. overcode-0.1.7/src/overcode/dependency_check.py +111 -0
  9. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/history_reader.py +98 -21
  10. overcode-0.1.7/src/overcode/hook_handler.py +108 -0
  11. overcode-0.1.7/src/overcode/hook_status_detector.py +189 -0
  12. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/launcher.py +17 -72
  13. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon.py +189 -23
  14. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon_core.py +13 -7
  15. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon_state.py +56 -4
  16. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/protocols.py +28 -1
  17. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/session_manager.py +49 -1
  18. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/settings.py +18 -6
  19. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_constants.py +32 -14
  20. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_detector.py +75 -15
  21. overcode-0.1.7/src/overcode/status_detector_factory.py +73 -0
  22. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_patterns.py +146 -0
  23. overcode-0.1.7/src/overcode/summary_columns.py +701 -0
  24. overcode-0.1.7/src/overcode/summary_groups.py +132 -0
  25. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_daemon.py +17 -52
  26. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_daemon_core.py +6 -2
  27. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_layout.sh +1 -1
  28. overcode-0.1.7/src/overcode/time_context.py +313 -0
  29. overcode-0.1.7/src/overcode/tmux_utils.py +120 -0
  30. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui.py +520 -168
  31. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui.tcss +36 -0
  32. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/input.py +67 -3
  33. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/navigation.py +9 -10
  34. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/session.py +168 -170
  35. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/view.py +64 -5
  36. overcode-0.1.7/src/overcode/tui_formatters.py +235 -0
  37. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_helpers.py +24 -213
  38. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_logic.py +18 -12
  39. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_render.py +2 -1
  40. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/__init__.py +4 -0
  41. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/command_bar.py +138 -1
  42. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/daemon_panel.py +31 -15
  43. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/daemon_status_bar.py +70 -30
  44. overcode-0.1.7/src/overcode/tui_widgets/fullscreen_preview.py +111 -0
  45. overcode-0.1.7/src/overcode/tui_widgets/help_overlay.py +185 -0
  46. overcode-0.1.7/src/overcode/tui_widgets/preview_pane.py +103 -0
  47. overcode-0.1.7/src/overcode/tui_widgets/session_summary.py +437 -0
  48. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/status_timeline.py +53 -17
  49. overcode-0.1.7/src/overcode/tui_widgets/summary_config_modal.py +165 -0
  50. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_api.py +5 -2
  51. {overcode-0.1.5 → overcode-0.1.7/src/overcode.egg-info}/PKG-INFO +1 -1
  52. {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/SOURCES.txt +11 -0
  53. overcode-0.1.5/src/overcode/__init__.py +0 -5
  54. overcode-0.1.5/src/overcode/dependency_check.py +0 -227
  55. overcode-0.1.5/src/overcode/tui_widgets/help_overlay.py +0 -71
  56. overcode-0.1.5/src/overcode/tui_widgets/preview_pane.py +0 -69
  57. overcode-0.1.5/src/overcode/tui_widgets/session_summary.py +0 -514
  58. {overcode-0.1.5 → overcode-0.1.7}/LICENSE +0 -0
  59. {overcode-0.1.5 → overcode-0.1.7}/MANIFEST.in +0 -0
  60. {overcode-0.1.5 → overcode-0.1.7}/README.md +0 -0
  61. {overcode-0.1.5 → overcode-0.1.7}/setup.cfg +0 -0
  62. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_claude_skill.md +0 -0
  63. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_logging.py +0 -0
  64. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/data_export.py +0 -0
  65. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/exceptions.py +0 -0
  66. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/implementations.py +0 -0
  67. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/interfaces.py +0 -0
  68. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/logging_config.py +0 -0
  69. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/mocks.py +0 -0
  70. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/pid_utils.py +0 -0
  71. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/presence_logger.py +0 -0
  72. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/standing_instructions.py +0 -0
  73. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_history.py +0 -0
  74. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/summarizer_client.py +0 -0
  75. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/summarizer_component.py +0 -0
  76. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/__init__.py +0 -0
  77. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/renderer.py +0 -0
  78. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tmux_driver.py +0 -0
  79. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tui_eye.py +0 -0
  80. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tui_eye_skill.md +0 -0
  81. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tmux_manager.py +0 -0
  82. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/__init__.py +0 -0
  83. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/daemon.py +0 -0
  84. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_chartjs.py +0 -0
  85. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_server.py +0 -0
  86. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_server_runner.py +0 -0
  87. {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_templates.py +0 -0
  88. {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/dependency_links.txt +0 -0
  89. {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/entry_points.txt +0 -0
  90. {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/requires.txt +0 -0
  91. {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/top_level.txt +0 -0
  92. {overcode-0.1.5 → overcode-0.1.7}/tests/test_e2e_multi_agent_jokes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: overcode
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: A supervisor for managing multiple Claude Code instances in tmux
5
5
  Author: Mike Bond
6
6
  Project-URL: Homepage, https://github.com/mkb23/overcode
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "overcode"
7
- version = "0.1.5"
7
+ version = "0.1.7"
8
8
  description = "A supervisor for managing multiple Claude Code instances in tmux"
9
9
  authors = [
10
10
  {name = "Mike Bond"}
@@ -0,0 +1,14 @@
1
+ """
2
+ Overcode - A supervisor for managing multiple Claude Code instances.
3
+ """
4
+
5
+ from pathlib import Path
6
+
7
+ _toml = Path(__file__).resolve().parent.parent.parent / "pyproject.toml"
8
+ if _toml.is_file():
9
+ import tomllib
10
+ with open(_toml, "rb") as _f:
11
+ __version__ = tomllib.load(_f)["project"]["version"]
12
+ else:
13
+ from importlib.metadata import version as _version
14
+ __version__ = _version("overcode")
@@ -0,0 +1,139 @@
1
+ """Read and write Claude Code settings.json files.
2
+
3
+ Provides a reusable editor for Claude Code's JSON settings, with
4
+ convenience methods for managing hooks.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import copy
10
+ import json
11
+ from pathlib import Path
12
+
13
+
14
+ class ClaudeConfigEditor:
15
+ """Read and write Claude Code settings.json files."""
16
+
17
+ def __init__(self, path: Path):
18
+ self.path = Path(path)
19
+
20
+ @classmethod
21
+ def user_level(cls) -> ClaudeConfigEditor:
22
+ """Editor for user-level settings (~/.claude/settings.json)."""
23
+ return cls(Path.home() / ".claude" / "settings.json")
24
+
25
+ @classmethod
26
+ def project_level(cls, project_dir: Path | None = None) -> ClaudeConfigEditor:
27
+ """Editor for project-level settings (.claude/settings.json).
28
+
29
+ Args:
30
+ project_dir: Project root. Defaults to cwd.
31
+ """
32
+ base = Path(project_dir) if project_dir else Path.cwd()
33
+ return cls(base / ".claude" / "settings.json")
34
+
35
+ def load(self) -> dict:
36
+ """Load settings from file.
37
+
38
+ Returns empty dict if file doesn't exist.
39
+ Raises ValueError on invalid JSON or non-object content.
40
+ """
41
+ if not self.path.exists():
42
+ return {}
43
+ text = self.path.read_text()
44
+ try:
45
+ data = json.loads(text)
46
+ except json.JSONDecodeError as e:
47
+ raise ValueError(f"Invalid JSON in {self.path}: {e}") from e
48
+ if not isinstance(data, dict):
49
+ raise ValueError(f"{self.path} contains non-object JSON")
50
+ return data
51
+
52
+ def save(self, settings: dict) -> None:
53
+ """Write settings to file. Creates parent dirs as needed."""
54
+ self.path.parent.mkdir(parents=True, exist_ok=True)
55
+ self.path.write_text(json.dumps(settings, indent=2) + "\n")
56
+
57
+ def has_hook(self, event: str, command: str) -> bool:
58
+ """Check if a command hook exists for the given event."""
59
+ settings = self.load()
60
+ for entry in settings.get("hooks", {}).get(event, []):
61
+ for hook in entry.get("hooks", []):
62
+ if hook.get("command") == command:
63
+ return True
64
+ return False
65
+
66
+ def add_hook(self, event: str, command: str, matcher: str = "") -> bool:
67
+ """Add a command hook for an event.
68
+
69
+ Returns True if the hook was added, False if it already exists.
70
+ """
71
+ settings = self.load()
72
+ # Check existing
73
+ for entry in settings.get("hooks", {}).get(event, []):
74
+ for hook in entry.get("hooks", []):
75
+ if hook.get("command") == command:
76
+ return False
77
+
78
+ updated = copy.deepcopy(settings)
79
+ if "hooks" not in updated:
80
+ updated["hooks"] = {}
81
+ if event not in updated["hooks"]:
82
+ updated["hooks"][event] = []
83
+
84
+ updated["hooks"][event].append({
85
+ "matcher": matcher,
86
+ "hooks": [{"type": "command", "command": command}],
87
+ })
88
+
89
+ self.save(updated)
90
+ return True
91
+
92
+ def remove_hook(self, event: str, command: str) -> bool:
93
+ """Remove a matcher group containing this command.
94
+
95
+ Returns True if found and removed, False if not found.
96
+ Cleans up empty event arrays and empty hooks dict.
97
+ """
98
+ settings = self.load()
99
+ hooks_dict = settings.get("hooks", {})
100
+ event_list = hooks_dict.get(event, [])
101
+
102
+ # Find the matcher group index containing this command
103
+ index_to_remove = None
104
+ for i, entry in enumerate(event_list):
105
+ for hook in entry.get("hooks", []):
106
+ if hook.get("command") == command:
107
+ index_to_remove = i
108
+ break
109
+ if index_to_remove is not None:
110
+ break
111
+
112
+ if index_to_remove is None:
113
+ return False
114
+
115
+ updated = copy.deepcopy(settings)
116
+ del updated["hooks"][event][index_to_remove]
117
+
118
+ # Clean up empty event array
119
+ if not updated["hooks"][event]:
120
+ del updated["hooks"][event]
121
+
122
+ # Clean up empty hooks dict
123
+ if not updated["hooks"]:
124
+ del updated["hooks"]
125
+
126
+ self.save(updated)
127
+ return True
128
+
129
+ def list_hooks_matching(self, command_prefix: str) -> list[tuple[str, str]]:
130
+ """Return [(event, command)] for all hooks whose command starts with prefix."""
131
+ settings = self.load()
132
+ results = []
133
+ for event, entries in settings.get("hooks", {}).items():
134
+ for entry in entries:
135
+ for hook in entry.get("hooks", []):
136
+ cmd = hook.get("command", "")
137
+ if cmd.startswith(command_prefix):
138
+ results.append((event, cmd))
139
+ return results