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,90 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
description: Missing or insufficient test coverage
|
|
3
|
+
labels: [test]
|
|
4
|
+
body:
|
|
5
|
+
- type: dropdown
|
|
6
|
+
id: priority
|
|
7
|
+
attributes:
|
|
8
|
+
label: Priority
|
|
9
|
+
options: [high, medium, low]
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: input
|
|
13
|
+
id: area
|
|
14
|
+
attributes:
|
|
15
|
+
label: Area
|
|
16
|
+
placeholder: frontend, backend, docs, devops, product
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: dropdown
|
|
20
|
+
id: size
|
|
21
|
+
attributes:
|
|
22
|
+
label: Size
|
|
23
|
+
options: [XS, S, M, L, XL]
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: textarea
|
|
27
|
+
id: behavior
|
|
28
|
+
attributes:
|
|
29
|
+
label: What behavior needs coverage?
|
|
30
|
+
description: Describe the behavior, template, script, or workflow that needs tests.
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: risk
|
|
35
|
+
attributes:
|
|
36
|
+
label: Risk
|
|
37
|
+
description: What regression or bug could slip through without this test?
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
- type: textarea
|
|
41
|
+
id: approach
|
|
42
|
+
attributes:
|
|
43
|
+
label: Suggested approach
|
|
44
|
+
placeholder: "Unit test, scaffold-output diff, CLI test, workflow validation..."
|
|
45
|
+
- type: textarea
|
|
46
|
+
id: references
|
|
47
|
+
attributes:
|
|
48
|
+
label: References
|
|
49
|
+
description: Related issues, PRs, ADRs, docs, designs, or external links.
|
|
50
|
+
- type: textarea
|
|
51
|
+
id: dependencies
|
|
52
|
+
attributes:
|
|
53
|
+
label: Dependencies
|
|
54
|
+
description: Blocked-by, parent, or follow-up relationships.
|
|
55
|
+
- type: textarea
|
|
56
|
+
id: acceptance
|
|
57
|
+
attributes:
|
|
58
|
+
label: Acceptance criteria
|
|
59
|
+
placeholder: |
|
|
60
|
+
- [ ] ...
|
|
61
|
+
validations:
|
|
62
|
+
required: true
|
|
63
|
+
- type: dropdown
|
|
64
|
+
id: agent_ready
|
|
65
|
+
attributes:
|
|
66
|
+
label: Agent ready
|
|
67
|
+
description: Can an AI agent implement this without human clarification?
|
|
68
|
+
options: ["Yes", "No"]
|
|
69
|
+
default: 1
|
|
70
|
+
- type: dropdown
|
|
71
|
+
id: confidence
|
|
72
|
+
attributes:
|
|
73
|
+
label: Confidence
|
|
74
|
+
description: How well-understood is the scope and approach?
|
|
75
|
+
options: [high, medium, low, unknown]
|
|
76
|
+
default: 3
|
|
77
|
+
- type: textarea
|
|
78
|
+
id: ready
|
|
79
|
+
attributes:
|
|
80
|
+
label: Definition of Ready
|
|
81
|
+
placeholder: What must be true before implementation starts?
|
|
82
|
+
validations:
|
|
83
|
+
required: true
|
|
84
|
+
- type: textarea
|
|
85
|
+
id: done
|
|
86
|
+
attributes:
|
|
87
|
+
label: Definition of Done
|
|
88
|
+
placeholder: What must be true before this can close?
|
|
89
|
+
validations:
|
|
90
|
+
required: true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# GitHub Copilot Instructions — {{project_name}}
|
|
2
|
+
|
|
3
|
+
Canonical agent rules: [AGENTS.md](../AGENTS.md) | Workflow: [.claude/project-init.md](../.claude/project-init.md)
|
|
4
|
+
|
|
5
|
+
## Issue & project tracking
|
|
6
|
+
|
|
7
|
+
- Tracking system: **GitHub Projects** (kanban board) + **GitHub Issues** (tickets)
|
|
8
|
+
- Create issues: load the `create_issue` skill and use `.claude/scripts/create_issue.sh`; it handles metadata gathering, priority labels, and acceptance criteria
|
|
9
|
+
- Board cards move automatically via `board-automation.yml` — no manual updates needed
|
|
10
|
+
- Use a dedicated branch for each issue; format it as `<issue_type>/<project_abbr>-<issue_number>-<slug>`, e.g. `feat/PI-42-add-auth`; no direct commits to `main`
|
|
11
|
+
- Issue and PR names use a project key: `<PROJECT-KEY>-<issue-number>`, e.g. `PI-42`
|
|
12
|
+
- PR titles must follow Conventional Commits with the issue key as scope: `type(PROJECT-123): description` where type ∈ {feat, fix, chore, docs, test}, e.g. `feat(PI-42): Add OAuth login`
|
|
13
|
+
- For small no-issue PRs omit the scope: `type: description`, e.g. `fix: Fix typo`
|
|
14
|
+
- PR body must still include the GitHub numeric reference `Closes #N` — auto-closes issue and moves board card to Done on merge (skip for no-issue PRs)
|
|
15
|
+
- Draft PRs are opened immediately when work starts, not when done
|
|
16
|
+
- When asked to push/finish a PR, load the `github_workflow` skill for the full lifecycle and review-cycle protocol.
|
|
17
|
+
- No direct commits to `main`
|
|
18
|
+
|
|
19
|
+
## Key rules
|
|
20
|
+
|
|
21
|
+
- Write failing tests before implementation (TDD)
|
|
22
|
+
- No secrets in code — use `os.environ` or `.env` (gitignored)
|
|
23
|
+
{{#if lint_command}}- `just lint` must pass before closing a task (`just --list` shows all recipes)
|
|
24
|
+
{{/if}}- Prefer deterministic bash/python over LLM calls for hooks and scripts
|
|
25
|
+
- Permanent decisions → `.claude/docs/adr/`; exploratory notes → `.claude/vault/`; reusable facts → `.claude/memory/`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# commit-msg hook: validate commit message format before push.
|
|
3
|
+
# Install: .claude/scripts/install-hooks.sh (runs automatically)
|
|
4
|
+
#
|
|
5
|
+
# Canonical format (Conventional Commits, ADR-006):
|
|
6
|
+
# feat(PI-42): Add OAuth login (issue-linked — key as scope)
|
|
7
|
+
# fix: Fix typo in README (no linked issue)
|
|
8
|
+
# feat(PI-9)!: Drop python 3.10 (breaking change)
|
|
9
|
+
#
|
|
10
|
+
# Legacy format still accepted during transition:
|
|
11
|
+
# [PI-42][feat] Add OAuth login
|
|
12
|
+
# [nojira][fix] Fix typo in README
|
|
13
|
+
#
|
|
14
|
+
# Any uppercase project key works: PI-, APP-, API-, WEB-, etc.
|
|
15
|
+
# Types: feat fix chore docs test
|
|
16
|
+
|
|
17
|
+
MSG=$(cat "$1")
|
|
18
|
+
|
|
19
|
+
# Allow merge commits, fixups, reverts, and WIP
|
|
20
|
+
if echo "$MSG" | grep -qE '^(Merge|Revert|fixup!|squash!|WIP)'; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Allow GitHub co-author trailers (multi-line commits)
|
|
25
|
+
FIRST_LINE=$(echo "$MSG" | head -1)
|
|
26
|
+
|
|
27
|
+
# Conventional Commits with optional issue-key scope (canonical)
|
|
28
|
+
if echo "$FIRST_LINE" | grep -qE '^(feat|fix|chore|docs|test)(\([A-Z][A-Z0-9]{1,9}-[0-9]+\))?!?: .+'; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Legacy bracket format (transition)
|
|
33
|
+
if echo "$FIRST_LINE" | grep -qE '^\[([A-Z][A-Z0-9]{1,9}-[0-9]+|nojira)\]\[(feat|fix|chore|docs|test)\] .+'; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
echo "" >&2
|
|
38
|
+
echo "ERROR: commit message format invalid." >&2
|
|
39
|
+
echo "" >&2
|
|
40
|
+
echo "Required: <type>(<PROJECT>-<n>): Short description" >&2
|
|
41
|
+
echo " or: <type>: Short description (no linked issue)" >&2
|
|
42
|
+
echo "" >&2
|
|
43
|
+
echo "Types: feat fix chore docs test" >&2
|
|
44
|
+
echo "" >&2
|
|
45
|
+
echo "Examples:" >&2
|
|
46
|
+
echo " feat(PI-42): Add OAuth login" >&2
|
|
47
|
+
echo " fix(APP-99): Handle null pointer in auth" >&2
|
|
48
|
+
echo " chore: Bump dev dependency" >&2
|
|
49
|
+
echo "" >&2
|
|
50
|
+
echo "Your message: $FIRST_LINE" >&2
|
|
51
|
+
echo "" >&2
|
|
52
|
+
exit 1
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-commit hook: scan staged changes for secrets with gitleaks (ADR-007).
|
|
3
|
+
# Install: .claude/scripts/install_hooks.sh
|
|
4
|
+
#
|
|
5
|
+
# Fail-open by design: if gitleaks is not installed the hook warns and lets
|
|
6
|
+
# the commit through — the CI secret-scan job is the hard backstop that
|
|
7
|
+
# scans full history and blocks the merge.
|
|
8
|
+
|
|
9
|
+
if ! command -v gitleaks >/dev/null 2>&1; then
|
|
10
|
+
echo "WARNING: gitleaks not installed — skipping local secret scan." >&2
|
|
11
|
+
echo " CI will still scan; install for fast local feedback:" >&2
|
|
12
|
+
echo " https://github.com/gitleaks/gitleaks#installing" >&2
|
|
13
|
+
exit 0
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
exec gitleaks git --pre-commit --staged --redact --no-banner --verbose
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Pre-push hook: lifecycle gate (ADR-007).
|
|
3
|
+
# 1. Prevent direct pushes to main/master.
|
|
4
|
+
# 2. Enforce a branch type prefix — (feat|fix|chore|docs|test)/<slug> —
|
|
5
|
+
# the same rule as dag_workflow.py's _BRANCH_RE. Full issue-key
|
|
6
|
+
# validation (<type>/<KEY>-<n>-<slug>) runs in CI via validate-pr.yml,
|
|
7
|
+
# where the linked issue can actually be checked.
|
|
8
|
+
# Install: .claude/scripts/install_hooks.sh
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
ZERO_SHA="0000000000000000000000000000000000000000"
|
|
13
|
+
|
|
14
|
+
while read local_ref local_sha remote_ref remote_sha; do
|
|
15
|
+
# Branch deletions are always allowed (cleaning up misnamed branches).
|
|
16
|
+
if [[ "$local_sha" == "$ZERO_SHA" ]]; then
|
|
17
|
+
continue
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Only gate branch refs — tags pass through.
|
|
21
|
+
if [[ "$remote_ref" != refs/heads/* ]]; then
|
|
22
|
+
continue
|
|
23
|
+
fi
|
|
24
|
+
branch_name="${remote_ref#refs/heads/}"
|
|
25
|
+
|
|
26
|
+
if [[ "$branch_name" == "main" || "$branch_name" == "master" ]]; then
|
|
27
|
+
echo "❌ ERROR: Direct push to main/master is not allowed"
|
|
28
|
+
echo ""
|
|
29
|
+
echo "To contribute:"
|
|
30
|
+
echo " 1. Create a new branch with the project key: git checkout -b <issue_type>/<project_abbr>-<issue_number>-<slug>"
|
|
31
|
+
echo " 2. Make your changes and push: .claude/scripts/push_branch.sh"
|
|
32
|
+
echo " 3. Open a PR on GitHub"
|
|
33
|
+
echo ""
|
|
34
|
+
echo "For more details, see .claude/project-init.md"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if ! echo "$branch_name" | grep -qE '^(feat|fix|chore|docs|test)/[A-Za-z0-9._/-]+$'; then
|
|
39
|
+
echo "❌ ERROR: branch '$branch_name' does not follow the lifecycle naming convention"
|
|
40
|
+
echo ""
|
|
41
|
+
echo "Enforced here: a type prefix — (feat|fix|chore|docs|test)/<slug>"
|
|
42
|
+
echo "Full convention (issue key validated in CI): <issue_type>/<project_abbr>-<issue_number>-<slug>"
|
|
43
|
+
echo " Issue-linked: feat/PI-42-add-oauth-login"
|
|
44
|
+
echo " No issue: chore/nojira-bump-dev-dependency"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "Use .claude/scripts/start_issue.sh or create_nojira_pr.sh to create branches."
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
exit 0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!-- PR Title Format: type(PROJECT-123): description OR type: description (no linked issue) -->
|
|
2
|
+
<!-- Valid types: feat | fix | chore | docs | test -->
|
|
3
|
+
|
|
4
|
+
## Summary
|
|
5
|
+
|
|
6
|
+
Closes #<!-- issue number -->
|
|
7
|
+
|
|
8
|
+
<!-- 2-4 bullet points describing what changed and why -->
|
|
9
|
+
|
|
10
|
+
-
|
|
11
|
+
|
|
12
|
+
## Changes
|
|
13
|
+
|
|
14
|
+
<!-- List the key files/components changed -->
|
|
15
|
+
|
|
16
|
+
-
|
|
17
|
+
|
|
18
|
+
## Test plan
|
|
19
|
+
|
|
20
|
+
- [ ] Tests pass
|
|
21
|
+
- [ ] Lint passes
|
|
22
|
+
- [ ] Manual smoke test (describe what you clicked/ran)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
name: Board automation
|
|
2
|
+
|
|
3
|
+
# Syncs issue and PR lifecycle state to GitHub Projects v2.
|
|
4
|
+
#
|
|
5
|
+
# Setup:
|
|
6
|
+
# 1. Create a PAT with Projects access.
|
|
7
|
+
# 2. Store it as PROJECT_TOKEN.
|
|
8
|
+
# 3. Set PROJECT_NUMBER to the target project number.
|
|
9
|
+
# 4. Optional project fields: Status, Priority, Area, Size.
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
issues:
|
|
13
|
+
types: [opened, edited, reopened, labeled, unlabeled]
|
|
14
|
+
pull_request:
|
|
15
|
+
types: [opened, ready_for_review, closed]
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
issues: read
|
|
20
|
+
pull-requests: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
board-sync:
|
|
24
|
+
runs-on: ubuntu-24.04
|
|
25
|
+
env:
|
|
26
|
+
GH_TOKEN: ${{ secrets.PROJECT_TOKEN }}
|
|
27
|
+
OWNER: ${{ github.repository_owner }}
|
|
28
|
+
REPO_NAME: ${{ github.event.repository.name }}
|
|
29
|
+
PROJECT_NUMBER: 1
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- name: Determine issue and status
|
|
33
|
+
id: context
|
|
34
|
+
env:
|
|
35
|
+
EVENT: ${{ github.event_name }}
|
|
36
|
+
ACTION: ${{ github.event.action }}
|
|
37
|
+
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
38
|
+
PR_BODY: ${{ github.event.pull_request.body }}
|
|
39
|
+
PR_MERGED: ${{ github.event.pull_request.merged }}
|
|
40
|
+
run: |
|
|
41
|
+
set -euo pipefail
|
|
42
|
+
|
|
43
|
+
status="skip"
|
|
44
|
+
issue=""
|
|
45
|
+
|
|
46
|
+
if [ "$EVENT" = "issues" ]; then
|
|
47
|
+
issue="$ISSUE_NUMBER"
|
|
48
|
+
if [ "$ACTION" = "opened" ] || [ "$ACTION" = "reopened" ]; then
|
|
49
|
+
status="To Do"
|
|
50
|
+
else
|
|
51
|
+
status="metadata"
|
|
52
|
+
fi
|
|
53
|
+
elif [ "$EVENT" = "pull_request" ] && [ "$ACTION" = "opened" ]; then
|
|
54
|
+
issue=$(printf '%s\n' "$PR_BODY" | grep -oiP '(closes|fixes|resolves)\s+#\K[0-9]+' | head -1 || true)
|
|
55
|
+
status="In Progress"
|
|
56
|
+
elif [ "$EVENT" = "pull_request" ] && [ "$ACTION" = "ready_for_review" ]; then
|
|
57
|
+
issue=$(printf '%s\n' "$PR_BODY" | grep -oiP '(closes|fixes|resolves)\s+#\K[0-9]+' | head -1 || true)
|
|
58
|
+
status="In Review"
|
|
59
|
+
elif [ "$EVENT" = "pull_request" ] && [ "$ACTION" = "closed" ] && [ "$PR_MERGED" = "true" ]; then
|
|
60
|
+
issue=$(printf '%s\n' "$PR_BODY" | grep -oiP '(closes|fixes|resolves)\s+#\K[0-9]+' | head -1 || true)
|
|
61
|
+
status="Done"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
echo "issue=$issue" >> "$GITHUB_OUTPUT"
|
|
65
|
+
echo "status=$status" >> "$GITHUB_OUTPUT"
|
|
66
|
+
|
|
67
|
+
- name: Sync project item fields
|
|
68
|
+
if: steps.context.outputs.issue != '' && steps.context.outputs.status != 'skip'
|
|
69
|
+
env:
|
|
70
|
+
ISSUE_NUM: ${{ steps.context.outputs.issue }}
|
|
71
|
+
TARGET_STATUS: ${{ steps.context.outputs.status }}
|
|
72
|
+
run: |
|
|
73
|
+
set -euo pipefail
|
|
74
|
+
|
|
75
|
+
PROJECT_DATA=$(gh api graphql -f query='
|
|
76
|
+
query($owner: String!, $number: Int!) {
|
|
77
|
+
user(login: $owner) {
|
|
78
|
+
projectV2(number: $number) {
|
|
79
|
+
id
|
|
80
|
+
fields(first: 50) {
|
|
81
|
+
nodes {
|
|
82
|
+
... on ProjectV2SingleSelectField {
|
|
83
|
+
id
|
|
84
|
+
name
|
|
85
|
+
options { id name }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
items(first: 100) {
|
|
90
|
+
nodes {
|
|
91
|
+
id
|
|
92
|
+
content {
|
|
93
|
+
... on Issue { number }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
organization(login: $owner) {
|
|
100
|
+
projectV2(number: $number) {
|
|
101
|
+
id
|
|
102
|
+
fields(first: 50) {
|
|
103
|
+
nodes {
|
|
104
|
+
... on ProjectV2SingleSelectField {
|
|
105
|
+
id
|
|
106
|
+
name
|
|
107
|
+
options { id name }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
items(first: 100) {
|
|
112
|
+
nodes {
|
|
113
|
+
id
|
|
114
|
+
content {
|
|
115
|
+
... on Issue { number }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}' -f owner="$OWNER" -F number="$PROJECT_NUMBER")
|
|
122
|
+
|
|
123
|
+
PROJECT=$(echo "$PROJECT_DATA" | jq -c '.data.user.projectV2 // .data.organization.projectV2 // empty')
|
|
124
|
+
PROJECT_ID=$(echo "$PROJECT" | jq -r '.id // empty')
|
|
125
|
+
|
|
126
|
+
if [ -z "$PROJECT_ID" ]; then
|
|
127
|
+
echo "Skipping project sync: project not found or PROJECT_TOKEN lacks access"
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
ITEM_ID=$(echo "$PROJECT" | jq -r --argjson n "$ISSUE_NUM" '(.items.nodes // [])[] | select(.content.number == $n) | .id')
|
|
132
|
+
|
|
133
|
+
if [ -z "$ITEM_ID" ]; then
|
|
134
|
+
echo "Issue #$ISSUE_NUM not found on board; adding it first"
|
|
135
|
+
ISSUE_NODE_ID=$(gh api graphql -f query='
|
|
136
|
+
query($owner: String!, $repo: String!, $number: Int!) {
|
|
137
|
+
repository(owner: $owner, name: $repo) {
|
|
138
|
+
issue(number: $number) { id }
|
|
139
|
+
}
|
|
140
|
+
}' -f owner="$OWNER" -f repo="$REPO_NAME" -F number="$ISSUE_NUM" | jq -r '.data.repository.issue.id')
|
|
141
|
+
|
|
142
|
+
ITEM_ID=$(gh api graphql -f query='
|
|
143
|
+
mutation($project: ID!, $content: ID!) {
|
|
144
|
+
addProjectV2ItemById(input: { projectId: $project, contentId: $content }) {
|
|
145
|
+
item { id }
|
|
146
|
+
}
|
|
147
|
+
}' -f project="$PROJECT_ID" -f content="$ISSUE_NODE_ID" | jq -r '.data.addProjectV2ItemById.item.id')
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
ISSUE_DATA=$(gh issue view "$ISSUE_NUM" -R "$OWNER/$REPO_NAME" --json labels,body)
|
|
151
|
+
parse_body_field() {
|
|
152
|
+
local heading="$1"
|
|
153
|
+
local body="$2"
|
|
154
|
+
printf '%s\n' "$body" | awk -v h="$heading" '
|
|
155
|
+
BEGIN { in_section = 0 }
|
|
156
|
+
$0 ~ "^[[:space:]]*###[[:space:]]+" h "[[:space:]]*$" { in_section = 1; next }
|
|
157
|
+
in_section && /^[[:space:]]*$/ { next }
|
|
158
|
+
in_section { print; exit }
|
|
159
|
+
'
|
|
160
|
+
}
|
|
161
|
+
BODY=$(echo "$ISSUE_DATA" | jq -r '.body')
|
|
162
|
+
PRIORITY=$(parse_body_field "Priority" "$BODY")
|
|
163
|
+
SIZE=$(parse_body_field "Size" "$BODY")
|
|
164
|
+
AGENT_READY=$(parse_body_field "Agent ready" "$BODY")
|
|
165
|
+
CONFIDENCE=$(parse_body_field "Confidence" "$BODY")
|
|
166
|
+
TYPE_LABEL=$(echo "$ISSUE_DATA" | jq -r '[.labels[].name | select(. == "feature" or . == "bug" or . == "chore" or . == "documentation" or . == "test")][0] // empty')
|
|
167
|
+
parse_area() {
|
|
168
|
+
local body="$1"
|
|
169
|
+
local area
|
|
170
|
+
|
|
171
|
+
area=$(printf '%s\n' "$body" | sed -n 's/^- Area: //Ip' | head -1)
|
|
172
|
+
if [ -n "$area" ]; then
|
|
173
|
+
printf '%s\n' "$area"
|
|
174
|
+
return 0
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
printf '%s\n' "$body" | awk '
|
|
178
|
+
BEGIN { in_area = 0 }
|
|
179
|
+
/^[[:space:]]*###[[:space:]]+Area[[:space:]]*$/ {
|
|
180
|
+
in_area = 1
|
|
181
|
+
next
|
|
182
|
+
}
|
|
183
|
+
in_area && /^[[:space:]]*$/ { next }
|
|
184
|
+
in_area {
|
|
185
|
+
print
|
|
186
|
+
exit
|
|
187
|
+
}
|
|
188
|
+
'
|
|
189
|
+
}
|
|
190
|
+
AREA=$(parse_area "$(echo "$ISSUE_DATA" | jq -r '.body')")
|
|
191
|
+
|
|
192
|
+
update_single_select() {
|
|
193
|
+
local field_name="$1"
|
|
194
|
+
local option_name="$2"
|
|
195
|
+
[ -n "$option_name" ] || return 0
|
|
196
|
+
|
|
197
|
+
local field_id
|
|
198
|
+
local option_id
|
|
199
|
+
field_id=$(echo "$PROJECT" | jq -r --arg f "$field_name" '(.fields.nodes // [])[] | select(.name == $f) | .id')
|
|
200
|
+
if [ -z "$field_id" ]; then
|
|
201
|
+
echo "Skipping missing project field: $field_name"
|
|
202
|
+
return 0
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
option_id=$(echo "$PROJECT" | jq -r --arg f "$field_name" --arg o "$option_name" '(.fields.nodes // [])[] | select(.name == $f) | (.options // [])[] | select(.name == $o) | .id')
|
|
206
|
+
if [ -z "$option_id" ]; then
|
|
207
|
+
echo "Skipping missing project option: $field_name=$option_name"
|
|
208
|
+
return 0
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
gh api graphql -f query='
|
|
212
|
+
mutation($project: ID!, $item: ID!, $field: ID!, $option: String!) {
|
|
213
|
+
updateProjectV2ItemFieldValue(input: {
|
|
214
|
+
projectId: $project
|
|
215
|
+
itemId: $item
|
|
216
|
+
fieldId: $field
|
|
217
|
+
value: { singleSelectOptionId: $option }
|
|
218
|
+
}) { projectV2Item { id } }
|
|
219
|
+
}' -f project="$PROJECT_ID" -f item="$ITEM_ID" -f field="$field_id" -f option="$option_id"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if [ "$TARGET_STATUS" != "metadata" ]; then
|
|
223
|
+
update_single_select "Status" "$TARGET_STATUS"
|
|
224
|
+
fi
|
|
225
|
+
update_single_select "Priority" "$PRIORITY"
|
|
226
|
+
update_single_select "Area" "$AREA"
|
|
227
|
+
update_single_select "Size" "$SIZE"
|
|
228
|
+
update_single_select "Agent ready" "$AGENT_READY"
|
|
229
|
+
update_single_select "Confidence" "$CONFIDENCE"
|
|
230
|
+
update_single_select "Type" "$TYPE_LABEL"
|
|
231
|
+
|
|
232
|
+
echo "Synced issue #$ISSUE_NUM to GitHub Project metadata fields"
|