@snipcodeit/mgw 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +517 -0
- package/bin/mgw-install.cjs +81 -0
- package/commands/ask.md +416 -0
- package/commands/assign.md +333 -0
- package/commands/board.md +1679 -0
- package/commands/help.md +119 -0
- package/commands/init.md +250 -0
- package/commands/issue.md +469 -0
- package/commands/issues.md +109 -0
- package/commands/link.md +122 -0
- package/commands/milestone.md +952 -0
- package/commands/next.md +375 -0
- package/commands/pr.md +277 -0
- package/commands/project.md +1801 -0
- package/commands/review.md +260 -0
- package/commands/roadmap.md +489 -0
- package/commands/run.md +1282 -0
- package/commands/status.md +526 -0
- package/commands/sync.md +243 -0
- package/commands/update.md +282 -0
- package/commands/workflows/board-sync.md +404 -0
- package/commands/workflows/github.md +385 -0
- package/commands/workflows/gsd.md +377 -0
- package/commands/workflows/state.md +412 -0
- package/commands/workflows/validation.md +144 -0
- package/dist/bin/mgw.cjs +291 -0
- package/dist/claude-Vp9qvImH.cjs +466 -0
- package/dist/lib/index.cjs +395 -0
- package/package.json +51 -0
- package/templates/schema.json +164 -0
- package/templates/vision-brief-schema.json +98 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mgw:assign
|
|
3
|
+
description: Claim an issue for a user — assigns via GitHub and updates board + state
|
|
4
|
+
argument-hint: "<issue-number> [username]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Bash
|
|
7
|
+
- Read
|
|
8
|
+
- Write
|
|
9
|
+
- Edit
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<objective>
|
|
13
|
+
Claim a GitHub issue for yourself or another team member. Three operations in one call:
|
|
14
|
+
|
|
15
|
+
1. **GitHub assignment** — `gh issue edit --add-assignee` to set the issue assignee
|
|
16
|
+
2. **State update** — write assignee to `.mgw/active/<issue>.json` (creates minimal entry
|
|
17
|
+
if not yet triaged)
|
|
18
|
+
3. **Board confirmation** — if a board is configured, emit the board URL so the team
|
|
19
|
+
can verify the assignment is reflected on the board item
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
- `mgw:assign 42` — assign issue #42 to yourself (@me)
|
|
23
|
+
- `mgw:assign 42 alice` — assign issue #42 to @alice
|
|
24
|
+
|
|
25
|
+
GitHub Projects v2 automatically syncs issue assignees to board items, so no direct
|
|
26
|
+
GraphQL mutation is needed for the board Assignees field.
|
|
27
|
+
|
|
28
|
+
Follows delegation boundary: only state and GitHub operations — no application code reads.
|
|
29
|
+
</objective>
|
|
30
|
+
|
|
31
|
+
<execution_context>
|
|
32
|
+
@~/.claude/commands/mgw/workflows/state.md
|
|
33
|
+
@~/.claude/commands/mgw/workflows/github.md
|
|
34
|
+
@~/.claude/commands/mgw/workflows/board-sync.md
|
|
35
|
+
</execution_context>
|
|
36
|
+
|
|
37
|
+
<context>
|
|
38
|
+
Arguments: $ARGUMENTS
|
|
39
|
+
|
|
40
|
+
State: .mgw/active/ (issue state — created if missing)
|
|
41
|
+
Board: .mgw/project.json (if configured — read for board URL only)
|
|
42
|
+
</context>
|
|
43
|
+
|
|
44
|
+
<process>
|
|
45
|
+
|
|
46
|
+
<step name="parse_args">
|
|
47
|
+
**Parse $ARGUMENTS into issue number and optional username:**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
ISSUE_NUMBER=$(echo "$ARGUMENTS" | awk '{print $1}')
|
|
51
|
+
USERNAME=$(echo "$ARGUMENTS" | awk '{print $2}')
|
|
52
|
+
|
|
53
|
+
# Validate issue number
|
|
54
|
+
if [ -z "$ISSUE_NUMBER" ]; then
|
|
55
|
+
echo "Usage: /mgw:assign <issue-number> [username]"
|
|
56
|
+
echo ""
|
|
57
|
+
echo " mgw:assign 42 — assign #42 to yourself"
|
|
58
|
+
echo " mgw:assign 42 alice — assign #42 to @alice"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if ! echo "$ISSUE_NUMBER" | grep -qE '^[0-9]+$'; then
|
|
63
|
+
echo "ERROR: Issue number must be numeric. Got: '${ISSUE_NUMBER}'"
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
```
|
|
67
|
+
</step>
|
|
68
|
+
|
|
69
|
+
<step name="validate_and_load">
|
|
70
|
+
**Initialize .mgw/ and load existing state (from state.md):**
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
|
|
74
|
+
if [ -z "$REPO_ROOT" ]; then
|
|
75
|
+
echo "ERROR: Not a git repository."
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
MGW_DIR="${REPO_ROOT}/.mgw"
|
|
80
|
+
|
|
81
|
+
# Ensure directory structure
|
|
82
|
+
mkdir -p "${MGW_DIR}/active" "${MGW_DIR}/completed"
|
|
83
|
+
|
|
84
|
+
# Ensure gitignore entries
|
|
85
|
+
for ENTRY in ".mgw/" ".worktrees/"; do
|
|
86
|
+
if ! grep -qF "${ENTRY}" "${REPO_ROOT}/.gitignore" 2>/dev/null; then
|
|
87
|
+
echo "${ENTRY}" >> "${REPO_ROOT}/.gitignore"
|
|
88
|
+
fi
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
# Initialize cross-refs if missing
|
|
92
|
+
if [ ! -f "${MGW_DIR}/cross-refs.json" ]; then
|
|
93
|
+
echo '{"links":[]}' > "${MGW_DIR}/cross-refs.json"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Find state file for this issue
|
|
97
|
+
STATE_FILE=$(ls "${MGW_DIR}/active/${ISSUE_NUMBER}-"*.json 2>/dev/null | head -1)
|
|
98
|
+
STATE_EXISTS=$( [ -n "$STATE_FILE" ] && echo "true" || echo "false" )
|
|
99
|
+
```
|
|
100
|
+
</step>
|
|
101
|
+
|
|
102
|
+
<step name="resolve_user">
|
|
103
|
+
**Resolve the assignee username:**
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# If no username provided, use the authenticated user
|
|
107
|
+
if [ -z "$USERNAME" ]; then
|
|
108
|
+
RESOLVED_USER=$(gh api user -q .login 2>/dev/null)
|
|
109
|
+
if [ -z "$RESOLVED_USER" ]; then
|
|
110
|
+
echo "ERROR: Cannot resolve current GitHub user. Check your gh auth status."
|
|
111
|
+
exit 1
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
RESOLVED_USER="$USERNAME"
|
|
115
|
+
# Validate user exists on GitHub
|
|
116
|
+
USER_EXISTS=$(gh api "users/${RESOLVED_USER}" -q .login 2>/dev/null)
|
|
117
|
+
if [ -z "$USER_EXISTS" ]; then
|
|
118
|
+
echo "ERROR: GitHub user '${RESOLVED_USER}' not found."
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
echo "MGW: Assigning #${ISSUE_NUMBER} to @${RESOLVED_USER}..."
|
|
124
|
+
```
|
|
125
|
+
</step>
|
|
126
|
+
|
|
127
|
+
<step name="resolve_coauthor">
|
|
128
|
+
**Build the Co-Authored-By tag for the assignee:**
|
|
129
|
+
|
|
130
|
+
GitHub assigns every account a noreply email: `{id}+{login}@users.noreply.github.com`.
|
|
131
|
+
This gets stored in the active state file so GSD commits for this issue use the right tag.
|
|
132
|
+
Falls back to the project-level default in `project.json` if resolution fails.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Fetch assignee's GitHub ID and display name
|
|
136
|
+
ASSIGNEE_DATA=$(gh api "users/${RESOLVED_USER}" --jq '{id: .id, name: .name}' 2>/dev/null)
|
|
137
|
+
ASSIGNEE_ID=$(echo "$ASSIGNEE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])" 2>/dev/null)
|
|
138
|
+
ASSIGNEE_NAME=$(echo "$ASSIGNEE_DATA" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['name'] or d.get('login',''))" 2>/dev/null)
|
|
139
|
+
|
|
140
|
+
if [ -n "$ASSIGNEE_ID" ]; then
|
|
141
|
+
COAUTHOR_TAG="${ASSIGNEE_NAME} <${ASSIGNEE_ID}+${RESOLVED_USER}@users.noreply.github.com>"
|
|
142
|
+
else
|
|
143
|
+
# Fall back to project-level default
|
|
144
|
+
COAUTHOR_TAG=$(python3 -c "
|
|
145
|
+
import json
|
|
146
|
+
try:
|
|
147
|
+
p = json.load(open('${MGW_DIR}/project.json'))
|
|
148
|
+
print(p.get('project', {}).get('coauthor', ''))
|
|
149
|
+
except:
|
|
150
|
+
print('')
|
|
151
|
+
" 2>/dev/null || echo "")
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
echo "MGW: Co-author tag: ${COAUTHOR_TAG}"
|
|
155
|
+
```
|
|
156
|
+
</step>
|
|
157
|
+
|
|
158
|
+
<step name="fetch_issue">
|
|
159
|
+
**Fetch issue metadata from GitHub:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --json number,title,url,labels,assignees,state 2>/dev/null)
|
|
163
|
+
if [ -z "$ISSUE_DATA" ]; then
|
|
164
|
+
echo "ERROR: Issue #${ISSUE_NUMBER} not found in this repo."
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
ISSUE_TITLE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['title'])" 2>/dev/null)
|
|
169
|
+
ISSUE_URL=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['url'])" 2>/dev/null)
|
|
170
|
+
ISSUE_STATE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['state'])" 2>/dev/null)
|
|
171
|
+
|
|
172
|
+
# Check if user is already assigned (idempotent)
|
|
173
|
+
ALREADY_ASSIGNED=$(echo "$ISSUE_DATA" | python3 -c "
|
|
174
|
+
import json,sys
|
|
175
|
+
d = json.load(sys.stdin)
|
|
176
|
+
assignees = [a['login'] for a in d.get('assignees', [])]
|
|
177
|
+
print('true' if '${RESOLVED_USER}' in assignees else 'false')
|
|
178
|
+
" 2>/dev/null)
|
|
179
|
+
```
|
|
180
|
+
</step>
|
|
181
|
+
|
|
182
|
+
<step name="assign_github">
|
|
183
|
+
**Assign the issue on GitHub:**
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
if [ "$ALREADY_ASSIGNED" = "true" ]; then
|
|
187
|
+
echo "MGW: @${RESOLVED_USER} is already assigned to #${ISSUE_NUMBER} — confirming state."
|
|
188
|
+
else
|
|
189
|
+
if ! gh issue edit "$ISSUE_NUMBER" --add-assignee "$RESOLVED_USER" 2>/dev/null; then
|
|
190
|
+
echo "ERROR: Failed to assign @${RESOLVED_USER} to #${ISSUE_NUMBER}."
|
|
191
|
+
echo " Check that the user has access to this repo."
|
|
192
|
+
exit 1
|
|
193
|
+
fi
|
|
194
|
+
echo "MGW: Assigned @${RESOLVED_USER} to #${ISSUE_NUMBER}."
|
|
195
|
+
fi
|
|
196
|
+
```
|
|
197
|
+
</step>
|
|
198
|
+
|
|
199
|
+
<step name="update_state">
|
|
200
|
+
**Write assignee to .mgw/active/ state (create minimal entry if needed):**
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
TIMESTAMP=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs current-timestamp --raw 2>/dev/null \
|
|
204
|
+
|| date -u +"%Y-%m-%dT%H:%M:%S.000Z")
|
|
205
|
+
|
|
206
|
+
if [ "$STATE_EXISTS" = "true" ]; then
|
|
207
|
+
# Update existing state file: set issue.assignee and coauthor fields
|
|
208
|
+
python3 -c "
|
|
209
|
+
import json
|
|
210
|
+
with open('${STATE_FILE}') as f:
|
|
211
|
+
state = json.load(f)
|
|
212
|
+
state['issue']['assignee'] = '${RESOLVED_USER}'
|
|
213
|
+
state['coauthor'] = '${COAUTHOR_TAG}'
|
|
214
|
+
state['updated_at'] = '${TIMESTAMP}'
|
|
215
|
+
with open('${STATE_FILE}', 'w') as f:
|
|
216
|
+
json.dump(state, f, indent=2)
|
|
217
|
+
print('updated')
|
|
218
|
+
" 2>/dev/null
|
|
219
|
+
|
|
220
|
+
else
|
|
221
|
+
# No state file — generate slug and create minimal entry
|
|
222
|
+
SLUG=\$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs generate-slug "${ISSUE_TITLE}" --raw 2>/dev/null | cut -c1-40 \
|
|
223
|
+
|| echo "issue-${ISSUE_NUMBER}")
|
|
224
|
+
|
|
225
|
+
NEW_STATE_FILE="${MGW_DIR}/active/${ISSUE_NUMBER}-${SLUG}.json"
|
|
226
|
+
|
|
227
|
+
python3 -c "
|
|
228
|
+
import json
|
|
229
|
+
state = {
|
|
230
|
+
'issue': {
|
|
231
|
+
'number': ${ISSUE_NUMBER},
|
|
232
|
+
'title': '${ISSUE_TITLE}',
|
|
233
|
+
'url': '${ISSUE_URL}',
|
|
234
|
+
'labels': [],
|
|
235
|
+
'assignee': '${RESOLVED_USER}'
|
|
236
|
+
},
|
|
237
|
+
'coauthor': '${COAUTHOR_TAG}',
|
|
238
|
+
'triage': {
|
|
239
|
+
'scope': { 'size': 'unknown', 'file_count': 0, 'files': [], 'systems': [] },
|
|
240
|
+
'validity': 'pending',
|
|
241
|
+
'security_risk': 'unknown',
|
|
242
|
+
'security_notes': '',
|
|
243
|
+
'conflicts': [],
|
|
244
|
+
'last_comment_count': 0,
|
|
245
|
+
'last_comment_at': None,
|
|
246
|
+
'gate_result': { 'status': 'pending', 'blockers': [], 'warnings': [], 'missing_fields': [] }
|
|
247
|
+
},
|
|
248
|
+
'gsd_route': None,
|
|
249
|
+
'gsd_artifacts': { 'type': None, 'path': None },
|
|
250
|
+
'pipeline_stage': 'new',
|
|
251
|
+
'comments_posted': [],
|
|
252
|
+
'linked_pr': None,
|
|
253
|
+
'linked_issues': [],
|
|
254
|
+
'linked_branches': [],
|
|
255
|
+
'created_at': '${TIMESTAMP}',
|
|
256
|
+
'updated_at': '${TIMESTAMP}'
|
|
257
|
+
}
|
|
258
|
+
with open('${NEW_STATE_FILE}', 'w') as f:
|
|
259
|
+
json.dump(state, f, indent=2)
|
|
260
|
+
print('created')
|
|
261
|
+
" 2>/dev/null
|
|
262
|
+
|
|
263
|
+
STATE_FILE="$NEW_STATE_FILE"
|
|
264
|
+
echo "MGW: Created minimal state entry at ${STATE_FILE}"
|
|
265
|
+
fi
|
|
266
|
+
```
|
|
267
|
+
</step>
|
|
268
|
+
|
|
269
|
+
<step name="check_board">
|
|
270
|
+
**Check if board is configured and emit board URL:**
|
|
271
|
+
|
|
272
|
+
GitHub Projects v2 automatically syncs issue assignees to board items. No direct
|
|
273
|
+
GraphQL mutation is needed — the board will reflect the new assignee when refreshed.
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
BOARD_URL=$(python3 -c "
|
|
277
|
+
import json, sys, os
|
|
278
|
+
try:
|
|
279
|
+
p = json.load(open('${MGW_DIR}/project.json'))
|
|
280
|
+
board = p.get('project', {}).get('project_board', {})
|
|
281
|
+
print(board.get('url', ''))
|
|
282
|
+
except:
|
|
283
|
+
print('')
|
|
284
|
+
" 2>/dev/null || echo "")
|
|
285
|
+
|
|
286
|
+
BOARD_ITEM_ID=$(python3 -c "
|
|
287
|
+
import json, sys
|
|
288
|
+
try:
|
|
289
|
+
p = json.load(open('${MGW_DIR}/project.json'))
|
|
290
|
+
for m in p.get('milestones', []):
|
|
291
|
+
for i in m.get('issues', []):
|
|
292
|
+
if i.get('github_number') == ${ISSUE_NUMBER}:
|
|
293
|
+
print(i.get('board_item_id', ''))
|
|
294
|
+
sys.exit(0)
|
|
295
|
+
print('')
|
|
296
|
+
except:
|
|
297
|
+
print('')
|
|
298
|
+
" 2>/dev/null || echo "")
|
|
299
|
+
|
|
300
|
+
BOARD_CONFIGURED=$( [ -n "$BOARD_URL" ] && echo "true" || echo "false" )
|
|
301
|
+
```
|
|
302
|
+
</step>
|
|
303
|
+
|
|
304
|
+
<step name="confirm">
|
|
305
|
+
**Emit assignment confirmation:**
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
echo ""
|
|
309
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
310
|
+
echo " MGW ► ISSUE ASSIGNED"
|
|
311
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
312
|
+
echo ""
|
|
313
|
+
echo " Issue : #${ISSUE_NUMBER} — ${ISSUE_TITLE}"
|
|
314
|
+
echo " URL : ${ISSUE_URL}"
|
|
315
|
+
echo " Assignee: @${RESOLVED_USER}"
|
|
316
|
+
echo " State : ${ISSUE_STATE}"
|
|
317
|
+
if [ "$BOARD_CONFIGURED" = "true" ]; then
|
|
318
|
+
echo " Board : ${BOARD_URL}"
|
|
319
|
+
if [ -n "$BOARD_ITEM_ID" ]; then
|
|
320
|
+
echo " (board item updated automatically by GitHub)"
|
|
321
|
+
else
|
|
322
|
+
echo " (issue not yet added to board — run /mgw:board show)"
|
|
323
|
+
fi
|
|
324
|
+
fi
|
|
325
|
+
echo ""
|
|
326
|
+
if [ "$ALREADY_ASSIGNED" = "true" ]; then
|
|
327
|
+
echo " Note: @${RESOLVED_USER} was already the assignee — state confirmed."
|
|
328
|
+
fi
|
|
329
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
330
|
+
```
|
|
331
|
+
</step>
|
|
332
|
+
|
|
333
|
+
</process>
|