devloop 0.10.0__tar.gz → 0.10.2__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 (157) hide show
  1. {devloop-0.10.0 → devloop-0.10.2}/PKG-INFO +1 -1
  2. {devloop-0.10.0 → devloop-0.10.2}/pyproject.toml +1 -1
  3. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/release.py +16 -22
  4. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/main.py +65 -32
  5. {devloop-0.10.0 → devloop-0.10.2}/LICENSE +0 -0
  6. {devloop-0.10.0 → devloop-0.10.2}/README.md +0 -0
  7. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/__init__.py +0 -0
  8. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/__init__.py +0 -0
  9. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/agent_health_monitor.py +0 -0
  10. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/ci_monitor.py +0 -0
  11. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/code_rabbit.py +0 -0
  12. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/doc_lifecycle.py +0 -0
  13. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/echo.py +0 -0
  14. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/file_logger.py +0 -0
  15. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/formatter.py +0 -0
  16. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/git_commit_assistant.py +0 -0
  17. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/linter.py +0 -0
  18. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/performance_profiler.py +0 -0
  19. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/sandbox_helper.py +0 -0
  20. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/security_scanner.py +0 -0
  21. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/snyk.py +0 -0
  22. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/test_runner.py +0 -0
  23. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/agents/type_checker.py +0 -0
  24. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/__init__.py +0 -0
  25. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/agent_rules.py +0 -0
  26. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/coderabbit_installer.py +0 -0
  27. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/__init__.py +0 -0
  28. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/agent_publish.py +0 -0
  29. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/audit.py +0 -0
  30. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/custom_agents.py +0 -0
  31. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/feedback.py +0 -0
  32. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/insights.py +0 -0
  33. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/marketplace.py +0 -0
  34. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/marketplace_server.py +0 -0
  35. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/mcp_server.py +0 -0
  36. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/metrics.py +0 -0
  37. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/summary.py +0 -0
  38. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/telemetry.py +0 -0
  39. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/commands/tools.py +0 -0
  40. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/main_v1.py +0 -0
  41. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/pre_push_check.py +0 -0
  42. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/prerequisites.py +0 -0
  43. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/pyodide_installer.py +0 -0
  44. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/snyk_installer.py +0 -0
  45. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/.devloop/tools-registry.json +0 -0
  46. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/README.md +0 -0
  47. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/agent-summary.md +0 -0
  48. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/devloop-findings.md +0 -0
  49. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/devloop-status.md +0 -0
  50. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/extract-findings.md +0 -0
  51. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/claude_commands/verify-work.md +0 -0
  52. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/devloop_agents_template.md +0 -0
  53. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/git_hooks/post-commit +0 -0
  54. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/git_hooks/pre-commit +0 -0
  55. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/git_hooks/pre-commit-checks +0 -0
  56. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/git_hooks/pre-push +0 -0
  57. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/supervisor/devloop.conf +0 -0
  58. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/cli/templates/systemd/devloop.service +0 -0
  59. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/__init__.py +0 -0
  60. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/base.py +0 -0
  61. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/filesystem.py +0 -0
  62. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/git.py +0 -0
  63. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/manager.py +0 -0
  64. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/process.py +0 -0
  65. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/collectors/system.py +0 -0
  66. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/__init__.py +0 -0
  67. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/action_logger.py +0 -0
  68. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/agent.py +0 -0
  69. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/agent_audit_logger.py +0 -0
  70. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/agent_template.py +0 -0
  71. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/amp_integration.py +0 -0
  72. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/amp_thread_mapper.py +0 -0
  73. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/auto_fix.py +0 -0
  74. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/backup_manager.py +0 -0
  75. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/claude_adapter.py +0 -0
  76. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/config.py +0 -0
  77. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/config_schema.py +0 -0
  78. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/context.py +0 -0
  79. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/context_store.py +0 -0
  80. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/contextual_feedback.py +0 -0
  81. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/custom_agent.py +0 -0
  82. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/daemon_health.py +0 -0
  83. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/debug_trace.py +0 -0
  84. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/error_handler.py +0 -0
  85. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/error_notifier.py +0 -0
  86. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/event.py +0 -0
  87. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/event_replayer.py +0 -0
  88. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/event_store.py +0 -0
  89. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/feedback.py +0 -0
  90. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/file_lock_manager.py +0 -0
  91. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/learning.py +0 -0
  92. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/manager.py +0 -0
  93. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/operational_health.py +0 -0
  94. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/pattern_analyzer.py +0 -0
  95. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/pattern_detector.py +0 -0
  96. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/performance.py +0 -0
  97. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/proactive_feedback.py +0 -0
  98. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/project_context.py +0 -0
  99. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/summary_formatter.py +0 -0
  100. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/summary_generator.py +0 -0
  101. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/telemetry.py +0 -0
  102. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/tool_dependencies.py +0 -0
  103. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/tool_registry.py +0 -0
  104. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/tool_runner.py +0 -0
  105. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/core/transactional_io.py +0 -0
  106. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/integrations/__init__.py +0 -0
  107. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/integrations/beads_integration.py +0 -0
  108. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/lsp/__init__.py +0 -0
  109. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/lsp/__main__.py +0 -0
  110. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/lsp/mapper.py +0 -0
  111. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/lsp/server.py +0 -0
  112. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/__init__.py +0 -0
  113. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/api.py +0 -0
  114. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/cache.py +0 -0
  115. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/http_server.py +0 -0
  116. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/installer.py +0 -0
  117. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/metadata.py +0 -0
  118. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/publisher.py +0 -0
  119. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/registry.py +0 -0
  120. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/registry_client.py +0 -0
  121. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/reviews.py +0 -0
  122. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/search.py +0 -0
  123. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/marketplace/signing.py +0 -0
  124. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/mcp/__init__.py +0 -0
  125. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/mcp/resources.py +0 -0
  126. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/mcp/server.py +0 -0
  127. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/mcp/subscriptions.py +0 -0
  128. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/mcp/tools.py +0 -0
  129. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/metrics/__init__.py +0 -0
  130. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/metrics/dora.py +0 -0
  131. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/metrics/value_metrics.py +0 -0
  132. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/__init__.py +0 -0
  133. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/artifactory_registry.py +0 -0
  134. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/ci_provider.py +0 -0
  135. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/circleci_provider.py +0 -0
  136. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/github_actions_provider.py +0 -0
  137. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/gitlab_ci_provider.py +0 -0
  138. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/jenkins_provider.py +0 -0
  139. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/provider_manager.py +0 -0
  140. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/pypi_registry.py +0 -0
  141. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/providers/registry_provider.py +0 -0
  142. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/release/__init__.py +0 -0
  143. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/release/release_manager.py +0 -0
  144. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/__init__.py +0 -0
  145. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/audit_logger.py +0 -0
  146. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/bubblewrap_sandbox.py +0 -0
  147. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/cgroups_helper.py +0 -0
  148. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/factory.py +0 -0
  149. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/no_sandbox.py +0 -0
  150. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/package.json +0 -0
  151. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/path_validator.py +0 -0
  152. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/pyodide_runner.js +0 -0
  153. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/pyodide_sandbox.py +0 -0
  154. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/sandbox.py +0 -0
  155. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/security/token_manager.py +0 -0
  156. {devloop-0.10.0 → devloop-0.10.2}/src/devloop/telemetry/__init__.py +0 -0
  157. {devloop-0.10.0 → devloop-0.10.2}/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.10.0
3
+ Version: 0.10.2
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.10.0"
3
+ version = "0.10.2"
4
4
  description = "Intelligent background agents for development workflow automation"
5
5
  authors = ["DevLoop Contributors <devloop@example.com>"]
6
6
  license = "MIT"
@@ -37,11 +37,10 @@ def publish(
37
37
  ) -> int:
38
38
  """Publish a new release.
39
39
 
40
- Performs the full release workflow:
40
+ Performs the release workflow:
41
41
  1. Pre-release checks (git clean, branch, CI passes, registry credentials)
42
42
  2. Create git tag (v{version} by default)
43
- 3. Publish to registry (PyPI, Artifactory, etc.)
44
- 4. Push tag to remote
43
+ 3. Push tag to remote (CI handles the actual PyPI publish)
45
44
 
46
45
  Example:
47
46
  devloop release publish 1.2.3
@@ -57,12 +56,12 @@ def publish(
57
56
  console.print("[yellow]Dry-run mode[/yellow] - no changes will be made")
58
57
  console.print()
59
58
 
60
- # Create config
59
+ # Create config — publish=False because CI handles PyPI on tag push
61
60
  config = ReleaseConfig(
62
61
  version=version,
63
62
  branch=branch,
64
63
  create_tag=not skip_tag,
65
- publish=not skip_publish,
64
+ publish=False,
66
65
  ci_provider=ci_provider,
67
66
  registry_provider=registry_provider,
68
67
  )
@@ -111,30 +110,25 @@ def publish(
111
110
  return 1
112
111
  console.print()
113
112
 
114
- # Publish if configured
115
- if config.publish:
116
- console.print("[bold]Publishing to registry...[/bold]")
117
- pub_result = manager.publish_release()
118
- if pub_result.success:
119
- console.print(
120
- f"[green]✓ Published to:[/green] {pub_result.registry_provider_name}"
121
- )
122
- if pub_result.url:
123
- console.print(f"[cyan]URL:[/cyan] {pub_result.url}")
124
- else:
125
- console.print(f"[red]✗ Publishing failed:[/red] {pub_result.error}")
126
- return 1
113
+ # Push tag — CI release workflow handles the actual PyPI publish
114
+ tag_name = f"v{version}"
115
+ if config.create_tag:
116
+ console.print(f"[bold]Pushing tag {tag_name} to remote...[/bold]")
117
+ manager._push_tag_to_remote(tag_name)
118
+ console.print("[green]✓ Tag pushed[/green] — CI will publish to PyPI")
127
119
  console.print()
128
120
 
129
121
  # Summary
130
- console.print("[bold][green]✓ Release successful![/green][/bold]")
122
+ console.print("[bold][green]✓ Release initiated![/green][/bold]")
131
123
  if checks_result.ci_provider_name:
132
124
  console.print(f" CI Provider: {checks_result.ci_provider_name}")
133
- if checks_result.registry_provider_name:
134
- console.print(f" Registry: {checks_result.registry_provider_name}")
135
125
  console.print(f" Version: {version}")
136
126
  if config.create_tag:
137
- console.print(f" Tag: v{version}")
127
+ console.print(f" Tag: {tag_name}")
128
+ console.print(
129
+ " PyPI: CI will publish automatically — monitor at "
130
+ "https://github.com/wioota/devloop/actions"
131
+ )
138
132
 
139
133
  return 0
140
134
 
@@ -7,7 +7,7 @@ import subprocess
7
7
  import sys
8
8
  import time
9
9
  from pathlib import Path
10
- from typing import Optional
10
+ from typing import Any, Optional
11
11
 
12
12
  import typer
13
13
  from rich.console import Console
@@ -641,7 +641,7 @@ def _setup_devloop_directory(path: Path) -> Path:
641
641
  return claude_dir
642
642
 
643
643
 
644
- def _read_init_manifest(claude_dir: Path) -> dict:
644
+ def _read_init_manifest(claude_dir: Path) -> dict[str, Any]:
645
645
  """Read .devloop/.init-manifest.json, returning defaults if missing or invalid."""
646
646
  import json
647
647
 
@@ -649,7 +649,8 @@ def _read_init_manifest(claude_dir: Path) -> dict:
649
649
  if not manifest_path.exists():
650
650
  return {"version": None, "managed": []}
651
651
  try:
652
- return json.loads(manifest_path.read_text())
652
+ result: dict[str, Any] = json.loads(manifest_path.read_text())
653
+ return result
653
654
  except (json.JSONDecodeError, OSError):
654
655
  return {"version": None, "managed": []}
655
656
 
@@ -1027,7 +1028,7 @@ def _setup_git_hooks(path: Path) -> None:
1027
1028
  checker.show_installation_guide(missing)
1028
1029
 
1029
1030
 
1030
- def _create_claude_hooks(agents_hooks_dir: Path) -> list:
1031
+ def _create_claude_hooks(agents_hooks_dir: Path) -> tuple[list[str], int]:
1031
1032
  """Create Claude Code hook scripts."""
1032
1033
  hooks = {
1033
1034
  "claude-session-start": """#!/bin/bash
@@ -1066,33 +1067,65 @@ exit 0
1066
1067
  #
1067
1068
  PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
1068
1069
  cd "$PROJECT_DIR" || exit 0
1069
- input_json=$(cat)
1070
- tool_name=$(echo "$input_json" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
1071
- tool_input=$(echo "$input_json" | jq -r '.tool_input // {}' 2>/dev/null || echo "{}")
1072
- if [[ "$tool_name" != "Write" && "$tool_name" != "Edit" ]]; then
1073
- exit 0
1074
- fi
1075
- file_path=$(echo "$tool_input" | jq -r '.path // ""' 2>/dev/null || echo "")
1076
- if [ -n "$file_path" ]; then
1077
- file_path=$(realpath "$file_path" 2>/dev/null || echo "$file_path")
1078
- fi
1079
- protected_patterns=(".beads/" ".devloop/" ".git/" ".agents/hooks/" ".claude/" "AGENTS.md" "CODING_RULES.md" "AMP_ONBOARDING.md")
1080
- is_protected=0
1081
- for pattern in "${protected_patterns[@]}"; do
1082
- if [[ "$file_path" == *"$pattern"* ]]; then
1083
- is_protected=1
1084
- break
1085
- fi
1086
- done
1087
- if [ $is_protected -eq 1 ]; then
1088
- cat >&2 <<EOF
1089
- 🚫 Protected file: $file_path
1090
- This file is protected by DevLoop to prevent accidental modifications.
1091
- If you need to modify this file, use manual editing or ask the user.
1092
- EOF
1093
- exit 2
1094
- fi
1095
- exit 0
1070
+
1071
+ INPUT_FILE=$(mktemp)
1072
+ cat > "$INPUT_FILE"
1073
+ export INPUT_FILE PROJECT_DIR
1074
+
1075
+ python3 << 'PYTHON_EOF'
1076
+ import json
1077
+ import os
1078
+ import sys
1079
+ from pathlib import Path
1080
+
1081
+ protected_patterns = [
1082
+ ".beads/", ".devloop/", ".git/", ".agents/hooks/", ".claude/",
1083
+ "AGENTS.md", "CODING_RULES.md", "AMP_ONBOARDING.md",
1084
+ ]
1085
+
1086
+ input_file = os.environ.get("INPUT_FILE", "")
1087
+ if not input_file:
1088
+ sys.exit(0)
1089
+ try:
1090
+ with open(input_file) as f:
1091
+ input_data = f.read()
1092
+ if not input_data.strip():
1093
+ sys.exit(0)
1094
+ hook_input = json.loads(input_data)
1095
+ except (json.JSONDecodeError, OSError):
1096
+ sys.exit(0)
1097
+
1098
+ tool_name = hook_input.get("tool_name", "")
1099
+ if tool_name not in ("Write", "Edit"):
1100
+ sys.exit(0)
1101
+
1102
+ tool_input = hook_input.get("tool_input", {})
1103
+ if not isinstance(tool_input, dict):
1104
+ sys.exit(0)
1105
+
1106
+ file_path = tool_input.get("file_path") or tool_input.get("path", "")
1107
+ if not file_path:
1108
+ sys.exit(0)
1109
+
1110
+ try:
1111
+ file_path = os.path.realpath(file_path)
1112
+ except OSError:
1113
+ pass
1114
+
1115
+ is_protected = any(pat in file_path for pat in protected_patterns)
1116
+ if is_protected:
1117
+ sys.stderr.write(
1118
+ f"\\U0001f6ab Protected file: {file_path}\\n"
1119
+ "This file is protected by DevLoop to prevent accidental modifications.\\n"
1120
+ "If you need to modify this file, use manual editing or ask the user.\\n"
1121
+ )
1122
+ sys.exit(2)
1123
+ sys.exit(0)
1124
+ PYTHON_EOF
1125
+
1126
+ EXIT_CODE=$?
1127
+ rm -f "$INPUT_FILE"
1128
+ exit $EXIT_CODE
1096
1129
  """,
1097
1130
  "check-devloop-context": """#!/bin/bash
1098
1131
  #
@@ -1425,7 +1458,7 @@ def _setup_mcp_server(path: Path) -> None:
1425
1458
 
1426
1459
  def _setup_claude_hooks(
1427
1460
  path: Path, agents_hooks_dir: Path, non_interactive: bool, upgrade: bool = False
1428
- ) -> list:
1461
+ ) -> list[str]:
1429
1462
  """Setup Claude Code hooks.
1430
1463
 
1431
1464
  Args:
File without changes
File without changes