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.
- ctrlcode/__init__.py +8 -0
- ctrlcode/agents/__init__.py +29 -0
- ctrlcode/agents/cleanup.py +388 -0
- ctrlcode/agents/communication.py +439 -0
- ctrlcode/agents/observability.py +421 -0
- ctrlcode/agents/react_loop.py +297 -0
- ctrlcode/agents/registry.py +211 -0
- ctrlcode/agents/result_parser.py +242 -0
- ctrlcode/agents/workflow.py +723 -0
- ctrlcode/analysis/__init__.py +28 -0
- ctrlcode/analysis/ast_diff.py +163 -0
- ctrlcode/analysis/bug_detector.py +149 -0
- ctrlcode/analysis/code_graphs.py +329 -0
- ctrlcode/analysis/semantic.py +205 -0
- ctrlcode/analysis/static.py +183 -0
- ctrlcode/analysis/synthesizer.py +281 -0
- ctrlcode/analysis/tests.py +189 -0
- ctrlcode/cleanup/__init__.py +16 -0
- ctrlcode/cleanup/auto_merge.py +350 -0
- ctrlcode/cleanup/doc_gardening.py +388 -0
- ctrlcode/cleanup/pr_automation.py +330 -0
- ctrlcode/cleanup/scheduler.py +356 -0
- ctrlcode/config.py +380 -0
- ctrlcode/embeddings/__init__.py +6 -0
- ctrlcode/embeddings/embedder.py +192 -0
- ctrlcode/embeddings/vector_store.py +213 -0
- ctrlcode/fuzzing/__init__.py +24 -0
- ctrlcode/fuzzing/analyzer.py +280 -0
- ctrlcode/fuzzing/budget.py +112 -0
- ctrlcode/fuzzing/context.py +665 -0
- ctrlcode/fuzzing/context_fuzzer.py +506 -0
- ctrlcode/fuzzing/derived_orchestrator.py +732 -0
- ctrlcode/fuzzing/oracle_adapter.py +135 -0
- ctrlcode/linters/__init__.py +11 -0
- ctrlcode/linters/hand_rolled_utils.py +221 -0
- ctrlcode/linters/yolo_parsing.py +217 -0
- ctrlcode/metrics/__init__.py +6 -0
- ctrlcode/metrics/dashboard.py +283 -0
- ctrlcode/metrics/tech_debt.py +663 -0
- ctrlcode/paths.py +68 -0
- ctrlcode/permissions.py +179 -0
- ctrlcode/providers/__init__.py +15 -0
- ctrlcode/providers/anthropic.py +138 -0
- ctrlcode/providers/base.py +77 -0
- ctrlcode/providers/openai.py +197 -0
- ctrlcode/providers/parallel.py +104 -0
- ctrlcode/server.py +871 -0
- ctrlcode/session/__init__.py +6 -0
- ctrlcode/session/baseline.py +57 -0
- ctrlcode/session/manager.py +967 -0
- ctrlcode/skills/__init__.py +10 -0
- ctrlcode/skills/builtin/commit.toml +29 -0
- ctrlcode/skills/builtin/docs.toml +25 -0
- ctrlcode/skills/builtin/refactor.toml +33 -0
- ctrlcode/skills/builtin/review.toml +28 -0
- ctrlcode/skills/builtin/test.toml +28 -0
- ctrlcode/skills/loader.py +111 -0
- ctrlcode/skills/registry.py +139 -0
- ctrlcode/storage/__init__.py +19 -0
- ctrlcode/storage/history_db.py +708 -0
- ctrlcode/tools/__init__.py +220 -0
- ctrlcode/tools/bash.py +112 -0
- ctrlcode/tools/browser.py +352 -0
- ctrlcode/tools/executor.py +153 -0
- ctrlcode/tools/explore.py +486 -0
- ctrlcode/tools/mcp.py +108 -0
- ctrlcode/tools/observability.py +561 -0
- ctrlcode/tools/registry.py +193 -0
- ctrlcode/tools/todo.py +291 -0
- ctrlcode/tools/update.py +266 -0
- ctrlcode/tools/webfetch.py +147 -0
- ctrlcode-0.1.0.dist-info/METADATA +93 -0
- ctrlcode-0.1.0.dist-info/RECORD +75 -0
- ctrlcode-0.1.0.dist-info/WHEEL +4 -0
- ctrlcode-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name = "commit"
|
|
2
|
+
description = "Create a git commit with semantic commit message"
|
|
3
|
+
requires_args = false
|
|
4
|
+
|
|
5
|
+
prompt = """
|
|
6
|
+
Create a git commit following these steps:
|
|
7
|
+
|
|
8
|
+
1. Run `git status` to see all changes (staged and unstaged)
|
|
9
|
+
2. Run `git diff` to review the actual changes
|
|
10
|
+
3. Run `git log --oneline -5` to see recent commit message style
|
|
11
|
+
4. Analyze the changes and create a semantic commit message that:
|
|
12
|
+
- Summarizes the nature of changes (new feature, bug fix, refactor, etc.)
|
|
13
|
+
- Is concise (1-2 sentences)
|
|
14
|
+
- Focuses on the "why" rather than the "what"
|
|
15
|
+
- Follows the commit style of this repository
|
|
16
|
+
5. Stage relevant files (avoid staging secrets like .env files)
|
|
17
|
+
6. Create the commit with message ending with:
|
|
18
|
+
Co-Authored-By: Ctrl+Code <noreply@ctrl.code>
|
|
19
|
+
|
|
20
|
+
Important:
|
|
21
|
+
- Do NOT commit files that contain secrets
|
|
22
|
+
- Do NOT push unless explicitly requested
|
|
23
|
+
- Use semantic commit format (e.g., "feat:", "fix:", "refactor:")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
examples = [
|
|
27
|
+
"/commit",
|
|
28
|
+
"skill:commit"
|
|
29
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name = "docs"
|
|
2
|
+
description = "Generate or improve documentation"
|
|
3
|
+
requires_args = true
|
|
4
|
+
|
|
5
|
+
prompt = """
|
|
6
|
+
Generate or improve documentation for: {args}
|
|
7
|
+
|
|
8
|
+
1. Read the relevant code files
|
|
9
|
+
2. Understand the purpose and functionality
|
|
10
|
+
3. Create documentation that includes:
|
|
11
|
+
- Clear description of what it does
|
|
12
|
+
- Usage examples
|
|
13
|
+
- Parameter descriptions
|
|
14
|
+
- Return value descriptions
|
|
15
|
+
- Important notes or warnings
|
|
16
|
+
4. Follow the existing documentation style in the project
|
|
17
|
+
5. Include code examples where helpful
|
|
18
|
+
|
|
19
|
+
Make the documentation clear, concise, and helpful for users.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
examples = [
|
|
23
|
+
"/docs FuzzingOrchestrator",
|
|
24
|
+
"skill:docs(MyClass)"
|
|
25
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name = "refactor"
|
|
2
|
+
description = "Refactor code for better quality"
|
|
3
|
+
requires_args = true
|
|
4
|
+
|
|
5
|
+
prompt = """
|
|
6
|
+
Refactor the code: {args}
|
|
7
|
+
|
|
8
|
+
1. Read and understand the current implementation
|
|
9
|
+
2. Identify improvement opportunities:
|
|
10
|
+
- Code duplication
|
|
11
|
+
- Complex functions (high cyclomatic complexity)
|
|
12
|
+
- Poor naming
|
|
13
|
+
- Missing abstractions
|
|
14
|
+
- Violation of SOLID principles
|
|
15
|
+
3. Propose refactoring changes with:
|
|
16
|
+
- Clear explanation of the issue
|
|
17
|
+
- Proposed solution
|
|
18
|
+
- Benefits of the change
|
|
19
|
+
- Any trade-offs
|
|
20
|
+
4. Make the changes incrementally
|
|
21
|
+
5. Ensure tests still pass after each change
|
|
22
|
+
|
|
23
|
+
Focus on improving:
|
|
24
|
+
- Readability
|
|
25
|
+
- Maintainability
|
|
26
|
+
- Testability
|
|
27
|
+
- Performance (where applicable)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
examples = [
|
|
31
|
+
"/refactor FuzzingOrchestrator.fuzz",
|
|
32
|
+
"skill:refactor(process_turn method)"
|
|
33
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name = "review"
|
|
2
|
+
description = "Review code changes for quality and potential issues"
|
|
3
|
+
requires_args = false
|
|
4
|
+
|
|
5
|
+
prompt = """
|
|
6
|
+
Review the current code changes:
|
|
7
|
+
|
|
8
|
+
1. Run `git diff` to see all changes
|
|
9
|
+
2. Analyze the changes for:
|
|
10
|
+
- Code quality and style
|
|
11
|
+
- Potential bugs or edge cases
|
|
12
|
+
- Security vulnerabilities
|
|
13
|
+
- Performance issues
|
|
14
|
+
- Missing error handling
|
|
15
|
+
- Test coverage gaps
|
|
16
|
+
3. Provide constructive feedback with:
|
|
17
|
+
- Positive observations (what's good)
|
|
18
|
+
- Issues found (severity: critical, major, minor)
|
|
19
|
+
- Specific suggestions for improvement
|
|
20
|
+
- Code examples where helpful
|
|
21
|
+
|
|
22
|
+
Format your review clearly with sections for each category.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
examples = [
|
|
26
|
+
"/review",
|
|
27
|
+
"skill:review"
|
|
28
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name = "test"
|
|
2
|
+
description = "Run tests and analyze results"
|
|
3
|
+
requires_args = false
|
|
4
|
+
|
|
5
|
+
prompt = """
|
|
6
|
+
Run tests and analyze results:
|
|
7
|
+
|
|
8
|
+
1. Identify the test framework (pytest, unittest, jest, etc.)
|
|
9
|
+
2. Run the test suite
|
|
10
|
+
3. If tests fail:
|
|
11
|
+
- Show the failing tests
|
|
12
|
+
- Analyze the failure reasons
|
|
13
|
+
- Suggest fixes
|
|
14
|
+
4. If tests pass:
|
|
15
|
+
- Report test coverage if available
|
|
16
|
+
- Suggest additional test cases for edge cases
|
|
17
|
+
5. Check for:
|
|
18
|
+
- Missing tests for new functionality
|
|
19
|
+
- Flaky tests
|
|
20
|
+
- Test performance issues
|
|
21
|
+
|
|
22
|
+
Provide a summary with test counts, pass rate, and recommendations.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
examples = [
|
|
26
|
+
"/test",
|
|
27
|
+
"skill:test"
|
|
28
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Skill loading from TOML files."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
import tomllib
|
|
10
|
+
except ImportError:
|
|
11
|
+
import tomli as tomllib # type: ignore # Python < 3.11
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class Skill:
|
|
18
|
+
"""A skill definition."""
|
|
19
|
+
|
|
20
|
+
name: str
|
|
21
|
+
prompt: str
|
|
22
|
+
description: str = ""
|
|
23
|
+
requires_args: bool = False
|
|
24
|
+
examples: list[str] = field(default_factory=list)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SkillLoader:
|
|
28
|
+
"""Loads skills from TOML files."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, skills_dir: Optional[Path] = None):
|
|
31
|
+
"""
|
|
32
|
+
Initialize skill loader.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
skills_dir: Directory containing skill TOML files
|
|
36
|
+
"""
|
|
37
|
+
self.skills_dir = skills_dir
|
|
38
|
+
|
|
39
|
+
def load_skill(self, skill_path: Path) -> Skill:
|
|
40
|
+
"""
|
|
41
|
+
Load a skill from TOML file.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
skill_path: Path to skill TOML file
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Loaded skill
|
|
48
|
+
"""
|
|
49
|
+
with open(skill_path, "rb") as f:
|
|
50
|
+
data = tomllib.load(f)
|
|
51
|
+
|
|
52
|
+
return Skill(
|
|
53
|
+
name=data["name"],
|
|
54
|
+
prompt=data["prompt"],
|
|
55
|
+
description=data.get("description", ""),
|
|
56
|
+
requires_args=data.get("requires_args", False),
|
|
57
|
+
examples=data.get("examples", []),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def load_all(self) -> dict[str, Skill]:
|
|
61
|
+
"""
|
|
62
|
+
Load all skills from directory.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Dict mapping skill name to skill
|
|
66
|
+
"""
|
|
67
|
+
skills: dict[str, Skill] = {}
|
|
68
|
+
|
|
69
|
+
if not self.skills_dir or not self.skills_dir.exists():
|
|
70
|
+
logger.warning(f"Skills directory not found: {self.skills_dir}")
|
|
71
|
+
return skills
|
|
72
|
+
|
|
73
|
+
for skill_file in self.skills_dir.glob("*.toml"):
|
|
74
|
+
try:
|
|
75
|
+
skill = self.load_skill(skill_file)
|
|
76
|
+
skills[skill.name] = skill
|
|
77
|
+
logger.info(f"Loaded skill: {skill.name}")
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.error(f"Failed to load skill {skill_file}: {e}")
|
|
80
|
+
|
|
81
|
+
return skills
|
|
82
|
+
|
|
83
|
+
def load_from_paths(self, paths: list[Path]) -> dict[str, Skill]:
|
|
84
|
+
"""
|
|
85
|
+
Load skills from multiple directories.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
paths: List of directory paths
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Dict mapping skill name to skill
|
|
92
|
+
"""
|
|
93
|
+
skills = {}
|
|
94
|
+
|
|
95
|
+
for path in paths:
|
|
96
|
+
if not path.exists():
|
|
97
|
+
logger.warning(f"Skill path not found: {path}")
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
for skill_file in path.glob("*.toml"):
|
|
101
|
+
try:
|
|
102
|
+
skill = self.load_skill(skill_file)
|
|
103
|
+
if skill.name in skills:
|
|
104
|
+
logger.warning(f"Skill '{skill.name}' already loaded, skipping")
|
|
105
|
+
else:
|
|
106
|
+
skills[skill.name] = skill
|
|
107
|
+
logger.info(f"Loaded skill: {skill.name}")
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error(f"Failed to load skill {skill_file}: {e}")
|
|
110
|
+
|
|
111
|
+
return skills
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Skill registry for managing and expanding skills."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from .loader import Skill
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SkillRegistry:
|
|
10
|
+
"""Registry for managing skills."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
"""Initialize skill registry."""
|
|
14
|
+
self.skills: dict[str, Skill] = {}
|
|
15
|
+
|
|
16
|
+
def register(self, skill: Skill) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Register a skill.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
skill: Skill to register
|
|
22
|
+
"""
|
|
23
|
+
self.skills[skill.name] = skill
|
|
24
|
+
|
|
25
|
+
def register_all(self, skills: dict[str, Skill]) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Register multiple skills.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
skills: Dict of skills to register
|
|
31
|
+
"""
|
|
32
|
+
self.skills.update(skills)
|
|
33
|
+
|
|
34
|
+
def get(self, name: str) -> Optional[Skill]:
|
|
35
|
+
"""
|
|
36
|
+
Get skill by name.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
name: Skill name
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Skill or None if not found
|
|
43
|
+
"""
|
|
44
|
+
return self.skills.get(name)
|
|
45
|
+
|
|
46
|
+
def list_skills(self) -> list[Skill]:
|
|
47
|
+
"""
|
|
48
|
+
List all registered skills.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
List of all skills
|
|
52
|
+
"""
|
|
53
|
+
return list(self.skills.values())
|
|
54
|
+
|
|
55
|
+
def expand(self, skill_name: str, args: str = "") -> str:
|
|
56
|
+
"""
|
|
57
|
+
Expand skill to full prompt.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
skill_name: Name of skill to expand
|
|
61
|
+
args: Optional arguments for skill
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Expanded prompt
|
|
65
|
+
|
|
66
|
+
Raises:
|
|
67
|
+
KeyError: If skill not found
|
|
68
|
+
"""
|
|
69
|
+
skill = self.skills.get(skill_name)
|
|
70
|
+
if not skill:
|
|
71
|
+
raise KeyError(f"Skill not found: {skill_name}")
|
|
72
|
+
|
|
73
|
+
# Check if args required
|
|
74
|
+
if skill.requires_args and not args:
|
|
75
|
+
raise ValueError(f"Skill '{skill_name}' requires arguments")
|
|
76
|
+
|
|
77
|
+
# Expand prompt with args
|
|
78
|
+
prompt = skill.prompt
|
|
79
|
+
|
|
80
|
+
# Simple template expansion
|
|
81
|
+
# Supports {args} placeholder
|
|
82
|
+
prompt = prompt.replace("{args}", args)
|
|
83
|
+
|
|
84
|
+
return prompt
|
|
85
|
+
|
|
86
|
+
def parse_skill_command(self, user_input: str) -> tuple[Optional[str], Optional[str]]:
|
|
87
|
+
"""
|
|
88
|
+
Parse skill command from user input.
|
|
89
|
+
|
|
90
|
+
Supports formats:
|
|
91
|
+
- /skill_name
|
|
92
|
+
- /skill_name arg1 arg2
|
|
93
|
+
- skill:skill_name
|
|
94
|
+
- skill:skill_name(arg1, arg2)
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
user_input: User input string
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Tuple of (skill_name, args) or (None, None) if not a skill command
|
|
101
|
+
"""
|
|
102
|
+
# Check for /skill format
|
|
103
|
+
if user_input.startswith("/"):
|
|
104
|
+
parts = user_input[1:].split(None, 1)
|
|
105
|
+
skill_name = parts[0]
|
|
106
|
+
args = parts[1] if len(parts) > 1 else ""
|
|
107
|
+
|
|
108
|
+
if skill_name in self.skills:
|
|
109
|
+
return skill_name, args
|
|
110
|
+
|
|
111
|
+
# Check for skill:name format
|
|
112
|
+
skill_pattern = r"^skill:(\w+)(?:\((.*)\))?$"
|
|
113
|
+
match = re.match(skill_pattern, user_input.strip())
|
|
114
|
+
if match:
|
|
115
|
+
skill_name = match.group(1)
|
|
116
|
+
args = match.group(2) or ""
|
|
117
|
+
|
|
118
|
+
if skill_name in self.skills:
|
|
119
|
+
return skill_name, args
|
|
120
|
+
|
|
121
|
+
return None, None
|
|
122
|
+
|
|
123
|
+
def process_input(self, user_input: str) -> tuple[str, bool]:
|
|
124
|
+
"""
|
|
125
|
+
Process user input and expand skills if found.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
user_input: User input string
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Tuple of (processed_input, was_skill)
|
|
132
|
+
"""
|
|
133
|
+
skill_name, args = self.parse_skill_command(user_input)
|
|
134
|
+
|
|
135
|
+
if skill_name:
|
|
136
|
+
expanded = self.expand(skill_name, args or "")
|
|
137
|
+
return expanded, True
|
|
138
|
+
|
|
139
|
+
return user_input, False
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Persistent storage for historical fuzzing data and knowledge base."""
|
|
2
|
+
|
|
3
|
+
from ctrlcode.storage.history_db import (
|
|
4
|
+
BugPattern,
|
|
5
|
+
CodeRecord,
|
|
6
|
+
FuzzingSession,
|
|
7
|
+
HistoryDB,
|
|
8
|
+
OracleRecord,
|
|
9
|
+
StoredTest,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"HistoryDB",
|
|
14
|
+
"FuzzingSession",
|
|
15
|
+
"CodeRecord",
|
|
16
|
+
"OracleRecord",
|
|
17
|
+
"BugPattern",
|
|
18
|
+
"StoredTest",
|
|
19
|
+
]
|