project-init 0.3.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.
- project_init/__init__.py +4 -0
- project_init/__main__.py +662 -0
- project_init/mcps.py +57 -0
- project_init/scaffold.py +374 -0
- project_init/templates/base/AGENTS.md.tmpl +50 -0
- project_init/templates/base/CLAUDE.md.tmpl +16 -0
- project_init/templates/base/CONTRIBUTING.md.tmpl +55 -0
- project_init/templates/base/GEMINI.md.tmpl +16 -0
- project_init/templates/base/LICENSE.tmpl +231 -0
- project_init/templates/base/SECURITY.md.tmpl +26 -0
- project_init/templates/base/docs/explanation/index.md +9 -0
- project_init/templates/base/docs/how-to/index.md +7 -0
- project_init/templates/base/docs/index.md.tmpl +20 -0
- project_init/templates/base/docs/reference/index.md +13 -0
- project_init/templates/base/docs/tutorials/index.md +7 -0
- project_init/templates/base/dot_claude/agents/README.md +30 -0
- project_init/templates/base/dot_claude/config.yaml.tmpl +31 -0
- project_init/templates/base/dot_claude/docs/README.md +26 -0
- project_init/templates/base/dot_claude/docs/adr/adr-001-memory-stack.md.tmpl +22 -0
- project_init/templates/base/dot_claude/docs/adr/adr-002-mcp-choices.md.tmpl +32 -0
- project_init/templates/base/dot_claude/docs/adr/adr-template.md +29 -0
- project_init/templates/base/dot_claude/docs/development/conventions.md.tmpl +31 -0
- project_init/templates/base/dot_claude/docs/development/testing.md +25 -0
- project_init/templates/base/dot_claude/docs/guides/developer-onboarding.md +110 -0
- project_init/templates/base/dot_claude/docs/guides/issue-metadata.md +27 -0
- project_init/templates/base/dot_claude/docs/guides/secrets.md +50 -0
- project_init/templates/base/dot_claude/docs/guides/using-memory.md +36 -0
- project_init/templates/base/dot_claude/hooks/README.md +15 -0
- project_init/templates/base/dot_claude/hooks/agent_guard_adapter.py.tmpl +64 -0
- project_init/templates/base/dot_claude/hooks/dag_workflow.py +610 -0
- project_init/templates/base/dot_claude/memory/MEMORY.md.tmpl +11 -0
- project_init/templates/base/dot_claude/memory/README.md +51 -0
- project_init/templates/base/dot_claude/memory/SCHEMA.md +52 -0
- project_init/templates/base/dot_claude/memory/feedback_conventions.md +11 -0
- project_init/templates/base/dot_claude/memory/project_context.md.tmpl +11 -0
- project_init/templates/base/dot_claude/memory/user_role.md +7 -0
- project_init/templates/base/dot_claude/project-init.md.tmpl +174 -0
- project_init/templates/base/dot_claude/rules/go.md +14 -0
- project_init/templates/base/dot_claude/rules/hooks.md +30 -0
- project_init/templates/base/dot_claude/rules/node.md +17 -0
- project_init/templates/base/dot_claude/rules/python.md +25 -0
- project_init/templates/base/dot_claude/scripts/README.md +15 -0
- project_init/templates/base/dot_claude/scripts/create_issue.sh +577 -0
- project_init/templates/base/dot_claude/scripts/create_nojira_pr.sh +3 -0
- project_init/templates/base/dot_claude/scripts/finish_pr.sh +3 -0
- project_init/templates/base/dot_claude/scripts/install_hooks.sh +55 -0
- project_init/templates/base/dot_claude/scripts/monitor_pr.sh +270 -0
- project_init/templates/base/dot_claude/scripts/promote_review.sh +3 -0
- project_init/templates/base/dot_claude/scripts/push_branch.sh +5 -0
- project_init/templates/base/dot_claude/scripts/push_wiki.sh +34 -0
- project_init/templates/base/dot_claude/scripts/setup_github.sh +219 -0
- project_init/templates/base/dot_claude/scripts/start_issue.sh +134 -0
- project_init/templates/base/dot_claude/settings.json.tmpl +83 -0
- project_init/templates/base/dot_claude/skills/README.md +12 -0
- project_init/templates/base/dot_claude/skills/plan/SKILL.md.tmpl +40 -0
- project_init/templates/base/dot_claude/vault/README.md +21 -0
- project_init/templates/base/dot_claude/vault/decisions/README.md +22 -0
- project_init/templates/base/dot_claude/vault/design/README.md +3 -0
- project_init/templates/base/dot_claude/vault/knowledge/README.md +5 -0
- project_init/templates/base/dot_claude/vault/sessions/README.md +5 -0
- project_init/templates/base/dot_devcontainer/devcontainer.json.tmpl +17 -0
- project_init/templates/base/dot_devcontainer/post-create.sh.tmpl +31 -0
- project_init/templates/base/dot_env.example.tmpl +13 -0
- project_init/templates/base/dot_github/CODEOWNERS.tmpl +12 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/bug.yml +98 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/chore.yml +82 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/config.yml +5 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/docs.yml +84 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/feature.yml +87 -0
- project_init/templates/base/dot_github/ISSUE_TEMPLATE/test.yml +90 -0
- project_init/templates/base/dot_github/copilot-instructions.md.tmpl +25 -0
- project_init/templates/base/dot_github/hooks/commit-msg +52 -0
- project_init/templates/base/dot_github/hooks/pre-commit +16 -0
- project_init/templates/base/dot_github/hooks/pre-push +51 -0
- project_init/templates/base/dot_github/pull_request_template.md +22 -0
- project_init/templates/base/dot_github/workflows/board-automation.yml +232 -0
- project_init/templates/base/dot_github/workflows/ci.yml.tmpl +204 -0
- project_init/templates/base/dot_github/workflows/docs.yml.tmpl +98 -0
- project_init/templates/base/dot_github/workflows/issue-validation.yml +72 -0
- project_init/templates/base/dot_github/workflows/review-status.yml +48 -0
- project_init/templates/base/dot_github/workflows/validate-pr.yml +103 -0
- project_init/templates/base/dot_gitignore.tmpl +41 -0
- project_init/templates/base/dot_golangci.yml.tmpl +20 -0
- project_init/templates/base/dot_vscode/extensions.json.tmpl +10 -0
- project_init/templates/base/dot_vscode/settings.json.tmpl +8 -0
- project_init/templates/base/eslint.config.mjs.tmpl +29 -0
- project_init/templates/base/justfile.tmpl +95 -0
- project_init/templates/base/mise.toml.tmpl +20 -0
- project_init/templates/base/mkdocs.yml.tmpl +32 -0
- project_init/templates/base/renovate.json +14 -0
- project_init/templates/base/ruff.toml.tmpl +31 -0
- project_init/templates/base/typedoc.json.tmpl +14 -0
- project_init/templates/codex/dot_agents/skills/add_adr/SKILL.md +33 -0
- project_init/templates/codex/dot_agents/skills/add_command/SKILL.md +63 -0
- project_init/templates/codex/dot_agents/skills/add_hook/SKILL.md +112 -0
- project_init/templates/codex/dot_agents/skills/audit/SKILL.md +146 -0
- project_init/templates/codex/dot_agents/skills/create_issue/SKILL.md +59 -0
- project_init/templates/codex/dot_agents/skills/github_workflow/SKILL.md +80 -0
- project_init/templates/codex/dot_agents/skills/request_review/SKILL.md +19 -0
- project_init/templates/codex/dot_agents/skills/review/SKILL.md +17 -0
- project_init/templates/codex/dot_agents/skills/save_memory/SKILL.md +17 -0
- project_init/templates/codex/dot_agents/skills/session_summary/SKILL.md +35 -0
- project_init/templates/codex/dot_agents/skills/start_task/SKILL.md +48 -0
- project_init/templates/codex/dot_agents/skills/status/SKILL.md +15 -0
- project_init/templates/codex/dot_codex/hooks.json.tmpl +17 -0
- project_init/templates/fallback/dot_claude/hooks/github_command_guard.sh +11 -0
- project_init/templates/fallback/dot_claude/hooks/post_edit_lint.sh +58 -0
- project_init/templates/fallback/dot_claude/hooks/pre_commit_gate.sh +81 -0
- project_init/templates/fallback/dot_claude/hooks/prod_guard.py +140 -0
- project_init/templates/fallback/dot_claude/hooks/session_setup.sh +62 -0
- project_init/templates/fallback/dot_claude/hooks/workflow_state_reminder.sh +72 -0
- project_init/templates/fallback/dot_claude/skills/INDEX.md +28 -0
- project_init/templates/fallback/dot_claude/skills/add_adr/SKILL.md +33 -0
- project_init/templates/fallback/dot_claude/skills/add_command/SKILL.md +63 -0
- project_init/templates/fallback/dot_claude/skills/add_hook/SKILL.md +112 -0
- project_init/templates/fallback/dot_claude/skills/audit/SKILL.md +146 -0
- project_init/templates/fallback/dot_claude/skills/create_issue/SKILL.md +59 -0
- project_init/templates/fallback/dot_claude/skills/github_workflow/SKILL.md +80 -0
- project_init/templates/fallback/dot_claude/skills/request_review/SKILL.md +19 -0
- project_init/templates/fallback/dot_claude/skills/review/SKILL.md +17 -0
- project_init/templates/fallback/dot_claude/skills/save_memory/SKILL.md +17 -0
- project_init/templates/fallback/dot_claude/skills/session_summary/SKILL.md +35 -0
- project_init/templates/fallback/dot_claude/skills/start_task/SKILL.md +48 -0
- project_init/templates/fallback/dot_claude/skills/status/SKILL.md +15 -0
- project_init/templates/gemini/dot_agents/skills/add_adr/SKILL.md +33 -0
- project_init/templates/gemini/dot_agents/skills/add_command/SKILL.md +63 -0
- project_init/templates/gemini/dot_agents/skills/add_hook/SKILL.md +112 -0
- project_init/templates/gemini/dot_agents/skills/audit/SKILL.md +146 -0
- project_init/templates/gemini/dot_agents/skills/create_issue/SKILL.md +59 -0
- project_init/templates/gemini/dot_agents/skills/github_workflow/SKILL.md +80 -0
- project_init/templates/gemini/dot_agents/skills/request_review/SKILL.md +19 -0
- project_init/templates/gemini/dot_agents/skills/review/SKILL.md +17 -0
- project_init/templates/gemini/dot_agents/skills/save_memory/SKILL.md +17 -0
- project_init/templates/gemini/dot_agents/skills/session_summary/SKILL.md +35 -0
- project_init/templates/gemini/dot_agents/skills/start_task/SKILL.md +48 -0
- project_init/templates/gemini/dot_agents/skills/status/SKILL.md +15 -0
- project_init/templates/gemini/dot_claude/scripts/setup_gemini.sh.tmpl +16 -0
- project_init/templates/gemini/dot_gemini-extension/commands/add_adr.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/add_command.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/add_hook.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/audit.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/create_issue.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/github_workflow.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/request_review.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/review.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/save_memory.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/session_summary.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/start_task.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/commands/status.toml +5 -0
- project_init/templates/gemini/dot_gemini-extension/gemini-extension.json.tmpl +6 -0
- project_init/templates/gemini/dot_gemini-extension/hooks/hooks.json.tmpl +18 -0
- project_init/templates/graphify/dot_claude/docs/guides/using-graphify.md +37 -0
- project_init/templates/graphify/dot_claude/rules/graphify.md +18 -0
- project_init/templates/graphify/dot_claude/scripts/setup_graphify.sh +40 -0
- project_init/templates/obsidian/dot_claude/scripts/lint_memory.sh +115 -0
- project_init/templates/obsidian/dot_claude/vault/decisions/adr-000-project-setup.md.tmpl +22 -0
- project_init/templates/obsidian/dot_claude/vault/dot_obsidian/README.md +31 -0
- project_init/templates/obsidian/dot_claude/vault/dot_obsidian/app.json +6 -0
- project_init/templates/obsidian/dot_claude/vault/dot_obsidian/community-plugins.json +1 -0
- project_init/templates/obsidian/dot_claude/vault/dot_obsidian/core-plugins.json +1 -0
- project_init/templates/obsidian/dot_claude/vault/log.md +6 -0
- project_init/templates/obsidian/dot_claude/vault/templates/decision.md +16 -0
- project_init/templates/obsidian/dot_claude/vault/templates/design-note.md +14 -0
- project_init/templates/obsidian/dot_claude/vault/templates/knowledge-note.md +12 -0
- project_init/templates/obsidian/dot_claude/vault/templates/session-note.md +16 -0
- project_init/templates/presets/obsidian-graphify.toml +16 -0
- project_init/templates/presets/obsidian-only.toml +14 -0
- project_init/upgrade.py +569 -0
- project_init-0.3.0.dist-info/METADATA +342 -0
- project_init-0.3.0.dist-info/RECORD +173 -0
- project_init-0.3.0.dist-info/WHEEL +4 -0
- project_init-0.3.0.dist-info/entry_points.txt +2 -0
- project_init-0.3.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github_workflow
|
|
3
|
+
description: Guides the agent through the full GitHub PR lifecycle — branch naming, push, review responses, and merge. Loaded automatically before any push, PR creation, review response, or merge action.
|
|
4
|
+
when_to_use: Load before any push, PR creation, PR review response, merge, or release action — including when lifecycle scripts fail and a git/gh fallback is needed.
|
|
5
|
+
user-invocable: false
|
|
6
|
+
effort: high
|
|
7
|
+
allowed-tools: Bash(git *) Bash(gh *) Bash(.claude/scripts/*) Read
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
Load this skill before any push, PR creation, review response, or merge action.
|
|
11
|
+
|
|
12
|
+
## Quick reference
|
|
13
|
+
|
|
14
|
+
| Step | Pattern |
|
|
15
|
+
|------|---------|
|
|
16
|
+
| Branch | `<type>/<PROJECT-KEY>-<n>-<kebab-slug>` e.g. `feat/PI-42-add-oauth` |
|
|
17
|
+
| PR title | `type(PROJECT-123): description` e.g. `feat(PI-42): Add OAuth login` |
|
|
18
|
+
| No-issue PR | `type: description` (no scope) e.g. `fix: Fix typo` |
|
|
19
|
+
| PR body | Must include `Closes #N` (skip for no-issue PRs) |
|
|
20
|
+
|
|
21
|
+
Commit messages use the same format (Conventional Commits). Legacy `[PROJECT-123][type]` is accepted by validators during transition but must not be emitted.
|
|
22
|
+
|
|
23
|
+
Types: `feat` `fix` `chore` `docs` `test`
|
|
24
|
+
|
|
25
|
+
## Standard lifecycle
|
|
26
|
+
|
|
27
|
+
1. **Start work** — use the `start_task` skill. It runs `start_issue.sh` which creates
|
|
28
|
+
the branch, pushes, and opens a draft PR.
|
|
29
|
+
For minor no-issue work, use `.claude/scripts/create_nojira_pr.sh <type> "description"`.
|
|
30
|
+
|
|
31
|
+
2. **Push during development:**
|
|
32
|
+
```bash
|
|
33
|
+
.claude/scripts/push_branch.sh
|
|
34
|
+
```
|
|
35
|
+
Never use bare `git push` — `push_branch.sh` retries transient GitHub errors.
|
|
36
|
+
|
|
37
|
+
3. **Finish — push, mark ready, and merge:**
|
|
38
|
+
```bash
|
|
39
|
+
.claude/scripts/finish_pr.sh [pr-number]
|
|
40
|
+
```
|
|
41
|
+
`finish_pr.sh` pushes, marks the draft ready, runs `monitor_pr.sh --merge`,
|
|
42
|
+
and handles review cycles automatically.
|
|
43
|
+
|
|
44
|
+
Or run steps individually:
|
|
45
|
+
```bash
|
|
46
|
+
.claude/scripts/push_branch.sh
|
|
47
|
+
.claude/scripts/promote_review.sh [pr-number]
|
|
48
|
+
.claude/scripts/monitor_pr.sh <pr-number> --merge
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Review cycle protocol
|
|
52
|
+
|
|
53
|
+
`monitor_pr.sh --merge` exits **1** for CI or merge failures and **2** when
|
|
54
|
+
`review/decision` fails while review cycles remain. Treat any non-zero exit as
|
|
55
|
+
unfinished work: inspect the printed failure, fix or retry, then rerun the
|
|
56
|
+
workflow. Do not report a PR as merged unless the script exits 0 after printing
|
|
57
|
+
the merged or auto-merge-enabled status.
|
|
58
|
+
|
|
59
|
+
1. Post a response for each review comment:
|
|
60
|
+
```
|
|
61
|
+
gh pr comment <pr-number> --body "**Review response:**
|
|
62
|
+
- [comment]: Fixing — <reason>
|
|
63
|
+
- [comment]: Not applying — <reason>"
|
|
64
|
+
```
|
|
65
|
+
2. Fix actionable code, then push: `.claude/scripts/push_branch.sh`
|
|
66
|
+
3. Re-run with the next cycle number:
|
|
67
|
+
```bash
|
|
68
|
+
.claude/scripts/monitor_pr.sh <pr-number> --merge --review-cycle <N>
|
|
69
|
+
```
|
|
70
|
+
4. After 1 review-fix cycle, the script auto force-merges with `--admin`.
|
|
71
|
+
|
|
72
|
+
**Before applying any comment:** read the current file state. Check whether the
|
|
73
|
+
comment is stale (already fixed), contradicts conventions, or is correct. Never
|
|
74
|
+
blindly apply a suggestion — post reasoning even when rejecting.
|
|
75
|
+
|
|
76
|
+
## Issue titles vs PR titles
|
|
77
|
+
|
|
78
|
+
- **Issue titles**: plain description only — type is carried by the label.
|
|
79
|
+
- **PR titles**: must include `[type]` — PR titles become merge commit messages in `git log`.
|
|
80
|
+
- **nojira**: for minor fixes without a tracking issue; no `Closes #N` needed.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: request_review
|
|
3
|
+
description: Mark the current draft PR ready for review
|
|
4
|
+
when_to_use: Use when a draft PR is ready to move from Draft to In Review status. Marks the PR ready and triggers board automation.
|
|
5
|
+
argument-hint: "[pr-number]"
|
|
6
|
+
allowed-tools: Bash Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Mark PR $ARGUMENTS (or current branch's PR if omitted) ready for review.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
1. **Promote to ready**:
|
|
14
|
+
```bash
|
|
15
|
+
.claude/scripts/promote_review.sh $ARGUMENTS
|
|
16
|
+
```
|
|
17
|
+
`board-automation.yml` moves the board card to **In Review** automatically.
|
|
18
|
+
|
|
19
|
+
2. **Next steps**: Reviewers will be pinged. When they request changes, they'll post comments on the PR.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review
|
|
3
|
+
description: Review staged or recent changes for bugs, style issues, and missed edge cases
|
|
4
|
+
when_to_use: Use when you want a code review of work — staged changes, a specific commit, or a commit range.
|
|
5
|
+
argument-hint: "[commit-range or file]"
|
|
6
|
+
allowed-tools: Bash Read Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Review the code changes specified by: $ARGUMENTS
|
|
10
|
+
|
|
11
|
+
If no argument given, review all staged changes (`git diff --cached`). If nothing is staged, review the last commit.
|
|
12
|
+
|
|
13
|
+
Focus on:
|
|
14
|
+
- **Bugs** — logic errors, off-by-ones, null/undefined risks
|
|
15
|
+
- **Security** — injection, auth issues, secret exposure
|
|
16
|
+
- **Style** — naming, dead code, overly complex logic
|
|
17
|
+
- **Edge cases** — empty inputs, concurrency, error paths
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: save_memory
|
|
3
|
+
description: Save a fact to project memory for future sessions
|
|
4
|
+
when_to_use: Use when you learn something important that should persist across conversations — a decision, a constraint, a pattern, or an external reference.
|
|
5
|
+
argument-hint: "<fact to remember>"
|
|
6
|
+
allowed-tools: Read Write Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Save this to project memory: $ARGUMENTS
|
|
10
|
+
|
|
11
|
+
Follow the memory convention in `.claude/memory/README.md`:
|
|
12
|
+
|
|
13
|
+
1. Decide the memory type (user / feedback / project / reference)
|
|
14
|
+
2. Choose a descriptive filename (e.g., `feedback_testing.md`, `project_deadline.md`)
|
|
15
|
+
3. Check if an existing memory file already covers this — update it instead of duplicating
|
|
16
|
+
4. Write the file with proper frontmatter (name, description, type)
|
|
17
|
+
5. Add a one-line entry to `.claude/memory/MEMORY.md`
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session_summary
|
|
3
|
+
description: Summarizes the current session and saves it to the vault. Use at the end of a work session to record completed work, decisions made, and open items for the next session.
|
|
4
|
+
when_to_use: Use when the user says "save the session", "wrap up", "summarize what we did", or at the end of a long work session.
|
|
5
|
+
effort: medium
|
|
6
|
+
allowed-tools: Bash(git *) Read Write Glob Grep
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Summarize this session and save it to the vault:
|
|
10
|
+
|
|
11
|
+
1. **Gather context**:
|
|
12
|
+
- Run `git log --since='2 hours ago' --oneline` for recent commits
|
|
13
|
+
- Run `git diff --stat` for uncommitted changes
|
|
14
|
+
- Review the conversation for key decisions and discoveries
|
|
15
|
+
|
|
16
|
+
2. **Write the summary** to `.claude/vault/sessions/` with today's date:
|
|
17
|
+
```
|
|
18
|
+
# Session YYYY-MM-DD (manual)
|
|
19
|
+
|
|
20
|
+
## What was done
|
|
21
|
+
- (bullet list of completed work)
|
|
22
|
+
|
|
23
|
+
## Decisions made
|
|
24
|
+
- (any architectural or approach decisions, with reasoning)
|
|
25
|
+
|
|
26
|
+
## Open items
|
|
27
|
+
- (anything left unfinished or discovered but not addressed)
|
|
28
|
+
|
|
29
|
+
## Notes
|
|
30
|
+
- (anything else worth remembering)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
3. **Update memory** if any reusable facts emerged (write to the project memory directory)
|
|
34
|
+
|
|
35
|
+
Keep the summary concise — a future agent should be able to skim it in 30 seconds.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: start_task
|
|
3
|
+
description: Creates a GitHub Issue, branch, and draft PR before implementation begins. Use before any non-trivial task to keep work traceable — one issue, one branch, one PR.
|
|
4
|
+
when_to_use: Use when the user says "start work on X", "create a ticket for Y", or "begin a new task". Do not use for trivial one-off changes that don't need tracking.
|
|
5
|
+
argument-hint: "[task title]"
|
|
6
|
+
allowed-tools: Bash(gh *) Bash(git *) Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Before starting any non-trivial task, create a GitHub Issue, a dedicated branch, and a draft PR. This keeps work traceable and every PR maps to exactly one issue.
|
|
10
|
+
|
|
11
|
+
## Mandatory scripts
|
|
12
|
+
|
|
13
|
+
| Action | Script | Never use |
|
|
14
|
+
|--------|--------|-----------|
|
|
15
|
+
| Start issue + branch + draft PR | `.claude/scripts/start_issue.sh <n> <type>` | bare `git checkout -b` + bare `gh pr create` |
|
|
16
|
+
| Push branch | `.claude/scripts/push_branch.sh` | bare `git push` |
|
|
17
|
+
| **Push + promote + merge (all-in-one)** | `.claude/scripts/finish_pr.sh <n>` | `gh pr ready`, bare `gh pr merge`, `gh pr checks --watch` |
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
1. **Clarify scope** — if $ARGUMENTS is empty or vague, ask the user for:
|
|
22
|
+
- Task title (one line, imperative: "Add X", "Fix Y", "Refactor Z")
|
|
23
|
+
- Work type: `feat` / `fix` / `chore` / `docs` / `test`
|
|
24
|
+
|
|
25
|
+
2. **Check for existing issue and PR** — run `gh issue list` and `gh pr list`. If an issue already exists, use its number. If a draft PR already exists for that issue, use it — do **not** create a second PR. Skip to step 5.
|
|
26
|
+
|
|
27
|
+
3. **Create the issue** — load the `create_issue` skill and follow it. It gathers priority, area, size, references, dependencies, and acceptance criteria before running:
|
|
28
|
+
```bash
|
|
29
|
+
ISSUE_NUMBER=$(.claude/scripts/create_issue.sh <type> "<title>" --priority <priority> --area "<area>" --size <size> --acceptance "<criterion>")
|
|
30
|
+
echo "Created issue #$ISSUE_NUMBER"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
4. **Start work** — create the branch, push, and open a draft PR:
|
|
34
|
+
```bash
|
|
35
|
+
.claude/scripts/start_issue.sh <issue-number> <type>
|
|
36
|
+
```
|
|
37
|
+
This derives the branch name (`<issue_type>/<project_abbr>-<issue_number>-<slug>`) from the issue title, pushes it, and opens a draft PR with the correct `[PROJECT-123][type]` title and `Closes #n` body.
|
|
38
|
+
|
|
39
|
+
5. **Proceed** — only begin implementation after the scripts have run successfully.
|
|
40
|
+
|
|
41
|
+
6. **When ready to merge** — load the `github_workflow` skill for the
|
|
42
|
+
full push → promote → monitor/merge lifecycle and review-cycle protocol.
|
|
43
|
+
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Every non-trivial task must have a GitHub Issue, a branch, and a draft PR — all before the first line of implementation code.
|
|
47
|
+
- One issue → one branch → one PR.
|
|
48
|
+
- `board-automation.yml` moves the board card to **In Progress** automatically when the PR is opened. No manual board move needed.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: status
|
|
3
|
+
description: Show project status — git state, recent commits, open tasks, and memory summary
|
|
4
|
+
when_to_use: Use when you want a quick snapshot of the project — current branch, uncommitted changes, recent work, active memory, and open TODOs.
|
|
5
|
+
allowed-tools: Bash Read Grep Glob
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Give me a concise project status report:
|
|
9
|
+
|
|
10
|
+
1. **Git state** — current branch, uncommitted changes, commits ahead/behind remote
|
|
11
|
+
2. **Recent work** — last 5 commits (one line each)
|
|
12
|
+
3. **Memory** — read `.claude/memory/MEMORY.md` and list the key facts
|
|
13
|
+
4. **Open items** — scan for TODO/FIXME/HACK in the codebase (top 10)
|
|
14
|
+
|
|
15
|
+
Keep the report under 30 lines. Use markdown formatting.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# github_command_guard.sh — delegate to dag_workflow.py guard.
|
|
3
|
+
# PreToolUse hook on Bash. Receives tool input JSON on stdin.
|
|
4
|
+
#
|
|
5
|
+
# All command-pattern matching, redirect rules, and DAG prerequisite checks
|
|
6
|
+
# live in dag_workflow.py next to this script. Adding a new banned command
|
|
7
|
+
# means editing COMMAND_RULES there, not this file.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
exec python3 "$(dirname "$0")/dag_workflow.py" guard
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post_edit_lint.sh — runs linter on edited files after Edit/Write/MultiEdit.
|
|
3
|
+
# Invoked by Claude Code's PostToolUse hook.
|
|
4
|
+
# Outputs additionalContext JSON if unfixable lint errors remain so Claude
|
|
5
|
+
# self-corrects in the same turn.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
10
|
+
INPUT=$(cat)
|
|
11
|
+
|
|
12
|
+
FILE=$(printf '%s' "$INPUT" | python3 -c "
|
|
13
|
+
import json, sys
|
|
14
|
+
try:
|
|
15
|
+
d = json.load(sys.stdin)
|
|
16
|
+
except Exception:
|
|
17
|
+
sys.exit(0)
|
|
18
|
+
ti = d.get('tool_input', {}) or {}
|
|
19
|
+
print(ti.get('file_path') or ti.get('filePath') or '')
|
|
20
|
+
" 2>/dev/null || true)
|
|
21
|
+
|
|
22
|
+
[ -z "$FILE" ] && exit 0
|
|
23
|
+
[ ! -f "$FILE" ] && exit 0
|
|
24
|
+
|
|
25
|
+
ERRORS=""
|
|
26
|
+
|
|
27
|
+
case "$FILE" in
|
|
28
|
+
*.py)
|
|
29
|
+
# Prefer 'uv run ruff' inside a uv-managed project so the hook uses
|
|
30
|
+
# the same ruff the project itself uses; fall back to a system ruff.
|
|
31
|
+
if { [ -f "$ROOT/pyproject.toml" ] || [ -f "$ROOT/uv.lock" ]; } && command -v uv &>/dev/null; then
|
|
32
|
+
uv run ruff check --fix --quiet "$FILE" 2>/dev/null || true
|
|
33
|
+
uv run ruff format --quiet "$FILE" 2>/dev/null || true
|
|
34
|
+
ERRORS=$(uv run ruff check --quiet "$FILE" 2>&1 || true)
|
|
35
|
+
elif command -v ruff &>/dev/null; then
|
|
36
|
+
ruff check --fix --quiet "$FILE" 2>/dev/null || true
|
|
37
|
+
ruff format --quiet "$FILE" 2>/dev/null || true
|
|
38
|
+
ERRORS=$(ruff check --quiet "$FILE" 2>&1 || true)
|
|
39
|
+
fi
|
|
40
|
+
;;
|
|
41
|
+
*.js|*.ts|*.jsx|*.tsx)
|
|
42
|
+
# Use bunx (bun's package runner) — consistent with project convention (PI-15).
|
|
43
|
+
if command -v bunx &>/dev/null; then
|
|
44
|
+
bunx eslint --fix --quiet "$FILE" 2>/dev/null || true
|
|
45
|
+
ERRORS=$(bunx eslint --quiet "$FILE" 2>&1 || true)
|
|
46
|
+
fi
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
|
|
50
|
+
if [ -n "$ERRORS" ]; then
|
|
51
|
+
python3 -c "
|
|
52
|
+
import json, sys
|
|
53
|
+
file, errors = sys.argv[1], sys.argv[2]
|
|
54
|
+
print(json.dumps({'additionalContext': f'Lint errors in {file} (after auto-fix attempt) — please fix before continuing:\n{errors}'}))
|
|
55
|
+
" "$FILE" "$ERRORS"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exit 0
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre_commit_gate.sh — blocks git commit if staged files fail linting.
|
|
3
|
+
# PreToolUse hook on Bash. Receives tool input JSON on stdin.
|
|
4
|
+
# Auto-fixes what it can and re-stages; blocks only if errors remain.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
CMD=$(printf '%s' "$INPUT" | python3 -c "
|
|
11
|
+
import json, sys
|
|
12
|
+
try:
|
|
13
|
+
d = json.load(sys.stdin)
|
|
14
|
+
except Exception:
|
|
15
|
+
sys.exit(0)
|
|
16
|
+
print((d.get('tool_input', {}) or {}).get('command', '') or '')
|
|
17
|
+
" 2>/dev/null || true)
|
|
18
|
+
|
|
19
|
+
# Only intercept git commit commands
|
|
20
|
+
case "$CMD" in
|
|
21
|
+
*"git commit"*) ;;
|
|
22
|
+
*) exit 0 ;;
|
|
23
|
+
esac
|
|
24
|
+
|
|
25
|
+
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
26
|
+
ERRORS=""
|
|
27
|
+
|
|
28
|
+
# Lint and auto-fix staged Python files. Prefer 'uv run ruff' inside a
|
|
29
|
+
# uv-managed project so the hook uses the same ruff the project itself uses;
|
|
30
|
+
# fall back to a system ruff binary.
|
|
31
|
+
mapfile -t STAGED_PY < <(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep '\.py$' || true)
|
|
32
|
+
if [ "${#STAGED_PY[@]}" -gt 0 ]; then
|
|
33
|
+
LINT_OUT=""
|
|
34
|
+
if { [ -f "$ROOT/pyproject.toml" ] || [ -f "$ROOT/uv.lock" ]; } && command -v uv &>/dev/null; then
|
|
35
|
+
uv run ruff check --fix --quiet "${STAGED_PY[@]}" 2>/dev/null || true
|
|
36
|
+
uv run ruff format --quiet "${STAGED_PY[@]}" 2>/dev/null || true
|
|
37
|
+
LINT_OUT=$(uv run ruff check --quiet "${STAGED_PY[@]}" 2>&1 || true)
|
|
38
|
+
elif command -v ruff &>/dev/null; then
|
|
39
|
+
ruff check --fix --quiet "${STAGED_PY[@]}" 2>/dev/null || true
|
|
40
|
+
ruff format --quiet "${STAGED_PY[@]}" 2>/dev/null || true
|
|
41
|
+
LINT_OUT=$(ruff check --quiet "${STAGED_PY[@]}" 2>&1 || true)
|
|
42
|
+
fi
|
|
43
|
+
if [ -n "$LINT_OUT" ]; then
|
|
44
|
+
ERRORS="${ERRORS}Python lint errors:\n${LINT_OUT}\n"
|
|
45
|
+
fi
|
|
46
|
+
# Re-stage auto-fixed files so the commit includes the fixes
|
|
47
|
+
git add "${STAGED_PY[@]}" 2>/dev/null || true
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Lint and auto-fix staged JS/TS files
|
|
51
|
+
# Use bunx (bun's package runner) — consistent with project convention (PI-15).
|
|
52
|
+
mapfile -t STAGED_JS < <(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '\.(js|ts|jsx|tsx)$' || true)
|
|
53
|
+
if [ "${#STAGED_JS[@]}" -gt 0 ] && command -v bunx &>/dev/null; then
|
|
54
|
+
bunx eslint --fix --quiet "${STAGED_JS[@]}" 2>/dev/null || true
|
|
55
|
+
LINT_OUT=$(bunx eslint --quiet "${STAGED_JS[@]}" 2>&1 || true)
|
|
56
|
+
if [ -n "$LINT_OUT" ]; then
|
|
57
|
+
ERRORS="${ERRORS}JS/TS lint errors:\n${LINT_OUT}\n"
|
|
58
|
+
fi
|
|
59
|
+
git add "${STAGED_JS[@]}" 2>/dev/null || true
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# PI-139: when the project ships a justfile with a lint recipe and just is
|
|
63
|
+
# installed, additionally gate on `just lint` — the same definition of "lint
|
|
64
|
+
# passes" CI and every agent use. Per-file findings above are preserved: the
|
|
65
|
+
# recipe is language-specific, so in a mixed repo it may not cover everything
|
|
66
|
+
# the per-file checks caught (a passing recipe must not wash those out).
|
|
67
|
+
if command -v just >/dev/null 2>&1 && [ -f "$ROOT/justfile" ] \
|
|
68
|
+
&& (cd "$ROOT" && just --show lint >/dev/null 2>&1); then
|
|
69
|
+
JUST_OUT=$(cd "$ROOT" && just lint 2>&1) || ERRORS="${ERRORS}Lint errors (just lint):\n${JUST_OUT}\n"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
if [ -n "$ERRORS" ]; then
|
|
73
|
+
python3 -c "
|
|
74
|
+
import json, sys
|
|
75
|
+
errors = sys.argv[1].replace('\\\\n', '\n')
|
|
76
|
+
msg = 'Pre-commit lint check failed. Fix these errors before committing:\n\n' + errors
|
|
77
|
+
print(json.dumps({'decision': 'block', 'reason': msg}))
|
|
78
|
+
" "$ERRORS"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
exit 0
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Prod-safety guard (PI-168, ADR-012): deny destructive infra/DB commands.
|
|
2
|
+
|
|
3
|
+
PreToolUse hook on Bash. Deterministic deny-table — no LLM, no network.
|
|
4
|
+
Destructive operations that bypass the git/CI boundary (cloud deletes,
|
|
5
|
+
DROP DATABASE, terraform destroy, …) get:
|
|
6
|
+
|
|
7
|
+
- ``ask`` in interactive sessions — a human confirms or rejects;
|
|
8
|
+
- ``block`` in fully autonomous sessions (``bypassPermissions``) — there is
|
|
9
|
+
no human to ask, so the command is blocked outright.
|
|
10
|
+
|
|
11
|
+
Escape hatch: ``safety.allow`` in ``.claude/config.yaml`` holds a JSON list
|
|
12
|
+
of regex patterns; a command matching any of them is never flagged. Use it
|
|
13
|
+
for known-safe contexts (e.g. a dev-cluster kubectl context).
|
|
14
|
+
|
|
15
|
+
This is a guardrail, not the security boundary (ADR-007/ADR-012): a
|
|
16
|
+
sufficiently creative command can evade a deny-list. The guarantee comes
|
|
17
|
+
from credential separation — agent sessions must never hold production
|
|
18
|
+
credentials (see .claude/docs/guides/secrets.md).
|
|
19
|
+
|
|
20
|
+
Fail-open by design: any internal error lets the command proceed.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
import re
|
|
27
|
+
import sys
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
# (pattern, label) — matched against the full command string. ``_SEG``
|
|
31
|
+
# tolerates global flags between the CLI name and the destructive verb
|
|
32
|
+
# (e.g. `kubectl --context prod delete …`) while stopping at pipeline and
|
|
33
|
+
# command separators; the cost is rare false positives on odd resource
|
|
34
|
+
# names, which the ask/allowlist paths absorb cheaply.
|
|
35
|
+
_SEG = r"[^|;&]*?"
|
|
36
|
+
DENY_RULES: list[tuple[re.Pattern[str], str]] = [
|
|
37
|
+
(re.compile(r"\bterraform\s+(destroy|apply\s+.*-destroy)\b"), "terraform destroy"),
|
|
38
|
+
(re.compile(rf"\bkubectl\b{_SEG}\bdelete\b"), "kubectl delete"),
|
|
39
|
+
(re.compile(rf"\bhelm\b{_SEG}\b(uninstall|delete)\b"), "helm uninstall"),
|
|
40
|
+
(re.compile(rf"\baws\b{_SEG}\b(delete|terminate|remove)\S*\b"), "aws delete/terminate"),
|
|
41
|
+
(re.compile(rf"\baws\b{_SEG}\bs3\s+(rb\b|rm\b{_SEG}--recursive)"), "aws s3 bucket/recursive removal"),
|
|
42
|
+
(re.compile(rf"\bgcloud\b{_SEG}\bdelete\b"), "gcloud delete"),
|
|
43
|
+
(re.compile(rf"\baz\b{_SEG}\bdelete\b"), "az delete"),
|
|
44
|
+
(re.compile(r"\bdrop\s+(table|database|schema)\b", re.IGNORECASE), "SQL DROP"),
|
|
45
|
+
(re.compile(r"\btruncate\s+table\b", re.IGNORECASE), "SQL TRUNCATE"),
|
|
46
|
+
(re.compile(r"\brm\s+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)[a-zA-Z]*\s+(/(?!tmp\b)|~)"),
|
|
47
|
+
"recursive force-remove outside the project"),
|
|
48
|
+
(re.compile(r"\bgh\s+repo\s+delete\b"), "gh repo delete"),
|
|
49
|
+
(re.compile(r"\bdocker\s+(volume\s+prune|system\s+prune)\b"), "docker prune"),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Fully autonomous mode: no human is watching the prompt, so "ask" is
|
|
53
|
+
# meaningless — block outright. Other modes (default, plan, acceptEdits)
|
|
54
|
+
# still surface an interactive permission prompt for Bash.
|
|
55
|
+
_AUTONOMOUS_MODES = {"bypassPermissions", "dangerouslySkipPermissions"}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _find_config(start: Path) -> Path | None:
|
|
59
|
+
"""Walk up from *start* to the project's .claude/config.yaml, if any."""
|
|
60
|
+
for candidate in (start, *start.parents):
|
|
61
|
+
config = candidate / ".claude" / "config.yaml"
|
|
62
|
+
if config.is_file():
|
|
63
|
+
return config
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _allow_patterns(root: Path) -> list[re.Pattern[str]]:
|
|
68
|
+
"""Read the safety.allow JSON list from .claude/config.yaml (fail-open).
|
|
69
|
+
|
|
70
|
+
*root* is the Bash tool's cwd, which may be a subdirectory after `cd` —
|
|
71
|
+
the config is located by walking up the tree.
|
|
72
|
+
"""
|
|
73
|
+
config = _find_config(root)
|
|
74
|
+
if config is None:
|
|
75
|
+
return []
|
|
76
|
+
try:
|
|
77
|
+
in_safety = False
|
|
78
|
+
for line in config.read_text(encoding="utf-8").splitlines():
|
|
79
|
+
if line.startswith("safety:"):
|
|
80
|
+
in_safety = True
|
|
81
|
+
continue
|
|
82
|
+
if in_safety:
|
|
83
|
+
if line.strip() and not line.startswith(" "):
|
|
84
|
+
break
|
|
85
|
+
stripped = line.strip()
|
|
86
|
+
if stripped.startswith("allow:"):
|
|
87
|
+
raw = stripped.split(":", 1)[1].strip()
|
|
88
|
+
return [re.compile(p) for p in json.loads(raw)]
|
|
89
|
+
except (OSError, json.JSONDecodeError, re.error):
|
|
90
|
+
pass
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def evaluate(command: str, permission_mode: str, allow: list[re.Pattern[str]]) -> dict | None:
|
|
95
|
+
"""Return the hook verdict for *command*, or None to let it through."""
|
|
96
|
+
if any(p.search(command) for p in allow):
|
|
97
|
+
return None
|
|
98
|
+
for pattern, label in DENY_RULES:
|
|
99
|
+
if pattern.search(command):
|
|
100
|
+
reason = (
|
|
101
|
+
f"prod_guard: '{label}' is a destructive operation. "
|
|
102
|
+
"If this is intentional and safe, add a matching regex to "
|
|
103
|
+
"safety.allow in .claude/config.yaml, or run it yourself. "
|
|
104
|
+
"(Guardrail only — real protection is credential separation, "
|
|
105
|
+
"see .claude/docs/guides/secrets.md.)"
|
|
106
|
+
)
|
|
107
|
+
if permission_mode in _AUTONOMOUS_MODES:
|
|
108
|
+
return {"decision": "block", "reason": reason}
|
|
109
|
+
return {
|
|
110
|
+
"hookSpecificOutput": {
|
|
111
|
+
"hookEventName": "PreToolUse",
|
|
112
|
+
"permissionDecision": "ask",
|
|
113
|
+
"permissionDecisionReason": reason,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main() -> int:
|
|
120
|
+
"""Read the PreToolUse payload from stdin; print a verdict if any."""
|
|
121
|
+
try:
|
|
122
|
+
payload = json.loads(sys.stdin.read() or "{}")
|
|
123
|
+
except json.JSONDecodeError:
|
|
124
|
+
return 0
|
|
125
|
+
command = ((payload.get("tool_input") or {}).get("command") or "").strip()
|
|
126
|
+
if not command:
|
|
127
|
+
return 0
|
|
128
|
+
mode = payload.get("permission_mode") or payload.get("permissionMode") or ""
|
|
129
|
+
root = Path(payload.get("cwd") or ".")
|
|
130
|
+
try:
|
|
131
|
+
verdict = evaluate(command, mode, _allow_patterns(root))
|
|
132
|
+
except Exception: # noqa: BLE001 — guardrail must never break the session
|
|
133
|
+
return 0
|
|
134
|
+
if verdict is not None:
|
|
135
|
+
sys.stdout.write(json.dumps(verdict))
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
sys.exit(main())
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SessionStart bootstrap (PI-146): make a fresh/remote container immediately
|
|
3
|
+
# usable — sync dependencies so tests and linters run instead of failing on
|
|
4
|
+
# a missing venv. Fast on warm environments: a content stamp of the
|
|
5
|
+
# dependency manifests short-circuits before any tool runs.
|
|
6
|
+
set -uo pipefail # not -e: a failed bootstrap must never break the session
|
|
7
|
+
|
|
8
|
+
ROOT="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
|
|
9
|
+
cd "$ROOT" || exit 0
|
|
10
|
+
STAMP=".claude/.session_setup_stamp"
|
|
11
|
+
LOG=".claude/logs/session_setup.log"
|
|
12
|
+
mkdir -p .claude/logs
|
|
13
|
+
|
|
14
|
+
fingerprint() {
|
|
15
|
+
cat pyproject.toml uv.lock package.json bun.lock bun.lockb go.mod go.sum 2>/dev/null \
|
|
16
|
+
| sha256sum | cut -d' ' -f1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
# The stamp alone is not enough: an ephemeral container can restore the repo
|
|
20
|
+
# (stamp included) without the synced environment, so check for it too.
|
|
21
|
+
env_present() {
|
|
22
|
+
{ [ ! -f pyproject.toml ] || [ -d .venv ]; } \
|
|
23
|
+
&& { [ ! -f package.json ] || [ -d node_modules ]; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
CURRENT="$(fingerprint)"
|
|
27
|
+
if [ -f "$STAMP" ] && [ "$(cat "$STAMP" 2>/dev/null)" = "$CURRENT" ] && env_present; then
|
|
28
|
+
exit 0 # warm environment — nothing to do
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
bootstrap() {
|
|
32
|
+
# Prefer the justfile setup recipe — the canonical bootstrap entry point.
|
|
33
|
+
if command -v just >/dev/null 2>&1 && [ -f justfile ] \
|
|
34
|
+
&& just --show setup >/dev/null 2>&1; then
|
|
35
|
+
just setup
|
|
36
|
+
elif [ -f pyproject.toml ] && command -v uv >/dev/null 2>&1; then
|
|
37
|
+
uv sync --extra dev 2>/dev/null || uv sync
|
|
38
|
+
elif [ -f package.json ] && command -v bun >/dev/null 2>&1; then
|
|
39
|
+
bun install
|
|
40
|
+
elif [ -f go.mod ] && command -v go >/dev/null 2>&1; then
|
|
41
|
+
go mod download
|
|
42
|
+
else
|
|
43
|
+
return 2 # nothing recognized to set up
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
bootstrap >"$LOG" 2>&1
|
|
48
|
+
case $? in
|
|
49
|
+
0)
|
|
50
|
+
echo "$CURRENT" >"$STAMP"
|
|
51
|
+
echo "session_setup: dependencies synced for fresh environment"
|
|
52
|
+
;;
|
|
53
|
+
2)
|
|
54
|
+
# No manifest/tool to bootstrap: stamp silently so later sessions skip
|
|
55
|
+
# the probe, but claim nothing — no sync happened.
|
|
56
|
+
echo "$CURRENT" >"$STAMP"
|
|
57
|
+
;;
|
|
58
|
+
*)
|
|
59
|
+
echo "session_setup: bootstrap failed — see $LOG" >&2
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
exit 0
|