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.
- {overcode-0.1.5/src/overcode.egg-info → overcode-0.1.7}/PKG-INFO +1 -1
- {overcode-0.1.5 → overcode-0.1.7}/pyproject.toml +1 -1
- overcode-0.1.7/src/overcode/__init__.py +14 -0
- overcode-0.1.7/src/overcode/claude_config.py +139 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/cli.py +550 -17
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/config.py +23 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_utils.py +9 -0
- overcode-0.1.7/src/overcode/dependency_check.py +111 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/history_reader.py +98 -21
- overcode-0.1.7/src/overcode/hook_handler.py +108 -0
- overcode-0.1.7/src/overcode/hook_status_detector.py +189 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/launcher.py +17 -72
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon.py +189 -23
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon_core.py +13 -7
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/monitor_daemon_state.py +56 -4
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/protocols.py +28 -1
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/session_manager.py +49 -1
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/settings.py +18 -6
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_constants.py +32 -14
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_detector.py +75 -15
- overcode-0.1.7/src/overcode/status_detector_factory.py +73 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_patterns.py +146 -0
- overcode-0.1.7/src/overcode/summary_columns.py +701 -0
- overcode-0.1.7/src/overcode/summary_groups.py +132 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_daemon.py +17 -52
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_daemon_core.py +6 -2
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/supervisor_layout.sh +1 -1
- overcode-0.1.7/src/overcode/time_context.py +313 -0
- overcode-0.1.7/src/overcode/tmux_utils.py +120 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui.py +520 -168
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui.tcss +36 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/input.py +67 -3
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/navigation.py +9 -10
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/session.py +168 -170
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/view.py +64 -5
- overcode-0.1.7/src/overcode/tui_formatters.py +235 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_helpers.py +24 -213
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_logic.py +18 -12
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_render.py +2 -1
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/__init__.py +4 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/command_bar.py +138 -1
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/daemon_panel.py +31 -15
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/daemon_status_bar.py +70 -30
- overcode-0.1.7/src/overcode/tui_widgets/fullscreen_preview.py +111 -0
- overcode-0.1.7/src/overcode/tui_widgets/help_overlay.py +185 -0
- overcode-0.1.7/src/overcode/tui_widgets/preview_pane.py +103 -0
- overcode-0.1.7/src/overcode/tui_widgets/session_summary.py +437 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_widgets/status_timeline.py +53 -17
- overcode-0.1.7/src/overcode/tui_widgets/summary_config_modal.py +165 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_api.py +5 -2
- {overcode-0.1.5 → overcode-0.1.7/src/overcode.egg-info}/PKG-INFO +1 -1
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/SOURCES.txt +11 -0
- overcode-0.1.5/src/overcode/__init__.py +0 -5
- overcode-0.1.5/src/overcode/dependency_check.py +0 -227
- overcode-0.1.5/src/overcode/tui_widgets/help_overlay.py +0 -71
- overcode-0.1.5/src/overcode/tui_widgets/preview_pane.py +0 -69
- overcode-0.1.5/src/overcode/tui_widgets/session_summary.py +0 -514
- {overcode-0.1.5 → overcode-0.1.7}/LICENSE +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/MANIFEST.in +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/README.md +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/setup.cfg +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_claude_skill.md +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/daemon_logging.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/data_export.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/exceptions.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/implementations.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/interfaces.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/logging_config.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/mocks.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/pid_utils.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/presence_logger.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/standing_instructions.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/status_history.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/summarizer_client.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/summarizer_component.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/__init__.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/renderer.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tmux_driver.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tui_eye.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/testing/tui_eye_skill.md +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tmux_manager.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/__init__.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/tui_actions/daemon.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_chartjs.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_server.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_server_runner.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode/web_templates.py +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/dependency_links.txt +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/entry_points.txt +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/requires.txt +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/src/overcode.egg-info/top_level.txt +0 -0
- {overcode-0.1.5 → overcode-0.1.7}/tests/test_e2e_multi_agent_jokes.py +0 -0
|
@@ -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
|