craftainer-crud-sdk 0.1.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 (142) hide show
  1. craftainer_crud_sdk-0.1.0/.claude/README.md +82 -0
  2. craftainer_crud_sdk-0.1.0/.claude/agents/plan-executor.md +72 -0
  3. craftainer_crud_sdk-0.1.0/.claude/hooks/self-check.sh +35 -0
  4. craftainer_crud_sdk-0.1.0/.claude/settings.json +36 -0
  5. craftainer_crud_sdk-0.1.0/.claude/skills/owasp-scan/SKILL.md +89 -0
  6. craftainer_crud_sdk-0.1.0/.devcontainer/Dockerfile +79 -0
  7. craftainer_crud_sdk-0.1.0/.devcontainer/Dockerfile.dockerignore +11 -0
  8. craftainer_crud_sdk-0.1.0/.devcontainer/README.md +164 -0
  9. craftainer_crud_sdk-0.1.0/.devcontainer/compose.instance.yml +5 -0
  10. craftainer_crud_sdk-0.1.0/.devcontainer/compose.yml +42 -0
  11. craftainer_crud_sdk-0.1.0/.devcontainer/devcontainer-lock.json +9 -0
  12. craftainer_crud_sdk-0.1.0/.devcontainer/devcontainer.json +39 -0
  13. craftainer_crud_sdk-0.1.0/.devcontainer/resolve-ssh-agent.sh +41 -0
  14. craftainer_crud_sdk-0.1.0/.gitattributes +1 -0
  15. craftainer_crud_sdk-0.1.0/.github/CONTENTS.md +31 -0
  16. craftainer_crud_sdk-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +48 -0
  17. craftainer_crud_sdk-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
  18. craftainer_crud_sdk-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  19. craftainer_crud_sdk-0.1.0/.github/renovate.json +62 -0
  20. craftainer_crud_sdk-0.1.0/.github/scripts/README.md +46 -0
  21. craftainer_crud_sdk-0.1.0/.github/scripts/check_devcontainer_lock.py +57 -0
  22. craftainer_crud_sdk-0.1.0/.github/scripts/compute_next_version.py +116 -0
  23. craftainer_crud_sdk-0.1.0/.github/scripts/moderate_apply_result.sh +65 -0
  24. craftainer_crud_sdk-0.1.0/.github/scripts/moderate_cleanup.sh +30 -0
  25. craftainer_crud_sdk-0.1.0/.github/scripts/moderate_package_result.sh +40 -0
  26. craftainer_crud_sdk-0.1.0/.github/scripts/prompts/bug-fix.md +32 -0
  27. craftainer_crud_sdk-0.1.0/.github/scripts/prompts/bug-triage.md +71 -0
  28. craftainer_crud_sdk-0.1.0/.github/scripts/prompts/feature-build.md +34 -0
  29. craftainer_crud_sdk-0.1.0/.github/scripts/prompts/feature-triage.md +62 -0
  30. craftainer_crud_sdk-0.1.0/.github/scripts/reset_issue_state.sh +32 -0
  31. craftainer_crud_sdk-0.1.0/.github/scripts/run_claude.sh +27 -0
  32. craftainer_crud_sdk-0.1.0/.github/scripts/template_sync.py +315 -0
  33. craftainer_crud_sdk-0.1.0/.github/scripts/template_sync_manifest.py +122 -0
  34. craftainer_crud_sdk-0.1.0/.github/template-sync-manifest.yml +164 -0
  35. craftainer_crud_sdk-0.1.0/.github/template-sync-state.json +4 -0
  36. craftainer_crud_sdk-0.1.0/.github/workflows/README.md +301 -0
  37. craftainer_crud_sdk-0.1.0/.github/workflows/checks.yml +75 -0
  38. craftainer_crud_sdk-0.1.0/.github/workflows/codspeed.yml +53 -0
  39. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-fix-apply.yml +48 -0
  40. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-fix.yml +125 -0
  41. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-bug-triage.yml +91 -0
  42. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-cleanup.yml +49 -0
  43. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-build-apply.yml +48 -0
  44. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-build.yml +125 -0
  45. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-feature-triage.yml +91 -0
  46. craftainer_crud_sdk-0.1.0/.github/workflows/moderate-setup.yml +38 -0
  47. craftainer_crud_sdk-0.1.0/.github/workflows/release.yml +205 -0
  48. craftainer_crud_sdk-0.1.0/.github/workflows/template-sync.yml +184 -0
  49. craftainer_crud_sdk-0.1.0/.gitignore +123 -0
  50. craftainer_crud_sdk-0.1.0/.mcp.json +8 -0
  51. craftainer_crud_sdk-0.1.0/.pre-commit-config.yaml +131 -0
  52. craftainer_crud_sdk-0.1.0/.secrets/.gitkeep +0 -0
  53. craftainer_crud_sdk-0.1.0/.secrets/README.md +23 -0
  54. craftainer_crud_sdk-0.1.0/.vscode/README.md +28 -0
  55. craftainer_crud_sdk-0.1.0/.vscode/extensions.json +3 -0
  56. craftainer_crud_sdk-0.1.0/.vscode/settings.json +4 -0
  57. craftainer_crud_sdk-0.1.0/.vscode/tasks.json +12 -0
  58. craftainer_crud_sdk-0.1.0/CLAUDE.md +144 -0
  59. craftainer_crud_sdk-0.1.0/LICENSE +674 -0
  60. craftainer_crud_sdk-0.1.0/Makefile +80 -0
  61. craftainer_crud_sdk-0.1.0/PKG-INFO +144 -0
  62. craftainer_crud_sdk-0.1.0/README.md +132 -0
  63. craftainer_crud_sdk-0.1.0/docs/README.md +35 -0
  64. craftainer_crud_sdk-0.1.0/docs/TEMPLATE.md +296 -0
  65. craftainer_crud_sdk-0.1.0/docs/adrs/0001-python-package-template.md +74 -0
  66. craftainer_crud_sdk-0.1.0/docs/adrs/0002-own-thin-cache-layer-with-sse-invalidation.md +134 -0
  67. craftainer_crud_sdk-0.1.0/docs/adrs/README.md +27 -0
  68. craftainer_crud_sdk-0.1.0/docs/adrs/template.md +23 -0
  69. craftainer_crud_sdk-0.1.0/docs/frs/README.md +30 -0
  70. craftainer_crud_sdk-0.1.0/docs/frs/template.md +21 -0
  71. craftainer_crud_sdk-0.1.0/docs/nfrs/README.md +28 -0
  72. craftainer_crud_sdk-0.1.0/docs/nfrs/template.md +27 -0
  73. craftainer_crud_sdk-0.1.0/docs/plans/README.md +63 -0
  74. craftainer_crud_sdk-0.1.0/docs/plans/template.md +35 -0
  75. craftainer_crud_sdk-0.1.0/pyproject.toml +97 -0
  76. craftainer_crud_sdk-0.1.0/scripts/README.md +29 -0
  77. craftainer_crud_sdk-0.1.0/scripts/develop.d/README.md +15 -0
  78. craftainer_crud_sdk-0.1.0/scripts/develop.sh +121 -0
  79. craftainer_crud_sdk-0.1.0/scripts/post-create.d/10-uv-sync.sh +6 -0
  80. craftainer_crud_sdk-0.1.0/scripts/post-create.d/README.md +12 -0
  81. craftainer_crud_sdk-0.1.0/scripts/post-create.sh +13 -0
  82. craftainer_crud_sdk-0.1.0/scripts/release-check.sh +90 -0
  83. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/__init__.py +152 -0
  84. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/auth.py +150 -0
  85. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/__init__.py +23 -0
  86. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/entry.py +145 -0
  87. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/keys.py +111 -0
  88. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/layer.py +740 -0
  89. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/memory.py +112 -0
  90. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/policy.py +61 -0
  91. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/cache/redis.py +201 -0
  92. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/client.py +952 -0
  93. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/errors.py +217 -0
  94. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/events.py +548 -0
  95. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/interfaces.py +183 -0
  96. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/manifest.py +397 -0
  97. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/py.typed +0 -0
  98. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/query.py +202 -0
  99. craftainer_crud_sdk-0.1.0/src/craftainer_crud_sdk/wiring.py +152 -0
  100. craftainer_crud_sdk-0.1.0/tests/README.md +15 -0
  101. craftainer_crud_sdk-0.1.0/tests/__init__.py +1 -0
  102. craftainer_crud_sdk-0.1.0/tests/bench/__init__.py +1 -0
  103. craftainer_crud_sdk-0.1.0/tests/bench/test_entry_bench.py +11 -0
  104. craftainer_crud_sdk-0.1.0/tests/fixtures/README.md +6 -0
  105. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/admin.manifest.json +1078 -0
  106. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/admin.request.json +9 -0
  107. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/anonymous.manifest.json +281 -0
  108. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/anonymous.request.json +7 -0
  109. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/hero-reader.manifest.json +828 -0
  110. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/hero-reader.request.json +9 -0
  111. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/legacy-reader.manifest.json +531 -0
  112. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/legacy-reader.request.json +9 -0
  113. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/server.json +69 -0
  114. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/stranger.manifest.json +281 -0
  115. craftainer_crud_sdk-0.1.0/tests/fixtures/manifest/stranger.request.json +9 -0
  116. craftainer_crud_sdk-0.1.0/tests/unit/__init__.py +1 -0
  117. craftainer_crud_sdk-0.1.0/tests/unit/_fake_server.py +214 -0
  118. craftainer_crud_sdk-0.1.0/tests/unit/_support.py +68 -0
  119. craftainer_crud_sdk-0.1.0/tests/unit/cache/__init__.py +1 -0
  120. craftainer_crud_sdk-0.1.0/tests/unit/cache/harness.py +126 -0
  121. craftainer_crud_sdk-0.1.0/tests/unit/cache/test_async_layer.py +326 -0
  122. craftainer_crud_sdk-0.1.0/tests/unit/cache/test_keys_policy_entry.py +163 -0
  123. craftainer_crud_sdk-0.1.0/tests/unit/cache/test_layer.py +742 -0
  124. craftainer_crud_sdk-0.1.0/tests/unit/cache/test_memory_redis.py +306 -0
  125. craftainer_crud_sdk-0.1.0/tests/unit/concurrency/README.md +12 -0
  126. craftainer_crud_sdk-0.1.0/tests/unit/concurrency/__init__.py +1 -0
  127. craftainer_crud_sdk-0.1.0/tests/unit/concurrency/test_interleavings.py +380 -0
  128. craftainer_crud_sdk-0.1.0/tests/unit/concurrency/world.py +566 -0
  129. craftainer_crud_sdk-0.1.0/tests/unit/test_auth.py +195 -0
  130. craftainer_crud_sdk-0.1.0/tests/unit/test_client.py +636 -0
  131. craftainer_crud_sdk-0.1.0/tests/unit/test_client_async.py +317 -0
  132. craftainer_crud_sdk-0.1.0/tests/unit/test_errors.py +130 -0
  133. craftainer_crud_sdk-0.1.0/tests/unit/test_events.py +814 -0
  134. craftainer_crud_sdk-0.1.0/tests/unit/test_hardening.py +454 -0
  135. craftainer_crud_sdk-0.1.0/tests/unit/test_interfaces.py +69 -0
  136. craftainer_crud_sdk-0.1.0/tests/unit/test_manifest.py +273 -0
  137. craftainer_crud_sdk-0.1.0/tests/unit/test_query.py +195 -0
  138. craftainer_crud_sdk-0.1.0/tests/unit/test_scope_and_if_match.py +225 -0
  139. craftainer_crud_sdk-0.1.0/tests/unit/test_sdk_e2e.py +253 -0
  140. craftainer_crud_sdk-0.1.0/tests/unit/test_sdk_e2e_async.py +261 -0
  141. craftainer_crud_sdk-0.1.0/tests/unit/test_wiring_policies.py +18 -0
  142. craftainer_crud_sdk-0.1.0/uv.lock +1106 -0
@@ -0,0 +1,82 @@
1
+ # .claude/
2
+
3
+ Project-scope Claude Code configuration, shared with every collaborator
4
+ who opens this repo (see the root `.gitignore`, which does *not* exclude
5
+ this directory).
6
+
7
+ - `settings.json` — enables official plugins from the built-in
8
+ `claude-plugins-official` marketplace via `enabledPlugins`:
9
+ - `context7` — pulls up-to-date, version-specific library
10
+ documentation and examples into context from Context7's hosted
11
+ remote MCP server (`https://mcp.context7.com/mcp`); no local
12
+ install. Works anonymously; set a `CONTEXT7_API_KEY` env var for
13
+ higher rate limits.
14
+ - `security-guidance` — security review for Claude-generated code:
15
+ pattern-based warnings on `Edit`/`Write`, an LLM diff review when
16
+ Claude finishes a turn, and an agentic reviewer on `git commit`
17
+ that traces cross-file data flow. Needs only Python 3.8+ on
18
+ `PATH` — an instance without a Python runtime of its own should
19
+ confirm this is still true before relying on it.
20
+
21
+ `settings.json` also wires up the `PostToolUse` hook below.
22
+ - `hooks/self-check.sh` — after Claude edits a file, runs the fast,
23
+ pre-commit-stage hooks from `../.pre-commit-config.yaml` (whitespace,
24
+ YAML/TOML/JSON checks, ...) against just that file via `prek`, so
25
+ issues are caught and auto-fixed immediately instead of first at commit
26
+ time. An instance's own slower, pre-push-only hooks stay pre-push-only —
27
+ too slow to run after every edit.
28
+ - `agents/plan-executor.md` — the subagent that executes one work item
29
+ of a `docs/plans/` plan on a faster model (`model: sonnet`), under
30
+ the main session as orchestrator (see `../CLAUDE.md`, "Execute
31
+ multi-item work as an orchestrator"). It holds the executor's side of
32
+ the protocol: what a brief contains, the write-set and no-push rules,
33
+ the escalation triggers, and the report format.
34
+ - The `PreToolUse` hook on `Bash` runs `snip hook`, the
35
+ [snip](https://github.com/edouard-claude/snip) token-filtering proxy:
36
+ it rewrites supported commands (`git` and others; an instance's own
37
+ language tooling once it's added) to return only their signal
38
+ (failures, diffs, summaries) instead of full output. `snip` itself is
39
+ installed by `scripts/develop.sh`, pinned to `SNIP_VERSION` in
40
+ `../.devcontainer/Dockerfile`, and checksum-verified against its published release
41
+ checksums — see "Versions and config" in `docs/TEMPLATE.md`. Chosen
42
+ over the more popular
43
+ [rtk](https://github.com/rtk-ai/rtk) because its filters are
44
+ declarative YAML data (auditable) and it fails open (a broken filter
45
+ passes the command through unfiltered rather than blocking it); rtk's
46
+ larger community and multi-maintainer team are the tradeoff, so revisit
47
+ this choice if `snip` goes unmaintained.
48
+ - `../.mcp.json` — project-scope MCP servers not available as a
49
+ `claude-plugins-official` plugin, so they can't go through
50
+ `enabledPlugins` above:
51
+ - `clear-thought` (`@waldzellai/clear-thought-onepointfive`) — a
52
+ local stdio MCP server exposing structured reasoning/mental-model
53
+ tools, launched via `npx`. `npx` needs Node.js, which is the only
54
+ reason `scripts/develop.sh` installs it (pinned via
55
+ `../.devcontainer/Dockerfile`'s `NODE_VERSION` ARG) — Claude Code itself doesn't need it.
56
+
57
+ An instance adds its own stack-specific MCP servers here (database,
58
+ object storage, browser automation, ...), pinned to an exact
59
+ version/commit the same way, and reading credentials via `${VAR}` from
60
+ the same env vars its own `../.devcontainer/compose.yml` gives its dev
61
+ service — never re-pinned a second time.
62
+
63
+ ## Do
64
+
65
+ - Keep this file's `enabledPlugins` pinned to specific plugin names —
66
+ don't rely on a whole marketplace being auto-installed.
67
+
68
+ ## Don't
69
+
70
+ - Add personal preferences here — this file is committed and shared;
71
+ use your own user-scope Claude Code settings for those instead.
72
+
73
+ ## Removing a tool
74
+
75
+ - **snip**: delete the `PreToolUse` hook entry in `settings.json` and the
76
+ `snip` install block (and `SNIP_VERSION` ARG in `../.devcontainer/Dockerfile`) from
77
+ `scripts/develop.sh`.
78
+ - **context7** / **security-guidance**: delete the plugin's
79
+ `enabledPlugins` entry in `settings.json`.
80
+ - **clear-thought**: delete its entry from `../.mcp.json`; if nothing
81
+ else needs Node.js, also remove its install block (and `NODE_VERSION`
82
+ ARG) from `../.devcontainer/Dockerfile`/`scripts/develop.sh`.
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: plan-executor
3
+ description: Executes exactly one work item of a docs/plans/ plan, briefed by the orchestrating main session. Works only inside its brief's write set, in its own worktree, and hands back to the orchestrator instead of improvising when it hits an escalation trigger. Use one per independent work item; run independent items in parallel.
4
+ model: sonnet
5
+ ---
6
+
7
+ You execute one work item from a plan under `docs/plans/`. The
8
+ orchestrator (the main session) wrote your brief. It owns every decision
9
+ outside that brief and talks to the user. Your job is to finish your
10
+ item and report it accurately.
11
+
12
+ ## Your brief contains
13
+
14
+ 1. **Goal:** the work item, quoted from the plan, plus any Decision log
15
+ entries that apply. Decision log entries are settled. Don't reopen
16
+ them.
17
+ 2. **Write set:** the only files and directories you may create, change
18
+ or delete. Everything else is read-only to you.
19
+ 3. **Interfaces:** names and signatures you must provide, or may rely on
20
+ from items running in parallel. Code against them exactly as stated,
21
+ even if the other side doesn't exist yet in your worktree.
22
+ 4. **Done when:** the checks that must pass before you report `done`.
23
+
24
+ ## Rules
25
+
26
+ - Before writing anything, read `CLAUDE.md`, then every `README.md` on
27
+ the path to each file in your write set, root first (`CLAUDE.md`,
28
+ "Before writing anything").
29
+ - Before relying on a third-party library's API, look it up with
30
+ context7. Don't code from memory.
31
+ - Commit only to your own `wip/<id>` branch in your worktree. Never
32
+ push, open a PR, create a repo, tag, publish, or delete a branch.
33
+ - Don't run integration tests, e2e tests or load tests unless your
34
+ brief says to. They share the devcontainer's services, and the
35
+ orchestrator runs them one at a time in its own lanes. Run lint,
36
+ type-check and unit tests for your write set yourself.
37
+ - Show real check output. "Looks done" is not verification.
38
+
39
+ ## Escalate: stop and hand back
40
+
41
+ Stop working and report `STATUS: escalate` as soon as any of these
42
+ happens. Don't work around it:
43
+
44
+ 1. A required check still fails after two distinct fix attempts.
45
+ 2. The fix needs a file outside your write set, or an interface that
46
+ differs from your brief.
47
+ 3. Your brief contradicts the code, a `README.md`, an ADR or an NFR, or
48
+ is ambiguous enough that you'd be guessing.
49
+ 4. A library or framework behaves differently from what your brief
50
+ assumes.
51
+ 5. The only way to pass a check is a suppression comment, a lower
52
+ coverage threshold, or a weakened guardrail.
53
+ 6. Anything under `.devcontainer/` would need to change. That needs a
54
+ rebuild only the user can trigger.
55
+ 7. Any outward-facing or hard-to-reverse action would be needed.
56
+ 8. A measurement contradicts the expected effect, or the run-to-run
57
+ spread is larger than the effect.
58
+
59
+ The orchestrator researches the problem, decides, and resumes you with
60
+ a message. Keep your worktree as it is when you escalate.
61
+
62
+ ## Report: your final message, exactly this shape
63
+
64
+ ```
65
+ STATUS: done | escalate
66
+ SUMMARY: <2-5 lines>
67
+ CHANGED: <files>
68
+ CHECKS: <command -> verbatim output tail, one per check>
69
+ COMMIT: <one-line message, then short bullets>
70
+ ESCALATION: <only when escalating: trigger number, what you tried,
71
+ evidence (verbatim output), options, your recommendation>
72
+ ```
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # PostToolUse hook (Edit|Write): after Claude edits a file, run the fast,
3
+ # pre-commit-stage hooks from .pre-commit-config.yaml against just that
4
+ # file via prek -- reusing the same config as `git commit` would, instead
5
+ # of a second, separate set of checks -- so whitespace/lint issues are
6
+ # caught (and auto-fixed where possible) immediately, not first at commit
7
+ # time. An instance's own slower, pre-push-only hooks (type checker, test
8
+ # suite, lock-file check, ...) are deliberately excluded here: too slow to
9
+ # run after every single edit.
10
+ set -euo pipefail
11
+
12
+ input="$(cat)"
13
+ file_path="$(jq -r '.tool_input.file_path // empty' <<<"$input")"
14
+
15
+ [ -n "$file_path" ] || exit 0
16
+ [ -f "$file_path" ] || exit 0
17
+
18
+ project_dir="${CLAUDE_PROJECT_DIR:-$PWD}"
19
+ case "$file_path" in
20
+ "$project_dir"/*) ;;
21
+ *) exit 0 ;;
22
+ esac
23
+
24
+ command -v prek >/dev/null 2>&1 || exit 0
25
+
26
+ cd "$project_dir"
27
+ relative_path="${file_path#"$project_dir"/}"
28
+
29
+ if ! output="$(prek run --files "$relative_path" 2>&1)"; then
30
+ echo "$output" >&2
31
+ echo "prek found issues in $relative_path (fixed automatically where possible) -- review the diff." >&2
32
+ exit 2
33
+ fi
34
+
35
+ exit 0
@@ -0,0 +1,36 @@
1
+ {
2
+ "attribution": {
3
+ "commit": "",
4
+ "pr": ""
5
+ },
6
+ "hooks": {
7
+ "PostToolUse": [
8
+ {
9
+ "matcher": "Edit|Write",
10
+ "hooks": [
11
+ {
12
+ "type": "command",
13
+ "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/self-check.sh",
14
+ "timeout": 60
15
+ }
16
+ ]
17
+ }
18
+ ],
19
+ "PreToolUse": [
20
+ {
21
+ "matcher": "Bash",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "snip hook",
26
+ "timeout": 10
27
+ }
28
+ ]
29
+ }
30
+ ]
31
+ },
32
+ "enabledPlugins": {
33
+ "context7@claude-plugins-official": true,
34
+ "security-guidance@claude-plugins-official": true
35
+ }
36
+ }
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: owasp-scan
3
+ description: Scan every file under src/ for OWASP Top Ten (2025) issues, in parallel, and report findings.
4
+ ---
5
+
6
+ # OWASP Top Ten scan
7
+
8
+ Check all files under `src/` against the OWASP Top Ten (2025) categories
9
+ and report what's found. Read-only — do not fix anything unless the user
10
+ asks afterward.
11
+
12
+ ## Steps
13
+
14
+ 1. **List target files.** `find src/ -type f -name "*.py"` (adjust the
15
+ extension filter if the tree isn't Python). Exclude generated/vendor
16
+ code if any exists under `src/`.
17
+
18
+ 2. **Batch them.** Group files by their top-level subdirectory under
19
+ `src/app/` (`controllers`, `crud`, `models`, `repositories`, `views`,
20
+ `health`, etc.), splitting further so no batch exceeds ~8 files. Each
21
+ batch becomes one subagent task.
22
+
23
+ 3. **Scan in parallel.** In a single message, launch one `Explore` agent
24
+ per batch (`run_in_background: false` is not needed — let them run in
25
+ parallel and collect results as they land). Give each agent:
26
+ - The exact file list for its batch (full paths).
27
+ - The checklist below.
28
+ - Instruction to report only findings it can point at a concrete
29
+ file:line and a concrete failure scenario (input/state that
30
+ triggers it) — no generic advice, no "consider adding X" without a
31
+ specific gap it fills.
32
+ - Instruction to reply in under 300 words per file with findings, or
33
+ say "no findings" for a file with none.
34
+
35
+ 4. **Aggregate.** Merge all agents' findings into one list, drop
36
+ duplicates, sort most-severe first (Injection/Broken Access
37
+ Control/Auth failures above Security Misconfiguration above
38
+ Logging/Monitoring gaps).
39
+
40
+ 5. **Report to the user** as a single markdown list grouped by OWASP
41
+ category, each item: `path:line — one-sentence defect — concrete
42
+ failure scenario`. Skip categories with no findings; don't pad the
43
+ report with reassurance about what's fine.
44
+
45
+ ## OWASP Top Ten (2025) checklist for each agent
46
+
47
+ - **A01 Broken Access Control** — missing/incorrect authz checks,
48
+ IDOR (object access by ID without ownership check), path traversal,
49
+ CORS misconfig, and SSRF (outbound requests built from
50
+ user-controlled URLs/hosts without allowlisting — folded into this
51
+ category for 2025).
52
+ - **A02 Security Misconfiguration** — debug mode/verbose errors enabled
53
+ in non-dev paths, permissive defaults, unnecessary exposed
54
+ endpoints/features.
55
+ - **A03 Software Supply Chain Failures** — dependency/version pinned
56
+ elsewhere with a known vulnerability you can name, unpinned/floating
57
+ versions for security-sensitive packages, unverified third-party
58
+ build steps or CI actions.
59
+ - **A04 Cryptographic Failures** — plaintext secrets/passwords, weak or
60
+ homemade crypto/hashing, hardcoded keys, missing TLS enforcement.
61
+ - **A05 Injection** — SQL/NoSQL/OS command/LDAP injection via
62
+ unsanitized input, unsafe `eval`/template rendering, missing
63
+ parameterization.
64
+ - **A06 Insecure Design** — missing rate limiting or business-logic
65
+ guards where abuse is plausible (only flag with a concrete abuse
66
+ path, not speculative).
67
+ - **A07 Authentication Failures** — weak session/token handling,
68
+ missing expiry/invalidation, predictable tokens, password rules
69
+ absent where passwords are set.
70
+ - **A08 Software or Data Integrity Failures** — unsafe deserialization
71
+ (pickle, yaml.load without safe loader), unsigned/unverified updates
72
+ or artifacts.
73
+ - **A09 Security Logging & Alerting Failures** — security-relevant
74
+ events (auth failures, access-control denials) not logged, sensitive
75
+ data (passwords, tokens) logged in plaintext, or no alerting hook for
76
+ events that should page someone.
77
+ - **A10 Mishandling of Exceptional Conditions** — broad/bare exception
78
+ handlers that swallow security-relevant errors, error paths that
79
+ fail open (e.g. an auth check that defaults to "allow" if the
80
+ check itself throws), inconsistent error handling that leaks
81
+ internals in one path and hides failures in another.
82
+
83
+ ## Notes
84
+
85
+ - This is a read pass, not `/security-review` or `/code-review` — don't
86
+ invoke those; this skill is narrower (OWASP-only, src/ only) and
87
+ faster because it fans out.
88
+ - If the user wants fixes applied afterward, treat that as a separate,
89
+ explicit follow-up — don't edit files as part of the scan itself.
@@ -0,0 +1,79 @@
1
+ # syntax=docker/dockerfile:1.28
2
+ # Single-stage build: develop (the devcontainer image). Built with the repo
3
+ # root as context (see compose.yml). The repo-root Dockerfile is reserved for
4
+ # an instance's own runtime image -- see docs/TEMPLATE.md's "Contents" and
5
+ # "Instance extension points".
6
+
7
+ ARG DEBIAN_VERSION=trixie
8
+
9
+ # renovate: datasource=github-releases depName=j178/prek
10
+ ARG PREK_VERSION=0.5.5
11
+
12
+ # renovate: datasource=npm depName=@anthropic-ai/claude-code
13
+ ARG CLAUDE_CODE_VERSION=2.1.296
14
+
15
+ # renovate: datasource=github-releases depName=edouard-claude/snip
16
+ ARG SNIP_VERSION=0.25.2
17
+
18
+ # renovate: datasource=github-releases depName=cli/cli
19
+ ARG GH_VERSION=2.102.0
20
+
21
+ # uv itself isn't a project dependency manager here -- it's only the
22
+ # mechanism scripts/develop.sh uses to install an exact, checksum-verified
23
+ # CPython build (see PYTHON_VERSION below) rather than an unpinned apt
24
+ # package. Left on PATH afterward; harmless, and an instance that adds
25
+ # Python as its own application runtime can reuse it directly.
26
+ # renovate: datasource=github-releases depName=astral-sh/uv
27
+ ARG UV_VERSION=0.13.0
28
+
29
+ # python3 is infrastructure tooling, not an application runtime this
30
+ # template assumes: prek needs an interpreter to build the venv for any
31
+ # "language: python" hook (.pre-commit-config.yaml's pre-commit-hooks
32
+ # repo), and .github/scripts/*.py need one directly.
33
+ # renovate: datasource=python-version depName=python
34
+ ARG PYTHON_VERSION=3.14.8
35
+
36
+ # Node.js is infrastructure tooling too, not an application runtime: npx
37
+ # (the clear-thought MCP server in .mcp.json) is the only thing that
38
+ # needs it.
39
+ # renovate: datasource=node-version depName=node
40
+ ARG NODE_VERSION=24.21.0
41
+
42
+ ARG SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
43
+ ARG SSL_CERT_DIR=/etc/ssl/certs
44
+
45
+ ########################################
46
+ # develop — interactive devcontainer image, based on Microsoft's generic
47
+ # base devcontainer image. No *application* language runtime is assumed
48
+ # here — an instance adds its own (Python via uv, Rust via rustup, Go,
49
+ # Node.js, OpenTofu/Ansible, ...) on top of this stage, following the same
50
+ # ARG/scripts/develop.sh pattern established below. The Python and Node.js
51
+ # this stage does install are infrastructure tooling only -- see the ARG
52
+ # comments above.
53
+ ########################################
54
+ FROM mcr.microsoft.com/devcontainers/base:${DEBIAN_VERSION} AS develop
55
+ ARG PREK_VERSION
56
+ ARG CLAUDE_CODE_VERSION
57
+ ARG SNIP_VERSION
58
+ ARG UV_VERSION
59
+ ARG PYTHON_VERSION
60
+ ARG NODE_VERSION
61
+ ARG GH_VERSION
62
+ ARG SSL_CERT_FILE
63
+ ARG SSL_CERT_DIR
64
+
65
+ ENV SSL_CERT_FILE=${SSL_CERT_FILE} \
66
+ SSL_CERT_DIR=${SSL_CERT_DIR} \
67
+ REQUESTS_CA_BUNDLE=${SSL_CERT_FILE} \
68
+ CURL_CA_BUNDLE=${SSL_CERT_FILE}
69
+
70
+ COPY scripts/develop.sh /tmp/develop.sh
71
+ RUN bash /tmp/develop.sh "$PREK_VERSION" "$CLAUDE_CODE_VERSION" "$SNIP_VERSION" "$UV_VERSION" "$PYTHON_VERSION" "$NODE_VERSION" "$GH_VERSION"
72
+
73
+ # Instance hook: scripts/develop.d/*.sh, see that directory's README.md.
74
+ COPY scripts/develop.d/ /tmp/develop.d/
75
+ RUN for f in /tmp/develop.d/*.sh; do [ -e "$f" ] || continue; echo "==> $f"; bash "$f" || exit 1; done
76
+
77
+ USER vscode
78
+ WORKDIR /workspace
79
+ CMD ["sleep", "infinity"]
@@ -0,0 +1,11 @@
1
+ # Build context excludes for the devcontainer image (.devcontainer/Dockerfile).
2
+ # BuildKit reads <Dockerfile>.dockerignore next to the Dockerfile in place of
3
+ # the context root's .dockerignore, which stays free for an instance's own
4
+ # runtime image.
5
+ .git
6
+ .github
7
+ .devcontainer
8
+ .secrets
9
+ docs
10
+ README.md
11
+ CLAUDE.md
@@ -0,0 +1,164 @@
1
+ # .devcontainer/
2
+
3
+ - `devcontainer.json` — references `compose.yml` and `compose.instance.yml`
4
+ as the devcontainer's `dockerComposeFile`, and configures the devcontainer itself
5
+ (features, mounts, editor settings).
6
+ - `Dockerfile` — the devcontainer image (a single `develop` stage), built
7
+ with the repo root as context; `Dockerfile.dockerignore` holds its
8
+ build-context excludes. The top-level `Dockerfile` is reserved for an
9
+ instance's runtime image (`../docs/TEMPLATE.md`).
10
+ - `compose.yml` — the dev service (`myapp`), built from `Dockerfile`'s
11
+ `develop` stage. An instance that needs backing services
12
+ (a database, a queue, ...) adds its own `stack/` directory (one
13
+ subdirectory per service, each with its own compose fragment) and an
14
+ `include:` list in `compose.instance.yml` pointing at them — see `stack/README.md`'s
15
+ "Devcontainer stack pattern" convention once an instance adds one.
16
+ - `compose.instance.yml` — instance-owned stub (`ignore` tier, never
17
+ touched by sync): an instance's `include:` list and extra `services.myapp`
18
+ settings. See `docs/TEMPLATE.md`'s "Instance extension points".
19
+
20
+ ## Docker-in-Docker vs. the host's Docker
21
+
22
+ This devcontainer has its own isolated Docker-in-Docker daemon (the
23
+ `docker-in-docker` feature in `devcontainer.json`). It is **not** the
24
+ same daemon running any backing-service stack an instance adds — that
25
+ stack is started by whatever invoked "Reopen in Container" against the
26
+ *host's* Docker. So `docker`/`docker compose` run from inside the
27
+ devcontainer can build and run throwaway containers of their own, but can
28
+ never see or `exec` into a sibling container an instance's `stack/`
29
+ starts — only a `docker compose` invoked on the host can.
30
+
31
+ ## SSH agent forwarding
32
+
33
+ `devcontainer.json`'s `mounts` bind-mounts two possible SSH agent
34
+ sockets, and `postStartCommand` runs `resolve-ssh-agent.sh` on every
35
+ container start to symlink whichever one actually works to
36
+ `/home/vscode/.ssh-agent-resolved.sock` — the path `remoteEnv` points
37
+ `SSH_AUTH_SOCK` at. Either way, `git push` over SSH inside the container
38
+ authenticates against the same agent and keys already loaded on the
39
+ host — no key material is ever copied into the container.
40
+
41
+ The two candidates:
42
+
43
+ - `/ssh-agent` — the host's own `SSH_AUTH_SOCK` (`${localEnv:SSH_AUTH_SOCK}`),
44
+ bind-mounted directly. This is a real Linux socket on WSL2, so normal
45
+ Unix permissions carry through and it works as-is (see "Windows" below
46
+ for getting an agent running there in the first place). If
47
+ `SSH_AUTH_SOCK` is unset when the container is created (true for
48
+ `devcontainers/ci` in `.github/workflows/checks.yml` — GitHub Actions
49
+ runners have no agent), `@devcontainers/cli` drops the empty source and
50
+ this mount degrades to a plain anonymous volume instead of erroring —
51
+ harmless there since CI never pushes.
52
+ - `/ssh-agent-macos` — Docker Desktop's own agent-forwarding proxy, always
53
+ available at the fixed path `/run/host-services/ssh-auth.sock` inside
54
+ its VM on both macOS and Windows. macOS has no real `SSH_AUTH_SOCK`
55
+ equivalent to WSL2's — the closest thing is this VM-internal proxy.
56
+
57
+ Both sockets arrive owned `root:root` mode `660` regardless of platform
58
+ or who owns the socket on the host side — bind-mounting a socket special
59
+ file carries over the owning uid/gid as seen inside the Docker Desktop
60
+ VM (root), not the host user's — so `remoteUser` (`vscode`) can't connect
61
+ to either without help. `resolve-ssh-agent.sh` runs `sudo chmod 666` on
62
+ each candidate before testing it (harmless: these are forwarding sockets
63
+ meant to be reachable by whatever connects to them, and chmod on a bind
64
+ mount only touches this one inode, not host files) — this needs the
65
+ passwordless `sudo` already granted to `vscode`.
66
+
67
+ Both also require an agent to actually be running on the host — on
68
+ macOS, `ssh-add -l` in a host Terminal should list a key; if it doesn't,
69
+ `ssh-add --apple-use-keychain ~/.ssh/id_ed25519` first — and a full
70
+ "Rebuild Container" after `mounts` changes (mounts don't apply to an
71
+ already-running container). `resolve-ssh-agent.sh` re-checks both
72
+ sockets on every start (not just container creation) since which one is
73
+ live can change if the host agent restarts between sessions.
74
+
75
+ ### Windows
76
+
77
+ Launching VS Code the normal way (Start Menu, a desktop shortcut, or
78
+ "Open Folder") starts it with no `SSH_AUTH_SOCK` at all — Windows has
79
+ no such environment variable, even with an agent (Git Bash's own, or
80
+ the Windows OpenSSH Authentication Agent service) already holding your
81
+ keys — so this always hits the degraded-mount path above, silently, and
82
+ `git push`/`pull` inside the container has no key material to
83
+ authenticate with no matter what's in your Windows `~/.ssh`.
84
+
85
+ With Docker Desktop's WSL2 backend (not the Hyper-V backend — that one
86
+ has no comparable path), fix this by running the agent inside a WSL2
87
+ distro instead, since that's the same Linux environment Docker Desktop
88
+ runs containers in and shares socket/filesystem access with:
89
+
90
+ 1. Make sure a real, user-facing WSL2 distro (e.g. Ubuntu — not just
91
+ the internal `docker-desktop` one) is installed and enabled under
92
+ Docker Desktop's Settings → Resources → WSL Integration.
93
+ 2. Inside that distro, copy your keys in from Windows and start an
94
+ agent:
95
+ ```sh
96
+ mkdir -p ~/.ssh && cp /mnt/c/Users/<you>/.ssh/id_ed25519* ~/.ssh/
97
+ chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_ed25519
98
+ eval "$(ssh-agent -s)"
99
+ ssh-add ~/.ssh/id_ed25519
100
+ ```
101
+ Put the `eval`/`ssh-add` lines in `~/.bashrc` (guard with
102
+ `ssh-add -l >/dev/null 2>&1 ||` so it doesn't spawn a new agent every
103
+ shell) so this survives new terminals without repeating it by hand.
104
+ 3. From that same WSL shell, run `code .` — this opens VS Code via the
105
+ Remote-WSL extension first, with `SSH_AUTH_SOCK` inherited from the
106
+ shell that launched it.
107
+ 4. From inside that window, run "Dev Containers: Reopen in Container".
108
+ `${localEnv:SSH_AUTH_SOCK}` now resolves to the WSL2 agent socket
109
+ instead of nothing, so the bind-mount above actually carries an
110
+ agent through.
111
+
112
+ Git Bash's own ssh-agent doesn't help here even though it holds the
113
+ same keys: its socket is an MSYS emulation, not a real Unix socket
114
+ Docker Desktop can bind-mount into a Linux container, and `code .` run
115
+ from a Git Bash prompt doesn't change what environment the VS Code GUI
116
+ process itself starts with anyway.
117
+
118
+ ## After a rebuild
119
+
120
+ "Dev Containers: Rebuild Container" reruns `postCreateCommand` from
121
+ scratch (dependency installs, `prek install`, ...) — no manual setup
122
+ step there. Still worth doing once the container is back:
123
+
124
+ - Run `prek run --all-files --hook-stage manual` — the same check-suite
125
+ that gates CI (`checks.yml`) — to confirm whatever motivated the
126
+ rebuild (a `.devcontainer/Dockerfile`/`scripts/develop.sh` change, a new tool) works
127
+ end-to-end inside the container, not just that the image built.
128
+ - If the change added, removed, or reordered a `mounts` entry, confirm
129
+ it actually took effect — mounts apply only at container creation, not
130
+ on every start (see "SSH agent forwarding" above for the concrete
131
+ case: a mount change needs a full rebuild, not just a restart).
132
+ - If `forwardPorts`/`portsAttributes` changed, check the Ports panel
133
+ picked up the new list.
134
+
135
+ A plain "Reopen in Container" (no rebuild) is enough for changes outside
136
+ `.devcontainer/` and `scripts/develop.sh` — those only
137
+ need the window reconnected, not a fresh image.
138
+
139
+ ## Do
140
+
141
+ - Add a new compose fragment's path to `compose.instance.yml`'s `include:`
142
+ list the same time you add the fragment — an unreferenced file starts
143
+ nothing.
144
+ - Keep service credentials and connection settings in the compose files'
145
+ `environment:` blocks, so opening the devcontainer is the only setup
146
+ step.
147
+ - Commit `devcontainer-lock.json` in the same change as any feature
148
+ edit in `devcontainer.json`. Regenerate it with
149
+ `npx @devcontainers/cli upgrade --workspace-folder .`, or by
150
+ rebuilding the devcontainer. Feature versions are bumped by hand,
151
+ not by Renovate: Renovate can't regenerate this file, so
152
+ `../.github/renovate.json` turns its devcontainer feature updates
153
+ off rather than let them merge with a stale lockfile.
154
+
155
+ ## Don't
156
+
157
+ - Bind- or volume-mount a language runtime's dependency tree, or anything
158
+ else you don't want a host antivirus/DLP tool scanning or syncing — it
159
+ lives purely in the container's writable layer instead. (`claude-config`
160
+ and `gh-config` are deliberate, narrow exceptions — small login and
161
+ session state; see `compose.yml`'s comments on those volumes.)
162
+ - Add a `ports:` mapping or a `networks:` block anywhere here.
163
+ - Pin a feature or image version as `latest` — pin the exact version so
164
+ Renovate can bump it deliberately.
@@ -0,0 +1,5 @@
1
+ # Instance-owned, never touched by template sync: an instance puts its
2
+ # `include:` stack fragments and any extra `services.myapp` settings
3
+ # (`env_file`, `environment`, `depends_on`) here, so `compose.yml` stays
4
+ # template-owned.
5
+ services: {}
@@ -0,0 +1,42 @@
1
+ # Dev service: the devcontainer itself, built from .devcontainer/Dockerfile's
2
+ # `develop` stage, with the repo root as build context. The workspace is bind-mounted so source
3
+ # edits are picked up without a rebuild. An instance that adds backing
4
+ # services (a database, a queue, ...) adds its own `include:` list to
5
+ # compose.instance.yml instead, pointing at its own `stack/*/compose.yml`
6
+ # fragments -- see
7
+ # .devcontainer/README.md's "Docker-in-Docker vs. the host's Docker".
8
+ services:
9
+ # Not `app`: Chrome HSTS-preloads the whole `.app` TLD, which also
10
+ # matches the bare hostname `app`, so a browser container (e.g. an
11
+ # instance's Selenium) would force http://app:<port> to HTTPS.
12
+ myapp:
13
+ build:
14
+ context: ..
15
+ dockerfile: .devcontainer/Dockerfile
16
+ target: develop
17
+ volumes:
18
+ - ..:/workspace:cached
19
+ # No volume for a language-runtime environment (venv, node_modules,
20
+ # target/, ...): a mounted one gets swept by Windows DLP/malware-
21
+ # scanning tools just like a bind-mounted one, so it lives purely in
22
+ # the container's writable layer instead -- see each language's own
23
+ # README for its specific env var/path convention once an instance
24
+ # adds one.
25
+ - claude-config:/home/vscode/.claude
26
+ - gh-config:/home/vscode/.config/gh
27
+ command: sleep infinity
28
+ # No ports: mapping and no networks: block — see .devcontainer/
29
+ # README.md's "Don't". An instance that needs host access adds
30
+ # forwardPorts to devcontainer.json; one with backing services
31
+ # reaches them by name on the default network compose generates for
32
+ # this project.
33
+
34
+ volumes:
35
+ # Persists Claude Code CLI session/credential state across container
36
+ # rebuilds. A small amount of session-state data, not a language
37
+ # runtime's dependency tree, so the DLP/malware-scanning concern above
38
+ # doesn't apply the same way here.
39
+ claude-config:
40
+ # Persists the GitHub CLI's login (`gh auth login`) across rebuilds, for
41
+ # the same reason.
42
+ gh-config:
@@ -0,0 +1,9 @@
1
+ {
2
+ "features": {
3
+ "ghcr.io/devcontainers/features/docker-in-docker:4.1.3": {
4
+ "version": "4.1.3",
5
+ "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:fae40f3c8be5a7c3b0db344c1d30341bc2c91ba38d1d5b14cd2ad72e6d65d084",
6
+ "integrity": "sha256:fae40f3c8be5a7c3b0db344c1d30341bc2c91ba38d1d5b14cd2ad72e6d65d084"
7
+ }
8
+ }
9
+ }