Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c18c552
feat(phase1): foundation -- CLAUDE.md, ROADMAP.md integration, debug …
Mar 1, 2026
b5b2496
feat(phase2): pipeline completion -- debug route, milestone gate, PR …
Mar 1, 2026
a0b8190
feat(A4): add migration and resolution functions to state.cjs
Mar 1, 2026
13bb053
feat(A1): add state detection matrix to mgw:project
Mar 1, 2026
4efccc8
feat(A2,A5): implement align_from_gsd and reconcile_drift steps
Mar 1, 2026
3d77179
feat(B1): add Vision Brief schema and document vision cycle in CLAUDE.md
Mar 1, 2026
88ef17d
feat(A3): add milestone-mapper step and maps-to link type
Mar 1, 2026
37f3d5f
feat(C1): update command consumers to use resolveActiveMilestoneIndex
Mar 1, 2026
c6bc557
feat(C2): add cross-milestone detection to mgw:run validate_and_load
Mar 1, 2026
4ec1b39
feat(B2): add vision_intake and vision_research steps to mgw:project
Mar 1, 2026
c6fc5c8
feat(C3): add GSD milestone consistency check to mgw:sync
Mar 1, 2026
04e3637
feat(B3): add vision_questioning loop to mgw:project
Mar 1, 2026
f1b4830
feat(C4): add milestone mapping verification on completion and extend
Mar 1, 2026
6ef9706
feat(B4): add vision_synthesis and vision_condense steps to mgw:project
Mar 1, 2026
b628466
feat(B5): wire vision-handoff into gsd:new-project Task spawn
Mar 1, 2026
90f2119
mirror+feat(assign): add coauthor resolution from GitHub noreply email
Mar 1, 2026
4873324
fix(state): call migrateProjectState() at validate_and_load; moderniz…
snipcodeit Mar 1, 2026
260816b
fix(project): synthesize alignment-report.json on Fresh path; add Ali…
snipcodeit Mar 1, 2026
2e2ed53
fix(run): enforce ROADMAP.md on milestone switch; re-validate gsd:qui…
snipcodeit Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
333 changes: 333 additions & 0 deletions .claude/commands/mgw/assign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
---
name: mgw:assign
description: Claim an issue for a user — assigns via GitHub and updates board + state
argument-hint: "<issue-number> [username]"
allowed-tools:
- Bash
- Read
- Write
- Edit
---

<objective>
Claim a GitHub issue for yourself or another team member. Three operations in one call:

1. **GitHub assignment** — `gh issue edit --add-assignee` to set the issue assignee
2. **State update** — write assignee to `.mgw/active/<issue>.json` (creates minimal entry
if not yet triaged)
3. **Board confirmation** — if a board is configured, emit the board URL so the team
can verify the assignment is reflected on the board item

Usage:
- `mgw:assign 42` — assign issue #42 to yourself (@me)
- `mgw:assign 42 alice` — assign issue #42 to @alice

GitHub Projects v2 automatically syncs issue assignees to board items, so no direct
GraphQL mutation is needed for the board Assignees field.

Follows delegation boundary: only state and GitHub operations — no application code reads.
</objective>

<execution_context>
@~/.claude/commands/mgw/workflows/state.md
@~/.claude/commands/mgw/workflows/github.md
@~/.claude/commands/mgw/workflows/board-sync.md
</execution_context>

<context>
Arguments: $ARGUMENTS

State: .mgw/active/ (issue state — created if missing)
Board: .mgw/project.json (if configured — read for board URL only)
</context>

<process>

<step name="parse_args">
**Parse $ARGUMENTS into issue number and optional username:**

```bash
ISSUE_NUMBER=$(echo "$ARGUMENTS" | awk '{print $1}')
USERNAME=$(echo "$ARGUMENTS" | awk '{print $2}')

# Validate issue number
if [ -z "$ISSUE_NUMBER" ]; then
echo "Usage: /mgw:assign <issue-number> [username]"
echo ""
echo " mgw:assign 42 — assign #42 to yourself"
echo " mgw:assign 42 alice — assign #42 to @alice"
exit 1
fi

if ! echo "$ISSUE_NUMBER" | grep -qE '^[0-9]+$'; then
echo "ERROR: Issue number must be numeric. Got: '${ISSUE_NUMBER}'"
exit 1
fi
```
</step>

<step name="validate_and_load">
**Initialize .mgw/ and load existing state (from state.md):**

```bash
REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -z "$REPO_ROOT" ]; then
echo "ERROR: Not a git repository."
exit 1
fi

MGW_DIR="${REPO_ROOT}/.mgw"

# Ensure directory structure
mkdir -p "${MGW_DIR}/active" "${MGW_DIR}/completed"

# Ensure gitignore entries
for ENTRY in ".mgw/" ".worktrees/"; do
if ! grep -qF "${ENTRY}" "${REPO_ROOT}/.gitignore" 2>/dev/null; then
echo "${ENTRY}" >> "${REPO_ROOT}/.gitignore"
fi
done

# Initialize cross-refs if missing
if [ ! -f "${MGW_DIR}/cross-refs.json" ]; then
echo '{"links":[]}' > "${MGW_DIR}/cross-refs.json"
fi

# Find state file for this issue
STATE_FILE=$(ls "${MGW_DIR}/active/${ISSUE_NUMBER}-"*.json 2>/dev/null | head -1)
STATE_EXISTS=$( [ -n "$STATE_FILE" ] && echo "true" || echo "false" )
```
</step>

<step name="resolve_user">
**Resolve the assignee username:**

```bash
# If no username provided, use the authenticated user
if [ -z "$USERNAME" ]; then
RESOLVED_USER=$(gh api user -q .login 2>/dev/null)
if [ -z "$RESOLVED_USER" ]; then
echo "ERROR: Cannot resolve current GitHub user. Check your gh auth status."
exit 1
fi
else
RESOLVED_USER="$USERNAME"
# Validate user exists on GitHub
USER_EXISTS=$(gh api "users/${RESOLVED_USER}" -q .login 2>/dev/null)
if [ -z "$USER_EXISTS" ]; then
echo "ERROR: GitHub user '${RESOLVED_USER}' not found."
exit 1
fi
fi

echo "MGW: Assigning #${ISSUE_NUMBER} to @${RESOLVED_USER}..."
```
</step>

<step name="resolve_coauthor">
**Build the Co-Authored-By tag for the assignee:**

GitHub assigns every account a noreply email: `{id}+{login}@users.noreply.github.com`.
This gets stored in the active state file so GSD commits for this issue use the right tag.
Falls back to the project-level default in `project.json` if resolution fails.

```bash
# Fetch assignee's GitHub ID and display name
ASSIGNEE_DATA=$(gh api "users/${RESOLVED_USER}" --jq '{id: .id, name: .name}' 2>/dev/null)
ASSIGNEE_ID=$(echo "$ASSIGNEE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])" 2>/dev/null)
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)

if [ -n "$ASSIGNEE_ID" ]; then
COAUTHOR_TAG="${ASSIGNEE_NAME} <${ASSIGNEE_ID}+${RESOLVED_USER}@users.noreply.github.com>"
else
# Fall back to project-level default
COAUTHOR_TAG=$(python3 -c "
import json
try:
p = json.load(open('${MGW_DIR}/project.json'))
print(p.get('project', {}).get('coauthor', ''))
except:
print('')
" 2>/dev/null || echo "")
fi

echo "MGW: Co-author tag: ${COAUTHOR_TAG}"
```
</step>

<step name="fetch_issue">
**Fetch issue metadata from GitHub:**

```bash
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --json number,title,url,labels,assignees,state 2>/dev/null)
if [ -z "$ISSUE_DATA" ]; then
echo "ERROR: Issue #${ISSUE_NUMBER} not found in this repo."
exit 1
fi

ISSUE_TITLE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['title'])" 2>/dev/null)
ISSUE_URL=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['url'])" 2>/dev/null)
ISSUE_STATE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin)['state'])" 2>/dev/null)

# Check if user is already assigned (idempotent)
ALREADY_ASSIGNED=$(echo "$ISSUE_DATA" | python3 -c "
import json,sys
d = json.load(sys.stdin)
assignees = [a['login'] for a in d.get('assignees', [])]
print('true' if '${RESOLVED_USER}' in assignees else 'false')
" 2>/dev/null)
```
</step>

<step name="assign_github">
**Assign the issue on GitHub:**

```bash
if [ "$ALREADY_ASSIGNED" = "true" ]; then
echo "MGW: @${RESOLVED_USER} is already assigned to #${ISSUE_NUMBER} — confirming state."
else
if ! gh issue edit "$ISSUE_NUMBER" --add-assignee "$RESOLVED_USER" 2>/dev/null; then
echo "ERROR: Failed to assign @${RESOLVED_USER} to #${ISSUE_NUMBER}."
echo " Check that the user has access to this repo."
exit 1
fi
echo "MGW: Assigned @${RESOLVED_USER} to #${ISSUE_NUMBER}."
fi
```
</step>

<step name="update_state">
**Write assignee to .mgw/active/ state (create minimal entry if needed):**

```bash
TIMESTAMP=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs current-timestamp --raw 2>/dev/null \
|| date -u +"%Y-%m-%dT%H:%M:%S.000Z")

if [ "$STATE_EXISTS" = "true" ]; then
# Update existing state file: set issue.assignee and coauthor fields
python3 -c "
import json
with open('${STATE_FILE}') as f:
state = json.load(f)
state['issue']['assignee'] = '${RESOLVED_USER}'
state['coauthor'] = '${COAUTHOR_TAG}'
state['updated_at'] = '${TIMESTAMP}'
with open('${STATE_FILE}', 'w') as f:
json.dump(state, f, indent=2)
print('updated')
" 2>/dev/null

else
# No state file — generate slug and create minimal entry
SLUG=\$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs generate-slug "${ISSUE_TITLE}" --raw 2>/dev/null | cut -c1-40 \
|| echo "issue-${ISSUE_NUMBER}")

NEW_STATE_FILE="${MGW_DIR}/active/${ISSUE_NUMBER}-${SLUG}.json"

python3 -c "
import json
state = {
'issue': {
'number': ${ISSUE_NUMBER},
'title': '${ISSUE_TITLE}',
'url': '${ISSUE_URL}',
'labels': [],
'assignee': '${RESOLVED_USER}'
},
'coauthor': '${COAUTHOR_TAG}',
'triage': {
'scope': { 'size': 'unknown', 'file_count': 0, 'files': [], 'systems': [] },
'validity': 'pending',
'security_risk': 'unknown',
'security_notes': '',
'conflicts': [],
'last_comment_count': 0,
'last_comment_at': None,
'gate_result': { 'status': 'pending', 'blockers': [], 'warnings': [], 'missing_fields': [] }
},
'gsd_route': None,
'gsd_artifacts': { 'type': None, 'path': None },
'pipeline_stage': 'new',
'comments_posted': [],
'linked_pr': None,
'linked_issues': [],
'linked_branches': [],
'created_at': '${TIMESTAMP}',
'updated_at': '${TIMESTAMP}'
}
with open('${NEW_STATE_FILE}', 'w') as f:
json.dump(state, f, indent=2)
print('created')
" 2>/dev/null

STATE_FILE="$NEW_STATE_FILE"
echo "MGW: Created minimal state entry at ${STATE_FILE}"
fi
```
</step>

<step name="check_board">
**Check if board is configured and emit board URL:**

GitHub Projects v2 automatically syncs issue assignees to board items. No direct
GraphQL mutation is needed — the board will reflect the new assignee when refreshed.

```bash
BOARD_URL=$(python3 -c "
import json, sys, os
try:
p = json.load(open('${MGW_DIR}/project.json'))
board = p.get('project', {}).get('project_board', {})
print(board.get('url', ''))
except:
print('')
" 2>/dev/null || echo "")

BOARD_ITEM_ID=$(python3 -c "
import json, sys
try:
p = json.load(open('${MGW_DIR}/project.json'))
for m in p.get('milestones', []):
for i in m.get('issues', []):
if i.get('github_number') == ${ISSUE_NUMBER}:
print(i.get('board_item_id', ''))
sys.exit(0)
print('')
except:
print('')
" 2>/dev/null || echo "")

BOARD_CONFIGURED=$( [ -n "$BOARD_URL" ] && echo "true" || echo "false" )
```
</step>

<step name="confirm">
**Emit assignment confirmation:**

```bash
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " MGW ► ISSUE ASSIGNED"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " Issue : #${ISSUE_NUMBER} — ${ISSUE_TITLE}"
echo " URL : ${ISSUE_URL}"
echo " Assignee: @${RESOLVED_USER}"
echo " State : ${ISSUE_STATE}"
if [ "$BOARD_CONFIGURED" = "true" ]; then
echo " Board : ${BOARD_URL}"
if [ -n "$BOARD_ITEM_ID" ]; then
echo " (board item updated automatically by GitHub)"
else
echo " (issue not yet added to board — run /mgw:board show)"
fi
fi
echo ""
if [ "$ALREADY_ASSIGNED" = "true" ]; then
echo " Note: @${RESOLVED_USER} was already the assignee — state confirmed."
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
```
</step>

</process>
8 changes: 7 additions & 1 deletion .claude/commands/mgw/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,14 @@ Return a structured report:
- Active overlaps: [list or 'none']

### Recommended GSD Route
- Route: gsd:quick | gsd:quick --full | gsd:new-milestone
- Route: gsd:quick | gsd:quick --full | gsd:new-milestone | gsd:diagnose-issues
- Reasoning: [why this route]

Route selection guide:
- gsd:quick -- Small, well-defined task (1-3 files, clear fix)
- gsd:quick --full -- Medium task needing plan verification (3-8 files, some complexity)
- gsd:new-milestone -- Large feature or multi-system change (9+ files or new system)
- gsd:diagnose-issues -- Bug report where root cause is unclear; symptoms are known but the fix is not obvious; needs investigation before planning
</output_format>
",
subagent_type="general-purpose",
Expand Down
5 changes: 5 additions & 0 deletions .claude/commands/mgw/link.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Reference formats:
- Issue: 42 or #42 or issue:42
- PR: pr:15 or pr:#15
- Branch: branch:fix/auth-42
- GitHub Milestone: milestone:N
- GSD Milestone: gsd-milestone:name
</objective>

<execution_context>
Expand All @@ -39,6 +41,8 @@ Normalize reference formats:
- Bare number or #N → "issue:N"
- pr:N or pr:#N → "pr:N"
- branch:name → "branch:name"
- milestone:N → "milestone:N" (GitHub milestone by number)
- gsd-milestone:name → "gsd-milestone:name" (GSD milestone by id/name)

If fewer than 2 refs provided:
```
Expand Down Expand Up @@ -68,6 +72,7 @@ Determine link type:
- issue + pr → "implements"
- issue + branch → "tracks"
- pr + branch → "tracks"
- milestone + gsd-milestone → "maps-to" (maps GitHub milestone to GSD milestone)

Check for duplicate (same a+b pair exists). If duplicate, report and skip.

Expand Down
Loading