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.
Files changed (151) hide show
  1. {devloop-0.7.0 → devloop-0.8.0}/PKG-INFO +1 -1
  2. {devloop-0.7.0 → devloop-0.8.0}/pyproject.toml +2 -2
  3. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/git_commit_assistant.py +1 -1
  4. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/performance_profiler.py +4 -1
  5. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/security_scanner.py +4 -1
  6. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/type_checker.py +4 -1
  7. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/agent_publish.py +16 -16
  8. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/feedback.py +2 -2
  9. devloop-0.8.0/src/devloop/cli/commands/insights.py +286 -0
  10. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/main.py +307 -141
  11. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/main_v1.py +3 -1
  12. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent_template.py +1 -1
  13. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/amp_integration.py +9 -8
  14. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/context_store.py +5 -0
  15. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/daemon_health.py +6 -9
  16. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/debug_trace.py +5 -3
  17. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/manager.py +4 -0
  18. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/summary_formatter.py +4 -2
  19. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/summary_generator.py +3 -3
  20. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/pypi_registry.py +17 -2
  21. {devloop-0.7.0 → devloop-0.8.0}/LICENSE +0 -0
  22. {devloop-0.7.0 → devloop-0.8.0}/README.md +0 -0
  23. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/__init__.py +0 -0
  24. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/__init__.py +0 -0
  25. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/agent_health_monitor.py +0 -0
  26. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/ci_monitor.py +0 -0
  27. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/code_rabbit.py +0 -0
  28. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/doc_lifecycle.py +0 -0
  29. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/echo.py +0 -0
  30. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/file_logger.py +0 -0
  31. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/formatter.py +0 -0
  32. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/linter.py +0 -0
  33. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/sandbox_helper.py +0 -0
  34. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/snyk.py +0 -0
  35. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/agents/test_runner.py +0 -0
  36. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/__init__.py +0 -0
  37. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/agent_rules.py +0 -0
  38. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/coderabbit_installer.py +0 -0
  39. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/__init__.py +0 -0
  40. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/audit.py +0 -0
  41. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/custom_agents.py +0 -0
  42. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/marketplace.py +0 -0
  43. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/marketplace_server.py +0 -0
  44. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/metrics.py +0 -0
  45. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/release.py +0 -0
  46. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/summary.py +0 -0
  47. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/telemetry.py +0 -0
  48. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/commands/tools.py +0 -0
  49. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/pre_push_check.py +0 -0
  50. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/prerequisites.py +0 -0
  51. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/pyodide_installer.py +0 -0
  52. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/snyk_installer.py +0 -0
  53. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/.devloop/tools-registry.json +0 -0
  54. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/README.md +0 -0
  55. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
  56. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
  57. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
  58. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
  59. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
  60. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/devloop_agents_template.md +0 -0
  61. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/post-commit +0 -0
  62. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
  63. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
  64. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/git_hooks/pre-push +0 -0
  65. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/supervisor/devloop.conf +0 -0
  66. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/cli/templates/systemd/devloop.service +0 -0
  67. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/__init__.py +0 -0
  68. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/base.py +0 -0
  69. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/filesystem.py +0 -0
  70. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/git.py +0 -0
  71. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/manager.py +0 -0
  72. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/process.py +0 -0
  73. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/collectors/system.py +0 -0
  74. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/__init__.py +0 -0
  75. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/action_logger.py +0 -0
  76. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent.py +0 -0
  77. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/agent_audit_logger.py +0 -0
  78. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/amp_thread_mapper.py +0 -0
  79. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/auto_fix.py +0 -0
  80. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/backup_manager.py +0 -0
  81. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/claude_adapter.py +0 -0
  82. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/config.py +0 -0
  83. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/config_schema.py +0 -0
  84. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/context.py +0 -0
  85. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/contextual_feedback.py +0 -0
  86. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/custom_agent.py +0 -0
  87. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/error_handler.py +0 -0
  88. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/error_notifier.py +0 -0
  89. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event.py +0 -0
  90. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event_replayer.py +0 -0
  91. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/event_store.py +0 -0
  92. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/feedback.py +0 -0
  93. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/file_lock_manager.py +0 -0
  94. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/learning.py +0 -0
  95. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/operational_health.py +0 -0
  96. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/pattern_analyzer.py +0 -0
  97. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/pattern_detector.py +0 -0
  98. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/performance.py +0 -0
  99. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/proactive_feedback.py +0 -0
  100. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/project_context.py +0 -0
  101. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/telemetry.py +0 -0
  102. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_dependencies.py +0 -0
  103. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_registry.py +0 -0
  104. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/tool_runner.py +0 -0
  105. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/core/transactional_io.py +0 -0
  106. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/integrations/__init__.py +0 -0
  107. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/integrations/beads_integration.py +0 -0
  108. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/__init__.py +0 -0
  109. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/__main__.py +0 -0
  110. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/mapper.py +0 -0
  111. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/lsp/server.py +0 -0
  112. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/__init__.py +0 -0
  113. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/api.py +0 -0
  114. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/cache.py +0 -0
  115. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/http_server.py +0 -0
  116. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/installer.py +0 -0
  117. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/metadata.py +0 -0
  118. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/publisher.py +0 -0
  119. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/registry.py +0 -0
  120. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/registry_client.py +0 -0
  121. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/reviews.py +0 -0
  122. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/search.py +0 -0
  123. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/marketplace/signing.py +0 -0
  124. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/__init__.py +0 -0
  125. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/dora.py +0 -0
  126. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/metrics/value_metrics.py +0 -0
  127. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/__init__.py +0 -0
  128. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/artifactory_registry.py +0 -0
  129. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/ci_provider.py +0 -0
  130. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/circleci_provider.py +0 -0
  131. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/github_actions_provider.py +0 -0
  132. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/gitlab_ci_provider.py +0 -0
  133. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/jenkins_provider.py +0 -0
  134. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/provider_manager.py +0 -0
  135. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/providers/registry_provider.py +0 -0
  136. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/release/__init__.py +0 -0
  137. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/release/release_manager.py +0 -0
  138. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/__init__.py +0 -0
  139. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/audit_logger.py +0 -0
  140. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/bubblewrap_sandbox.py +0 -0
  141. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/cgroups_helper.py +0 -0
  142. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/factory.py +0 -0
  143. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/no_sandbox.py +0 -0
  144. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/package.json +0 -0
  145. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/path_validator.py +0 -0
  146. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/pyodide_runner.js +0 -0
  147. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/pyodide_sandbox.py +0 -0
  148. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/sandbox.py +0 -0
  149. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/security/token_manager.py +0 -0
  150. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/telemetry/__init__.py +0 -0
  151. {devloop-0.7.0 → devloop-0.8.0}/src/devloop/telemetry/telemetry_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devloop
3
- Version: 0.7.0
3
+ Version: 0.8.0
4
4
  Summary: Intelligent background agents for development workflow automation
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "devloop"
3
- version = "0.7.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 = false
132
+ no_implicit_optional = true
133
133
  warn_redundant_casts = true
134
134
  warn_unused_ignores = true
135
135
  warn_no_return = true
@@ -46,7 +46,7 @@ class CommitSuggestion:
46
46
  message: str,
47
47
  confidence: float,
48
48
  reasoning: str,
49
- alternatives: List[str] = None,
49
+ alternatives: Optional[List[str]] = None,
50
50
  ):
51
51
  self.message = message
52
52
  self.confidence = confidence
@@ -35,7 +35,10 @@ class PerformanceResult:
35
35
  """Performance analysis result."""
36
36
 
37
37
  def __init__(
38
- self, tool: str, metrics: List[Dict[str, Any]], errors: List[str] = None
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, tool: str, issues: List[Dict[str, Any]], errors: List[str] = None
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, tool: str, issues: List[Dict[str, Any]], errors: List[str] = None
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.echo("Errors (blocking):", fg=typer.colors.RED)
79
+ typer.secho("Errors (blocking):", fg=typer.colors.RED)
80
80
  for error in readiness["errors"]:
81
- typer.echo(f" ✗ {error}", fg=typer.colors.RED)
81
+ typer.secho(f" ✗ {error}", fg=typer.colors.RED)
82
82
  raise typer.Exit(1)
83
83
 
84
84
  if readiness["warnings"]:
85
- typer.echo("Warnings:", fg=typer.colors.YELLOW)
85
+ typer.secho("Warnings:", fg=typer.colors.YELLOW)
86
86
  for warning in readiness["warnings"]:
87
- typer.echo(f" ⚠ {warning}", fg=typer.colors.YELLOW)
87
+ typer.secho(f" ⚠ {warning}", fg=typer.colors.YELLOW)
88
88
 
89
89
  if readiness["ready"]:
90
- typer.echo("✓ Agent is ready to publish", fg=typer.colors.GREEN)
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.echo(f"✓ {message}", fg=typer.colors.GREEN)
114
+ typer.secho(f"✓ {message}", fg=typer.colors.GREEN)
115
115
  else:
116
- typer.echo(f"✗ {message}", fg=typer.colors.RED, err=True)
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.echo(f"Status: {status}", fg=color)
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.echo(f" {symbol} {check_name}", fg=color)
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.echo("Errors (blocking):", fg=typer.colors.RED)
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.echo("Warnings:", fg=typer.colors.YELLOW)
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.echo(
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.echo(
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.echo(f"✓ {result_message}", fg=typer.colors.GREEN)
317
+ typer.secho(f"✓ {result_message}", fg=typer.colors.GREEN)
318
318
  else:
319
- typer.echo(f"✗ {result_message}", fg=typer.colors.RED, err=True)
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
+ )