claude-code-kit 0.7.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 (209) hide show
  1. claude_code_kit-0.7.0.dist-info/METADATA +384 -0
  2. claude_code_kit-0.7.0.dist-info/RECORD +209 -0
  3. claude_code_kit-0.7.0.dist-info/WHEEL +4 -0
  4. claude_code_kit-0.7.0.dist-info/entry_points.txt +4 -0
  5. claude_code_kit-0.7.0.dist-info/licenses/LICENSE +21 -0
  6. claude_kit/__init__.py +10 -0
  7. claude_kit/__main__.py +8 -0
  8. claude_kit/_payload/agents/acceptance-reviewer.md +60 -0
  9. claude_kit/_payload/agents/auditor.md +76 -0
  10. claude_kit/_payload/agents/dependency-scanner.md +84 -0
  11. claude_kit/_payload/agents/developer.md +187 -0
  12. claude_kit/_payload/agents/devils-advocate.md +62 -0
  13. claude_kit/_payload/agents/devops-engineer.md +134 -0
  14. claude_kit/_payload/agents/e2e-tester.md +152 -0
  15. claude_kit/_payload/agents/em-reviewer.md +105 -0
  16. claude_kit/_payload/agents/incident-responder.md +64 -0
  17. claude_kit/_payload/agents/merge-reviewer.md +194 -0
  18. claude_kit/_payload/agents/observability-engineer.md +94 -0
  19. claude_kit/_payload/agents/orchestrator.md +551 -0
  20. claude_kit/_payload/agents/owasp-reviewer.md +76 -0
  21. claude_kit/_payload/agents/policy-validator.md +63 -0
  22. claude_kit/_payload/agents/pr-raiser.md +138 -0
  23. claude_kit/_payload/agents/risk-classifier.md +50 -0
  24. claude_kit/_payload/agents/sdlc-code-reviewer.md +196 -0
  25. claude_kit/_payload/agents/secret-scanner.md +70 -0
  26. claude_kit/_payload/agents/security-reviewer.md +80 -0
  27. claude_kit/_payload/agents/senior-backend-dev.md +199 -0
  28. claude_kit/_payload/agents/senior-frontend-dev.md +181 -0
  29. claude_kit/_payload/agents/senior-tester.md +206 -0
  30. claude_kit/_payload/agents/spec-doc-writer.md +331 -0
  31. claude_kit/_payload/agents/story-planner.md +56 -0
  32. claude_kit/_payload/agents/technical-architect.md +139 -0
  33. claude_kit/_payload/agents/tester.md +193 -0
  34. claude_kit/_payload/agents/ui-designer.md +73 -0
  35. claude_kit/_payload/agents/unit-tester.md +119 -0
  36. claude_kit/_payload/catalog/mcp.yaml +54 -0
  37. claude_kit/_payload/catalog/org.yaml +145 -0
  38. claude_kit/_payload/catalog/profiles.yaml +96 -0
  39. claude_kit/_payload/catalog/stacks.yaml +96 -0
  40. claude_kit/_payload/commands/init.md +36 -0
  41. claude_kit/_payload/commands/sdlc.md +18 -0
  42. claude_kit/_payload/commands/status.md +20 -0
  43. claude_kit/_payload/hooks/hooks.json +58 -0
  44. claude_kit/_payload/hooks/scripts/audit-log.sh +18 -0
  45. claude_kit/_payload/hooks/scripts/guard-secrets.sh +26 -0
  46. claude_kit/_payload/hooks/scripts/lint-fix.sh +38 -0
  47. claude_kit/_payload/hooks/scripts/load-continuity.sh +32 -0
  48. claude_kit/_payload/hooks/scripts/load-learnings.sh +40 -0
  49. claude_kit/_payload/hooks/scripts/type-check.sh +23 -0
  50. claude_kit/_payload/hooks/scripts/validate-frontmatter.sh +34 -0
  51. claude_kit/_payload/hooks/scripts/validate-settings.sh +21 -0
  52. claude_kit/_payload/hooks/scripts/warn-large-edits.sh +24 -0
  53. claude_kit/_payload/hooks/scripts/warn-missing-tests.sh +24 -0
  54. claude_kit/_payload/hooks/scripts/warn-sensitive-files.sh +30 -0
  55. claude_kit/_payload/hooks/scripts/warn-shared-modules.sh +33 -0
  56. claude_kit/_payload/rules/agent-guardrails.md +83 -0
  57. claude_kit/_payload/rules/agent-memory.md +106 -0
  58. claude_kit/_payload/rules/agent-resilience.md +61 -0
  59. claude_kit/_payload/rules/autonomy-levels.md +30 -0
  60. claude_kit/_payload/rules/code-organization.md +312 -0
  61. claude_kit/_payload/rules/continuity.md +84 -0
  62. claude_kit/_payload/rules/design-patterns.md +422 -0
  63. claude_kit/_payload/rules/devops-observability.md +57 -0
  64. claude_kit/_payload/rules/documentation.md +326 -0
  65. claude_kit/_payload/rules/evals.md +62 -0
  66. claude_kit/_payload/rules/frontend-best-practices.md +157 -0
  67. claude_kit/_payload/rules/goal-setting-and-monitoring.md +72 -0
  68. claude_kit/_payload/rules/human-in-the-loop.md +64 -0
  69. claude_kit/_payload/rules/linting-and-formatting.md +220 -0
  70. claude_kit/_payload/rules/mandatory-workflow.md +309 -0
  71. claude_kit/_payload/rules/model-tiers.md +34 -0
  72. claude_kit/_payload/rules/quality-gates.md +107 -0
  73. claude_kit/_payload/rules/rarv-cycle.md +31 -0
  74. claude_kit/_payload/rules/reasoning-techniques.md +62 -0
  75. claude_kit/_payload/rules/responsive-and-accessibility.md +353 -0
  76. claude_kit/_payload/rules/risk-classification.md +36 -0
  77. claude_kit/_payload/rules/testing.md +417 -0
  78. claude_kit/_payload/rules/tool-design.md +66 -0
  79. claude_kit/_payload/skills/_references/accessibility-checklist.md +160 -0
  80. claude_kit/_payload/skills/_references/orchestration-patterns.md +405 -0
  81. claude_kit/_payload/skills/_references/performance-checklist.md +153 -0
  82. claude_kit/_payload/skills/_references/security-checklist.md +134 -0
  83. claude_kit/_payload/skills/_references/testing-patterns.md +236 -0
  84. claude_kit/_payload/skills/accessibility-review/SKILL.md +56 -0
  85. claude_kit/_payload/skills/api-and-interface-design/SKILL.md +294 -0
  86. claude_kit/_payload/skills/api-integration/SKILL.md +348 -0
  87. claude_kit/_payload/skills/archive-sprint/SKILL.md +31 -0
  88. claude_kit/_payload/skills/backlog/SKILL.md +41 -0
  89. claude_kit/_payload/skills/backlog/item-template.md +20 -0
  90. claude_kit/_payload/skills/browser-testing-with-devtools/SKILL.md +302 -0
  91. claude_kit/_payload/skills/ci-cd-and-automation/SKILL.md +402 -0
  92. claude_kit/_payload/skills/code-review-and-quality/SKILL.md +347 -0
  93. claude_kit/_payload/skills/code-simplification/SKILL.md +331 -0
  94. claude_kit/_payload/skills/component-design/SKILL.md +171 -0
  95. claude_kit/_payload/skills/consolidate-learnings/SKILL.md +55 -0
  96. claude_kit/_payload/skills/context-engineering/SKILL.md +321 -0
  97. claude_kit/_payload/skills/debugging-and-error-recovery/SKILL.md +300 -0
  98. claude_kit/_payload/skills/decision/SKILL.md +46 -0
  99. claude_kit/_payload/skills/decision/adr-template.md +36 -0
  100. claude_kit/_payload/skills/deprecation-and-migration/SKILL.md +207 -0
  101. claude_kit/_payload/skills/documentation-and-adrs/SKILL.md +299 -0
  102. claude_kit/_payload/skills/doubt-driven-development/SKILL.md +243 -0
  103. claude_kit/_payload/skills/execute/SKILL.md +27 -0
  104. claude_kit/_payload/skills/frontend-ui-engineering/SKILL.md +328 -0
  105. claude_kit/_payload/skills/git-workflow-and-versioning/SKILL.md +300 -0
  106. claude_kit/_payload/skills/idea-refine/SKILL.md +178 -0
  107. claude_kit/_payload/skills/idea-refine/examples.md +238 -0
  108. claude_kit/_payload/skills/idea-refine/frameworks.md +99 -0
  109. claude_kit/_payload/skills/idea-refine/refinement-criteria.md +113 -0
  110. claude_kit/_payload/skills/idea-refine/scripts/idea-refine.sh +15 -0
  111. claude_kit/_payload/skills/incident-postmortem/SKILL.md +74 -0
  112. claude_kit/_payload/skills/incremental-implementation/SKILL.md +245 -0
  113. claude_kit/_payload/skills/interview-me/SKILL.md +221 -0
  114. claude_kit/_payload/skills/load-testing/SKILL.md +83 -0
  115. claude_kit/_payload/skills/manual-test/SKILL.md +516 -0
  116. claude_kit/_payload/skills/performance-optimization/SKILL.md +277 -0
  117. claude_kit/_payload/skills/planning-and-task-breakdown/SKILL.md +223 -0
  118. claude_kit/_payload/skills/playwright-verification/SKILL.md +205 -0
  119. claude_kit/_payload/skills/refresh-docs/SKILL.md +63 -0
  120. claude_kit/_payload/skills/remember/SKILL.md +96 -0
  121. claude_kit/_payload/skills/scope/SKILL.md +52 -0
  122. claude_kit/_payload/skills/scope/scope-template.md +82 -0
  123. claude_kit/_payload/skills/sdlc/SKILL.md +83 -0
  124. claude_kit/_payload/skills/security-and-hardening/SKILL.md +368 -0
  125. claude_kit/_payload/skills/security-verification/SKILL.md +209 -0
  126. claude_kit/_payload/skills/shipping-and-launch/SKILL.md +309 -0
  127. claude_kit/_payload/skills/smoke-test/SKILL.md +78 -0
  128. claude_kit/_payload/skills/source-driven-development/SKILL.md +195 -0
  129. claude_kit/_payload/skills/spec-driven-development/SKILL.md +200 -0
  130. claude_kit/_payload/skills/sprint/SKILL.md +67 -0
  131. claude_kit/_payload/skills/sprint/sprint-template.md +90 -0
  132. claude_kit/_payload/skills/test-driven-development/SKILL.md +383 -0
  133. claude_kit/_payload/skills/threat-model/SKILL.md +60 -0
  134. claude_kit/_payload/skills/triage/SKILL.md +87 -0
  135. claude_kit/_payload/skills/ui-ux-design/SKILL.md +71 -0
  136. claude_kit/_payload/skills/unit-test/SKILL.md +237 -0
  137. claude_kit/_payload/skills/using-agent-skills/SKILL.md +180 -0
  138. claude_kit/_payload/templates/CLAUDE.md +238 -0
  139. claude_kit/_payload/templates/CLAUDE.stack.md.tmpl +53 -0
  140. claude_kit/_payload/templates/CONTINUITY.template.md +35 -0
  141. claude_kit/_payload/templates/README.claude-sdlc.md.tmpl +219 -0
  142. claude_kit/_payload/templates/agent-memory/MEMORY.md +30 -0
  143. claude_kit/_payload/templates/agent-memory/api/.gitkeep +0 -0
  144. claude_kit/_payload/templates/agent-memory/architecture/.gitkeep +0 -0
  145. claude_kit/_payload/templates/agent-memory/debugging/.gitkeep +0 -0
  146. claude_kit/_payload/templates/agent-memory/gotchas/.gitkeep +0 -0
  147. claude_kit/_payload/templates/agent-memory/patterns/.gitkeep +0 -0
  148. claude_kit/_payload/templates/agent-memory/performance/.gitkeep +0 -0
  149. claude_kit/_payload/templates/artifacts/adr.md +18 -0
  150. claude_kit/_payload/templates/artifacts/feature-spec.md +29 -0
  151. claude_kit/_payload/templates/artifacts/release-plan.md +23 -0
  152. claude_kit/_payload/templates/artifacts/runbook.md +24 -0
  153. claude_kit/_payload/templates/artifacts/security-review.md +23 -0
  154. claude_kit/_payload/templates/artifacts/test-plan.md +22 -0
  155. claude_kit/_payload/templates/org/README.md +53 -0
  156. claude_kit/_payload/templates/org/agents/data-workflow-agent.md +59 -0
  157. claude_kit/_payload/templates/org/agents/founder-prototype-agent.md +61 -0
  158. claude_kit/_payload/templates/org/agents/internal-tools-builder.md +63 -0
  159. claude_kit/_payload/templates/org/agents/pm-copilot.md +60 -0
  160. claude_kit/_payload/templates/org/agents/support-ticket-engineer.md +63 -0
  161. claude_kit/_payload/templates/org/packs/devops-and-release/README.md +46 -0
  162. claude_kit/_payload/templates/org/packs/devops-and-release/pack.yaml +32 -0
  163. claude_kit/_payload/templates/org/packs/engineering-core/README.md +46 -0
  164. claude_kit/_payload/templates/org/packs/engineering-core/pack.yaml +44 -0
  165. claude_kit/_payload/templates/org/packs/non-engineer-builder/README.md +53 -0
  166. claude_kit/_payload/templates/org/packs/non-engineer-builder/pack.yaml +39 -0
  167. claude_kit/_payload/templates/org/packs/onboarding-and-docs/README.md +49 -0
  168. claude_kit/_payload/templates/org/packs/onboarding-and-docs/pack.yaml +26 -0
  169. claude_kit/_payload/templates/org/packs/product-to-code/README.md +50 -0
  170. claude_kit/_payload/templates/org/packs/product-to-code/pack.yaml +34 -0
  171. claude_kit/_payload/templates/org/packs/quality-and-review/README.md +53 -0
  172. claude_kit/_payload/templates/org/packs/quality-and-review/pack.yaml +40 -0
  173. claude_kit/_payload/templates/org/packs/security-and-compliance/README.md +50 -0
  174. claude_kit/_payload/templates/org/packs/security-and-compliance/pack.yaml +36 -0
  175. claude_kit/_payload/templates/org/rules/ai-working-agreement.md +45 -0
  176. claude_kit/_payload/templates/org/rules/ambiguity-resolution.md +36 -0
  177. claude_kit/_payload/templates/org/rules/branch-and-pr-policy.md +41 -0
  178. claude_kit/_payload/templates/org/rules/compliance-policy.md +50 -0
  179. claude_kit/_payload/templates/org/rules/non-engineer-safe-coding.md +37 -0
  180. claude_kit/_payload/templates/org/rules/pii-policy.md +46 -0
  181. claude_kit/_payload/templates/org/rules/production-data-policy.md +35 -0
  182. claude_kit/_payload/templates/org/rules/prompt-to-task-conversion.md +30 -0
  183. claude_kit/_payload/templates/org/rules/prototype-boundaries.md +40 -0
  184. claude_kit/_payload/templates/org/rules/secrets-policy.md +34 -0
  185. claude_kit/_payload/templates/org/skills/customer-issue-to-fix/SKILL.md +61 -0
  186. claude_kit/_payload/templates/org/skills/feature-from-idea/SKILL.md +56 -0
  187. claude_kit/_payload/templates/org/skills/prompt-to-safe-task/SKILL.md +59 -0
  188. claude_kit/_payload/templates/org/skills/prototype-to-production/SKILL.md +61 -0
  189. claude_kit/_payload/templates/org/skills/repo-onboarding/SKILL.md +60 -0
  190. claude_kit/_payload/templates/settings.json +53 -0
  191. claude_kit/_payload/templates/stacks/backend/python/fastapi/rules/fastapi-patterns.md +64 -0
  192. claude_kit/_payload/templates/stacks/db/mongodb/agents/migration-specialist.md +61 -0
  193. claude_kit/_payload/templates/stacks/db/mongodb/agents/mongodb-specialist.md +59 -0
  194. claude_kit/_payload/templates/stacks/db/mongodb/rules/mongodb-patterns.md +39 -0
  195. claude_kit/_payload/templates/stacks/db/postgres/agents/db-performance-reviewer.md +66 -0
  196. claude_kit/_payload/templates/stacks/db/postgres/agents/migration-specialist.md +56 -0
  197. claude_kit/_payload/templates/stacks/db/postgres/agents/postgres-specialist.md +58 -0
  198. claude_kit/_payload/templates/stacks/db/postgres/rules/database-performance.md +64 -0
  199. claude_kit/_payload/templates/stacks/db/postgres/rules/postgres-patterns.md +43 -0
  200. claude_kit/_payload/templates/stacks/frontend/react/rules/react-patterns.md +63 -0
  201. claude_kit/catalog.py +476 -0
  202. claude_kit/cli.py +327 -0
  203. claude_kit/hooks.py +246 -0
  204. claude_kit/models.py +205 -0
  205. claude_kit/prompts.py +209 -0
  206. claude_kit/render.py +146 -0
  207. claude_kit/scaffold.py +492 -0
  208. claude_kit/upgrader.py +294 -0
  209. claude_kit/validator.py +197 -0
claude_kit/cli.py ADDED
@@ -0,0 +1,327 @@
1
+ """Command-line interface for claude-kit (``claude-kit`` · aliases ``ckit`` / ``claude-sdlc``).
2
+
3
+ A Cookiecutter-style scaffolder for a Claude Code **configuration** (no application code, no Docker):
4
+ ``init`` asks ordered questions and lays down ``CLAUDE.md`` + ``.claude/`` (rules, the profile's
5
+ agents/skills, hooks, artifact templates, config) + an optional ``.mcp.json`` and a README. Lifecycle
6
+ commands — ``validate``, ``doctor``, ``diff``, ``upgrade``, ``list-options``, ``status`` — manage it.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from contextlib import ExitStack
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ import typer
16
+
17
+ from claude_kit import __version__, catalog, prompts, scaffold, upgrader, validator
18
+
19
+ BANNER = r"""
20
+ ___ _ _ _ ___ ___ _ _____ _____
21
+ / __| | /_\ | | | \| __| | |/ /_ _|_ _|
22
+ | (__| |__ / _ \| |_| | |) | _| | ' < | | | |
23
+ \___|____/_/ \_\\___/|___/|___| |_|\_\___| |_| autonomous SDLC config for Claude Code
24
+ """
25
+
26
+ app = typer.Typer(
27
+ add_completion=False,
28
+ no_args_is_help=False,
29
+ help="Scaffold and manage a Claude Code autonomous-SDLC configuration.",
30
+ )
31
+ research_app = typer.Typer(
32
+ no_args_is_help=True, help="Research helpers (license-respecting)."
33
+ )
34
+ app.add_typer(research_app, name="research")
35
+
36
+
37
+ def _version_callback(value: bool) -> None:
38
+ if value:
39
+ typer.echo(f"claude-kit {__version__}")
40
+ raise typer.Exit()
41
+
42
+
43
+ @app.callback(invoke_without_command=True)
44
+ def _root(
45
+ ctx: typer.Context,
46
+ version: bool = typer.Option(
47
+ False,
48
+ "-V",
49
+ "--version",
50
+ callback=_version_callback,
51
+ is_eager=True,
52
+ help="print the version",
53
+ ),
54
+ ) -> None:
55
+ """Show the banner + help when invoked with no subcommand."""
56
+ if ctx.invoked_subcommand is None:
57
+ typer.echo(BANNER)
58
+ typer.echo(ctx.get_help())
59
+
60
+
61
+ def _print_report(ok: bool, messages: list[str]) -> None:
62
+ """Print a check report and exit non-zero on failure."""
63
+ for line in messages:
64
+ typer.echo(line)
65
+ if not ok:
66
+ raise typer.Exit(1)
67
+
68
+
69
+ @app.command()
70
+ def init(
71
+ path: Optional[str] = typer.Argument(
72
+ None, help="target project dir (prompted if omitted; default: current dir)"
73
+ ),
74
+ defaults: bool = typer.Option(
75
+ False, "--defaults", help="non-interactive; use catalog defaults"
76
+ ),
77
+ config: Optional[str] = typer.Option(
78
+ None, "--config", help="non-interactive; read the selection from a YAML file"
79
+ ),
80
+ force: bool = typer.Option(
81
+ False,
82
+ "--force",
83
+ help="overwrite existing CLAUDE.md / settings.json / .mcp.json",
84
+ ),
85
+ ) -> None:
86
+ """Scaffold a Claude Code SDLC configuration into a project."""
87
+ non_interactive = defaults or config is not None
88
+ with ExitStack() as stack:
89
+ src = scaffold.payload_dir(stack)
90
+
91
+ # 1) Target path.
92
+ if path is None:
93
+ raw = "." if non_interactive else input("Target path [.]: ").strip() or "."
94
+ else:
95
+ raw = path
96
+ target = Path(raw).expanduser().resolve()
97
+ if not target.exists():
98
+ if not non_interactive and not typer.confirm(
99
+ f"Create {target}?", default=True
100
+ ):
101
+ typer.echo("aborted.")
102
+ raise typer.Exit(0)
103
+ target.mkdir(parents=True, exist_ok=True)
104
+
105
+ # 2) Existing .claude handling: merge / overwrite / backup / abort.
106
+ overwrite = force
107
+ if (target / ".claude").exists():
108
+ if force:
109
+ mode = "overwrite"
110
+ elif non_interactive:
111
+ mode = "merge"
112
+ else:
113
+ mode = (
114
+ typer.prompt(
115
+ ".claude already exists — [merge/overwrite/backup/abort]",
116
+ default="merge",
117
+ )
118
+ .strip()
119
+ .lower()
120
+ )
121
+ if mode == "abort":
122
+ typer.echo("aborted — nothing changed.")
123
+ raise typer.Exit(0)
124
+ if mode == "overwrite":
125
+ overwrite = True
126
+ if mode == "backup":
127
+ n = 1
128
+ while (target / f".claude.bak-{n}").exists():
129
+ n += 1
130
+ (target / ".claude").rename(target / f".claude.bak-{n}")
131
+ typer.echo(f" • backed up existing .claude/ -> .claude.bak-{n}")
132
+
133
+ # 3) Resolve the selection.
134
+ try:
135
+ if config is not None:
136
+ selection = prompts.from_config(config, src)
137
+ elif defaults:
138
+ selection = catalog.defaults(src)
139
+ else:
140
+ selection = prompts.interactive(src)
141
+ plan = catalog.resolve(src, selection)
142
+ except (ValueError, FileNotFoundError) as exc:
143
+ typer.echo(f"error: {exc}", err=True)
144
+ raise typer.Exit(2) from exc
145
+
146
+ # 4) Install.
147
+ typer.echo(f"\nclaude-kit: installing into {target}")
148
+ for line in scaffold.install_sdlc(src, target, plan, force=overwrite):
149
+ typer.echo(line)
150
+
151
+ typer.echo(
152
+ "\nDone. Open the project in Claude Code and run `/sdlc <your task>` to start the pipeline."
153
+ )
154
+
155
+
156
+ @app.command()
157
+ def validate(
158
+ path: str = typer.Argument(".", help="target project dir (default: .)"),
159
+ ) -> None:
160
+ """Structurally validate a scaffolded .claude/ configuration."""
161
+ _print_report(*validator.validate(path))
162
+
163
+
164
+ @app.command()
165
+ def doctor(
166
+ path: str = typer.Argument(".", help="target project dir (default: .)"),
167
+ ) -> None:
168
+ """Run validation plus environment/health checks with fix hints."""
169
+ _print_report(*validator.doctor(path))
170
+
171
+
172
+ @app.command()
173
+ def diff(
174
+ path: str = typer.Argument(".", help="target project dir (default: .)"),
175
+ ) -> None:
176
+ """Preview what an upgrade would change (no writes)."""
177
+ _print_report(*upgrader.diff(path))
178
+
179
+
180
+ @app.command()
181
+ def upgrade(
182
+ path: str = typer.Argument(".", help="target project dir (default: .)"),
183
+ force: bool = typer.Option(
184
+ False, "--force", help="overwrite user-modified kit files"
185
+ ),
186
+ ) -> None:
187
+ """Refresh kit-owned files, backing up user-modified ones."""
188
+ _print_report(*upgrader.upgrade(path, force=force))
189
+
190
+
191
+ @app.command("list-options")
192
+ def list_options() -> None:
193
+ """List the available frontend/backend/database/profile/MCP options from the catalog."""
194
+ with ExitStack() as stack:
195
+ src = scaffold.payload_dir(stack)
196
+ opts = catalog.list_options(src)
197
+
198
+ def _badge(entry: dict) -> str:
199
+ return "" if entry.get("status", "live") == "live" else " (coming soon)"
200
+
201
+ typer.echo("\nFrontend frameworks:")
202
+ for fe in opts["frontend"]:
203
+ langs = ", ".join(fe.get("languages", [])) or "—"
204
+ typer.echo(f" • {fe['id']}: {fe['label']}{_badge(fe)} [languages: {langs}]")
205
+ typer.echo("\nBackend languages & frameworks:")
206
+ for be in opts["backend"]:
207
+ typer.echo(f" • {be['id']}: {be['label']}{_badge(be)}")
208
+ for fw in be["frameworks"]:
209
+ typer.echo(f" - {fw['id']}: {fw['label']}{_badge(fw)}")
210
+ typer.echo("\nDatabases:")
211
+ for db in opts["database"]:
212
+ typer.echo(f" • {db['id']}: {db['label']}")
213
+ typer.echo("\nSDLC profiles:")
214
+ for pr in opts["profiles"]:
215
+ typer.echo(f" • {pr['id']}: {pr['label']}")
216
+ typer.echo("\nMCP integrations (optional):")
217
+ for mc in opts["mcp"]:
218
+ typer.echo(f" • {mc['id']}: {mc['label']}")
219
+
220
+
221
+ @app.command()
222
+ def status(
223
+ path: str = typer.Argument(".", help="target project dir (default: .)"),
224
+ ) -> None:
225
+ """Show what's installed and the current working memory."""
226
+ target = Path(path).expanduser().resolve()
227
+ dest = target / ".claude"
228
+ typer.echo(f"claude-kit status for {target}")
229
+ if not dest.is_dir():
230
+ typer.echo(" not installed — run `claude-kit init` here.")
231
+ return
232
+ for name in ("rules", "agents", "skills", "hooks"):
233
+ d = dest / name
234
+ if d.is_dir():
235
+ n = sum(1 for p in d.iterdir() if p.name != ".gitkeep")
236
+ typer.echo(f" • {name}/: {n}")
237
+ else:
238
+ typer.echo(f" • {name}/: (missing)")
239
+ options = dest / "config" / "init-options.json"
240
+ if options.is_file():
241
+ import json
242
+
243
+ data = json.loads(options.read_text(encoding="utf-8"))
244
+ sel = data.get("selection", {})
245
+ typer.echo(
246
+ f" • selection: {sel.get('frontend_framework')} + "
247
+ f"{sel.get('backend_language')}/{sel.get('backend_framework')} + "
248
+ f"{sel.get('database')} · profile={sel.get('profile')} · mcp={sel.get('mcp') or 'none'}"
249
+ )
250
+ continuity = dest / "CONTINUITY.md"
251
+ if continuity.is_file():
252
+ typer.echo("\n working memory (.claude/CONTINUITY.md):")
253
+ for line in continuity.read_text(
254
+ encoding="utf-8", errors="replace"
255
+ ).splitlines()[:30]:
256
+ typer.echo(f" {line}")
257
+ else:
258
+ typer.echo("\n no CONTINUITY.md yet (no pipeline run recorded).")
259
+
260
+
261
+ @app.command()
262
+ def version() -> None:
263
+ """Print the version."""
264
+ typer.echo(f"claude-kit {__version__}")
265
+
266
+
267
+ @app.command("package-org-pack")
268
+ def package_org_pack(
269
+ pack: str = typer.Argument(
270
+ ..., help="org-pack id under .claude/org-packs/ (e.g. engineering-core)"
271
+ ),
272
+ out: Optional[str] = typer.Option(
273
+ None, "--out", help="output directory for the packaged plugin"
274
+ ),
275
+ ) -> None:
276
+ """(Planned) Package an org-pack into a reusable, versioned plugin-style directory."""
277
+ typer.echo(
278
+ "package-org-pack is planned but not yet implemented.\n"
279
+ "When available it will bundle the selected org-pack (manifest + the skills/agents/hooks it "
280
+ "references + settings + README + CHANGELOG + version + license + compatibility metadata) into "
281
+ "a distributable plugin directory for an internal registry.\n"
282
+ f"(given: pack={pack}, out={out or 'dist/org-packs/'})"
283
+ )
284
+
285
+
286
+ @app.command("install-org-pack")
287
+ def install_org_pack(
288
+ source: str = typer.Argument(
289
+ ..., help="path or registry id of an approved org-pack"
290
+ ),
291
+ user: bool = typer.Option(
292
+ False, "--user", help="install into user-level ~/.claude instead of this repo"
293
+ ),
294
+ ) -> None:
295
+ """(Planned) Install an approved org-pack into a repo or user-level Claude config."""
296
+ typer.echo(
297
+ "install-org-pack is planned but not yet implemented.\n"
298
+ "When available it will verify a pack's compatibility metadata and merge its components into "
299
+ "the target .claude/ (repo) or ~/.claude (user) config, recording the pack id + version for "
300
+ "safe upgrades.\n"
301
+ f"(given: source={source}, target={'user (~/.claude)' if user else 'repo (.claude)'})"
302
+ )
303
+
304
+
305
+ @research_app.command("import-sources")
306
+ def research_import_sources(
307
+ sources: str = typer.Argument(
308
+ ..., help="YAML file of explicit, license-cleared sources"
309
+ ),
310
+ ) -> None:
311
+ """(Planned) Summarise explicit, license-cleared sources into original skill/agent proposals."""
312
+ typer.echo(
313
+ "research import-sources is planned but not yet implemented.\n"
314
+ "When available it will: read explicit source URLs/files from the given YAML, record each "
315
+ "source's name/URL/license/author/date, summarise ideas into ORIGINAL skill/agent proposals "
316
+ "(never copying proprietary text), and require human approval before adding anything.\n"
317
+ f"(given: {sources})"
318
+ )
319
+
320
+
321
+ def main() -> None:
322
+ """Console-script entry point."""
323
+ app()
324
+
325
+
326
+ if __name__ == "__main__":
327
+ main()
claude_kit/hooks.py ADDED
@@ -0,0 +1,246 @@
1
+ """Hook registry — the single definition of every hook claude-kit can install.
2
+
3
+ A *profile* selects hook ids (see ``catalog/profiles.yaml``); the installer turns the selected ids
4
+ into (a) the set of ``.sh`` scripts to copy into ``.claude/hooks/`` and (b) an assembled
5
+ ``.claude/settings.json`` ``hooks`` block. Keeping the registry in one module lets both
6
+ :mod:`claude_kit.catalog` (to resolve the ``all`` token) and :mod:`claude_kit.scaffold`
7
+ (to build settings) share it without duplication.
8
+
9
+ Hooks are deliberately **conservative**: guardrails block obviously dangerous actions; the quality
10
+ hooks only *suggest* running tools. Script-backed hooks reference ``$CLAUDE_PROJECT_DIR`` so they
11
+ work in a scaffolded project (the plugin variant uses ``${CLAUDE_PLUGIN_ROOT}``).
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Any
17
+
18
+ # --- inline guard commands (no script file needed) -------------------------------------------------
19
+
20
+ _RM_RF_GUARD = (
21
+ "CMD=$(jq -r '.tool_input.command'); "
22
+ "if echo \"$CMD\" | grep -qE 'rm[[:space:]]+-[^[:space:]]*r[^[:space:]]*f'; then "
23
+ "echo 'BLOCKED: rm -rf is disabled by claude-kit. Move to trash or delete specific paths "
24
+ "explicitly.' >&2; exit 2; fi"
25
+ )
26
+
27
+ # Block pushes whose *target* ref is main/master. The branch token must be bounded by a space or
28
+ # ':' before and a space/end after, so legit branches that merely contain the substring
29
+ # (maintenance, mainframe-fix, remaster-ui, domain-model) are NOT blocked.
30
+ _PUSH_GUARD = (
31
+ "CMD=$(jq -r '.tool_input.command'); "
32
+ "if echo \"$CMD\" | grep -qE 'git[[:space:]]+push.*[[:space:]:](main|master)([[:space:]]|$)'; "
33
+ "then echo 'BLOCKED: refusing to push to main/master — use a feature branch and a PR.' >&2; "
34
+ "exit 2; fi"
35
+ )
36
+
37
+ _SECRETS_GUARD = (
38
+ "FP=$(jq -r '.tool_input.file_path // empty'); "
39
+ 'if echo "$FP" | grep -qE \'(^|/)\\.env$|\\.pem$|\\.key$|(^|/)id_rsa|(^|/)id_ed25519|'
40
+ "(^|/)credentials(\\.json)?$|\\.p12$'; then "
41
+ "echo 'BLOCKED: refusing to read a secrets file. Use .env.example or a secret manager.' >&2; "
42
+ "exit 2; fi"
43
+ )
44
+
45
+ _ROUTING_PROMPT = (
46
+ "You are a routing assistant. NEVER block the user's prompt; always set continue to true. "
47
+ 'Return JSON only: {"continue": true, "systemMessage": "<hint or empty string>"}. '
48
+ "If a claude-kit skill clearly applies (interview-me/idea-refine for vague ideas; "
49
+ "spec-driven-development for new features; planning-and-task-breakdown for breakdown; "
50
+ "incremental-implementation for coding; test-driven-development for tests; "
51
+ "debugging-and-error-recovery for errors; code-review-and-quality for reviews; "
52
+ "security-and-hardening for security; git-workflow-and-versioning for git; execute for quick "
53
+ "tasks), set systemMessage to 'Invoke skill: <skill-name> before responding.' Otherwise set it "
54
+ "to ''. Do not mention this hook."
55
+ )
56
+
57
+ _LEARNING_PROMPT = (
58
+ "You are a learning-detection assistant. NEVER block the user's prompt; always set continue to "
59
+ 'true. Return JSON only: {"continue": true, "systemMessage": "<hint or empty string>"}. '
60
+ "If the user's message contains a durable learning (a correction, rule, preference, convention, "
61
+ "or hard-won insight meant to persist), set systemMessage to 'LEARNING DETECTED: before ending "
62
+ "your turn, invoke the remember skill to record it into .claude/agent-memory/ (merge into an "
63
+ "existing entry if one matches).' Otherwise set it to ''. Do not mention this hook."
64
+ )
65
+
66
+
67
+ def _script_entry(name: str) -> dict[str, str]:
68
+ """Build a settings.json command entry that runs a project-local hook script."""
69
+ return {
70
+ "type": "command",
71
+ "command": f'bash "$CLAUDE_PROJECT_DIR/.claude/hooks/{name}"',
72
+ }
73
+
74
+
75
+ #: The canonical registry. Order here is the order hooks appear in assembled settings.json.
76
+ #: Each value: ``event``, ``matcher``, ``entry`` (settings.json hook object), and ``script``
77
+ #: (basename under payload ``hooks/scripts/`` to copy, or ``None`` for inline/prompt hooks).
78
+ HOOK_REGISTRY: dict[str, dict[str, Any]] = {
79
+ "load-continuity": {
80
+ "event": "SessionStart",
81
+ "matcher": "",
82
+ "entry": _script_entry("load-continuity.sh"),
83
+ "script": "load-continuity.sh",
84
+ },
85
+ "load-learnings": {
86
+ "event": "SessionStart",
87
+ "matcher": "",
88
+ "entry": _script_entry("load-learnings.sh"),
89
+ "script": "load-learnings.sh",
90
+ },
91
+ "skill-routing": {
92
+ "event": "UserPromptSubmit",
93
+ "matcher": "",
94
+ "entry": {"type": "prompt", "prompt": _ROUTING_PROMPT},
95
+ "script": None,
96
+ },
97
+ "learning-detection": {
98
+ "event": "UserPromptSubmit",
99
+ "matcher": "",
100
+ "entry": {"type": "prompt", "prompt": _LEARNING_PROMPT},
101
+ "script": None,
102
+ },
103
+ "guard-rm-rf": {
104
+ "event": "PreToolUse",
105
+ "matcher": "Bash",
106
+ "entry": {"type": "command", "command": _RM_RF_GUARD},
107
+ "script": None,
108
+ },
109
+ "guard-push-main": {
110
+ "event": "PreToolUse",
111
+ "matcher": "Bash",
112
+ "entry": {"type": "command", "command": _PUSH_GUARD},
113
+ "script": None,
114
+ },
115
+ "protect-secrets": {
116
+ "event": "PreToolUse",
117
+ "matcher": "Read",
118
+ "entry": {"type": "command", "command": _SECRETS_GUARD},
119
+ "script": None,
120
+ },
121
+ "guard-commit-secrets": {
122
+ "event": "PreToolUse",
123
+ "matcher": "Bash",
124
+ "entry": _script_entry("guard-secrets.sh"),
125
+ "script": "guard-secrets.sh",
126
+ },
127
+ "warn-shared-modules": {
128
+ "event": "PreToolUse",
129
+ "matcher": "Edit|Write",
130
+ "entry": _script_entry("warn-shared-modules.sh"),
131
+ "script": "warn-shared-modules.sh",
132
+ },
133
+ "warn-sensitive-files": {
134
+ "event": "PreToolUse",
135
+ "matcher": "Edit|Write",
136
+ "entry": _script_entry("warn-sensitive-files.sh"),
137
+ "script": "warn-sensitive-files.sh",
138
+ },
139
+ "warn-large-edits": {
140
+ "event": "PreToolUse",
141
+ "matcher": "Edit|Write",
142
+ "entry": _script_entry("warn-large-edits.sh"),
143
+ "script": "warn-large-edits.sh",
144
+ },
145
+ "validate-frontmatter": {
146
+ "event": "PreToolUse",
147
+ "matcher": "Write",
148
+ "entry": _script_entry("validate-frontmatter.sh"),
149
+ "script": "validate-frontmatter.sh",
150
+ },
151
+ "validate-settings": {
152
+ "event": "PreToolUse",
153
+ "matcher": "Write",
154
+ "entry": _script_entry("validate-settings.sh"),
155
+ "script": "validate-settings.sh",
156
+ },
157
+ "warn-missing-tests": {
158
+ "event": "PostToolUse",
159
+ "matcher": "Edit|Write",
160
+ "entry": _script_entry("warn-missing-tests.sh"),
161
+ "script": "warn-missing-tests.sh",
162
+ },
163
+ "audit-log": {
164
+ "event": "PostToolUse",
165
+ "matcher": "",
166
+ "entry": _script_entry("audit-log.sh"),
167
+ "script": "audit-log.sh",
168
+ },
169
+ "lint-fix": {
170
+ "event": "Stop",
171
+ "matcher": "",
172
+ "entry": _script_entry("lint-fix.sh"),
173
+ "script": "lint-fix.sh",
174
+ },
175
+ "type-check": {
176
+ "event": "Stop",
177
+ "matcher": "",
178
+ "entry": _script_entry("type-check.sh"),
179
+ "script": "type-check.sh",
180
+ },
181
+ }
182
+
183
+ #: Event ordering for a stable, readable settings.json.
184
+ _EVENT_ORDER = (
185
+ "SessionStart",
186
+ "UserPromptSubmit",
187
+ "PreToolUse",
188
+ "PostToolUse",
189
+ "Stop",
190
+ )
191
+
192
+
193
+ def all_ids() -> list[str]:
194
+ """Return every hook id, in registry order (used to expand the ``all`` profile token)."""
195
+ return list(HOOK_REGISTRY)
196
+
197
+
198
+ def scripts_for(hook_ids: list[str]) -> list[str]:
199
+ """Return the script basenames needed by ``hook_ids`` (inline/prompt hooks contribute none)."""
200
+ out: list[str] = []
201
+ for hid in hook_ids:
202
+ spec = HOOK_REGISTRY.get(hid)
203
+ if spec and spec["script"]:
204
+ out.append(spec["script"])
205
+ return sorted(set(out))
206
+
207
+
208
+ def build_settings(hook_ids: list[str]) -> dict[str, Any]:
209
+ """Assemble a ``settings.json`` document from the selected hook ids.
210
+
211
+ Groups the selected hooks by event and matcher, preserving registry order, into the schema
212
+ Claude Code expects (``{"hooks": {EVENT: [{"matcher": …, "hooks": [entry, …]}]}}``).
213
+
214
+ Args:
215
+ hook_ids: Hook ids to enable.
216
+
217
+ Returns:
218
+ A JSON-serialisable settings mapping (always includes an explanatory ``$comment``).
219
+ """
220
+ selected = [hid for hid in HOOK_REGISTRY if hid in set(hook_ids)]
221
+ # event -> matcher -> [entries]
222
+ grouped: dict[str, dict[str, list[dict[str, Any]]]] = {}
223
+ for hid in selected:
224
+ spec = HOOK_REGISTRY[hid]
225
+ grouped.setdefault(spec["event"], {}).setdefault(spec["matcher"], []).append(
226
+ spec["entry"]
227
+ )
228
+
229
+ hooks_block: dict[str, list[dict[str, Any]]] = {}
230
+ ordered_events = [e for e in _EVENT_ORDER if e in grouped] + [
231
+ e for e in grouped if e not in _EVENT_ORDER
232
+ ]
233
+ for event in ordered_events:
234
+ hooks_block[event] = [
235
+ {"matcher": matcher, "hooks": entries}
236
+ for matcher, entries in grouped[event].items()
237
+ ]
238
+
239
+ return {
240
+ "$comment": (
241
+ "Claude Code settings installed by claude-kit. Hooks wire the SDLC working-memory, "
242
+ "learnings, guardrails, and quality checks to scripts in .claude/hooks/. Personal "
243
+ "overrides belong in .claude/settings.local.json (gitignored)."
244
+ ),
245
+ "hooks": hooks_block,
246
+ }