Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,245 changes: 1,245 additions & 0 deletions .claude/commands/mgw/board.md

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions .claude/commands/mgw/issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,74 @@ Also add branch cross-ref:
BRANCH=$(git branch --show-current)
```
Add to linked_branches if not main/master.

After writing the state file, sync the board Status field (non-blocking):
```bash
# Board sync — update board Status field to reflect new pipeline_stage
# Source the shared utility from board-sync.md, then call it
# Reads REPO_ROOT from environment (set in validate_and_load / init_state)
update_board_status() {
local ISSUE_NUMBER="$1"
local NEW_STAGE="$2"
if [ -z "$ISSUE_NUMBER" ] || [ -z "$NEW_STAGE" ]; then return 0; fi
BOARD_NODE_ID=$(python3 -c "
import json,sys,os
try:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('node_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$BOARD_NODE_ID" ]; then return 0; fi
ITEM_ID=$(python3 -c "
import json,sys
try:
p=json.load(open('${REPO_ROOT}/.mgw/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 "")
if [ -z "$ITEM_ID" ]; then return 0; fi
FIELD_ID=$(python3 -c "
import json,sys,os
try:
s='${REPO_ROOT}/.mgw/board-schema.json'
if os.path.exists(s):
print(json.load(open(s)).get('fields',{}).get('status',{}).get('field_id',''))
else:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('fields',{}).get('status',{}).get('field_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$FIELD_ID" ]; then return 0; fi
OPTION_ID=$(python3 -c "
import json,sys,os
try:
stage='${NEW_STAGE}'
s='${REPO_ROOT}/.mgw/board-schema.json'
if os.path.exists(s):
print(json.load(open(s)).get('fields',{}).get('status',{}).get('options',{}).get(stage,''))
else:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('fields',{}).get('status',{}).get('options',{}).get(stage,''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$OPTION_ID" ]; then return 0; fi
gh api graphql -f query='
mutation($projectId:ID!,$itemId:ID!,$fieldId:ID!,$optionId:String!){
updateProjectV2ItemFieldValue(input:{projectId:$projectId,itemId:$itemId,fieldId:$fieldId,value:{singleSelectOptionId:$optionId}}){projectV2Item{id}}
}
' -f projectId="$BOARD_NODE_ID" -f itemId="$ITEM_ID" \
-f fieldId="$FIELD_ID" -f optionId="$OPTION_ID" 2>/dev/null || true
}

# Call after state file is written — non-blocking, never fails the pipeline
update_board_status $ISSUE_NUMBER "$pipeline_stage"
```

See @~/.claude/commands/mgw/workflows/board-sync.md for the full utility and data source reference.
</step>

<step name="offer_next">
Expand Down Expand Up @@ -465,5 +533,6 @@ Consider closing or commenting on the issue with your reasoning.
- [ ] Passed issues get mgw:triaged label
- [ ] User confirms, overrides, or rejects
- [ ] State file written to .mgw/active/ (if accepted) with comment tracking fields and gate_result
- [ ] Board Status field updated via update_board_status (non-blocking — failure does not block)
- [ ] Next steps offered
</success_criteria>
7 changes: 7 additions & 0 deletions .claude/commands/mgw/pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Works in two modes:
@~/.claude/commands/mgw/workflows/github.md
@~/.claude/commands/mgw/workflows/gsd.md
@~/.claude/commands/mgw/workflows/validation.md
@~/.claude/commands/mgw/workflows/board-sync.md
</execution_context>

<context>
Expand Down Expand Up @@ -247,6 +248,11 @@ Update state file:
- Set pipeline_stage to "pr-created"

Add cross-ref: issue → PR link in cross-refs.json.

Sync PR to board (non-blocking):
```bash
sync_pr_to_board $ISSUE_NUMBER $PR_NUMBER # non-blocking — add PR as board item
```
</step>

<step name="report">
Expand Down Expand Up @@ -274,4 +280,5 @@ Testing procedures posted as PR comment.
- [ ] Testing procedures posted as separate PR comment
- [ ] State file updated with PR number (linked mode)
- [ ] Cross-ref added (linked mode)
- [ ] PR added to board as board item after creation (non-blocking, linked mode only)
</success_criteria>
153 changes: 152 additions & 1 deletion .claude/commands/mgw/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Checkpoints requiring user input:
@~/.claude/commands/mgw/workflows/github.md
@~/.claude/commands/mgw/workflows/gsd.md
@~/.claude/commands/mgw/workflows/validation.md
@~/.claude/commands/mgw/workflows/board-sync.md
</execution_context>

<context>
Expand All @@ -57,6 +58,110 @@ REPO_ROOT=$(git rev-parse --show-toplevel)
DEFAULT=$(gh repo view --json defaultBranchRef -q .defaultBranchRef.name)
```

Define the board sync utilities (non-blocking — see board-sync.md for full reference):
```bash
update_board_status() {
local ISSUE_NUMBER="$1"
local NEW_STAGE="$2"
if [ -z "$ISSUE_NUMBER" ] || [ -z "$NEW_STAGE" ]; then return 0; fi
BOARD_NODE_ID=$(python3 -c "
import json,sys,os
try:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('node_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$BOARD_NODE_ID" ]; then return 0; fi
ITEM_ID=$(python3 -c "
import json,sys
try:
p=json.load(open('${REPO_ROOT}/.mgw/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 "")
if [ -z "$ITEM_ID" ]; then return 0; fi
FIELD_ID=$(python3 -c "
import json,sys,os
try:
s='${REPO_ROOT}/.mgw/board-schema.json'
if os.path.exists(s):
print(json.load(open(s)).get('fields',{}).get('status',{}).get('field_id',''))
else:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('fields',{}).get('status',{}).get('field_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$FIELD_ID" ]; then return 0; fi
OPTION_ID=$(python3 -c "
import json,sys,os
try:
stage='${NEW_STAGE}'
s='${REPO_ROOT}/.mgw/board-schema.json'
if os.path.exists(s):
print(json.load(open(s)).get('fields',{}).get('status',{}).get('options',{}).get(stage,''))
else:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('fields',{}).get('status',{}).get('options',{}).get(stage,''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$OPTION_ID" ]; then return 0; fi
gh api graphql -f query='
mutation($projectId:ID!,$itemId:ID!,$fieldId:ID!,$optionId:String!){
updateProjectV2ItemFieldValue(input:{projectId:$projectId,itemId:$itemId,fieldId:$fieldId,value:{singleSelectOptionId:$optionId}}){projectV2Item{id}}
}
' -f projectId="$BOARD_NODE_ID" -f itemId="$ITEM_ID" \
-f fieldId="$FIELD_ID" -f optionId="$OPTION_ID" 2>/dev/null || true
}

update_board_agent_state() {
local ISSUE_NUMBER="$1"
local STATE_TEXT="$2"
if [ -z "$ISSUE_NUMBER" ]; then return 0; fi
BOARD_NODE_ID=$(python3 -c "
import json,sys,os
try:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('node_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$BOARD_NODE_ID" ]; then return 0; fi
ITEM_ID=$(python3 -c "
import json,sys
try:
p=json.load(open('${REPO_ROOT}/.mgw/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 "")
if [ -z "$ITEM_ID" ]; then return 0; fi
FIELD_ID=$(python3 -c "
import json,sys,os
try:
s='${REPO_ROOT}/.mgw/board-schema.json'
if os.path.exists(s):
print(json.load(open(s)).get('fields',{}).get('ai_agent_state',{}).get('field_id',''))
else:
p=json.load(open('${REPO_ROOT}/.mgw/project.json'))
print(p.get('project',{}).get('project_board',{}).get('fields',{}).get('ai_agent_state',{}).get('field_id',''))
except: print('')
" 2>/dev/null || echo "")
if [ -z "$FIELD_ID" ]; then return 0; fi
gh api graphql -f query='
mutation($projectId:ID!,$itemId:ID!,$fieldId:ID!,$text:String!){
updateProjectV2ItemFieldValue(input:{projectId:$projectId,itemId:$itemId,fieldId:$fieldId,value:{text:$text}}){projectV2Item{id}}
}
' -f projectId="$BOARD_NODE_ID" -f itemId="$ITEM_ID" \
-f fieldId="$FIELD_ID" -f text="$STATE_TEXT" 2>/dev/null || true
}
```

Parse $ARGUMENTS for issue number. If missing:
```
AskUserQuestion(
Expand Down Expand Up @@ -248,7 +353,7 @@ Return ONLY valid JSON:
|---------------|--------|
| **informational** | Log: "MGW: ${NEW_COUNT} new comment(s) reviewed — informational, continuing." Update `triage.last_comment_count` in state file. Continue pipeline. |
| **material** | Log: "MGW: Material comment(s) detected — scope may have changed." Update state: add new_requirements to triage context. Update `triage.last_comment_count`. Re-read issue body for updated requirements. Continue with enriched context (pass new_requirements to planner). Check for security keywords in material comments (see below). |
| **blocking** | Log: "MGW: Blocking comment detected — pipeline paused." Update state: `pipeline_stage = "blocked"`. Apply mgw:blocked label. Post comment on issue: `> **MGW** . \`pipeline-blocked\` . Blocked by stakeholder comment. Reason: ${blocking_reason}`. Stop pipeline execution. |
| **blocking** | Log: "MGW: Blocking comment detected — pipeline paused." Update state: `pipeline_stage = "blocked"`. Apply mgw:blocked label. Call `update_board_status $ISSUE_NUMBER "blocked"` (non-blocking). Post comment on issue: `> **MGW** . \`pipeline-blocked\` . Blocked by stakeholder comment. Reason: ${blocking_reason}`. Stop pipeline execution. |

**Security keyword check for material comments:**
```bash
Expand Down Expand Up @@ -339,6 +444,9 @@ Log comment in state file (at `${REPO_ROOT}/.mgw/active/`).
Only run this step if gsd_route is "gsd:quick" or "gsd:quick --full".

Update pipeline_stage to "executing" in state file (at `${REPO_ROOT}/.mgw/active/`).
```bash
update_board_status $ISSUE_NUMBER "executing" # non-blocking board sync
```

Determine flags:
- "gsd:quick" → $QUICK_FLAGS = ""
Expand Down Expand Up @@ -373,6 +481,9 @@ mkdir -p "$QUICK_DIR"
```

3. **Spawn planner (task agent):**
```bash
update_board_agent_state $ISSUE_NUMBER "Planning" # non-blocking agent state
```
```
Task(
prompt="
Expand Down Expand Up @@ -475,6 +586,9 @@ If issues found and iteration < 2: spawn planner revision, then re-check.
If iteration >= 2: offer force proceed or abort.

7. **Spawn executor (task agent):**
```bash
update_board_agent_state $ISSUE_NUMBER "Executing" # non-blocking agent state
```
```
Task(
prompt="
Expand Down Expand Up @@ -506,6 +620,9 @@ VERIFY_RESULT=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs verify-summary "$
Parse JSON result. Use `passed` field for go/no-go. Checks summary existence, files created, and commits.

9. **(If --full) Spawn verifier:**
```bash
update_board_agent_state $ISSUE_NUMBER "Verifying" # non-blocking agent state
```
```
Task(
prompt="
Expand Down Expand Up @@ -539,6 +656,9 @@ node ~/.claude/get-shit-done/bin/gsd-tools.cjs commit "docs(quick-${next_num}):
```

Update state (at `${REPO_ROOT}/.mgw/active/`): gsd_artifacts.path = $QUICK_DIR, pipeline_stage = "verifying".
```bash
update_board_status $ISSUE_NUMBER "verifying" # non-blocking board sync
```
</step>

<step name="execute_gsd_milestone">
Expand Down Expand Up @@ -576,6 +696,7 @@ Set pipeline_stage to "discussing" and apply "mgw:discussing" label:
```bash
gh issue edit ${ISSUE_NUMBER} --remove-label "mgw:in-progress" 2>/dev/null
gh issue edit ${ISSUE_NUMBER} --add-label "mgw:discussing" 2>/dev/null
update_board_status $ISSUE_NUMBER "discussing" # non-blocking board sync
```

Present to user:
Expand Down Expand Up @@ -623,6 +744,9 @@ If proceed: apply "mgw:approved" label and continue.
```

Update pipeline_stage to "planning" (at `${REPO_ROOT}/.mgw/active/`).
```bash
update_board_status $ISSUE_NUMBER "planning" # non-blocking board sync
```

2. **If resuming with pipeline_stage = "planning" and ROADMAP.md exists:**
Discover phases from ROADMAP and run the full per-phase GSD lifecycle:
Expand Down Expand Up @@ -661,6 +785,9 @@ If proceed: apply "mgw:approved" label and continue.
```

**b. Spawn planner agent (gsd:plan-phase):**
```bash
update_board_agent_state $ISSUE_NUMBER "Planning phase ${PHASE_NUMBER}" # non-blocking agent state
```
```
Task(
prompt="
Expand Down Expand Up @@ -707,6 +834,7 @@ If proceed: apply "mgw:approved" label and continue.
```bash
EXEC_INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs init execute-phase "${PHASE_NUMBER}")
# Parse EXEC_INIT JSON for: executor_model, verifier_model, phase_dir, plans, incomplete_plans, plan_count
update_board_agent_state $ISSUE_NUMBER "Executing phase ${PHASE_NUMBER}" # non-blocking agent state
```
```
Task(
Expand Down Expand Up @@ -739,6 +867,9 @@ If proceed: apply "mgw:approved" label and continue.
```

**e. Spawn verifier agent (gsd:verify-phase):**
```bash
update_board_agent_state $ISSUE_NUMBER "Verifying phase ${PHASE_NUMBER}" # non-blocking agent state
```
```
Task(
prompt="
Expand Down Expand Up @@ -785,6 +916,9 @@ COMMENTEOF
```

After ALL phases complete → update pipeline_stage to "verifying" (at `${REPO_ROOT}/.mgw/active/`).
```bash
update_board_status $ISSUE_NUMBER "verifying" # non-blocking board sync
```
</step>

<step name="post_execution_update">
Expand Down Expand Up @@ -822,6 +956,9 @@ gh issue comment ${ISSUE_NUMBER} --body "$EXEC_BODY" 2>/dev/null || true
```

Update pipeline_stage to "pr-pending" (at `${REPO_ROOT}/.mgw/active/`).
```bash
update_board_status $ISSUE_NUMBER "pr-created" # non-blocking board sync (pr-pending maps to pr-created on board)
```
</step>

<step name="create_pr">
Expand Down Expand Up @@ -992,6 +1129,12 @@ Update state (at `${REPO_ROOT}/.mgw/active/`):
- linked_pr = PR number
- pipeline_stage = "pr-created"

```bash
update_board_status $ISSUE_NUMBER "pr-created" # non-blocking board sync
update_board_agent_state $ISSUE_NUMBER "" # clear agent state after PR creation (non-blocking)
sync_pr_to_board $ISSUE_NUMBER $PR_NUMBER # non-blocking — add PR as board item
```

Add cross-ref (at `${REPO_ROOT}/.mgw/cross-refs.json`): issue → PR.
</step>

Expand Down Expand Up @@ -1052,6 +1195,9 @@ gh issue comment ${ISSUE_NUMBER} --body "$PR_READY_BODY" 2>/dev/null || true
```

Update pipeline_stage to "done" (at `${REPO_ROOT}/.mgw/active/`).
```bash
update_board_status $ISSUE_NUMBER "done" # non-blocking board sync
```

Report to user:
```
Expand Down Expand Up @@ -1092,5 +1238,10 @@ Next:
- [ ] Worktree cleaned up, user returned to main workspace
- [ ] mgw:in-progress label removed at completion
- [ ] State file updated through all pipeline stages
- [ ] Board Status field synced at each pipeline_stage transition (non-blocking)
- [ ] AI Agent State field set before each GSD agent spawn (non-blocking)
- [ ] AI Agent State field cleared after PR creation (non-blocking)
- [ ] PR added to board as board item after creation (non-blocking)
- [ ] Board sync failures never block pipeline execution
- [ ] User prompted to run /mgw:sync after merge
</success_criteria>
Loading