ctrlcode 0.1.0__py3-none-any.whl

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 (75) hide show
  1. ctrlcode/__init__.py +8 -0
  2. ctrlcode/agents/__init__.py +29 -0
  3. ctrlcode/agents/cleanup.py +388 -0
  4. ctrlcode/agents/communication.py +439 -0
  5. ctrlcode/agents/observability.py +421 -0
  6. ctrlcode/agents/react_loop.py +297 -0
  7. ctrlcode/agents/registry.py +211 -0
  8. ctrlcode/agents/result_parser.py +242 -0
  9. ctrlcode/agents/workflow.py +723 -0
  10. ctrlcode/analysis/__init__.py +28 -0
  11. ctrlcode/analysis/ast_diff.py +163 -0
  12. ctrlcode/analysis/bug_detector.py +149 -0
  13. ctrlcode/analysis/code_graphs.py +329 -0
  14. ctrlcode/analysis/semantic.py +205 -0
  15. ctrlcode/analysis/static.py +183 -0
  16. ctrlcode/analysis/synthesizer.py +281 -0
  17. ctrlcode/analysis/tests.py +189 -0
  18. ctrlcode/cleanup/__init__.py +16 -0
  19. ctrlcode/cleanup/auto_merge.py +350 -0
  20. ctrlcode/cleanup/doc_gardening.py +388 -0
  21. ctrlcode/cleanup/pr_automation.py +330 -0
  22. ctrlcode/cleanup/scheduler.py +356 -0
  23. ctrlcode/config.py +380 -0
  24. ctrlcode/embeddings/__init__.py +6 -0
  25. ctrlcode/embeddings/embedder.py +192 -0
  26. ctrlcode/embeddings/vector_store.py +213 -0
  27. ctrlcode/fuzzing/__init__.py +24 -0
  28. ctrlcode/fuzzing/analyzer.py +280 -0
  29. ctrlcode/fuzzing/budget.py +112 -0
  30. ctrlcode/fuzzing/context.py +665 -0
  31. ctrlcode/fuzzing/context_fuzzer.py +506 -0
  32. ctrlcode/fuzzing/derived_orchestrator.py +732 -0
  33. ctrlcode/fuzzing/oracle_adapter.py +135 -0
  34. ctrlcode/linters/__init__.py +11 -0
  35. ctrlcode/linters/hand_rolled_utils.py +221 -0
  36. ctrlcode/linters/yolo_parsing.py +217 -0
  37. ctrlcode/metrics/__init__.py +6 -0
  38. ctrlcode/metrics/dashboard.py +283 -0
  39. ctrlcode/metrics/tech_debt.py +663 -0
  40. ctrlcode/paths.py +68 -0
  41. ctrlcode/permissions.py +179 -0
  42. ctrlcode/providers/__init__.py +15 -0
  43. ctrlcode/providers/anthropic.py +138 -0
  44. ctrlcode/providers/base.py +77 -0
  45. ctrlcode/providers/openai.py +197 -0
  46. ctrlcode/providers/parallel.py +104 -0
  47. ctrlcode/server.py +871 -0
  48. ctrlcode/session/__init__.py +6 -0
  49. ctrlcode/session/baseline.py +57 -0
  50. ctrlcode/session/manager.py +967 -0
  51. ctrlcode/skills/__init__.py +10 -0
  52. ctrlcode/skills/builtin/commit.toml +29 -0
  53. ctrlcode/skills/builtin/docs.toml +25 -0
  54. ctrlcode/skills/builtin/refactor.toml +33 -0
  55. ctrlcode/skills/builtin/review.toml +28 -0
  56. ctrlcode/skills/builtin/test.toml +28 -0
  57. ctrlcode/skills/loader.py +111 -0
  58. ctrlcode/skills/registry.py +139 -0
  59. ctrlcode/storage/__init__.py +19 -0
  60. ctrlcode/storage/history_db.py +708 -0
  61. ctrlcode/tools/__init__.py +220 -0
  62. ctrlcode/tools/bash.py +112 -0
  63. ctrlcode/tools/browser.py +352 -0
  64. ctrlcode/tools/executor.py +153 -0
  65. ctrlcode/tools/explore.py +486 -0
  66. ctrlcode/tools/mcp.py +108 -0
  67. ctrlcode/tools/observability.py +561 -0
  68. ctrlcode/tools/registry.py +193 -0
  69. ctrlcode/tools/todo.py +291 -0
  70. ctrlcode/tools/update.py +266 -0
  71. ctrlcode/tools/webfetch.py +147 -0
  72. ctrlcode-0.1.0.dist-info/METADATA +93 -0
  73. ctrlcode-0.1.0.dist-info/RECORD +75 -0
  74. ctrlcode-0.1.0.dist-info/WHEEL +4 -0
  75. ctrlcode-0.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,266 @@
1
+ """Built-in file update tools for modifying existing files."""
2
+
3
+ import fcntl
4
+ import re
5
+ from pathlib import Path
6
+ from typing import Any
7
+ import signal
8
+ from contextlib import contextmanager
9
+
10
+
11
+ class UpdateFileTools:
12
+ """Built-in tools for updating file contents."""
13
+
14
+ def __init__(self, workspace_root: str | Path):
15
+ """
16
+ Initialize update tools.
17
+
18
+ Args:
19
+ workspace_root: Root directory for file operations
20
+ """
21
+ self.workspace_root = Path(workspace_root).resolve()
22
+
23
+ @staticmethod
24
+ def _validate_regex_pattern(pattern: str) -> tuple[bool, str]:
25
+ """
26
+ Validate regex pattern to prevent ReDoS attacks.
27
+
28
+ Args:
29
+ pattern: Regex pattern to validate
30
+
31
+ Returns:
32
+ Tuple of (is_valid, error_message)
33
+ """
34
+ # Length limit to prevent excessive complexity
35
+ if len(pattern) > 500:
36
+ return False, "Pattern too long (max 500 chars)"
37
+
38
+ # Check for dangerous nested quantifiers that cause catastrophic backtracking
39
+ dangerous_patterns = [
40
+ r'\([^)]*\+[^)]*\)\+', # (a+)+ style
41
+ r'\([^)]*\*[^)]*\)\+', # (a*)+ style
42
+ r'\([^)]*\+[^)]*\)\*', # (a+)* style
43
+ r'\([^)]*\*[^)]*\)\*', # (a*)* style
44
+ ]
45
+
46
+ for dangerous in dangerous_patterns:
47
+ if re.search(dangerous, pattern):
48
+ return False, "Pattern contains nested quantifiers that may cause ReDoS"
49
+
50
+ # Try to compile pattern to check validity
51
+ try:
52
+ re.compile(pattern)
53
+ except re.error as e:
54
+ return False, f"Invalid regex pattern: {e}"
55
+
56
+ return True, ""
57
+
58
+ @contextmanager
59
+ def _regex_timeout(self, seconds: int = 1):
60
+ """Context manager to timeout regex operations."""
61
+ def timeout_handler(signum, frame):
62
+ raise TimeoutError("Regex search timed out")
63
+
64
+ # Set alarm (Unix only)
65
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
66
+ signal.alarm(seconds)
67
+ try:
68
+ yield
69
+ finally:
70
+ signal.alarm(0)
71
+ signal.signal(signal.SIGALRM, old_handler)
72
+
73
+ def update_file(
74
+ self,
75
+ path: str,
76
+ operation: str,
77
+ start_line: int | None = None,
78
+ end_line: int | None = None,
79
+ content: str | None = None,
80
+ search_pattern: str | None = None,
81
+ ) -> dict[str, Any]:
82
+ """
83
+ Update a file by replacing, inserting, or deleting lines.
84
+
85
+ Args:
86
+ path: File path relative to workspace root
87
+ operation: Operation to perform (replace, insert, delete)
88
+ start_line: Start line number (1-indexed, inclusive)
89
+ end_line: End line number (1-indexed, inclusive)
90
+ content: Content for replace/insert operations
91
+ search_pattern: Regex pattern to find target lines (alternative to line numbers)
92
+
93
+ Returns:
94
+ Dict with success status and metadata
95
+ """
96
+ try:
97
+ file_path = (self.workspace_root / path).resolve()
98
+
99
+ # Security: ensure path is within workspace
100
+ if not str(file_path).startswith(str(self.workspace_root)):
101
+ return {"error": "Path outside workspace"}
102
+
103
+ if not file_path.exists():
104
+ return {"error": "File not found"}
105
+
106
+ if not file_path.is_file():
107
+ return {"error": "Not a file"}
108
+
109
+ # Validate operation
110
+ if operation not in ["replace", "insert", "delete"]:
111
+ return {"error": f"Invalid operation: {operation}"}
112
+
113
+ # Read file
114
+ with open(file_path, "r", encoding="utf-8") as f:
115
+ lines = f.readlines()
116
+
117
+ total_lines = len(lines)
118
+ original_line_count = total_lines
119
+
120
+ # Determine target line range
121
+ if search_pattern:
122
+ # Security: validate regex pattern to prevent ReDoS
123
+ is_valid, error_msg = self._validate_regex_pattern(search_pattern)
124
+ if not is_valid:
125
+ return {"error": f"Invalid search pattern: {error_msg}"}
126
+
127
+ # Find lines matching pattern with timeout protection
128
+ matches = []
129
+ try:
130
+ with self._regex_timeout(seconds=2):
131
+ compiled_pattern = re.compile(search_pattern)
132
+ for i, line in enumerate(lines, 1):
133
+ if compiled_pattern.search(line):
134
+ matches.append(i)
135
+ except TimeoutError:
136
+ return {"error": "Regex search timed out - pattern may be too complex"}
137
+
138
+ if not matches:
139
+ return {"error": f"Pattern '{search_pattern}' not found"}
140
+
141
+ # Use first match for single-line operations, or all matches for multi-line
142
+ if operation == "delete" and len(matches) > 1:
143
+ # Delete all matching lines
144
+ start_line = matches[0]
145
+ end_line = matches[-1]
146
+ else:
147
+ start_line = matches[0]
148
+ end_line = matches[0]
149
+
150
+ # Validate line numbers
151
+ if start_line is None:
152
+ return {"error": "start_line or search_pattern required"}
153
+
154
+ if start_line < 1 or start_line > total_lines:
155
+ return {"error": f"start_line {start_line} out of range (1-{total_lines})"}
156
+
157
+ if end_line is None:
158
+ end_line = start_line
159
+
160
+ if end_line < start_line or end_line > total_lines:
161
+ return {"error": f"end_line {end_line} invalid (must be {start_line}-{total_lines})"}
162
+
163
+ # Perform operation
164
+ if operation == "replace":
165
+ if content is None:
166
+ return {"error": "content required for replace operation"}
167
+
168
+ # Ensure content ends with newline
169
+ if not content.endswith("\n"):
170
+ content += "\n"
171
+
172
+ # Replace lines
173
+ new_lines = lines[: start_line - 1] + [content] + lines[end_line:]
174
+
175
+ elif operation == "insert":
176
+ if content is None:
177
+ return {"error": "content required for insert operation"}
178
+
179
+ # Ensure content ends with newline
180
+ if not content.endswith("\n"):
181
+ content += "\n"
182
+
183
+ # Insert at line (before start_line)
184
+ new_lines = lines[: start_line - 1] + [content] + lines[start_line - 1 :]
185
+
186
+ elif operation == "delete":
187
+ # Delete lines
188
+ new_lines = lines[: start_line - 1] + lines[end_line:]
189
+
190
+ else:
191
+ return {"error": f"Unsupported operation: {operation}"}
192
+
193
+ # Write file atomically with locking
194
+ temp_path = file_path.with_suffix(file_path.suffix + ".tmp")
195
+
196
+ try:
197
+ # Write to temp file with exclusive lock
198
+ with open(temp_path, "w", encoding="utf-8") as f:
199
+ fcntl.flock(f.fileno(), fcntl.LOCK_EX)
200
+ try:
201
+ f.writelines(new_lines)
202
+ f.flush()
203
+ finally:
204
+ fcntl.flock(f.fileno(), fcntl.LOCK_UN)
205
+
206
+ # Atomic rename
207
+ temp_path.replace(file_path)
208
+
209
+ except Exception as e:
210
+ if temp_path.exists():
211
+ temp_path.unlink()
212
+ raise e
213
+
214
+ return {
215
+ "success": True,
216
+ "path": str(file_path), # Return absolute path so TUI can read it back
217
+ "operation": operation,
218
+ "lines_before": original_line_count,
219
+ "lines_after": len(new_lines),
220
+ "affected_range": f"{start_line}-{end_line}",
221
+ }
222
+
223
+ except UnicodeDecodeError:
224
+ return {"error": "File is binary or uses unsupported encoding"}
225
+ except Exception as e:
226
+ return {"error": str(e)}
227
+
228
+
229
+ # Tool schemas for LLM providers (Anthropic/OpenAI format)
230
+ UPDATE_TOOL_SCHEMAS = [
231
+ {
232
+ "name": "update_file",
233
+ "description": "**USE THIS** to modify existing files. Replaces, inserts, or deletes specific line ranges. WORKFLOW: (1) read_file to see current content, (2) determine which lines to modify, (3) call update_file with line numbers and new content.",
234
+ "input_schema": {
235
+ "type": "object",
236
+ "properties": {
237
+ "path": {
238
+ "type": "string",
239
+ "description": "File path relative to workspace root",
240
+ },
241
+ "operation": {
242
+ "type": "string",
243
+ "description": "Operation to perform",
244
+ "enum": ["replace", "insert", "delete"],
245
+ },
246
+ "start_line": {
247
+ "type": "integer",
248
+ "description": "Start line number (1-indexed, inclusive). Required unless using search_pattern.",
249
+ },
250
+ "end_line": {
251
+ "type": "integer",
252
+ "description": "End line number (1-indexed, inclusive). Defaults to start_line if not specified.",
253
+ },
254
+ "content": {
255
+ "type": "string",
256
+ "description": "Content for replace/insert operations",
257
+ },
258
+ "search_pattern": {
259
+ "type": "string",
260
+ "description": "Regex pattern to find target lines (alternative to line numbers)",
261
+ },
262
+ },
263
+ "required": ["path", "operation"],
264
+ },
265
+ },
266
+ ]
@@ -0,0 +1,147 @@
1
+ """Built-in web fetch tools for making HTTP requests."""
2
+
3
+ import urllib.request
4
+ import urllib.error
5
+ from typing import Any
6
+
7
+
8
+ class WebFetchTools:
9
+ """Built-in tools for fetching web content."""
10
+
11
+ def fetch(
12
+ self,
13
+ url: str,
14
+ method: str = "GET",
15
+ headers: dict[str, str] | None = None,
16
+ body: str | None = None,
17
+ timeout: int = 30,
18
+ ) -> dict[str, Any]:
19
+ """
20
+ Make an HTTP request.
21
+
22
+ Args:
23
+ url: URL to fetch
24
+ method: HTTP method (GET, POST, PUT, DELETE)
25
+ headers: Optional HTTP headers
26
+ body: Optional request body (for POST/PUT)
27
+ timeout: Timeout in seconds (default 30, max 120)
28
+
29
+ Returns:
30
+ Dict with response body, status code, and headers
31
+ """
32
+ try:
33
+ # Validate URL protocol
34
+ if not url.startswith(("http://", "https://")):
35
+ return {"error": "Only http and https protocols are supported"}
36
+
37
+ # Basic SSRF protection - block localhost
38
+ if any(
39
+ host in url.lower()
40
+ for host in ["localhost", "127.0.0.1", "0.0.0.0", "[::1]"]
41
+ ):
42
+ return {"error": "Cannot fetch from localhost"}
43
+
44
+ # Enforce timeout limits
45
+ if timeout > 120:
46
+ timeout = 120
47
+ if timeout < 1:
48
+ timeout = 1
49
+
50
+ # Validate HTTP method
51
+ method = method.upper()
52
+ if method not in ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH"]:
53
+ return {"error": f"Unsupported HTTP method: {method}"}
54
+
55
+ # Prepare request
56
+ request_headers = headers or {}
57
+ request_body = body.encode("utf-8") if body else None
58
+
59
+ req = urllib.request.Request(
60
+ url,
61
+ data=request_body,
62
+ headers=request_headers,
63
+ method=method,
64
+ )
65
+
66
+ # Execute request
67
+ with urllib.request.urlopen(req, timeout=timeout) as response:
68
+ response_body = response.read().decode("utf-8")
69
+ response_headers = dict(response.headers)
70
+ status_code = response.status
71
+
72
+ return {
73
+ "body": response_body,
74
+ "status_code": status_code,
75
+ "headers": response_headers,
76
+ "url": url,
77
+ "method": method,
78
+ "success": True,
79
+ }
80
+
81
+ except urllib.error.HTTPError as e:
82
+ # HTTP errors (4xx, 5xx)
83
+ error_body = e.read().decode("utf-8") if e.fp else ""
84
+ return {
85
+ "error": f"HTTP {e.code}: {e.reason}",
86
+ "status_code": e.code,
87
+ "body": error_body,
88
+ "url": url,
89
+ "success": False,
90
+ }
91
+
92
+ except urllib.error.URLError as e:
93
+ # Network errors (DNS, connection, etc.)
94
+ return {
95
+ "error": f"Request failed: {str(e.reason)}",
96
+ "url": url,
97
+ "success": False,
98
+ }
99
+
100
+ except TimeoutError:
101
+ return {
102
+ "error": f"Request timed out after {timeout} seconds",
103
+ "url": url,
104
+ "success": False,
105
+ }
106
+
107
+ except Exception as e:
108
+ return {"error": str(e), "url": url, "success": False}
109
+
110
+
111
+ # Tool schemas for LLM providers (Anthropic/OpenAI format)
112
+ WEBFETCH_TOOL_SCHEMAS = [
113
+ {
114
+ "name": "fetch",
115
+ "description": "Make HTTP requests to fetch web content or call APIs. Supports GET, POST, PUT, DELETE with custom headers and body.",
116
+ "input_schema": {
117
+ "type": "object",
118
+ "properties": {
119
+ "url": {
120
+ "type": "string",
121
+ "description": "URL to fetch (must be http or https)",
122
+ },
123
+ "method": {
124
+ "type": "string",
125
+ "description": "HTTP method",
126
+ "enum": ["GET", "POST", "PUT", "DELETE", "HEAD", "PATCH"],
127
+ "default": "GET",
128
+ },
129
+ "headers": {
130
+ "type": "object",
131
+ "description": "Optional HTTP headers as key-value pairs",
132
+ "additionalProperties": {"type": "string"},
133
+ },
134
+ "body": {
135
+ "type": "string",
136
+ "description": "Optional request body (for POST/PUT/PATCH)",
137
+ },
138
+ "timeout": {
139
+ "type": "integer",
140
+ "description": "Timeout in seconds (default 30, max 120)",
141
+ "default": 30,
142
+ },
143
+ },
144
+ "required": ["url"],
145
+ },
146
+ },
147
+ ]
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctrlcode
3
+ Version: 0.1.0
4
+ Summary: Adaptive coding harness with differential fuzzing - transforms AI slop into production-ready code
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: aiohttp>=3.10.0
7
+ Requires-Dist: anthropic>=0.40.0
8
+ Requires-Dist: apscheduler>=3.11.2
9
+ Requires-Dist: ctrlcode-tui
10
+ Requires-Dist: faiss-cpu>=1.13.2
11
+ Requires-Dist: harness-utils>=0.3.1
12
+ Requires-Dist: mcp>=1.0.0
13
+ Requires-Dist: networkx>=3.6.1
14
+ Requires-Dist: openai>=1.54.0
15
+ Requires-Dist: platformdirs>=4.5.1
16
+ Requires-Dist: playwright>=1.58.0
17
+ Requires-Dist: sentence-transformers>=5.2.2
18
+ Requires-Dist: tiktoken>=0.12.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ # ctrl+code
22
+
23
+ Adaptive coding harness with differential fuzzing - transforms AI slop into production-ready code.
24
+
25
+ ## Configuration
26
+
27
+ ctrl+code follows platform conventions for config and data storage:
28
+
29
+ | Platform | Config | Data | Cache |
30
+ |----------|--------|------|-------|
31
+ | **Linux** | `~/.config/ctrlcode/` | `~/.local/share/ctrlcode/` | `~/.cache/ctrlcode/` |
32
+ | **macOS** | `~/Library/Application Support/ctrlcode/` | `~/Library/Application Support/ctrlcode/` | `~/Library/Caches/ctrlcode/` |
33
+ | **Windows** | `%APPDATA%\ctrlcode\` | `%LOCALAPPDATA%\ctrlcode\` | `%LOCALAPPDATA%\ctrlcode\Cache\` |
34
+
35
+ ### Environment Variables
36
+
37
+ Override default directories:
38
+ - `CTRLCODE_CONFIG_DIR`: Config file location
39
+ - `CTRLCODE_DATA_DIR`: Session logs and persistent data
40
+ - `CTRLCODE_CACHE_DIR`: Conversation storage and temp files
41
+
42
+ ### Configuration File
43
+
44
+ Copy `config.example.toml` to your config directory as `config.toml` and fill in your API keys.
45
+
46
+ ### Agent Instructions (AGENT.md)
47
+
48
+ Customize agent behavior with `AGENT.md` files, loaded hierarchically:
49
+
50
+ 1. **Global** (`~/.config/ctrlcode/AGENT.md`) - Your personal defaults across all projects
51
+ 2. **Project** (`<workspace>/AGENT.md`) - Project-specific instructions
52
+
53
+ Example global `AGENT.md`:
54
+ ```markdown
55
+ # Global Agent Defaults
56
+
57
+ - Always use semantic commit messages
58
+ - Show tool results explicitly
59
+ - Prefer built-in tools over scripts
60
+ ```
61
+
62
+ Example project `AGENT.md`:
63
+ ```markdown
64
+ # MyProject Instructions
65
+
66
+ ## Architecture
67
+ - Frontend: React + TypeScript
68
+ - Backend: FastAPI + PostgreSQL
69
+
70
+ ## Style
71
+ - Use async/await for all I/O
72
+ - Prefer functional components
73
+ ```
74
+
75
+ Instructions are injected into the system prompt, giving the agent context about your preferences and project structure.
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ uv pip install ctrlcode
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ Start the TUI (auto-launches server):
86
+ ```bash
87
+ ctrlcode
88
+ ```
89
+
90
+ Or start server separately:
91
+ ```bash
92
+ ctrlcode-server
93
+ ```
@@ -0,0 +1,75 @@
1
+ ctrlcode/__init__.py,sha256=kAT6NmF-Gb1M3VNRna0be9CzF8JHROpvCI9wYFNZbgk,207
2
+ ctrlcode/config.py,sha256=fNcAh9oP39Iv1IMbGa-KymAt_qWSTX_MbQKiAsqOc6o,13332
3
+ ctrlcode/paths.py,sha256=eD-RWOra2aGkhhBYbfqHHh2BaGvljZjIJdeO9N2Cuxg,2046
4
+ ctrlcode/permissions.py,sha256=Y4fqYK9iuGH9bqHbYrEpbpc38_PgkKNkjhM8lYaXmkU,5600
5
+ ctrlcode/server.py,sha256=ArRA_mfHRPqYtws66ag_jV1hgGyfTO3nLaPByjAQ3rg,31722
6
+ ctrlcode/agents/__init__.py,sha256=CS7JCKjxLV9uZOvcfX6yE6pBgf9Y2j2FWPIwgAL6-Z4,747
7
+ ctrlcode/agents/cleanup.py,sha256=tq7PVLX1UIGi1EtvtLk-DzKwg3u8fG4nDGLAaOHyJw8,11548
8
+ ctrlcode/agents/communication.py,sha256=9jD1Zz3fDXL4sK_RjjfXuw1AHW3saWnivZ1aI_6iH84,14196
9
+ ctrlcode/agents/observability.py,sha256=m1iNfmSKy_AuKzU4nAq7CetTLJqME69VUz8y61h7768,11950
10
+ ctrlcode/agents/react_loop.py,sha256=meOb1t7P_a6cACDCWhzEFpZyAmyqO_kSiHAHn1JQYfE,12214
11
+ ctrlcode/agents/registry.py,sha256=ulR0r_ulFrLMmMCORhH0mu9pDWtGUI2jjO1e-sMIi58,6638
12
+ ctrlcode/agents/result_parser.py,sha256=NN9QxL2iaRDN_SAaVLRT65NJhW2MkkzqmAAuQ75SFvw,8615
13
+ ctrlcode/agents/workflow.py,sha256=8rMmndcpjQYxJ9MnDL3ueCswHPHiJqKkAZpNUM9i5D4,22973
14
+ ctrlcode/analysis/__init__.py,sha256=ALlJjhWNH6i7I0V_eOOzxvxgpyapvOXH0dv7Yy6ojVo,798
15
+ ctrlcode/analysis/ast_diff.py,sha256=7VslujMfOjWKLwL-aBJq9bueFXoJWWn4o8QppRETBH0,4977
16
+ ctrlcode/analysis/bug_detector.py,sha256=ZMUf81G1VQl5zWFWRX87oB-LVvAfJ-ibaPphhqcmZxQ,4814
17
+ ctrlcode/analysis/code_graphs.py,sha256=eoDPpnx-daupny37Z_ls8JZHpnKUmy3F8RyayurNlcM,10827
18
+ ctrlcode/analysis/semantic.py,sha256=ebZQOlEepW7kTpAxONXSeuJZqGD0gyVVW0a2CZXUJ8I,5647
19
+ ctrlcode/analysis/static.py,sha256=MwMMbyyff2bcz5lCwmOxgQPrH0JxYzlvN39axRm-2EY,5215
20
+ ctrlcode/analysis/synthesizer.py,sha256=wzgIvc6U4paWvj6zaHsOI5YJYf8T2Lg6orL3n97v8VY,9322
21
+ ctrlcode/analysis/tests.py,sha256=xfFv6fjcPxQTwaTBSkq2c6k_X2y3oXtdVtSGEdECCY0,5077
22
+ ctrlcode/cleanup/__init__.py,sha256=op6vMRW6qfwdrXfxKfa-ryUO9zQA1mZde0ebD_lXrjg,410
23
+ ctrlcode/cleanup/auto_merge.py,sha256=wn3P_n_GzxDIWUVQF7Xdr7Y3MlWiOi8qJCOHUT0DFeU,11563
24
+ ctrlcode/cleanup/doc_gardening.py,sha256=---B8_vHiLEdbR_ixyp0tIXcBLdZMdkMVdKMY1Vsd1E,12948
25
+ ctrlcode/cleanup/pr_automation.py,sha256=jg2-sjU-bGuoFVsS8G2560dvrVERptFMWw0ORpvM5dY,9742
26
+ ctrlcode/cleanup/scheduler.py,sha256=Oz1DPFYK3xfmSmWBBWhWHHWqLpJexnxhPDGq4VXfznw,11953
27
+ ctrlcode/embeddings/__init__.py,sha256=ONEGtZi3GHk_KzUYDPU0GPEDTui2QkzPGLdxkWqS4ZI,238
28
+ ctrlcode/embeddings/embedder.py,sha256=XAFWzniSrjEmnCFENtWIjWgvLCfcxEbi3-essUpKHJ8,6233
29
+ ctrlcode/embeddings/vector_store.py,sha256=EGQSFFsdSilWfhN-KoYP4ow96BlCFjermOWE0Uyezk4,7112
30
+ ctrlcode/fuzzing/__init__.py,sha256=7m7q9GS67Wy30lxePFzlt_j77A3UMExArsrLQuvyKZY,846
31
+ ctrlcode/fuzzing/analyzer.py,sha256=4daiYnRIbLQz_cwGajajBJVQOd225-IoGEjgIajxwrg,9300
32
+ ctrlcode/fuzzing/budget.py,sha256=MN-E9DnUXR2KG68C2IfrfptzhnyeN0NY08RRWrD4feU,3076
33
+ ctrlcode/fuzzing/context.py,sha256=RKHBCFpp39X0FijvX3c2MujCOiXSNRKpGM9Ds_6ZwSY,24181
34
+ ctrlcode/fuzzing/context_fuzzer.py,sha256=m0GH9jRPEQJmceBSOxMc9k26rWk-vYQX33oorVBJ0nw,17436
35
+ ctrlcode/fuzzing/derived_orchestrator.py,sha256=lcwivhUIENxmpv8QWDsD69zZkUXC7rYODmCXjl_Fvag,28302
36
+ ctrlcode/fuzzing/oracle_adapter.py,sha256=_rO_ylebUnjT5nS_2Z0P1KTkhJMh9MLGEeXBa-9Tj2g,4210
37
+ ctrlcode/linters/__init__.py,sha256=L3bleyJIZr2foHrOQBufhErhlUnKV8UiVto4gwyBeg4,304
38
+ ctrlcode/linters/hand_rolled_utils.py,sha256=zCmb5Wd2ixteaSGMU85qrJUeIvY3290aEiTdwEKs2CU,6232
39
+ ctrlcode/linters/yolo_parsing.py,sha256=nn187KRVio9WniJMy0NFqA4TnyGCw8rXgWkEJYCSnCk,6433
40
+ ctrlcode/metrics/__init__.py,sha256=6S4RCHT6leSY0ONzB4-bvufBIkKcvK_PbDV2SANeHU0,241
41
+ ctrlcode/metrics/dashboard.py,sha256=M2kOQKvtV7UQ_Uz2lVPJsWJRMXByAxsiOg00xNWgdrE,9903
42
+ ctrlcode/metrics/tech_debt.py,sha256=gFTIdg2r39U1LuGkElI1J2km8vnQyqziQ6PPtKKqv8A,21299
43
+ ctrlcode/providers/__init__.py,sha256=xJcoWTMOd5EZYNctGDuRy3GRJudW6TwDGG78crEtPII,361
44
+ ctrlcode/providers/anthropic.py,sha256=SNaA2eZ_An9xO9zk9BQtaHxrUPPBngMcA-DP5FZf-6Y,5188
45
+ ctrlcode/providers/base.py,sha256=fubYqGjj5twihic28wg8PIBwFOBIp3fQD9sNK5y53_M,1994
46
+ ctrlcode/providers/openai.py,sha256=H1lwPfnLO7I7h7Hs6zTEAmdsJkTO7Ud48miH23EAv-c,7560
47
+ ctrlcode/providers/parallel.py,sha256=ucYh8byJKM0Rq1YA9eHURh99nxMPp_bDoDZSoavepqE,3041
48
+ ctrlcode/session/__init__.py,sha256=Kv-V1cPLJVCh0Oal9_d4i-JJ6A-8hZUzbKe3TRgG6fY,206
49
+ ctrlcode/session/baseline.py,sha256=3fok8n0SeI3IaqiPfO3t9rl7uA72SmhvgOULHUjHQY4,1700
50
+ ctrlcode/session/manager.py,sha256=MgwzXxqCt8Z6dT62n3sP2Hz1FN-qJjIDKhHVxcQfKJY,43271
51
+ ctrlcode/skills/__init__.py,sha256=p-62spnGJNkZqBsjHKVvvycXiUjEG3s7vgqWY5x_YYo,179
52
+ ctrlcode/skills/loader.py,sha256=kauoPOFuuaCwkIS66zufdADKVWRFgY2NjLmKfcS_b4o,3055
53
+ ctrlcode/skills/registry.py,sha256=2SCo8n1p_Nu1FNuW05WxotKTQCgUuHpVTKPkVvQZR88,3565
54
+ ctrlcode/skills/builtin/commit.toml,sha256=biDWNESOCzlJ8s555_dQtZl6AiYgz3cU2yxeWmPNITI,984
55
+ ctrlcode/skills/builtin/docs.toml,sha256=VdJ5O1iCVZCnC_fh-vOSqvhn3z_oCaPr5W6OtUBcIxw,651
56
+ ctrlcode/skills/builtin/refactor.toml,sha256=aXIZ_0alp54IAjhbKBxbhQzkyixebqtdmYtE5nXMJLs,797
57
+ ctrlcode/skills/builtin/review.toml,sha256=h3Kmo29vmz7lDrkCne8QpFwZMUR-MWyLL4GManisfkI,711
58
+ ctrlcode/skills/builtin/test.toml,sha256=NgYPf9v_pVwGSU-zaANAGRsbsvlLcxdkGjRowP8HFnA,637
59
+ ctrlcode/storage/__init__.py,sha256=t8_whfiyzIpXwDkSqz_woKsIZDLiGCzmwx5ENHleKBI,347
60
+ ctrlcode/storage/history_db.py,sha256=Ih9X1Dsx0AjwwrfgXSfInf7zDn-gwbnCT8p9JkdgD9A,22429
61
+ ctrlcode/tools/__init__.py,sha256=KvK2ED4OiesYQUNsSv0sq3a-0dbM_ZzjI1_QARypoRs,6240
62
+ ctrlcode/tools/bash.py,sha256=8k-qSFLU-r6j8-IgfiCWTHzv0ZQUS4HE2K8n_AEsI-8,3586
63
+ ctrlcode/tools/browser.py,sha256=SPOUX3zfNPBn1UIvX7USt_6rgLoJY2BYMZBKr1_BM0k,12155
64
+ ctrlcode/tools/executor.py,sha256=ybRPyKlW4sA8bLyYwXf7vofX4NF0le1ur--bvvQ9VQw,4749
65
+ ctrlcode/tools/explore.py,sha256=WLChsk4KO_Ulaqb2XZNhxKdZPSoCM6r3KWOVEVDZB_E,16252
66
+ ctrlcode/tools/mcp.py,sha256=ijCMVKmrvRH4tjUHxvBdpq2FAI2VVqw0Z5Ry1WFZtX8,3131
67
+ ctrlcode/tools/observability.py,sha256=ubeKxqD--Vdy8a3I7N0Mofn4_8aS6tQq_pzUTHJTv2g,18333
68
+ ctrlcode/tools/registry.py,sha256=6g08SGzYLHmQwta9Q-89WJh8frC_owwcJIYQX5Eu8Fc,5643
69
+ ctrlcode/tools/todo.py,sha256=LyNmZzgSVaBzL5FO0MEroQ4WDgWnIYri2TymsKjCQuk,10315
70
+ ctrlcode/tools/update.py,sha256=TZsmdugryoWKdcZyKEe2wZbaG5NzLIotdIbj2OGDvBE,9817
71
+ ctrlcode/tools/webfetch.py,sha256=tMOoMA9zr3ErZbKPdsu5XxTvdA_QHZ1kja6J56n-RgE,4834
72
+ ctrlcode-0.1.0.dist-info/METADATA,sha256=cwVabi2IcF61b3OFVbW5PkzTYm6QqVYr-StJUV21Y7k,2564
73
+ ctrlcode-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
74
+ ctrlcode-0.1.0.dist-info/entry_points.txt,sha256=Q2-XnFYshpszkPxrw0dFD59SDJRnrNs32QpAQdoR-C4,90
75
+ ctrlcode-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ctrlcode = ctrlcode_tui.app:main
3
+ ctrlcode-server = ctrlcode.server:main