capt-hook 6.2.0__tar.gz → 6.4.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 (108) hide show
  1. {capt_hook-6.2.0 → capt_hook-6.4.0}/PKG-INFO +1 -1
  2. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/cli.py +3 -1
  3. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/context.py +4 -3
  4. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/durable.py +1 -8
  5. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/loader.py +5 -6
  6. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/capt-hook.toml +1 -1
  7. capt_hook-6.4.0/captain_hook/packs/general/models.py +734 -0
  8. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/manager.py +2 -2
  9. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/llm.py +4 -4
  10. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/prompt.py +5 -6
  11. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/session.py +0 -6
  12. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/settings.py +14 -9
  13. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/state.py +0 -2
  14. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/testing/helpers.py +60 -44
  15. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/testing/session_cache.py +3 -1
  16. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/util/model_cache.py +2 -3
  17. {capt_hook-6.2.0 → capt_hook-6.4.0}/pyproject.toml +1 -1
  18. capt_hook-6.2.0/captain_hook/packs/general/models.py +0 -378
  19. {capt_hook-6.2.0 → capt_hook-6.4.0}/LICENSE +0 -0
  20. {capt_hook-6.2.0 → capt_hook-6.4.0}/README.md +0 -0
  21. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/.claude-plugin/plugin.json +0 -0
  22. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/__init__.py +0 -0
  23. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/__main__.py +0 -0
  24. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/app.py +0 -0
  25. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/ast_grep.py +0 -0
  26. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/classifiers/__init__.py +0 -0
  27. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/classifiers/conductor.py +0 -0
  28. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/classifiers/droid.py +0 -0
  29. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/classifiers/native.py +0 -0
  30. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/command.py +0 -0
  31. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/conditions.py +0 -0
  32. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/contexts.py +0 -0
  33. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/decisions.py +0 -0
  34. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/dispatch.py +0 -0
  35. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/events.py +0 -0
  36. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/fields.py +0 -0
  37. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/file.py +0 -0
  38. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/log.py +0 -0
  39. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/__init__.py +0 -0
  40. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/__init__.py +0 -0
  41. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/_lib.py +0 -0
  42. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/commands.py +0 -0
  43. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/docs.py +0 -0
  44. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/plans.py +0 -0
  45. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/prompts.py +0 -0
  46. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/review.py +0 -0
  47. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/tasks.py +0 -0
  48. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/general/tombstones.py +0 -0
  49. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/go/__init__.py +0 -0
  50. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/go/capt-hook.toml +0 -0
  51. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/go/testing.py +0 -0
  52. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/go/toolchain.py +0 -0
  53. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/python/__init__.py +0 -0
  54. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/python/capt-hook.toml +0 -0
  55. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/python/style.py +0 -0
  56. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/python/testing.py +0 -0
  57. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/python/toolchain.py +0 -0
  58. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/steering/__init__.py +0 -0
  59. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/steering/capt-hook.toml +0 -0
  60. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/packs/steering/steering.py +0 -0
  61. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/__init__.py +0 -0
  62. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/commands.py +0 -0
  63. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/lint.py +0 -0
  64. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/nudge.py +0 -0
  65. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/rewrite.py +0 -0
  66. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/primitives/workflow.py +0 -0
  67. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/py.typed +0 -0
  68. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/__init__.py +0 -0
  69. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/cli.py +0 -0
  70. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/dashboard.py +0 -0
  71. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/fix.py +0 -0
  72. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/formats.py +0 -0
  73. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/judge.py +0 -0
  74. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/pipeline.py +0 -0
  75. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/repo.py +0 -0
  76. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/scan.py +0 -0
  77. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/settings.py +0 -0
  78. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/store.py +0 -0
  79. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/review/sync.py +0 -0
  80. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/signals/__init__.py +0 -0
  81. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/signals/nlp.py +0 -0
  82. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/authoring-hooks/SKILL.md +0 -0
  83. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/authoring-hooks/references/capt-hook-api.md +0 -0
  84. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/authoring-hooks/references/pattern-catalog.md +0 -0
  85. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/authoring-hooks/references/pitfalls.md +0 -0
  86. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/authoring-hooks/references/testing-hooks.md +0 -0
  87. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/bootstrapping-hooks/SKILL.md +0 -0
  88. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/scanning-sessions/SKILL.md +0 -0
  89. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/scanning-sessions/references/pr-workflow.md +0 -0
  90. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/scanning-sessions/references/review-cli.md +0 -0
  91. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/translating-styleguides/SKILL.md +0 -0
  92. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/translating-styleguides/references/llm-rule-patterns.md +0 -0
  93. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/translating-styleguides/references/matcher-reference.md +0 -0
  94. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/skills/translating-styleguides/references/tier-rubric.md +0 -0
  95. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/style/__init__.py +0 -0
  96. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/style/ast_grep.py +0 -0
  97. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/style/matchers.py +0 -0
  98. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/style/scope.py +0 -0
  99. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/style/types.py +0 -0
  100. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/tasks.py +0 -0
  101. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/templates/example_hook.py.tmpl +0 -0
  102. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/testing/__init__.py +0 -0
  103. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/testing/types.py +0 -0
  104. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/types.py +0 -0
  105. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/util/__init__.py +0 -0
  106. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/util/fs.py +0 -0
  107. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/util/http.py +0 -0
  108. {capt_hook-6.2.0 → capt_hook-6.4.0}/captain_hook/util/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capt-hook
3
- Version: 6.2.0
3
+ Version: 6.4.0
4
4
  Summary: Declarative hook framework for Claude Code
5
5
  Keywords: claude,claude-code,hooks,llm,agents,guardrails,cli
6
6
  Author: Yasyf Mohamedali
@@ -501,7 +501,9 @@ def run_tests(json_output: bool = False) -> None:
501
501
  @click.pass_context
502
502
  def cli(ctx: click.Context, hooks: str | None, root_path: str | None) -> None:
503
503
  """Captain Hook — declarative hook framework for Claude Code lifecycle events."""
504
- root = Path(root_path) if root_path else Path(env) if (env := os.environ.get("CLAUDE_PROJECT_DIR")) else Path.cwd()
504
+ from captain_hook.settings import resolve_project_dir
505
+
506
+ root = Path(root_path) if root_path else Path(p) if (p := resolve_project_dir()) else Path.cwd()
505
507
  ctx.obj = CliState(root=root, hooks=hooks or str(root / ".claude" / "hooks"))
506
508
 
507
509
 
@@ -21,6 +21,7 @@ from spawnllm.proc import run_cli
21
21
  from captain_hook.classifiers import detect
22
22
  from captain_hook.prompt import Prompt
23
23
  from captain_hook.session import SessionStore
24
+ from captain_hook.settings import resolve_project_dir
24
25
 
25
26
  if TYPE_CHECKING:
26
27
  from collections.abc import Sequence
@@ -78,7 +79,7 @@ def lift_session(events: Sequence[TranscriptEvent], *, path: Path | None = None)
78
79
  from captain_hook.app import _state
79
80
 
80
81
  classifier = _state.classifier or detect(
81
- cwd=os.environ.get("CLAUDE_PROJECT_DIR") or os.environ.get("FACTORY_PROJECT_DIR"),
82
+ cwd=resolve_project_dir(),
82
83
  transcript_path=str(path) if path else None,
83
84
  events=events,
84
85
  )
@@ -185,7 +186,7 @@ class HookContext:
185
186
  input=input,
186
187
  timeout=timeout,
187
188
  env=os.environ | (env or {}),
188
- cwd=os.environ.get("CLAUDE_PROJECT_DIR") or os.environ.get("FACTORY_PROJECT_DIR"),
189
+ cwd=resolve_project_dir(),
189
190
  )
190
191
  except (OSError, subprocess.SubprocessError):
191
192
  if throw:
@@ -309,7 +310,7 @@ class HookContext:
309
310
  ) -> str | BaseModel:
310
311
  diff_text = self.diff("uncommitted" if diff is True else diff) if diff else None
311
312
  prompt = self.assemble_prompt(template, args, kwargs, transcript=transcript, diff_text=diff_text)
312
- cwd = os.environ.get("CLAUDE_PROJECT_DIR") or os.environ.get("FACTORY_PROJECT_DIR")
313
+ cwd = resolve_project_dir()
313
314
  if response_model is not None:
314
315
  return extract_sync(
315
316
  prompt, response_model, specialty=specialty, model=model, agent=agent, cwd=cwd, timeout=timeout
@@ -26,13 +26,11 @@ if TYPE_CHECKING:
26
26
 
27
27
  Scope = Literal["project", "global"]
28
28
 
29
- DURABLE_ROOT_OVERRIDE: list[Path] = []
30
-
31
29
 
32
30
  def durable_root() -> Path:
33
31
  from captain_hook.settings import resolve_state_dir
34
32
 
35
- return DURABLE_ROOT_OVERRIDE[-1] if DURABLE_ROOT_OVERRIDE else resolve_state_dir() / "hooks" / "durable"
33
+ return resolve_state_dir() / "hooks" / "durable"
36
34
 
37
35
 
38
36
  def project_key(repo_root: Path) -> str:
@@ -98,11 +96,6 @@ class DurableStore:
98
96
  if model not in cls.TRACKED:
99
97
  cls.TRACKED.append(model)
100
98
 
101
- @classmethod
102
- def untrack(cls, model: type[BaseModel]) -> None:
103
- if model in cls.TRACKED:
104
- cls.TRACKED.remove(model)
105
-
106
99
  @classmethod
107
100
  def tracked_models(cls) -> Sequence[type[BaseModel]]:
108
101
  return tuple(cls.TRACKED)
@@ -13,7 +13,7 @@ from typing import TYPE_CHECKING
13
13
 
14
14
  from loguru import logger
15
15
 
16
- from captain_hook.app import State, _state, on
16
+ from captain_hook.app import _state, on
17
17
  from captain_hook.settings import build_settings
18
18
  from captain_hook.types import Event
19
19
 
@@ -47,8 +47,7 @@ def import_or_reload(fqn: str, fresh_this_pass: set[str]) -> ModuleType:
47
47
  return mod
48
48
 
49
49
 
50
- def discover_hooks(hooks_dir: str | Path, state: State | None = None) -> None:
51
- target = state or _state
50
+ def discover_hooks(hooks_dir: str | Path) -> None:
52
51
  hooks_path = Path(hooks_dir).resolve()
53
52
  if str(hooks_path.parent) not in sys.path:
54
53
  sys.path.insert(0, str(hooks_path.parent))
@@ -60,9 +59,9 @@ def discover_hooks(hooks_dir: str | Path, state: State | None = None) -> None:
60
59
 
61
60
  if CONF_MODULE in top_level:
62
61
  conf_module = import_or_reload(f"{pkg}.{CONF_MODULE}", fresh_this_pass)
63
- target.settings = build_settings(conf_module)
62
+ _state.settings = build_settings(conf_module)
64
63
  if classifier := getattr(conf_module, "classifier", None):
65
- target.classifier = classifier
64
+ _state.classifier = classifier
66
65
 
67
66
  all_modules = {
68
67
  info.name
@@ -80,7 +79,7 @@ def discover_hooks(hooks_dir: str | Path, state: State | None = None) -> None:
80
79
  import_or_reload(fqn, fresh_this_pass)
81
80
  except Exception as exc:
82
81
  logger.bind(module=fqn).opt(exception=True).warning("skipped unloadable hook module")
83
- target.load_errors.append((fqn, exc))
82
+ _state.load_errors.append((fqn, exc))
84
83
 
85
84
 
86
85
  def ensure_pack_package(fqn: str, search_paths: list[str]) -> ModuleType:
@@ -1,5 +1,5 @@
1
1
  name = "general"
2
- version = "0.7.0"
2
+ version = "0.9.0"
3
3
  description = "Language-agnostic guards: git/command safety, doc & prompt nudges, task & plan discipline, and code-review + docs-freshness gates."
4
4
  hooks = "."
5
5
  nlp = true