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,577 @@
1
+ #!/bin/bash
2
+ # Create a GitHub issue with typed labels and planning metadata.
3
+ # Priority, Size, Agent ready, and Confidence are set directly on the
4
+ # GitHub Project v2 board — not written to the issue body.
5
+ #
6
+ # Usage:
7
+ # .claude/scripts/create_issue.sh <type> "Short description" [metadata flags]
8
+ #
9
+ # Types: feat fix chore docs test
10
+ #
11
+ # Prints the created issue number to stdout so it can be piped:
12
+ # .claude/scripts/create_issue.sh feat "Add OAuth login" --priority high | xargs -I{} .claude/scripts/start_issue.sh {} feat
13
+
14
+ set -euo pipefail
15
+
16
+ VALID_TYPES="feat fix chore docs test"
17
+ VALID_SCALES="epic task"
18
+ VALID_PRIORITIES="high medium low"
19
+ VALID_SIZES="XS S M L XL"
20
+ VALID_CONFIDENCES="high medium low unknown"
21
+
22
+ usage() {
23
+ cat <<'EOF'
24
+ Usage: create_issue.sh <type> "Short description" [options]
25
+
26
+ Types:
27
+ feat fix chore docs test
28
+
29
+ Options:
30
+ --priority high|medium|low Set Priority on the project board
31
+ --size XS|S|M|L|XL Set Size on the project board
32
+ --agent-ready Yes|No Set Agent ready on the project board
33
+ --confidence high|medium|low|unknown Set Confidence on the project board
34
+ --area VALUE Record affected area in body metadata
35
+ --scale epic|task Mark as epic (parent) or task (leaf); adds scale label
36
+ --parent VALUE Link new issue as sub-issue of VALUE
37
+ Formats: 42, #42, owner/repo#42, or full issue URL
38
+ --reference VALUE Add a reference; repeatable
39
+ --dependency VALUE Add a dependency; repeatable
40
+ --acceptance VALUE Add an acceptance criterion; repeatable
41
+ --assignee USER Assign the issue
42
+ --milestone NAME Set milestone by name
43
+ --body-file FILE Append extra markdown body content
44
+ -h, --help Show this help
45
+
46
+ Sub-issues:
47
+ --parent links the new issue as a native GitHub sub-issue of the given parent.
48
+ Cross-repo parents use owner/repo#42 or the full issue URL.
49
+ --scale epic marks this issue as a parent work item (adds scale:epic label).
50
+
51
+ Metadata model:
52
+ GitHub labels: type and scale when labels exist or can be created.
53
+ GitHub Project fields: priority, size, agent-ready, confidence (set directly via GraphQL).
54
+ Markdown body: area, scale, parent, references, dependencies, acceptance criteria,
55
+ Definition of Ready, and Definition of Done.
56
+
57
+ Missing label fallback:
58
+ If a label is missing and cannot be created, issue creation continues without
59
+ that label because the same metadata is still stored in the markdown body.
60
+ EOF
61
+ }
62
+
63
+ if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
64
+ usage
65
+ exit 0
66
+ fi
67
+
68
+ if [ $# -lt 2 ]; then
69
+ usage >&2
70
+ exit 1
71
+ fi
72
+
73
+ TYPE="$1"
74
+ DESCRIPTION="$2"
75
+ shift 2
76
+
77
+ PRIORITY=""
78
+ SIZE=""
79
+ AGENT_READY=""
80
+ CONFIDENCE=""
81
+ AREA=""
82
+ SCALE=""
83
+ PARENT=""
84
+ ASSIGNEE=""
85
+ MILESTONE=""
86
+ BODY_FILE=""
87
+ REFERENCES=()
88
+ DEPENDENCIES=()
89
+ ACCEPTANCE=()
90
+
91
+ contains_word() {
92
+ local needle="$1"
93
+ local haystack="$2"
94
+ echo "$haystack" | grep -qw "$needle"
95
+ }
96
+
97
+ require_option_value() {
98
+ local option="$1"
99
+ local value="${2:-}"
100
+ if [ -z "$value" ]; then
101
+ echo "ERROR: missing value for '$option'" >&2
102
+ usage >&2
103
+ exit 1
104
+ fi
105
+ }
106
+
107
+ while [ $# -gt 0 ]; do
108
+ case "$1" in
109
+ --priority)
110
+ require_option_value "$1" "${2:-}"
111
+ PRIORITY="$2"
112
+ shift 2
113
+ ;;
114
+ --size)
115
+ require_option_value "$1" "${2:-}"
116
+ SIZE="$2"
117
+ shift 2
118
+ ;;
119
+ --agent-ready)
120
+ require_option_value "$1" "${2:-}"
121
+ AGENT_READY="$2"
122
+ shift 2
123
+ ;;
124
+ --confidence)
125
+ require_option_value "$1" "${2:-}"
126
+ CONFIDENCE="$2"
127
+ shift 2
128
+ ;;
129
+ --area)
130
+ require_option_value "$1" "${2:-}"
131
+ AREA="$2"
132
+ shift 2
133
+ ;;
134
+ --scale)
135
+ require_option_value "$1" "${2:-}"
136
+ SCALE="$2"
137
+ shift 2
138
+ ;;
139
+ --parent)
140
+ require_option_value "$1" "${2:-}"
141
+ PARENT="$2"
142
+ shift 2
143
+ ;;
144
+ --reference)
145
+ require_option_value "$1" "${2:-}"
146
+ REFERENCES+=("$2")
147
+ shift 2
148
+ ;;
149
+ --dependency)
150
+ require_option_value "$1" "${2:-}"
151
+ DEPENDENCIES+=("$2")
152
+ shift 2
153
+ ;;
154
+ --acceptance)
155
+ require_option_value "$1" "${2:-}"
156
+ ACCEPTANCE+=("$2")
157
+ shift 2
158
+ ;;
159
+ --assignee)
160
+ require_option_value "$1" "${2:-}"
161
+ ASSIGNEE="$2"
162
+ shift 2
163
+ ;;
164
+ --milestone)
165
+ require_option_value "$1" "${2:-}"
166
+ MILESTONE="$2"
167
+ shift 2
168
+ ;;
169
+ --body-file)
170
+ require_option_value "$1" "${2:-}"
171
+ BODY_FILE="$2"
172
+ shift 2
173
+ ;;
174
+ -h|--help)
175
+ usage
176
+ exit 0
177
+ ;;
178
+ *)
179
+ echo "ERROR: unknown option '$1'" >&2
180
+ usage >&2
181
+ exit 1
182
+ ;;
183
+ esac
184
+ done
185
+
186
+ if ! contains_word "$TYPE" "$VALID_TYPES"; then
187
+ echo "ERROR: invalid type '$TYPE'. Valid types: $VALID_TYPES" >&2
188
+ exit 1
189
+ fi
190
+
191
+ if [ -z "$DESCRIPTION" ]; then
192
+ echo "ERROR: description cannot be empty" >&2
193
+ exit 1
194
+ fi
195
+
196
+ if [ -n "$PRIORITY" ] && ! contains_word "$PRIORITY" "$VALID_PRIORITIES"; then
197
+ echo "ERROR: invalid priority '$PRIORITY'. Valid: $VALID_PRIORITIES" >&2
198
+ exit 1
199
+ fi
200
+
201
+ if [ -n "$SIZE" ] && ! contains_word "$SIZE" "$VALID_SIZES"; then
202
+ echo "ERROR: invalid size '$SIZE'. Valid: $VALID_SIZES" >&2
203
+ exit 1
204
+ fi
205
+
206
+ if [ -n "$CONFIDENCE" ] && ! contains_word "$CONFIDENCE" "$VALID_CONFIDENCES"; then
207
+ echo "ERROR: invalid confidence '$CONFIDENCE'. Valid: $VALID_CONFIDENCES" >&2
208
+ exit 1
209
+ fi
210
+
211
+ if [ -n "$SCALE" ] && ! contains_word "$SCALE" "$VALID_SCALES"; then
212
+ echo "ERROR: invalid scale '$SCALE'. Valid scales: $VALID_SCALES" >&2
213
+ exit 1
214
+ fi
215
+
216
+ if [ -n "$BODY_FILE" ] && [ ! -f "$BODY_FILE" ]; then
217
+ echo "ERROR: body file not found: $BODY_FILE" >&2
218
+ exit 1
219
+ fi
220
+
221
+ # ---------------------------------------------------------------------------
222
+ # Parse --parent reference into owner / repo / number.
223
+ # Accepts: 42, #42, owner/repo#42, or full GitHub issue URL.
224
+ # Sets globals PARENT_OWNER, PARENT_REPO, PARENT_NUMBER.
225
+ # ---------------------------------------------------------------------------
226
+ PARENT_OWNER=""
227
+ PARENT_REPO=""
228
+ PARENT_NUMBER=""
229
+
230
+ parse_parent() {
231
+ local raw="${1#\#}" # strip leading #
232
+ if [[ "$raw" =~ ^https://github\.com/([^/]+)/([^/]+)/issues/([0-9]+)$ ]]; then
233
+ PARENT_OWNER="${BASH_REMATCH[1]}"
234
+ PARENT_REPO="${BASH_REMATCH[2]}"
235
+ PARENT_NUMBER="${BASH_REMATCH[3]}"
236
+ elif [[ "$raw" =~ ^([^/#]+)/([^#]+)#([0-9]+)$ ]]; then
237
+ PARENT_OWNER="${BASH_REMATCH[1]}"
238
+ PARENT_REPO="${BASH_REMATCH[2]}"
239
+ PARENT_NUMBER="${BASH_REMATCH[3]}"
240
+ elif [[ "$raw" =~ ^[0-9]+$ ]]; then
241
+ PARENT_OWNER=$(gh repo view --json owner -q .owner.login)
242
+ PARENT_REPO=$(gh repo view --json name -q .name)
243
+ PARENT_NUMBER="$raw"
244
+ else
245
+ echo "ERROR: cannot parse parent '$1'. Use: 42, #42, owner/repo#42, or full URL" >&2
246
+ exit 1
247
+ fi
248
+ }
249
+
250
+ if [ -n "$PARENT" ]; then
251
+ parse_parent "$PARENT"
252
+ fi
253
+
254
+ case "$TYPE" in
255
+ feat) TYPE_LABEL="feature" ;;
256
+ fix) TYPE_LABEL="bug" ;;
257
+ chore) TYPE_LABEL="chore" ;;
258
+ docs) TYPE_LABEL="documentation" ;;
259
+ test) TYPE_LABEL="test" ;;
260
+ esac
261
+
262
+ TITLE="$DESCRIPTION"
263
+
264
+ BODY_PATH=$(mktemp)
265
+ trap 'rm -f "$BODY_PATH"' EXIT
266
+
267
+ write_list() {
268
+ local fallback="$1"
269
+ shift
270
+ if [ "$#" -eq 0 ]; then
271
+ echo "- [ ] $fallback"
272
+ return
273
+ fi
274
+ for item in "$@"; do
275
+ [ -n "$item" ] && echo "- [ ] $item"
276
+ done
277
+ }
278
+
279
+ write_bullets() {
280
+ local fallback="$1"
281
+ shift
282
+ if [ "$#" -eq 0 ]; then
283
+ echo "- $fallback"
284
+ return
285
+ fi
286
+ for item in "$@"; do
287
+ [ -n "$item" ] && echo "- $item"
288
+ done
289
+ }
290
+
291
+ {
292
+ echo "## Summary"
293
+ echo
294
+ echo "$DESCRIPTION"
295
+ echo
296
+ echo "## Metadata"
297
+ echo
298
+ echo "- Type: $TYPE"
299
+ [ -n "$SCALE" ] && echo "- Scale: $SCALE"
300
+ [ -n "$AREA" ] && echo "- Area: $AREA"
301
+ if [ -n "$PARENT" ]; then
302
+ echo "- Parent: $PARENT_OWNER/$PARENT_REPO#$PARENT_NUMBER"
303
+ fi
304
+ if [ -n "$ASSIGNEE" ]; then
305
+ echo "- Assignee: @$ASSIGNEE"
306
+ fi
307
+ if [ -n "$MILESTONE" ]; then
308
+ echo "- Milestone: $MILESTONE"
309
+ fi
310
+ if [ "${#REFERENCES[@]}" -gt 0 ]; then
311
+ echo
312
+ echo "## References"
313
+ echo
314
+ write_bullets "None" "${REFERENCES[@]}"
315
+ fi
316
+ if [ "${#DEPENDENCIES[@]}" -gt 0 ]; then
317
+ echo
318
+ echo "## Dependencies"
319
+ echo
320
+ write_bullets "None" "${DEPENDENCIES[@]}"
321
+ fi
322
+ echo
323
+ echo "## Acceptance criteria"
324
+ echo
325
+ write_list "Define acceptance criteria before implementation" "${ACCEPTANCE[@]}"
326
+ echo
327
+ echo "## Definition of Ready"
328
+ echo
329
+ echo "- [ ] Acceptance criteria are clear enough to verify"
330
+ echo "- [ ] Dependencies, references, and affected areas are recorded"
331
+ echo
332
+ echo "## Definition of Done"
333
+ echo
334
+ echo "- [ ] Implementation is complete"
335
+ echo "- [ ] Relevant checks, tests, or manual validation are documented"
336
+ } > "$BODY_PATH"
337
+
338
+ if [ -n "$BODY_FILE" ]; then
339
+ {
340
+ echo
341
+ echo "## Additional context"
342
+ echo
343
+ cat "$BODY_FILE"
344
+ } >> "$BODY_PATH"
345
+ fi
346
+
347
+ ensure_label() {
348
+ local name="$1"
349
+ local color="$2"
350
+ local description="$3"
351
+ if gh label list --search "$name" --json name -q '.[].name' 2>/dev/null | grep -Fxq "$name"; then
352
+ echo "$name"
353
+ return
354
+ fi
355
+ if gh label create "$name" --color "$color" --description "$description" >/dev/null 2>&1; then
356
+ echo "$name"
357
+ return
358
+ fi
359
+ echo "Warning: label missing and could not be created: $name" >&2
360
+ }
361
+
362
+ LABEL_ARGS=()
363
+ if LABEL=$(ensure_label "$TYPE_LABEL" "0075ca" "Issue type"); then
364
+ [ -n "$LABEL" ] && LABEL_ARGS+=(--label "$LABEL")
365
+ fi
366
+ if [ -n "$SCALE" ]; then
367
+ if LABEL=$(ensure_label "scale:$SCALE" "f9d0c4" "Issue scale"); then
368
+ [ -n "$LABEL" ] && LABEL_ARGS+=(--label "$LABEL")
369
+ fi
370
+ fi
371
+
372
+ CREATE_ARGS=(--title "$TITLE" --body-file "$BODY_PATH")
373
+ if [ -n "$ASSIGNEE" ]; then
374
+ CREATE_ARGS+=(--assignee "$ASSIGNEE")
375
+ fi
376
+ if [ -n "$MILESTONE" ]; then
377
+ CREATE_ARGS+=(--milestone "$MILESTONE")
378
+ fi
379
+
380
+ ISSUE_URL=$(gh issue create "${CREATE_ARGS[@]}" "${LABEL_ARGS[@]}")
381
+ ISSUE_NUMBER=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
382
+
383
+ # ---------------------------------------------------------------------------
384
+ # Set Priority / Size / Agent ready / Confidence directly on the GitHub
385
+ # Project v2 board. These are not written to the issue body — the board is
386
+ # the authoritative location for these fields.
387
+ # PROJECT_NUMBER defaults to 1; override with the env var if needed.
388
+ # ---------------------------------------------------------------------------
389
+ sync_project_fields() {
390
+ local issue_num="$1"
391
+ local project_num owner repo_name project_data project_id item_id
392
+
393
+ project_num="${PROJECT_NUMBER:-1}"
394
+ owner=$(gh repo view --json owner -q .owner.login)
395
+ repo_name=$(gh repo view --json name -q .name)
396
+
397
+ project_data=$(gh api graphql -f query='
398
+ query($owner: String!, $number: Int!) {
399
+ user(login: $owner) {
400
+ projectV2(number: $number) {
401
+ id
402
+ fields(first: 50) {
403
+ nodes {
404
+ ... on ProjectV2SingleSelectField {
405
+ id name options { id name }
406
+ }
407
+ }
408
+ }
409
+ items(first: 100) {
410
+ nodes { id content { ... on Issue { number } } }
411
+ }
412
+ }
413
+ }
414
+ organization(login: $owner) {
415
+ projectV2(number: $number) {
416
+ id
417
+ fields(first: 50) {
418
+ nodes {
419
+ ... on ProjectV2SingleSelectField {
420
+ id name options { id name }
421
+ }
422
+ }
423
+ }
424
+ items(first: 100) {
425
+ nodes { id content { ... on Issue { number } } }
426
+ }
427
+ }
428
+ }
429
+ }' -f owner="$owner" -F number="$project_num" 2>/dev/null) || true
430
+ [ -z "$project_data" ] && project_data='{}'
431
+
432
+ # Write project data to a temp file to avoid single-quote issues in shell args
433
+ local pdata_file
434
+ pdata_file=$(mktemp)
435
+ printf '%s' "$project_data" > "$pdata_file"
436
+
437
+ project_id=$(python3 - "$pdata_file" <<'PYEOF' 2>/dev/null || true
438
+ import sys, json
439
+ d = json.load(open(sys.argv[1])).get('data', {})
440
+ p = (d.get('user') or d.get('organization') or {}).get('projectV2') or {}
441
+ print(p.get('id', ''))
442
+ PYEOF
443
+ )
444
+
445
+ if [ -z "$project_id" ]; then
446
+ rm -f "$pdata_file"
447
+ echo "Warning: project #$project_num not found — skipping field sync (set PROJECT_NUMBER if needed)" >&2
448
+ return 0
449
+ fi
450
+
451
+ item_id=$(python3 - "$pdata_file" "$issue_num" <<'PYEOF' 2>/dev/null || true
452
+ import sys, json
453
+ d = json.load(open(sys.argv[1])).get('data', {})
454
+ p = (d.get('user') or d.get('organization') or {}).get('projectV2') or {}
455
+ for item in (p.get('items') or {}).get('nodes', []):
456
+ if (item.get('content') or {}).get('number') == int(sys.argv[2]):
457
+ print(item['id'])
458
+ break
459
+ PYEOF
460
+ )
461
+
462
+ if [ -z "$item_id" ]; then
463
+ local issue_node_id
464
+ issue_node_id=$(gh api "repos/$owner/$repo_name/issues/$issue_num" --jq '.node_id' 2>/dev/null || true)
465
+ if [ -n "$issue_node_id" ]; then
466
+ item_id=$(gh api graphql -f query='
467
+ mutation($project: ID!, $content: ID!) {
468
+ addProjectV2ItemById(input: { projectId: $project, contentId: $content }) {
469
+ item { id }
470
+ }
471
+ }' -f project="$project_id" -f content="$issue_node_id" \
472
+ --jq '.data.addProjectV2ItemById.item.id' 2>/dev/null || true)
473
+ fi
474
+ fi
475
+
476
+ if [ -z "$item_id" ]; then
477
+ echo "Warning: could not add #$issue_num to project #$project_num — skipping field sync" >&2
478
+ return 0
479
+ fi
480
+
481
+ set_field() {
482
+ local field_name="$1" option_name="$2"
483
+ [ -n "$option_name" ] || return 0
484
+
485
+ local field_id option_id
486
+ field_id=$(python3 - "$pdata_file" "$field_name" <<'PYEOF' 2>/dev/null || true
487
+ import sys, json
488
+ d = json.load(open(sys.argv[1])).get('data', {})
489
+ p = (d.get('user') or d.get('organization') or {}).get('projectV2') or {}
490
+ for f in (p.get('fields') or {}).get('nodes', []):
491
+ if f.get('name') == sys.argv[2]:
492
+ print(f.get('id', ''))
493
+ break
494
+ PYEOF
495
+ )
496
+
497
+ if [ -z "$field_id" ]; then
498
+ echo "Warning: project field '$field_name' not found — skipping" >&2
499
+ return 0
500
+ fi
501
+
502
+ option_id=$(python3 - "$pdata_file" "$field_name" "$option_name" <<'PYEOF' 2>/dev/null || true
503
+ import sys, json
504
+ d = json.load(open(sys.argv[1])).get('data', {})
505
+ p = (d.get('user') or d.get('organization') or {}).get('projectV2') or {}
506
+ for f in (p.get('fields') or {}).get('nodes', []):
507
+ if f.get('name') == sys.argv[2]:
508
+ for opt in f.get('options', []):
509
+ if opt.get('name') == sys.argv[3]:
510
+ print(opt['id'])
511
+ break
512
+ break
513
+ PYEOF
514
+ )
515
+
516
+ if [ -z "$option_id" ]; then
517
+ echo "Warning: option '$option_name' not found in field '$field_name' — skipping" >&2
518
+ return 0
519
+ fi
520
+
521
+ gh api graphql -f query='
522
+ mutation($project: ID!, $item: ID!, $field: ID!, $option: String!) {
523
+ updateProjectV2ItemFieldValue(input: {
524
+ projectId: $project
525
+ itemId: $item
526
+ fieldId: $field
527
+ value: { singleSelectOptionId: $option }
528
+ }) { projectV2Item { id } }
529
+ }' \
530
+ -f project="$project_id" -f item="$item_id" \
531
+ -f field="$field_id" -f option="$option_id" > /dev/null 2>&1 \
532
+ || echo "Warning: failed to set '$field_name'='$option_name'" >&2
533
+ }
534
+
535
+ [ -n "$PRIORITY" ] && set_field "Priority" "$PRIORITY"
536
+ [ -n "$SIZE" ] && set_field "Size" "$SIZE"
537
+ [ -n "$AGENT_READY" ] && set_field "Agent ready" "$AGENT_READY"
538
+ [ -n "$CONFIDENCE" ] && set_field "Confidence" "$CONFIDENCE"
539
+ rm -f "$pdata_file"
540
+ }
541
+
542
+ if [ -n "$PRIORITY" ] || [ -n "$SIZE" ] || [ -n "$AGENT_READY" ] || [ -n "$CONFIDENCE" ]; then
543
+ sync_project_fields "$ISSUE_NUMBER"
544
+ fi
545
+
546
+ # ---------------------------------------------------------------------------
547
+ # Link as a native GitHub sub-issue when --parent was specified.
548
+ # Uses the addSubIssue GraphQL mutation (supports cross-repo parents via URL).
549
+ # ---------------------------------------------------------------------------
550
+ if [ -n "$PARENT" ]; then
551
+ PARENT_NODE_ID=$(gh api graphql -f query='
552
+ query($owner: String!, $repo: String!, $number: Int!) {
553
+ repository(owner: $owner, name: $repo) {
554
+ issue(number: $number) { id }
555
+ }
556
+ }' \
557
+ -f owner="$PARENT_OWNER" \
558
+ -f repo="$PARENT_REPO" \
559
+ -F number="$PARENT_NUMBER" \
560
+ --jq '.data.repository.issue.id')
561
+
562
+ CHILD_NODE_ID=$(gh api "repos/:owner/:repo/issues/$ISSUE_NUMBER" --jq '.node_id')
563
+
564
+ gh api graphql -f query='
565
+ mutation($parent: ID!, $child: ID!) {
566
+ addSubIssue(input: { issueId: $parent, subIssueId: $child }) {
567
+ issue { number }
568
+ subIssue { number }
569
+ }
570
+ }' \
571
+ -f parent="$PARENT_NODE_ID" \
572
+ -f child="$CHILD_NODE_ID" > /dev/null
573
+
574
+ echo "Linked #$ISSUE_NUMBER as sub-issue of $PARENT_OWNER/$PARENT_REPO#$PARENT_NUMBER" >&2
575
+ fi
576
+
577
+ echo "$ISSUE_NUMBER"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Thin shim — actual logic lives in .claude/hooks/dag_workflow.py.
3
+ exec python3 "$(dirname "$0")/../hooks/dag_workflow.py" create-pr-nojira "$@"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Thin shim — actual logic lives in .claude/hooks/dag_workflow.py.
3
+ exec python3 "$(dirname "$0")/../hooks/dag_workflow.py" finish "$@"
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # Install git hooks from .github/hooks/ to .git/hooks/
3
+ # Run this once after cloning or when hooks are updated
4
+
5
+ set -e
6
+
7
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
8
+ GIT_HOOKS_SRC="$REPO_ROOT/.github/hooks"
9
+ GIT_HOOKS_DST="$REPO_ROOT/.git/hooks"
10
+
11
+ if [ ! -d "$GIT_HOOKS_DST" ]; then
12
+ echo "Error: .git/hooks directory not found. Are you in a git repository?"
13
+ exit 1
14
+ fi
15
+
16
+ if [ ! -d "$GIT_HOOKS_SRC" ]; then
17
+ echo "Warning: .github/hooks directory not found."
18
+ exit 0
19
+ fi
20
+
21
+ echo "Installing git hooks from .github/hooks/ to .git/hooks/..."
22
+
23
+ for hook_file in "$GIT_HOOKS_SRC"/*; do
24
+ [ -f "$hook_file" ] || continue
25
+
26
+ hook_name=$(basename "$hook_file")
27
+ hook_dst="$GIT_HOOKS_DST/$hook_name"
28
+
29
+ if [ -e "$hook_dst" ] && [ ! -L "$hook_dst" ]; then
30
+ # File exists and is not a symlink - back it up
31
+ mv "$hook_dst" "$hook_dst.backup.$(date +%s)"
32
+ echo " Backed up existing $hook_name"
33
+ fi
34
+
35
+ # Create symlink (or copy if symlinks not available)
36
+ if cp -P "$hook_file" "$hook_dst" 2>/dev/null; then
37
+ chmod +x "$hook_dst"
38
+ else
39
+ echo " ✗ Failed to install $hook_name"
40
+ exit 1
41
+ fi
42
+ done
43
+
44
+ # The pre-commit hook scans staged changes with gitleaks (ADR-007).
45
+ # It fails open when gitleaks is missing — CI is the hard backstop — but
46
+ # local feedback is much faster, so nudge here.
47
+ if ! command -v gitleaks >/dev/null 2>&1; then
48
+ echo ""
49
+ echo "NOTE: gitleaks is not installed — the pre-commit secret scan will be"
50
+ echo "skipped locally (CI still scans). Install it for fast local feedback:"
51
+ echo " https://github.com/gitleaks/gitleaks#installing"
52
+ fi
53
+
54
+ echo "To reinstall hooks after pulling changes, run:"
55
+ echo " .claude/scripts/install_hooks.sh"