canopy-cli 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. canopy/__init__.py +2 -0
  2. canopy/actions/__init__.py +32 -0
  3. canopy/actions/aliases.py +421 -0
  4. canopy/actions/augments.py +55 -0
  5. canopy/actions/bootstrap.py +249 -0
  6. canopy/actions/bot_resolutions.py +123 -0
  7. canopy/actions/bot_status.py +133 -0
  8. canopy/actions/commit.py +511 -0
  9. canopy/actions/conflicts.py +314 -0
  10. canopy/actions/doctor.py +1459 -0
  11. canopy/actions/draft_replies.py +185 -0
  12. canopy/actions/drift.py +241 -0
  13. canopy/actions/errors.py +115 -0
  14. canopy/actions/evacuate.py +192 -0
  15. canopy/actions/feature_state.py +607 -0
  16. canopy/actions/historian.py +612 -0
  17. canopy/actions/ide_workspace.py +49 -0
  18. canopy/actions/last_visit.py +83 -0
  19. canopy/actions/migrate_slots.py +313 -0
  20. canopy/actions/preflight_state.py +97 -0
  21. canopy/actions/push.py +199 -0
  22. canopy/actions/reads.py +304 -0
  23. canopy/actions/resume.py +582 -0
  24. canopy/actions/review_filter.py +135 -0
  25. canopy/actions/ship.py +399 -0
  26. canopy/actions/slot_details.py +208 -0
  27. canopy/actions/slot_load.py +383 -0
  28. canopy/actions/slots.py +221 -0
  29. canopy/actions/stash.py +230 -0
  30. canopy/actions/switch.py +775 -0
  31. canopy/actions/switch_preflight.py +192 -0
  32. canopy/actions/thread_actions.py +88 -0
  33. canopy/actions/thread_resolutions.py +101 -0
  34. canopy/actions/triage.py +286 -0
  35. canopy/agent/__init__.py +5 -0
  36. canopy/agent/runner.py +129 -0
  37. canopy/agent_setup/__init__.py +264 -0
  38. canopy/agent_setup/skills/augment-canopy/SKILL.md +116 -0
  39. canopy/agent_setup/skills/using-canopy/SKILL.md +191 -0
  40. canopy/cli/__init__.py +0 -0
  41. canopy/cli/main.py +4152 -0
  42. canopy/cli/render.py +98 -0
  43. canopy/cli/ui.py +150 -0
  44. canopy/features/__init__.py +2 -0
  45. canopy/features/coordinator.py +1256 -0
  46. canopy/git/__init__.py +0 -0
  47. canopy/git/hooks.py +173 -0
  48. canopy/git/multi.py +435 -0
  49. canopy/git/repo.py +859 -0
  50. canopy/git/templates/post-checkout.py +67 -0
  51. canopy/graph/__init__.py +0 -0
  52. canopy/integrations/__init__.py +0 -0
  53. canopy/integrations/github.py +983 -0
  54. canopy/integrations/linear.py +307 -0
  55. canopy/integrations/precommit.py +239 -0
  56. canopy/mcp/__init__.py +0 -0
  57. canopy/mcp/client.py +329 -0
  58. canopy/mcp/server.py +1797 -0
  59. canopy/providers/__init__.py +105 -0
  60. canopy/providers/github_issues.py +289 -0
  61. canopy/providers/linear.py +341 -0
  62. canopy/providers/types.py +149 -0
  63. canopy/workspace/__init__.py +4 -0
  64. canopy/workspace/config.py +378 -0
  65. canopy/workspace/context.py +224 -0
  66. canopy/workspace/discovery.py +197 -0
  67. canopy/workspace/workspace.py +173 -0
  68. canopy_cli-3.1.0.dist-info/METADATA +282 -0
  69. canopy_cli-3.1.0.dist-info/RECORD +71 -0
  70. canopy_cli-3.1.0.dist-info/WHEEL +4 -0
  71. canopy_cli-3.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ # __CANOPY_HOOK_MARKER__ post-checkout v1
3
+ """Canopy post-checkout hook — records HEAD state to .canopy/state/heads.json.
4
+
5
+ Installed by `canopy hooks install`. Never blocks git operations on errors.
6
+ Chains to a pre-existing post-checkout.canopy-chained if present.
7
+ """
8
+ import fcntl
9
+ import json
10
+ import os
11
+ import subprocess
12
+ import sys
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+
16
+ # Substituted at install time.
17
+ CANOPY_REPO = "__CANOPY_REPO__"
18
+ CANOPY_WORKSPACE_ROOT = Path("__CANOPY_WORKSPACE_ROOT__")
19
+
20
+
21
+ def _record_state() -> None:
22
+ if len(sys.argv) < 4:
23
+ return
24
+ prev_sha, new_sha, is_branch_checkout = sys.argv[1], sys.argv[2], sys.argv[3]
25
+ # Only record on branch checkouts (not file checkouts).
26
+ if is_branch_checkout != "1":
27
+ return
28
+
29
+ branch = subprocess.run(
30
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"],
31
+ capture_output=True, text=True, check=False,
32
+ ).stdout.strip()
33
+ if not branch or branch == "HEAD":
34
+ return # detached; skip
35
+
36
+ ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
37
+ entry = {"branch": branch, "sha": new_sha, "prev_sha": prev_sha, "ts": ts}
38
+
39
+ state_dir = CANOPY_WORKSPACE_ROOT / ".canopy" / "state"
40
+ state_dir.mkdir(parents=True, exist_ok=True)
41
+ state_file = state_dir / "heads.json"
42
+ lock_file = state_dir / "heads.json.lock"
43
+
44
+ with open(lock_file, "w") as lock:
45
+ fcntl.flock(lock.fileno(), fcntl.LOCK_EX)
46
+ try:
47
+ state = json.loads(state_file.read_text())
48
+ except (FileNotFoundError, json.JSONDecodeError):
49
+ state = {}
50
+ state[CANOPY_REPO] = entry
51
+ tmp = state_file.with_suffix(".json.tmp")
52
+ tmp.write_text(json.dumps(state, indent=2))
53
+ os.replace(tmp, state_file)
54
+
55
+
56
+ def _chain_existing() -> None:
57
+ chained = Path(__file__).parent / "post-checkout.canopy-chained"
58
+ if chained.is_file() and os.access(chained, os.X_OK):
59
+ os.execv(str(chained), [str(chained), *sys.argv[1:]])
60
+
61
+
62
+ if __name__ == "__main__":
63
+ try:
64
+ _record_state()
65
+ except Exception:
66
+ pass # never block git on hook failure
67
+ _chain_existing()
File without changes
File without changes