devloop 0.7.0__tar.gz → 0.8.0__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.
- {devloop-0.7.0 → devloop-0.8.0}/PKG-INFO +1 -1
- {devloop-0.7.0 → devloop-0.8.0}/pyproject.toml +2 -2
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/git_commit_assistant.py +1 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/performance_profiler.py +4 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/security_scanner.py +4 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/type_checker.py +4 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/agent_publish.py +16 -16
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/feedback.py +2 -2
- devloop-0.8.0/src/devloop/cli/commands/insights.py +286 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/main.py +307 -141
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/main_v1.py +3 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent_template.py +1 -1
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/amp_integration.py +9 -8
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/context_store.py +5 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/daemon_health.py +6 -9
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/debug_trace.py +5 -3
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/manager.py +4 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/summary_formatter.py +4 -2
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/summary_generator.py +3 -3
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/pypi_registry.py +17 -2
- {devloop-0.7.0 → devloop-0.8.0}/LICENSE +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/README.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/agent_health_monitor.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/ci_monitor.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/code_rabbit.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/doc_lifecycle.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/echo.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/file_logger.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/formatter.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/linter.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/sandbox_helper.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/snyk.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/test_runner.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/agent_rules.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/coderabbit_installer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/audit.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/custom_agents.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/marketplace.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/marketplace_server.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/metrics.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/release.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/summary.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/telemetry.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/tools.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/pre_push_check.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/prerequisites.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/pyodide_installer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/snyk_installer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/.devloop/tools-registry.json +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/README.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/devloop_agents_template.md +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/post-commit +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-push +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/supervisor/devloop.conf +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/systemd/devloop.service +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/base.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/filesystem.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/git.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/process.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/system.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/action_logger.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent_audit_logger.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/amp_thread_mapper.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/auto_fix.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/backup_manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/claude_adapter.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/config.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/config_schema.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/context.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/contextual_feedback.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/custom_agent.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/error_handler.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/error_notifier.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event_replayer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event_store.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/feedback.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/file_lock_manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/learning.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/operational_health.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/pattern_analyzer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/pattern_detector.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/performance.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/proactive_feedback.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/project_context.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/telemetry.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_dependencies.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_registry.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_runner.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/transactional_io.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/integrations/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/integrations/beads_integration.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/__main__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/mapper.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/server.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/api.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/cache.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/http_server.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/installer.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/metadata.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/publisher.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/registry.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/registry_client.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/reviews.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/search.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/signing.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/dora.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/value_metrics.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/artifactory_registry.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/ci_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/circleci_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/github_actions_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/gitlab_ci_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/jenkins_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/provider_manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/registry_provider.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/release/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/release/release_manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/audit_logger.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/bubblewrap_sandbox.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/cgroups_helper.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/factory.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/no_sandbox.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/package.json +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/path_validator.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/pyodide_runner.js +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/pyodide_sandbox.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/sandbox.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/token_manager.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/telemetry/__init__.py +0 -0
- {devloop-0.7.0 → devloop-0.8.0}/src/devloop/telemetry/telemetry_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "devloop"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.0"
|
|
4
4
|
description = "Intelligent background agents for development workflow automation"
|
|
5
5
|
authors = ["DevLoop Contributors <devloop@example.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -129,7 +129,7 @@ disallow_untyped_defs = false
|
|
|
129
129
|
disallow_incomplete_defs = false
|
|
130
130
|
check_untyped_defs = false
|
|
131
131
|
disallow_untyped_decorators = false
|
|
132
|
-
no_implicit_optional =
|
|
132
|
+
no_implicit_optional = true
|
|
133
133
|
warn_redundant_casts = true
|
|
134
134
|
warn_unused_ignores = true
|
|
135
135
|
warn_no_return = true
|
|
@@ -35,7 +35,10 @@ class PerformanceResult:
|
|
|
35
35
|
"""Performance analysis result."""
|
|
36
36
|
|
|
37
37
|
def __init__(
|
|
38
|
-
self,
|
|
38
|
+
self,
|
|
39
|
+
tool: str,
|
|
40
|
+
metrics: List[Dict[str, Any]],
|
|
41
|
+
errors: Optional[List[str]] = None,
|
|
39
42
|
):
|
|
40
43
|
self.tool = tool
|
|
41
44
|
self.metrics = metrics
|
|
@@ -41,7 +41,10 @@ class SecurityResult:
|
|
|
41
41
|
"""Security scan result."""
|
|
42
42
|
|
|
43
43
|
def __init__(
|
|
44
|
-
self,
|
|
44
|
+
self,
|
|
45
|
+
tool: str,
|
|
46
|
+
issues: List[Dict[str, Any]],
|
|
47
|
+
errors: Optional[List[str]] = None,
|
|
45
48
|
):
|
|
46
49
|
self.tool = tool
|
|
47
50
|
self.issues = issues
|
|
@@ -30,7 +30,10 @@ class TypeCheckResult:
|
|
|
30
30
|
"""Type check result."""
|
|
31
31
|
|
|
32
32
|
def __init__(
|
|
33
|
-
self,
|
|
33
|
+
self,
|
|
34
|
+
tool: str,
|
|
35
|
+
issues: List[Dict[str, Any]],
|
|
36
|
+
errors: Optional[List[str]] = None,
|
|
34
37
|
):
|
|
35
38
|
self.tool = tool
|
|
36
39
|
self.issues = issues
|
|
@@ -76,18 +76,18 @@ def publish(
|
|
|
76
76
|
typer.echo("=" * 50)
|
|
77
77
|
|
|
78
78
|
if readiness["errors"]:
|
|
79
|
-
typer.
|
|
79
|
+
typer.secho("Errors (blocking):", fg=typer.colors.RED)
|
|
80
80
|
for error in readiness["errors"]:
|
|
81
|
-
typer.
|
|
81
|
+
typer.secho(f" ✗ {error}", fg=typer.colors.RED)
|
|
82
82
|
raise typer.Exit(1)
|
|
83
83
|
|
|
84
84
|
if readiness["warnings"]:
|
|
85
|
-
typer.
|
|
85
|
+
typer.secho("Warnings:", fg=typer.colors.YELLOW)
|
|
86
86
|
for warning in readiness["warnings"]:
|
|
87
|
-
typer.
|
|
87
|
+
typer.secho(f" ⚠ {warning}", fg=typer.colors.YELLOW)
|
|
88
88
|
|
|
89
89
|
if readiness["ready"]:
|
|
90
|
-
typer.
|
|
90
|
+
typer.secho("✓ Agent is ready to publish", fg=typer.colors.GREEN)
|
|
91
91
|
|
|
92
92
|
typer.echo()
|
|
93
93
|
|
|
@@ -97,7 +97,7 @@ def publish(
|
|
|
97
97
|
signer = AgentSigner(signer_id or "devloop-agent")
|
|
98
98
|
success, signature = signer.sign_agent(agent_dir)
|
|
99
99
|
|
|
100
|
-
if success:
|
|
100
|
+
if success and signature is not None:
|
|
101
101
|
signer.save_signature(agent_dir, signature)
|
|
102
102
|
typer.echo(f"✓ Agent signed by {signature.signer}")
|
|
103
103
|
typer.echo(f" Checksum: {signature.checksum[:16]}...")
|
|
@@ -111,9 +111,9 @@ def publish(
|
|
|
111
111
|
success, message = publisher.publish_agent(agent_dir, force=force)
|
|
112
112
|
|
|
113
113
|
if success:
|
|
114
|
-
typer.
|
|
114
|
+
typer.secho(f"✓ {message}", fg=typer.colors.GREEN)
|
|
115
115
|
else:
|
|
116
|
-
typer.
|
|
116
|
+
typer.secho(f"✗ {message}", fg=typer.colors.RED, err=True)
|
|
117
117
|
raise typer.Exit(1)
|
|
118
118
|
|
|
119
119
|
except Exception as e:
|
|
@@ -164,7 +164,7 @@ def check(
|
|
|
164
164
|
# Show status
|
|
165
165
|
status = "✓ READY" if readiness["ready"] else "✗ NOT READY"
|
|
166
166
|
color = typer.colors.GREEN if readiness["ready"] else typer.colors.RED
|
|
167
|
-
typer.
|
|
167
|
+
typer.secho(f"Status: {status}", fg=color)
|
|
168
168
|
typer.echo()
|
|
169
169
|
|
|
170
170
|
# Show checks
|
|
@@ -173,19 +173,19 @@ def check(
|
|
|
173
173
|
for check_name, result in readiness["checks"].items():
|
|
174
174
|
symbol = "✓" if result else "✗"
|
|
175
175
|
color = typer.colors.GREEN if result else typer.colors.RED
|
|
176
|
-
typer.
|
|
176
|
+
typer.secho(f" {symbol} {check_name}", fg=color)
|
|
177
177
|
|
|
178
178
|
# Show errors
|
|
179
179
|
if readiness["errors"]:
|
|
180
180
|
typer.echo()
|
|
181
|
-
typer.
|
|
181
|
+
typer.secho("Errors (blocking):", fg=typer.colors.RED)
|
|
182
182
|
for error in readiness["errors"]:
|
|
183
183
|
typer.echo(f" • {error}")
|
|
184
184
|
|
|
185
185
|
# Show warnings
|
|
186
186
|
if readiness["warnings"]:
|
|
187
187
|
typer.echo()
|
|
188
|
-
typer.
|
|
188
|
+
typer.secho("Warnings:", fg=typer.colors.YELLOW)
|
|
189
189
|
for warning in readiness["warnings"]:
|
|
190
190
|
typer.echo(f" • {warning}")
|
|
191
191
|
|
|
@@ -193,7 +193,7 @@ def check(
|
|
|
193
193
|
typer.echo()
|
|
194
194
|
updates = publisher.check_updates(agent_dir)
|
|
195
195
|
if updates.get("has_updates"):
|
|
196
|
-
typer.
|
|
196
|
+
typer.secho(
|
|
197
197
|
f"Update available: {updates['published_version']} → "
|
|
198
198
|
f"{updates['local_version']}",
|
|
199
199
|
fg=typer.colors.BLUE,
|
|
@@ -256,7 +256,7 @@ def version(
|
|
|
256
256
|
success = VersionManager.update_agent_json(agent_dir, new_version)
|
|
257
257
|
|
|
258
258
|
if success:
|
|
259
|
-
typer.
|
|
259
|
+
typer.secho(
|
|
260
260
|
f"✓ Version bumped: {current} → {new_version}", fg=typer.colors.GREEN
|
|
261
261
|
)
|
|
262
262
|
else:
|
|
@@ -314,9 +314,9 @@ def deprecate(
|
|
|
314
314
|
)
|
|
315
315
|
|
|
316
316
|
if success:
|
|
317
|
-
typer.
|
|
317
|
+
typer.secho(f"✓ {result_message}", fg=typer.colors.GREEN)
|
|
318
318
|
else:
|
|
319
|
-
typer.
|
|
319
|
+
typer.secho(f"✗ {result_message}", fg=typer.colors.RED, err=True)
|
|
320
320
|
raise typer.Exit(1)
|
|
321
321
|
|
|
322
322
|
except Exception as e:
|
|
@@ -15,7 +15,7 @@ app = typer.Typer(help="Feedback and performance monitoring")
|
|
|
15
15
|
console = Console()
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def get_feedback_api(project_dir: Path = None) -> FeedbackAPI:
|
|
18
|
+
def get_feedback_api(project_dir: Optional[Path] = None) -> FeedbackAPI:
|
|
19
19
|
"""Get feedback API instance for the project."""
|
|
20
20
|
if project_dir is None:
|
|
21
21
|
project_dir = Path.cwd()
|
|
@@ -25,7 +25,7 @@ def get_feedback_api(project_dir: Path = None) -> FeedbackAPI:
|
|
|
25
25
|
return FeedbackAPI(feedback_store)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def get_performance_monitor(project_dir: Path = None) -> PerformanceMonitor:
|
|
28
|
+
def get_performance_monitor(project_dir: Optional[Path] = None) -> PerformanceMonitor:
|
|
29
29
|
"""Get performance monitor instance for the project."""
|
|
30
30
|
if project_dir is None:
|
|
31
31
|
project_dir = Path.cwd()
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""Agent insights CLI command.
|
|
2
|
+
|
|
3
|
+
Displays detected patterns from the self-improvement agent with filtering
|
|
4
|
+
and formatting options.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
from devloop.core.pattern_detector import DetectedPattern, get_pattern_detector
|
|
17
|
+
|
|
18
|
+
app = typer.Typer(help="View agent insights and detected patterns")
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _format_threads(threads: list[str], max_display: int = 3) -> str:
|
|
23
|
+
"""Format thread list for display."""
|
|
24
|
+
if not threads:
|
|
25
|
+
return "-"
|
|
26
|
+
if len(threads) <= max_display:
|
|
27
|
+
return ", ".join(threads)
|
|
28
|
+
return f"{', '.join(threads[:max_display])} (+{len(threads) - max_display} more)"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _format_evidence(evidence: dict) -> str:
|
|
32
|
+
"""Format evidence dictionary for display."""
|
|
33
|
+
if not evidence:
|
|
34
|
+
return "-"
|
|
35
|
+
parts = []
|
|
36
|
+
for key, value in evidence.items():
|
|
37
|
+
if key in ("examples", "affected_threads"):
|
|
38
|
+
continue # Skip these, shown separately
|
|
39
|
+
if isinstance(value, list):
|
|
40
|
+
parts.append(f"{key}: {len(value)}")
|
|
41
|
+
else:
|
|
42
|
+
parts.append(f"{key}: {value}")
|
|
43
|
+
return ", ".join(parts) if parts else "-"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _severity_style(severity: str) -> str:
|
|
47
|
+
"""Get rich style for severity level."""
|
|
48
|
+
styles = {
|
|
49
|
+
"error": "red bold",
|
|
50
|
+
"warning": "yellow",
|
|
51
|
+
"medium": "yellow",
|
|
52
|
+
"info": "blue",
|
|
53
|
+
}
|
|
54
|
+
return styles.get(severity.lower(), "white")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _print_table(patterns: list[DetectedPattern]) -> None:
|
|
58
|
+
"""Print patterns as a rich table."""
|
|
59
|
+
table = Table(title="Detected Patterns")
|
|
60
|
+
table.add_column("Pattern", style="cyan", no_wrap=True)
|
|
61
|
+
table.add_column("Severity", no_wrap=True)
|
|
62
|
+
table.add_column("Confidence", justify="right")
|
|
63
|
+
table.add_column("Message")
|
|
64
|
+
table.add_column("Threads")
|
|
65
|
+
|
|
66
|
+
for pattern in patterns:
|
|
67
|
+
severity_styled = f"[{_severity_style(pattern.severity)}]{pattern.severity}[/]"
|
|
68
|
+
confidence_str = f"{pattern.confidence:.0%}"
|
|
69
|
+
threads_str = _format_threads(pattern.affected_threads)
|
|
70
|
+
|
|
71
|
+
table.add_row(
|
|
72
|
+
pattern.pattern_name,
|
|
73
|
+
severity_styled,
|
|
74
|
+
confidence_str,
|
|
75
|
+
pattern.message,
|
|
76
|
+
threads_str,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
console.print(table)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _print_detailed(patterns: list[DetectedPattern]) -> None:
|
|
83
|
+
"""Print patterns with full details."""
|
|
84
|
+
for i, pattern in enumerate(patterns):
|
|
85
|
+
if i > 0:
|
|
86
|
+
console.print()
|
|
87
|
+
|
|
88
|
+
severity_styled = f"[{_severity_style(pattern.severity)}]{pattern.severity}[/]"
|
|
89
|
+
console.print(f"[bold cyan]{pattern.pattern_name}[/] ({severity_styled})")
|
|
90
|
+
console.print(f" Message: {pattern.message}")
|
|
91
|
+
console.print(f" Confidence: {pattern.confidence:.0%}")
|
|
92
|
+
|
|
93
|
+
if pattern.affected_threads:
|
|
94
|
+
console.print(f" Threads: {_format_threads(pattern.affected_threads, 5)}")
|
|
95
|
+
|
|
96
|
+
if pattern.evidence:
|
|
97
|
+
console.print(f" Evidence: {_format_evidence(pattern.evidence)}")
|
|
98
|
+
|
|
99
|
+
if pattern.recommendation:
|
|
100
|
+
console.print(f" [green]Recommendation:[/] {pattern.recommendation}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _print_json(patterns: list[DetectedPattern]) -> None:
|
|
104
|
+
"""Print patterns as JSON."""
|
|
105
|
+
data = [pattern.to_dict() for pattern in patterns]
|
|
106
|
+
console.print(json.dumps(data, indent=2, default=str))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@app.command("list")
|
|
110
|
+
def list_patterns(
|
|
111
|
+
severity: Optional[str] = typer.Option(
|
|
112
|
+
None, "--severity", "-s", help="Filter by severity (info, warning, error)"
|
|
113
|
+
),
|
|
114
|
+
pattern: Optional[str] = typer.Option(
|
|
115
|
+
None, "--pattern", "-p", help="Filter by pattern name"
|
|
116
|
+
),
|
|
117
|
+
thread: Optional[str] = typer.Option(
|
|
118
|
+
None, "--thread", "-t", help="Filter by thread ID"
|
|
119
|
+
),
|
|
120
|
+
limit: int = typer.Option(50, "--limit", "-n", help="Maximum patterns to show"),
|
|
121
|
+
min_confidence: float = typer.Option(
|
|
122
|
+
0.0, "--min-confidence", help="Minimum confidence (0.0 to 1.0)"
|
|
123
|
+
),
|
|
124
|
+
output_format: str = typer.Option(
|
|
125
|
+
"table", "--format", "-f", help="Output format: table, detailed, json"
|
|
126
|
+
),
|
|
127
|
+
) -> None:
|
|
128
|
+
"""List detected patterns from agent activity."""
|
|
129
|
+
try:
|
|
130
|
+
detector = get_pattern_detector()
|
|
131
|
+
|
|
132
|
+
# Get patterns based on filters
|
|
133
|
+
if thread:
|
|
134
|
+
patterns = detector.get_patterns_for_thread(thread)
|
|
135
|
+
elif pattern:
|
|
136
|
+
patterns = detector.get_patterns_by_type(pattern)
|
|
137
|
+
else:
|
|
138
|
+
patterns = detector.get_recent_patterns(limit=limit, severity=severity)
|
|
139
|
+
|
|
140
|
+
# Apply additional filters
|
|
141
|
+
if min_confidence > 0:
|
|
142
|
+
patterns = [p for p in patterns if p.confidence >= min_confidence]
|
|
143
|
+
|
|
144
|
+
if severity and not thread and not pattern:
|
|
145
|
+
# Already filtered by get_recent_patterns
|
|
146
|
+
pass
|
|
147
|
+
elif severity:
|
|
148
|
+
patterns = [p for p in patterns if p.severity.lower() == severity.lower()]
|
|
149
|
+
|
|
150
|
+
# Limit results
|
|
151
|
+
patterns = patterns[:limit]
|
|
152
|
+
|
|
153
|
+
if not patterns:
|
|
154
|
+
console.print("[yellow]No patterns detected yet.[/]")
|
|
155
|
+
console.print("Run 'devloop insights detect' to analyze activity.")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# Output based on format
|
|
159
|
+
if output_format == "json":
|
|
160
|
+
_print_json(patterns)
|
|
161
|
+
elif output_format == "detailed":
|
|
162
|
+
_print_detailed(patterns)
|
|
163
|
+
else:
|
|
164
|
+
_print_table(patterns)
|
|
165
|
+
|
|
166
|
+
console.print(f"\n[dim]Showing {len(patterns)} pattern(s)[/]")
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
console.print(f"[red]Error listing patterns: {e}[/]")
|
|
170
|
+
raise typer.Exit(1)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@app.command("detect")
|
|
174
|
+
def detect_patterns(
|
|
175
|
+
hours: int = typer.Option(24, "--hours", "-h", help="Time window in hours"),
|
|
176
|
+
min_occurrences: int = typer.Option(
|
|
177
|
+
2, "--min-occurrences", help="Minimum occurrences to trigger pattern"
|
|
178
|
+
),
|
|
179
|
+
output_format: str = typer.Option(
|
|
180
|
+
"table", "--format", "-f", help="Output format: table, detailed, json"
|
|
181
|
+
),
|
|
182
|
+
save: bool = typer.Option(True, "--save/--no-save", help="Save detected patterns"),
|
|
183
|
+
) -> None:
|
|
184
|
+
"""Detect patterns in recent activity."""
|
|
185
|
+
try:
|
|
186
|
+
detector = get_pattern_detector()
|
|
187
|
+
|
|
188
|
+
console.print(
|
|
189
|
+
f"[dim]Analyzing activity from last {hours} hours "
|
|
190
|
+
f"(min {min_occurrences} occurrences)...[/]"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
patterns = detector.detect_patterns(
|
|
194
|
+
time_window_hours=hours,
|
|
195
|
+
min_occurrences=min_occurrences,
|
|
196
|
+
save_results=save,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if not patterns:
|
|
200
|
+
console.print("[green]No patterns detected.[/]")
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Output based on format
|
|
204
|
+
if output_format == "json":
|
|
205
|
+
_print_json(patterns)
|
|
206
|
+
elif output_format == "detailed":
|
|
207
|
+
_print_detailed(patterns)
|
|
208
|
+
else:
|
|
209
|
+
_print_table(patterns)
|
|
210
|
+
|
|
211
|
+
console.print(f"\n[dim]Detected {len(patterns)} pattern(s)[/]")
|
|
212
|
+
if save:
|
|
213
|
+
console.print("[dim]Patterns saved to ~/.devloop/patterns.jsonl[/]")
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
console.print(f"[red]Error detecting patterns: {e}[/]")
|
|
217
|
+
raise typer.Exit(1)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@app.command("summary")
|
|
221
|
+
def pattern_summary(
|
|
222
|
+
hours: int = typer.Option(24, "--hours", "-h", help="Time window in hours"),
|
|
223
|
+
) -> None:
|
|
224
|
+
"""Show summary of pattern activity."""
|
|
225
|
+
try:
|
|
226
|
+
detector = get_pattern_detector()
|
|
227
|
+
patterns = detector.get_recent_patterns(limit=200)
|
|
228
|
+
|
|
229
|
+
if not patterns:
|
|
230
|
+
console.print("[yellow]No patterns recorded yet.[/]")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
# Group by pattern name
|
|
234
|
+
by_name: dict[str, list[DetectedPattern]] = {}
|
|
235
|
+
for p in patterns:
|
|
236
|
+
by_name.setdefault(p.pattern_name, []).append(p)
|
|
237
|
+
|
|
238
|
+
# Group by severity
|
|
239
|
+
by_severity: dict[str, int] = {}
|
|
240
|
+
for p in patterns:
|
|
241
|
+
by_severity[p.severity] = by_severity.get(p.severity, 0) + 1
|
|
242
|
+
|
|
243
|
+
console.print("[bold]Pattern Summary[/]")
|
|
244
|
+
console.print()
|
|
245
|
+
|
|
246
|
+
# Severity breakdown
|
|
247
|
+
console.print("[cyan]By Severity:[/]")
|
|
248
|
+
for sev in ["error", "warning", "medium", "info"]:
|
|
249
|
+
if sev in by_severity:
|
|
250
|
+
style = _severity_style(sev)
|
|
251
|
+
console.print(f" [{style}]{sev}:[/] {by_severity[sev]}")
|
|
252
|
+
|
|
253
|
+
console.print()
|
|
254
|
+
|
|
255
|
+
# Pattern breakdown
|
|
256
|
+
console.print("[cyan]By Pattern Type:[/]")
|
|
257
|
+
for name, items in sorted(by_name.items(), key=lambda x: -len(x[1])):
|
|
258
|
+
avg_confidence = sum(p.confidence for p in items) / len(items)
|
|
259
|
+
console.print(f" {name}: {len(items)} (avg conf: {avg_confidence:.0%})")
|
|
260
|
+
|
|
261
|
+
console.print()
|
|
262
|
+
console.print(f"[dim]Total: {len(patterns)} pattern(s) recorded[/]")
|
|
263
|
+
|
|
264
|
+
except Exception as e:
|
|
265
|
+
console.print(f"[red]Error generating summary: {e}[/]")
|
|
266
|
+
raise typer.Exit(1)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
@app.callback(invoke_without_command=True)
|
|
270
|
+
def main(ctx: typer.Context) -> None:
|
|
271
|
+
"""View agent insights and detected patterns.
|
|
272
|
+
|
|
273
|
+
The insights command helps you understand patterns in your development
|
|
274
|
+
workflow by analyzing CLI actions and agent activity.
|
|
275
|
+
"""
|
|
276
|
+
if ctx.invoked_subcommand is None:
|
|
277
|
+
# Default to list command with explicit defaults
|
|
278
|
+
ctx.invoke(
|
|
279
|
+
list_patterns,
|
|
280
|
+
severity=None,
|
|
281
|
+
pattern=None,
|
|
282
|
+
thread=None,
|
|
283
|
+
limit=50,
|
|
284
|
+
min_confidence=0.0,
|
|
285
|
+
output_format="table",
|
|
286
|
+
)
|