fraim-framework 1.0.8 → 1.0.10
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/github/phase-change.yml +218 -0
- package/github/status-change.yml +68 -0
- package/github/sync-on-pr-review.yml +66 -0
- package/package.json +1 -1
- package/setup.js +82 -130
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
name: Phase Change Handler
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
issues: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
handle-phase-change:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
if: |
|
|
16
|
+
contains(github.event.label.name, 'phase:') ||
|
|
17
|
+
(github.event.action == 'unlabeled' && contains(github.event.label.name, 'phase:'))
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Setup Git
|
|
27
|
+
run: |
|
|
28
|
+
git config --global user.name "github-actions[bot]"
|
|
29
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
30
|
+
|
|
31
|
+
- name: Auth GitHub CLI
|
|
32
|
+
run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token
|
|
33
|
+
|
|
34
|
+
- name: Extract issue details
|
|
35
|
+
id: issue-details
|
|
36
|
+
run: |
|
|
37
|
+
ISSUE_NUMBER="${{ github.event.issue.number }}"
|
|
38
|
+
ISSUE_TITLE="${{ github.event.issue.title }}"
|
|
39
|
+
BRANCH_NAME="feature/${ISSUE_NUMBER}-$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')"
|
|
40
|
+
|
|
41
|
+
echo "issue_number=$ISSUE_NUMBER" >> $GITHUB_OUTPUT
|
|
42
|
+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
43
|
+
echo "issue_title=$ISSUE_TITLE" >> $GITHUB_OUTPUT
|
|
44
|
+
|
|
45
|
+
- name: Determine current phase
|
|
46
|
+
id: current-phase
|
|
47
|
+
run: |
|
|
48
|
+
LABELS='${{ toJson(github.event.issue.labels) }}'
|
|
49
|
+
CURRENT_PHASE=""
|
|
50
|
+
|
|
51
|
+
# Extract current phase from labels
|
|
52
|
+
for label in $(echo "$LABELS" | jq -r '.[].name'); do
|
|
53
|
+
if [[ $label == phase:* ]]; then
|
|
54
|
+
CURRENT_PHASE=$label
|
|
55
|
+
break
|
|
56
|
+
fi
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
echo "current_phase=$CURRENT_PHASE" >> $GITHUB_OUTPUT
|
|
60
|
+
echo "Current phase: $CURRENT_PHASE"
|
|
61
|
+
|
|
62
|
+
- name: Ensure branch exists and sync
|
|
63
|
+
run: |
|
|
64
|
+
BRANCH_NAME="${{ steps.issue-details.outputs.branch_name }}"
|
|
65
|
+
BASE="master"
|
|
66
|
+
|
|
67
|
+
git fetch origin "$BASE"
|
|
68
|
+
|
|
69
|
+
if git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then
|
|
70
|
+
echo "Branch $BRANCH_NAME exists, syncing..."
|
|
71
|
+
git fetch origin "$BRANCH_NAME"
|
|
72
|
+
git switch "$BRANCH_NAME"
|
|
73
|
+
git pull --rebase origin "$BRANCH_NAME"
|
|
74
|
+
else
|
|
75
|
+
echo "Branch $BRANCH_NAME does not exist, creating from $BASE..."
|
|
76
|
+
git switch -c "$BRANCH_NAME" "origin/$BASE"
|
|
77
|
+
git push -u origin "$BRANCH_NAME"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Design: add EMPTY commit to guarantee a diff
|
|
81
|
+
- name: phase:design → ensure empty commit (commit if missing)
|
|
82
|
+
if: steps.current-phase.outputs.current_phase == 'phase:design'
|
|
83
|
+
shell: bash
|
|
84
|
+
env:
|
|
85
|
+
ISSUE: ${{ github.event.issue.number }}
|
|
86
|
+
run: |
|
|
87
|
+
ISSUE_NUMBER="${{ steps.issue-details.outputs.issue_number }}"
|
|
88
|
+
ISSUE_TITLE="${{ steps.issue-details.outputs.issue_title }}"
|
|
89
|
+
BRANCH_NAME="${{ steps.issue-details.outputs.branch_name }}"
|
|
90
|
+
|
|
91
|
+
# Create empty commit to guarantee branch has commits
|
|
92
|
+
git commit --allow-empty -m "Design phase initiated for Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
|
|
93
|
+
git push origin "$BRANCH_NAME"
|
|
94
|
+
|
|
95
|
+
# Create or update Design PR
|
|
96
|
+
PR_TITLE="[Design] RFC for Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
|
|
97
|
+
PR_BODY="## Design RFC for Issue #$ISSUE_NUMBER
|
|
98
|
+
|
|
99
|
+
This PR contains the design RFC for: $ISSUE_TITLE
|
|
100
|
+
|
|
101
|
+
**Phase:** Design
|
|
102
|
+
**Issue:** #$ISSUE_NUMBER
|
|
103
|
+
**Branch:** \`$BRANCH_NAME\`
|
|
104
|
+
|
|
105
|
+
### RFC Document
|
|
106
|
+
- 📋 [RFC Document]($RFC_FILE)
|
|
107
|
+
|
|
108
|
+
**Note:** This is a Design PR. Implementation will follow in a separate PR after design approval.
|
|
109
|
+
|
|
110
|
+
"
|
|
111
|
+
|
|
112
|
+
# Check if PR already exists
|
|
113
|
+
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
114
|
+
|
|
115
|
+
if [ -n "$EXISTING_PR" ]; then
|
|
116
|
+
echo "Updating existing PR #$EXISTING_PR"
|
|
117
|
+
gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body "$PR_BODY"
|
|
118
|
+
gh pr edit "$EXISTING_PR" --add-label "phase:design" --remove-label "phase:tests" --remove-label "phase:impl"
|
|
119
|
+
else
|
|
120
|
+
echo "Creating new Design PR"
|
|
121
|
+
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base master --head "$BRANCH_NAME" --draft
|
|
122
|
+
# Add labels to the new PR
|
|
123
|
+
sleep 2 # Brief delay to ensure PR is created
|
|
124
|
+
NEW_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number')
|
|
125
|
+
gh pr edit "$NEW_PR" --add-label "phase:design" --remove-label "phase:tests" --remove-label "phase:impl"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
- name: Handle test phase
|
|
129
|
+
if: steps.current-phase.outputs.current_phase == 'phase:tests'
|
|
130
|
+
run: |
|
|
131
|
+
ISSUE_NUMBER="${{ steps.issue-details.outputs.issue_number }}"
|
|
132
|
+
ISSUE_TITLE="${{ steps.issue-details.outputs.issue_title }}"
|
|
133
|
+
BRANCH_NAME="${{ steps.issue-details.outputs.branch_name }}"
|
|
134
|
+
|
|
135
|
+
# Create or update Test Implementation PR
|
|
136
|
+
PR_TITLE="[Testing] Test Cases for Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
|
|
137
|
+
PR_BODY="## Test cases for Issue #$ISSUE_NUMBER
|
|
138
|
+
|
|
139
|
+
This PR contains the test cases for: $ISSUE_TITLE
|
|
140
|
+
|
|
141
|
+
**Phase:** Tests
|
|
142
|
+
**Issue:** #$ISSUE_NUMBER
|
|
143
|
+
**Branch:** \`$BRANCH_NAME\`
|
|
144
|
+
|
|
145
|
+
### Test Cases
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
**Note:** This is a Test PR. Implementation will follow after test approval.
|
|
149
|
+
|
|
150
|
+
"
|
|
151
|
+
|
|
152
|
+
# Check if PR already exists
|
|
153
|
+
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
154
|
+
|
|
155
|
+
if [ -n "$EXISTING_PR" ]; then
|
|
156
|
+
echo "Updating existing PR #$EXISTING_PR"
|
|
157
|
+
gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body "$PR_BODY"
|
|
158
|
+
gh pr edit "$EXISTING_PR" --add-label "phase:tests" --remove-label "phase:design" --remove-label "phase:impl"
|
|
159
|
+
else
|
|
160
|
+
echo "Creating new Test Plan PR"
|
|
161
|
+
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base master --head "$BRANCH_NAME" --draft
|
|
162
|
+
# Add labels to the new PR
|
|
163
|
+
sleep 2 # Brief delay to ensure PR is created
|
|
164
|
+
NEW_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number')
|
|
165
|
+
gh pr edit "$NEW_PR" --add-label "phase:tests" --remove-label "phase:design" --remove-label "phase:impl"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
- name: Handle implementation phase
|
|
169
|
+
if: steps.current-phase.outputs.current_phase == 'phase:impl'
|
|
170
|
+
run: |
|
|
171
|
+
ISSUE_NUMBER="${{ steps.issue-details.outputs.issue_number }}"
|
|
172
|
+
ISSUE_TITLE="${{ steps.issue-details.outputs.issue_title }}"
|
|
173
|
+
BRANCH_NAME="${{ steps.issue-details.outputs.branch_name }}"
|
|
174
|
+
|
|
175
|
+
# Create or update Implementation PR
|
|
176
|
+
PR_TITLE="[Implementation] Fixes for Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
|
|
177
|
+
PR_BODY="## Fixes for Issue #$ISSUE_NUMBER
|
|
178
|
+
|
|
179
|
+
This PR contains the fixes for: $ISSUE_TITLE
|
|
180
|
+
|
|
181
|
+
**Phase:** Implementation
|
|
182
|
+
**Issue:** #$ISSUE_NUMBER
|
|
183
|
+
**Branch:** \`$BRANCH_NAME\`
|
|
184
|
+
|
|
185
|
+
### Fixes
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
Closes #$ISSUE_NUMBER
|
|
189
|
+
|
|
190
|
+
"
|
|
191
|
+
|
|
192
|
+
# Check if PR already exists
|
|
193
|
+
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
194
|
+
|
|
195
|
+
if [ -n "$EXISTING_PR" ]; then
|
|
196
|
+
echo "Updating existing PR #$EXISTING_PR"
|
|
197
|
+
gh pr edit "$EXISTING_PR" --title "$PR_TITLE" --body "$PR_BODY"
|
|
198
|
+
gh pr edit "$EXISTING_PR" --add-label "phase:impl" --remove-label "phase:design" --remove-label "phase:tests"
|
|
199
|
+
else
|
|
200
|
+
echo "Creating new Implementation PR"
|
|
201
|
+
gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base master --head "$BRANCH_NAME" --draft
|
|
202
|
+
# Add labels to the new PR
|
|
203
|
+
sleep 2 # Brief delay to ensure PR is created
|
|
204
|
+
NEW_PR=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number')
|
|
205
|
+
gh pr edit "$NEW_PR" --add-label "phase:impl" --remove-label "phase:design" --remove-label "phase:tests"
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
- name: Normalize ISSUE and PR status to WIP (authoritative)
|
|
209
|
+
run: |
|
|
210
|
+
gh issue edit ${{ github.event.issue.number }} --add-label "status:wip" --remove-label "status:needs-review" --remove-label "status:complete" || true
|
|
211
|
+
|
|
212
|
+
# Get the PR number for the current branch and normalize its status
|
|
213
|
+
BRANCH_NAME="${{ steps.issue-details.outputs.branch_name }}"
|
|
214
|
+
PR_NUMBER=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
|
215
|
+
|
|
216
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
217
|
+
gh pr edit "$PR_NUMBER" --add-label "status:wip" --remove-label "status:needs-review" --remove-label "status:complete" || true
|
|
218
|
+
fi
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Status Change → Flip PR Draft/Ready
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
pull-requests: write
|
|
9
|
+
issues: read
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
# Serialize per ISSUE; last status wins
|
|
13
|
+
concurrency:
|
|
14
|
+
group: status-change-${{ github.event.issue.number }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
status-change:
|
|
19
|
+
if: startsWith(github.event.label.name, 'status:')
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout repository
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Setup Git
|
|
29
|
+
run: |
|
|
30
|
+
git config --global user.name "github-actions[bot]"
|
|
31
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
32
|
+
|
|
33
|
+
- name: Auth GitHub CLI
|
|
34
|
+
run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token
|
|
35
|
+
|
|
36
|
+
- name: Derive branch + find PR
|
|
37
|
+
id: pr
|
|
38
|
+
shell: bash
|
|
39
|
+
env:
|
|
40
|
+
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
41
|
+
ISSUE_TITLE: ${{ github.event.issue.title }}
|
|
42
|
+
run: |
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
slug=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g;s/^-+|-+$//g')
|
|
45
|
+
BR="feature/${ISSUE_NUMBER}-${slug}"
|
|
46
|
+
PR=$(gh pr list --state open --head "$BR" --json number --jq '.[0].number')
|
|
47
|
+
echo "pr=$PR" >> $GITHUB_OUTPUT
|
|
48
|
+
echo "label=${{ github.event.label.name }}" >> $GITHUB_OUTPUT
|
|
49
|
+
|
|
50
|
+
- name: Flip PR state (Draft/Ready) based on label
|
|
51
|
+
if: steps.pr.outputs.pr != ''
|
|
52
|
+
shell: bash
|
|
53
|
+
env:
|
|
54
|
+
PR: ${{ steps.pr.outputs.pr }}
|
|
55
|
+
LABEL: ${{ steps.pr.outputs.label }}
|
|
56
|
+
run: |
|
|
57
|
+
set -euo pipefail
|
|
58
|
+
case "$LABEL" in
|
|
59
|
+
status:needs-review)
|
|
60
|
+
gh pr ready "$PR"
|
|
61
|
+
gh pr edit "$PR" --add-label "status:needs-review" --remove-label "status:wip" --remove-label "status:complete" || true
|
|
62
|
+
;;
|
|
63
|
+
status:wip)
|
|
64
|
+
gh pr edit "$PR" --add-label "status:wip" --remove-label "status:needs-review" --remove-label "status:complete" || true
|
|
65
|
+
;;
|
|
66
|
+
*)
|
|
67
|
+
echo "No action for label: $LABEL" ;;
|
|
68
|
+
esac
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Sync Issue on PR Review
|
|
2
|
+
on:
|
|
3
|
+
pull_request_review:
|
|
4
|
+
types: [submitted]
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
pull-requests: write
|
|
8
|
+
issues: write
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: review-sync-${{ github.event.pull_request.number }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
sync-on-review:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
# no job-level `if` — we branch inside the script
|
|
18
|
+
env:
|
|
19
|
+
GH_TOKEN: ${{ secrets.PR_AUTOMATION_TOKEN || secrets.GITHUB_TOKEN }}
|
|
20
|
+
REPO: ${{ github.repository }}
|
|
21
|
+
steps:
|
|
22
|
+
- name: Derive review state, issue number, and branch
|
|
23
|
+
id: vars
|
|
24
|
+
shell: bash
|
|
25
|
+
run: |
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
STATE="${{ github.event.review.state }}"
|
|
28
|
+
BR="${{ github.event.pull_request.head.ref }}" # e.g., feature/26-slug
|
|
29
|
+
ISSUE="$(sed -nE 's#^feature/([0-9]+)-.*#\1#p' <<< "$BR" || true)"
|
|
30
|
+
echo "state=$STATE" >> $GITHUB_OUTPUT
|
|
31
|
+
echo "issue=$ISSUE" >> $GITHUB_OUTPUT
|
|
32
|
+
echo "branch=$BR" >> $GITHUB_OUTPUT
|
|
33
|
+
echo "Detected review state: $STATE; branch: $BR; issue: $ISSUE"
|
|
34
|
+
|
|
35
|
+
- name: Handle review states
|
|
36
|
+
if: steps.vars.outputs.issue != ''
|
|
37
|
+
shell: bash
|
|
38
|
+
env:
|
|
39
|
+
STATE: ${{ steps.vars.outputs.state }}
|
|
40
|
+
ISSUE: ${{ steps.vars.outputs.issue }}
|
|
41
|
+
PRNUM: ${{ github.event.pull_request.number }}
|
|
42
|
+
REPO: ${{ env.REPO }}
|
|
43
|
+
run: |
|
|
44
|
+
set -euo pipefail
|
|
45
|
+
case "$STATE" in
|
|
46
|
+
changes_requested)
|
|
47
|
+
# Flip to WIP
|
|
48
|
+
gh issue edit "$ISSUE" --repo "$REPO" \
|
|
49
|
+
--add-label "status:wip" --remove-label "status:needs-review" --remove-label "status:complete" || true
|
|
50
|
+
gh pr edit "$PRNUM" --repo "$REPO" \
|
|
51
|
+
--add-label "status:wip" --remove-label "status:needs-review" --remove-label "status:complete" || true
|
|
52
|
+
;;
|
|
53
|
+
approved)
|
|
54
|
+
# Flip to Complete
|
|
55
|
+
gh issue edit "$ISSUE" --repo "$REPO" \
|
|
56
|
+
--add-label "status:complete" --remove-label "status:wip" --remove-label "status:needs-review" || true
|
|
57
|
+
gh pr edit "$PRNUM" --repo "$REPO" --add-label "status:complete" --remove-label "status:wip" --remove-label "status:needs-review" || true
|
|
58
|
+
;;
|
|
59
|
+
commented)
|
|
60
|
+
# No-op; comments don’t change state
|
|
61
|
+
echo "Review was 'commented' — no state change."
|
|
62
|
+
;;
|
|
63
|
+
*)
|
|
64
|
+
echo "Unknown review state: $STATE"
|
|
65
|
+
;;
|
|
66
|
+
esac
|
package/package.json
CHANGED
package/setup.js
CHANGED
|
@@ -167,6 +167,67 @@ Owner: <agent>
|
|
|
167
167
|
logSuccess('Project structure created');
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
async function setupGitHubCLI() {
|
|
171
|
+
logStep('GitHub CLI Setup');
|
|
172
|
+
logInfo('To create GitHub labels automatically, you need GitHub CLI installed and authenticated.');
|
|
173
|
+
|
|
174
|
+
// Check if gh is installed
|
|
175
|
+
try {
|
|
176
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
177
|
+
logSuccess('GitHub CLI is already installed');
|
|
178
|
+
} catch (error) {
|
|
179
|
+
logWarning('GitHub CLI is not installed');
|
|
180
|
+
logInfo('Installing GitHub CLI...');
|
|
181
|
+
logInfo('📥 Download from: https://cli.github.com/');
|
|
182
|
+
logInfo('💻 Or use package manager:');
|
|
183
|
+
logInfo(' Windows: winget install GitHub.cli');
|
|
184
|
+
logInfo(' macOS: brew install gh');
|
|
185
|
+
logInfo(' Ubuntu/Debian: sudo apt install gh');
|
|
186
|
+
logInfo(' CentOS/RHEL: sudo yum install gh');
|
|
187
|
+
|
|
188
|
+
const waitForInstall = await askQuestion('Press Enter after installing GitHub CLI, or type "skip" to continue without it');
|
|
189
|
+
if (waitForInstall === 'skip') {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Check again
|
|
194
|
+
try {
|
|
195
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
196
|
+
logSuccess('GitHub CLI is now available');
|
|
197
|
+
} catch (error) {
|
|
198
|
+
logWarning('GitHub CLI still not available, continuing without it');
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Check if authenticated
|
|
204
|
+
try {
|
|
205
|
+
execSync('gh auth status', { stdio: 'pipe' });
|
|
206
|
+
logSuccess('GitHub CLI is already authenticated');
|
|
207
|
+
return true;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
logWarning('GitHub CLI is not authenticated');
|
|
210
|
+
logInfo('You need to authenticate with GitHub to create labels automatically.');
|
|
211
|
+
logInfo('🔐 Run: gh auth login');
|
|
212
|
+
logInfo(' This will open a browser for OAuth authentication');
|
|
213
|
+
|
|
214
|
+
const waitForAuth = await askQuestion('Press Enter after authenticating, or type "skip" to continue without authentication');
|
|
215
|
+
if (waitForAuth === 'skip') {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check authentication again
|
|
220
|
+
try {
|
|
221
|
+
execSync('gh auth status', { stdio: 'pipe' });
|
|
222
|
+
logSuccess('GitHub CLI is now authenticated');
|
|
223
|
+
return true;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
logWarning('GitHub CLI authentication failed, continuing without it');
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
170
231
|
function createGitHubLabels() {
|
|
171
232
|
const labels = [
|
|
172
233
|
{ name: 'phase:design', color: '0e8a16', description: 'Design phase - RFC creation and review' },
|
|
@@ -175,7 +236,6 @@ function createGitHubLabels() {
|
|
|
175
236
|
{ name: 'status:wip', color: 'fbca04', description: 'Work in progress' },
|
|
176
237
|
{ name: 'status:needs-review', color: 'd93f0b', description: 'Ready for review' },
|
|
177
238
|
{ name: 'status:complete', color: '0e8a16', description: 'Completed and approved' },
|
|
178
|
-
{ name: 'status:approved', color: '0e8a16', description: 'Approved and ready to merge' },
|
|
179
239
|
{ name: 'status:changes-requested', color: 'd93f0b', description: 'Changes requested in review' },
|
|
180
240
|
{ name: 'ai-agent:cursor', color: '5319e7', description: 'Assigned to Cursor AI agent' },
|
|
181
241
|
{ name: 'ai-agent:claude', color: 'c2e0c6', description: 'Assigned to Claude AI agent' },
|
|
@@ -209,7 +269,6 @@ function createLabelsConfigFile() {
|
|
|
209
269
|
{ name: 'status:wip', color: 'fbca04', description: 'Work in progress' },
|
|
210
270
|
{ name: 'status:needs-review', color: 'd93f0b', description: 'Ready for review' },
|
|
211
271
|
{ name: 'status:complete', color: '0e8a16', description: 'Completed and approved' },
|
|
212
|
-
{ name: 'status:approved', color: '0e8a16', description: 'Approved and ready to merge' },
|
|
213
272
|
{ name: 'status:changes-requested', color: 'd93f0b', description: 'Changes requested in review' },
|
|
214
273
|
{ name: 'ai-agent:cursor', color: '5319e7', description: 'Assigned to Cursor AI agent' },
|
|
215
274
|
{ name: 'ai-agent:claude', color: 'c2e0c6', description: 'Assigned to Claude AI agent' },
|
|
@@ -226,98 +285,17 @@ function createLabelsConfigFile() {
|
|
|
226
285
|
}
|
|
227
286
|
|
|
228
287
|
function createGitHubWorkflows() {
|
|
229
|
-
//
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
on:
|
|
233
|
-
issues:
|
|
234
|
-
types: [labeled, unlabeled]
|
|
235
|
-
|
|
236
|
-
jobs:
|
|
237
|
-
phase-change:
|
|
238
|
-
runs-on: ubuntu-latest
|
|
239
|
-
if: contains(github.event.issue.labels.*.name, 'phase:') || contains(github.event.label.name, 'phase:')
|
|
240
|
-
steps:
|
|
241
|
-
- name: Checkout
|
|
242
|
-
uses: actions/checkout@v4
|
|
243
|
-
|
|
244
|
-
- name: Phase Change Handler
|
|
245
|
-
run: |
|
|
246
|
-
echo "Phase change detected for issue #\${{ github.event.issue.number }}"
|
|
247
|
-
echo "New phase: \${{ github.event.label.name || 'phase removed' }}"
|
|
248
|
-
|
|
249
|
-
- name: Update Issue Status
|
|
250
|
-
if: contains(github.event.label.name, 'phase:')
|
|
251
|
-
run: |
|
|
252
|
-
gh issue edit \${{ github.event.issue.number }} --add-label "status:wip"
|
|
253
|
-
echo "Status updated to WIP for new phase"
|
|
254
|
-
`;
|
|
255
|
-
|
|
256
|
-
// Status change workflow
|
|
257
|
-
const statusChangeWorkflow = `name: Status Change Automation
|
|
258
|
-
|
|
259
|
-
on:
|
|
260
|
-
issues:
|
|
261
|
-
types: [labeled, unlabeled]
|
|
262
|
-
|
|
263
|
-
jobs:
|
|
264
|
-
status-change:
|
|
265
|
-
runs-on: ubuntu-latest
|
|
266
|
-
if: contains(github.event.issue.labels.*.name, 'status:') || contains(github.event.label.name, 'status:')
|
|
267
|
-
steps:
|
|
268
|
-
- name: Checkout
|
|
269
|
-
uses: actions/checkout@v4
|
|
270
|
-
|
|
271
|
-
- name: Status Change Handler
|
|
272
|
-
run: |
|
|
273
|
-
echo "Status change detected for issue #\${{ github.event.issue.number }}"
|
|
274
|
-
echo "New status: \${{ github.event.label.name || 'status removed' }}"
|
|
275
|
-
|
|
276
|
-
- name: Notify Team
|
|
277
|
-
if: contains(github.event.label.name, 'status:needs-review')
|
|
278
|
-
run: |
|
|
279
|
-
echo "Issue ready for review - notifying team"
|
|
280
|
-
`;
|
|
281
|
-
|
|
282
|
-
// Sync on PR review workflow
|
|
283
|
-
const syncOnPRReviewWorkflow = `name: Sync on PR Review
|
|
284
|
-
|
|
285
|
-
on:
|
|
286
|
-
pull_request_review:
|
|
287
|
-
types: [submitted, edited, dismissed]
|
|
288
|
-
|
|
289
|
-
jobs:
|
|
290
|
-
sync-review:
|
|
291
|
-
runs-on: ubuntu-latest
|
|
292
|
-
steps:
|
|
293
|
-
- name: Checkout
|
|
294
|
-
uses: actions/checkout@v4
|
|
295
|
-
|
|
296
|
-
- name: Sync Review Status
|
|
297
|
-
env:
|
|
298
|
-
GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
299
|
-
run: |
|
|
300
|
-
PR_NUMBER="\${{ github.event.pull_request.number }}"
|
|
301
|
-
REVIEW_STATE="\${{ github.event.review.state }}"
|
|
302
|
-
|
|
303
|
-
echo "PR #\$PR_NUMBER review state: \$REVIEW_STATE"
|
|
304
|
-
|
|
305
|
-
if [ "\$REVIEW_STATE" = "approved" ]; then
|
|
306
|
-
echo "PR approved - updating status"
|
|
307
|
-
gh pr edit \$PR_NUMBER --add-label "status:approved"
|
|
308
|
-
elif [ "\$REVIEW_STATE" = "changes_requested" ]; then
|
|
309
|
-
echo "Changes requested - updating status"
|
|
310
|
-
gh pr edit \$PR_NUMBER --add-label "status:changes-requested"
|
|
311
|
-
fi
|
|
312
|
-
|
|
313
|
-
echo "Review sync complete"
|
|
314
|
-
`;
|
|
315
|
-
|
|
316
|
-
writeFile('.github/workflows/phase-change.yml', phaseChangeWorkflow);
|
|
317
|
-
writeFile('.github/workflows/status-change.yml', statusChangeWorkflow);
|
|
318
|
-
writeFile('.github/workflows/sync-on-pr-review.yml', syncOnPRReviewWorkflow);
|
|
288
|
+
// Get the directory where this script is located (FRAIM package directory)
|
|
289
|
+
const fraimDir = __dirname;
|
|
319
290
|
|
|
320
|
-
|
|
291
|
+
// Copy actual workflow files from FRAIM/github folder
|
|
292
|
+
const workflowsSrc = path.join(fraimDir, 'github');
|
|
293
|
+
if (fs.existsSync(workflowsSrc)) {
|
|
294
|
+
copyDirectory(workflowsSrc, '.github/workflows');
|
|
295
|
+
logSuccess('GitHub workflows copied from FRAIM/github folder');
|
|
296
|
+
} else {
|
|
297
|
+
logWarning(`github folder not found at ${workflowsSrc}, skipping workflow creation`);
|
|
298
|
+
}
|
|
321
299
|
}
|
|
322
300
|
|
|
323
301
|
function createAgentFolders() {
|
|
@@ -361,19 +339,6 @@ async function runWizard() {
|
|
|
361
339
|
// Check prerequisites
|
|
362
340
|
logStep('Step 1: Checking Prerequisites');
|
|
363
341
|
|
|
364
|
-
// Check if gh CLI is available
|
|
365
|
-
let ghAvailable = false;
|
|
366
|
-
try {
|
|
367
|
-
execSync('gh --version', { stdio: 'pipe' });
|
|
368
|
-
logSuccess('GitHub CLI (gh) is available');
|
|
369
|
-
ghAvailable = true;
|
|
370
|
-
} catch (error) {
|
|
371
|
-
logWarning('GitHub CLI (gh) is not installed or not available');
|
|
372
|
-
logInfo('You can still set up FRAIM, but GitHub labels will need to be created manually');
|
|
373
|
-
logInfo('Install GitHub CLI: https://cli.github.com/');
|
|
374
|
-
ghAvailable = false;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
342
|
// Check if we're in a git repository
|
|
378
343
|
try {
|
|
379
344
|
execSync('git rev-parse --git-dir', { stdio: 'pipe' });
|
|
@@ -417,17 +382,18 @@ async function runWizard() {
|
|
|
417
382
|
|
|
418
383
|
// Step 5: GitHub Labels
|
|
419
384
|
logStep('Step 5: GitHub Labels');
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
385
|
+
const setupLabels = await askQuestion('Would you like to create GitHub labels for FRAIM?', 'y');
|
|
386
|
+
|
|
387
|
+
if (setupLabels === 'y' || setupLabels === 'yes') {
|
|
388
|
+
const ghAvailable = await setupGitHubCLI();
|
|
389
|
+
if (ghAvailable) {
|
|
424
390
|
createGitHubLabels();
|
|
425
391
|
} else {
|
|
426
|
-
logInfo('
|
|
392
|
+
logInfo('GitHub CLI not available - creating labels configuration file instead');
|
|
393
|
+
createLabelsConfigFile();
|
|
427
394
|
}
|
|
428
395
|
} else {
|
|
429
|
-
logInfo('GitHub
|
|
430
|
-
createLabelsConfigFile();
|
|
396
|
+
logInfo('Skipping GitHub label setup');
|
|
431
397
|
}
|
|
432
398
|
|
|
433
399
|
// Step 6: Summary
|
|
@@ -436,13 +402,7 @@ async function runWizard() {
|
|
|
436
402
|
logSuccess('FRAIM has been successfully set up in your repository!');
|
|
437
403
|
logInfo('Next steps:');
|
|
438
404
|
logInfo('1. Commit and push these files to GitHub');
|
|
439
|
-
|
|
440
|
-
if (ghAvailable) {
|
|
441
|
-
logInfo('2. GitHub labels have been created automatically');
|
|
442
|
-
} else {
|
|
443
|
-
logInfo('2. Import GitHub labels from .github/labels.json using the web interface');
|
|
444
|
-
}
|
|
445
|
-
|
|
405
|
+
logInfo('2. Import GitHub labels from .github/labels.json using the web interface');
|
|
446
406
|
logInfo('3. Create your first issue with phase labels');
|
|
447
407
|
logInfo('4. Start coordinating your AI agents!');
|
|
448
408
|
|
|
@@ -464,14 +424,6 @@ function runSetup() {
|
|
|
464
424
|
|
|
465
425
|
try {
|
|
466
426
|
// Check prerequisites
|
|
467
|
-
try {
|
|
468
|
-
execSync('gh --version', { stdio: 'pipe' });
|
|
469
|
-
} catch (error) {
|
|
470
|
-
logError('GitHub CLI (gh) is not installed or not available');
|
|
471
|
-
logInfo('Please install GitHub CLI: https://cli.github.com/');
|
|
472
|
-
process.exit(1);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
427
|
try {
|
|
476
428
|
execSync('git rev-parse --git-dir', { stdio: 'pipe' });
|
|
477
429
|
} catch (error) {
|
|
@@ -485,13 +437,13 @@ function runSetup() {
|
|
|
485
437
|
ensureDirectory('.github/workflows');
|
|
486
438
|
createAgentFolders();
|
|
487
439
|
createGitHubWorkflows();
|
|
488
|
-
|
|
440
|
+
createLabelsConfigFile();
|
|
489
441
|
|
|
490
442
|
logHeader('🎉 Setup Complete!');
|
|
491
443
|
logSuccess('FRAIM has been successfully set up in your repository!');
|
|
492
444
|
logInfo('Next steps:');
|
|
493
445
|
logInfo('1. Commit and push these files to GitHub');
|
|
494
|
-
logInfo('2.
|
|
446
|
+
logInfo('2. Import GitHub labels from .github/labels.json using the web interface');
|
|
495
447
|
logInfo('3. Create your first issue with phase labels');
|
|
496
448
|
logInfo('4. Start coordinating your AI agents!');
|
|
497
449
|
|