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.
Files changed (173) hide show
  1. project_init/__init__.py +4 -0
  2. project_init/__main__.py +662 -0
  3. project_init/mcps.py +57 -0
  4. project_init/scaffold.py +374 -0
  5. project_init/templates/base/AGENTS.md.tmpl +50 -0
  6. project_init/templates/base/CLAUDE.md.tmpl +16 -0
  7. project_init/templates/base/CONTRIBUTING.md.tmpl +55 -0
  8. project_init/templates/base/GEMINI.md.tmpl +16 -0
  9. project_init/templates/base/LICENSE.tmpl +231 -0
  10. project_init/templates/base/SECURITY.md.tmpl +26 -0
  11. project_init/templates/base/docs/explanation/index.md +9 -0
  12. project_init/templates/base/docs/how-to/index.md +7 -0
  13. project_init/templates/base/docs/index.md.tmpl +20 -0
  14. project_init/templates/base/docs/reference/index.md +13 -0
  15. project_init/templates/base/docs/tutorials/index.md +7 -0
  16. project_init/templates/base/dot_claude/agents/README.md +30 -0
  17. project_init/templates/base/dot_claude/config.yaml.tmpl +31 -0
  18. project_init/templates/base/dot_claude/docs/README.md +26 -0
  19. project_init/templates/base/dot_claude/docs/adr/adr-001-memory-stack.md.tmpl +22 -0
  20. project_init/templates/base/dot_claude/docs/adr/adr-002-mcp-choices.md.tmpl +32 -0
  21. project_init/templates/base/dot_claude/docs/adr/adr-template.md +29 -0
  22. project_init/templates/base/dot_claude/docs/development/conventions.md.tmpl +31 -0
  23. project_init/templates/base/dot_claude/docs/development/testing.md +25 -0
  24. project_init/templates/base/dot_claude/docs/guides/developer-onboarding.md +110 -0
  25. project_init/templates/base/dot_claude/docs/guides/issue-metadata.md +27 -0
  26. project_init/templates/base/dot_claude/docs/guides/secrets.md +50 -0
  27. project_init/templates/base/dot_claude/docs/guides/using-memory.md +36 -0
  28. project_init/templates/base/dot_claude/hooks/README.md +15 -0
  29. project_init/templates/base/dot_claude/hooks/agent_guard_adapter.py.tmpl +64 -0
  30. project_init/templates/base/dot_claude/hooks/dag_workflow.py +610 -0
  31. project_init/templates/base/dot_claude/memory/MEMORY.md.tmpl +11 -0
  32. project_init/templates/base/dot_claude/memory/README.md +51 -0
  33. project_init/templates/base/dot_claude/memory/SCHEMA.md +52 -0
  34. project_init/templates/base/dot_claude/memory/feedback_conventions.md +11 -0
  35. project_init/templates/base/dot_claude/memory/project_context.md.tmpl +11 -0
  36. project_init/templates/base/dot_claude/memory/user_role.md +7 -0
  37. project_init/templates/base/dot_claude/project-init.md.tmpl +174 -0
  38. project_init/templates/base/dot_claude/rules/go.md +14 -0
  39. project_init/templates/base/dot_claude/rules/hooks.md +30 -0
  40. project_init/templates/base/dot_claude/rules/node.md +17 -0
  41. project_init/templates/base/dot_claude/rules/python.md +25 -0
  42. project_init/templates/base/dot_claude/scripts/README.md +15 -0
  43. project_init/templates/base/dot_claude/scripts/create_issue.sh +577 -0
  44. project_init/templates/base/dot_claude/scripts/create_nojira_pr.sh +3 -0
  45. project_init/templates/base/dot_claude/scripts/finish_pr.sh +3 -0
  46. project_init/templates/base/dot_claude/scripts/install_hooks.sh +55 -0
  47. project_init/templates/base/dot_claude/scripts/monitor_pr.sh +270 -0
  48. project_init/templates/base/dot_claude/scripts/promote_review.sh +3 -0
  49. project_init/templates/base/dot_claude/scripts/push_branch.sh +5 -0
  50. project_init/templates/base/dot_claude/scripts/push_wiki.sh +34 -0
  51. project_init/templates/base/dot_claude/scripts/setup_github.sh +219 -0
  52. project_init/templates/base/dot_claude/scripts/start_issue.sh +134 -0
  53. project_init/templates/base/dot_claude/settings.json.tmpl +83 -0
  54. project_init/templates/base/dot_claude/skills/README.md +12 -0
  55. project_init/templates/base/dot_claude/skills/plan/SKILL.md.tmpl +40 -0
  56. project_init/templates/base/dot_claude/vault/README.md +21 -0
  57. project_init/templates/base/dot_claude/vault/decisions/README.md +22 -0
  58. project_init/templates/base/dot_claude/vault/design/README.md +3 -0
  59. project_init/templates/base/dot_claude/vault/knowledge/README.md +5 -0
  60. project_init/templates/base/dot_claude/vault/sessions/README.md +5 -0
  61. project_init/templates/base/dot_devcontainer/devcontainer.json.tmpl +17 -0
  62. project_init/templates/base/dot_devcontainer/post-create.sh.tmpl +31 -0
  63. project_init/templates/base/dot_env.example.tmpl +13 -0
  64. project_init/templates/base/dot_github/CODEOWNERS.tmpl +12 -0
  65. project_init/templates/base/dot_github/ISSUE_TEMPLATE/bug.yml +98 -0
  66. project_init/templates/base/dot_github/ISSUE_TEMPLATE/chore.yml +82 -0
  67. project_init/templates/base/dot_github/ISSUE_TEMPLATE/config.yml +5 -0
  68. project_init/templates/base/dot_github/ISSUE_TEMPLATE/docs.yml +84 -0
  69. project_init/templates/base/dot_github/ISSUE_TEMPLATE/feature.yml +87 -0
  70. project_init/templates/base/dot_github/ISSUE_TEMPLATE/test.yml +90 -0
  71. project_init/templates/base/dot_github/copilot-instructions.md.tmpl +25 -0
  72. project_init/templates/base/dot_github/hooks/commit-msg +52 -0
  73. project_init/templates/base/dot_github/hooks/pre-commit +16 -0
  74. project_init/templates/base/dot_github/hooks/pre-push +51 -0
  75. project_init/templates/base/dot_github/pull_request_template.md +22 -0
  76. project_init/templates/base/dot_github/workflows/board-automation.yml +232 -0
  77. project_init/templates/base/dot_github/workflows/ci.yml.tmpl +204 -0
  78. project_init/templates/base/dot_github/workflows/docs.yml.tmpl +98 -0
  79. project_init/templates/base/dot_github/workflows/issue-validation.yml +72 -0
  80. project_init/templates/base/dot_github/workflows/review-status.yml +48 -0
  81. project_init/templates/base/dot_github/workflows/validate-pr.yml +103 -0
  82. project_init/templates/base/dot_gitignore.tmpl +41 -0
  83. project_init/templates/base/dot_golangci.yml.tmpl +20 -0
  84. project_init/templates/base/dot_vscode/extensions.json.tmpl +10 -0
  85. project_init/templates/base/dot_vscode/settings.json.tmpl +8 -0
  86. project_init/templates/base/eslint.config.mjs.tmpl +29 -0
  87. project_init/templates/base/justfile.tmpl +95 -0
  88. project_init/templates/base/mise.toml.tmpl +20 -0
  89. project_init/templates/base/mkdocs.yml.tmpl +32 -0
  90. project_init/templates/base/renovate.json +14 -0
  91. project_init/templates/base/ruff.toml.tmpl +31 -0
  92. project_init/templates/base/typedoc.json.tmpl +14 -0
  93. project_init/templates/codex/dot_agents/skills/add_adr/SKILL.md +33 -0
  94. project_init/templates/codex/dot_agents/skills/add_command/SKILL.md +63 -0
  95. project_init/templates/codex/dot_agents/skills/add_hook/SKILL.md +112 -0
  96. project_init/templates/codex/dot_agents/skills/audit/SKILL.md +146 -0
  97. project_init/templates/codex/dot_agents/skills/create_issue/SKILL.md +59 -0
  98. project_init/templates/codex/dot_agents/skills/github_workflow/SKILL.md +80 -0
  99. project_init/templates/codex/dot_agents/skills/request_review/SKILL.md +19 -0
  100. project_init/templates/codex/dot_agents/skills/review/SKILL.md +17 -0
  101. project_init/templates/codex/dot_agents/skills/save_memory/SKILL.md +17 -0
  102. project_init/templates/codex/dot_agents/skills/session_summary/SKILL.md +35 -0
  103. project_init/templates/codex/dot_agents/skills/start_task/SKILL.md +48 -0
  104. project_init/templates/codex/dot_agents/skills/status/SKILL.md +15 -0
  105. project_init/templates/codex/dot_codex/hooks.json.tmpl +17 -0
  106. project_init/templates/fallback/dot_claude/hooks/github_command_guard.sh +11 -0
  107. project_init/templates/fallback/dot_claude/hooks/post_edit_lint.sh +58 -0
  108. project_init/templates/fallback/dot_claude/hooks/pre_commit_gate.sh +81 -0
  109. project_init/templates/fallback/dot_claude/hooks/prod_guard.py +140 -0
  110. project_init/templates/fallback/dot_claude/hooks/session_setup.sh +62 -0
  111. project_init/templates/fallback/dot_claude/hooks/workflow_state_reminder.sh +72 -0
  112. project_init/templates/fallback/dot_claude/skills/INDEX.md +28 -0
  113. project_init/templates/fallback/dot_claude/skills/add_adr/SKILL.md +33 -0
  114. project_init/templates/fallback/dot_claude/skills/add_command/SKILL.md +63 -0
  115. project_init/templates/fallback/dot_claude/skills/add_hook/SKILL.md +112 -0
  116. project_init/templates/fallback/dot_claude/skills/audit/SKILL.md +146 -0
  117. project_init/templates/fallback/dot_claude/skills/create_issue/SKILL.md +59 -0
  118. project_init/templates/fallback/dot_claude/skills/github_workflow/SKILL.md +80 -0
  119. project_init/templates/fallback/dot_claude/skills/request_review/SKILL.md +19 -0
  120. project_init/templates/fallback/dot_claude/skills/review/SKILL.md +17 -0
  121. project_init/templates/fallback/dot_claude/skills/save_memory/SKILL.md +17 -0
  122. project_init/templates/fallback/dot_claude/skills/session_summary/SKILL.md +35 -0
  123. project_init/templates/fallback/dot_claude/skills/start_task/SKILL.md +48 -0
  124. project_init/templates/fallback/dot_claude/skills/status/SKILL.md +15 -0
  125. project_init/templates/gemini/dot_agents/skills/add_adr/SKILL.md +33 -0
  126. project_init/templates/gemini/dot_agents/skills/add_command/SKILL.md +63 -0
  127. project_init/templates/gemini/dot_agents/skills/add_hook/SKILL.md +112 -0
  128. project_init/templates/gemini/dot_agents/skills/audit/SKILL.md +146 -0
  129. project_init/templates/gemini/dot_agents/skills/create_issue/SKILL.md +59 -0
  130. project_init/templates/gemini/dot_agents/skills/github_workflow/SKILL.md +80 -0
  131. project_init/templates/gemini/dot_agents/skills/request_review/SKILL.md +19 -0
  132. project_init/templates/gemini/dot_agents/skills/review/SKILL.md +17 -0
  133. project_init/templates/gemini/dot_agents/skills/save_memory/SKILL.md +17 -0
  134. project_init/templates/gemini/dot_agents/skills/session_summary/SKILL.md +35 -0
  135. project_init/templates/gemini/dot_agents/skills/start_task/SKILL.md +48 -0
  136. project_init/templates/gemini/dot_agents/skills/status/SKILL.md +15 -0
  137. project_init/templates/gemini/dot_claude/scripts/setup_gemini.sh.tmpl +16 -0
  138. project_init/templates/gemini/dot_gemini-extension/commands/add_adr.toml +5 -0
  139. project_init/templates/gemini/dot_gemini-extension/commands/add_command.toml +5 -0
  140. project_init/templates/gemini/dot_gemini-extension/commands/add_hook.toml +5 -0
  141. project_init/templates/gemini/dot_gemini-extension/commands/audit.toml +5 -0
  142. project_init/templates/gemini/dot_gemini-extension/commands/create_issue.toml +5 -0
  143. project_init/templates/gemini/dot_gemini-extension/commands/github_workflow.toml +5 -0
  144. project_init/templates/gemini/dot_gemini-extension/commands/request_review.toml +5 -0
  145. project_init/templates/gemini/dot_gemini-extension/commands/review.toml +5 -0
  146. project_init/templates/gemini/dot_gemini-extension/commands/save_memory.toml +5 -0
  147. project_init/templates/gemini/dot_gemini-extension/commands/session_summary.toml +5 -0
  148. project_init/templates/gemini/dot_gemini-extension/commands/start_task.toml +5 -0
  149. project_init/templates/gemini/dot_gemini-extension/commands/status.toml +5 -0
  150. project_init/templates/gemini/dot_gemini-extension/gemini-extension.json.tmpl +6 -0
  151. project_init/templates/gemini/dot_gemini-extension/hooks/hooks.json.tmpl +18 -0
  152. project_init/templates/graphify/dot_claude/docs/guides/using-graphify.md +37 -0
  153. project_init/templates/graphify/dot_claude/rules/graphify.md +18 -0
  154. project_init/templates/graphify/dot_claude/scripts/setup_graphify.sh +40 -0
  155. project_init/templates/obsidian/dot_claude/scripts/lint_memory.sh +115 -0
  156. project_init/templates/obsidian/dot_claude/vault/decisions/adr-000-project-setup.md.tmpl +22 -0
  157. project_init/templates/obsidian/dot_claude/vault/dot_obsidian/README.md +31 -0
  158. project_init/templates/obsidian/dot_claude/vault/dot_obsidian/app.json +6 -0
  159. project_init/templates/obsidian/dot_claude/vault/dot_obsidian/community-plugins.json +1 -0
  160. project_init/templates/obsidian/dot_claude/vault/dot_obsidian/core-plugins.json +1 -0
  161. project_init/templates/obsidian/dot_claude/vault/log.md +6 -0
  162. project_init/templates/obsidian/dot_claude/vault/templates/decision.md +16 -0
  163. project_init/templates/obsidian/dot_claude/vault/templates/design-note.md +14 -0
  164. project_init/templates/obsidian/dot_claude/vault/templates/knowledge-note.md +12 -0
  165. project_init/templates/obsidian/dot_claude/vault/templates/session-note.md +16 -0
  166. project_init/templates/presets/obsidian-graphify.toml +16 -0
  167. project_init/templates/presets/obsidian-only.toml +14 -0
  168. project_init/upgrade.py +569 -0
  169. project_init-0.3.0.dist-info/METADATA +342 -0
  170. project_init-0.3.0.dist-info/RECORD +173 -0
  171. project_init-0.3.0.dist-info/WHEEL +4 -0
  172. project_init-0.3.0.dist-info/entry_points.txt +2 -0
  173. 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"