fraim-framework 2.0.30 → 2.0.34

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 (67) hide show
  1. package/bin/fraim.js +3 -18
  2. package/dist/src/cli/commands/init.js +29 -2
  3. package/dist/src/cli/commands/sync.js +82 -70
  4. package/dist/src/utils/script-sync-utils.js +216 -0
  5. package/dist/tests/debug-tools.js +6 -5
  6. package/dist/tests/test-chalk-regression.js +58 -8
  7. package/dist/tests/test-cli.js +70 -5
  8. package/dist/tests/test-end-to-end-hybrid-validation.js +328 -0
  9. package/dist/tests/test-first-run-journey.js +43 -3
  10. package/dist/tests/test-hybrid-script-execution.js +340 -0
  11. package/dist/tests/test-mcp-connection.js +2 -2
  12. package/dist/tests/test-mcp-issue-integration.js +12 -4
  13. package/dist/tests/test-mcp-lifecycle-methods.js +4 -4
  14. package/dist/tests/test-node-compatibility.js +24 -2
  15. package/dist/tests/test-prep-issue.js +4 -1
  16. package/dist/tests/test-script-location-independence.js +173 -0
  17. package/dist/tests/test-script-sync.js +557 -0
  18. package/dist/tests/test-session-rehydration.js +2 -2
  19. package/dist/tests/test-standalone.js +3 -3
  20. package/dist/tests/test-sync-version-update.js +1 -1
  21. package/dist/tests/test-telemetry.js +2 -2
  22. package/dist/tests/test-user-journey.js +8 -4
  23. package/dist/tests/test-utils.js +13 -0
  24. package/dist/tests/test-wizard.js +2 -2
  25. package/package.json +3 -3
  26. package/registry/rules/agent-testing-guidelines.md +502 -502
  27. package/registry/rules/ephemeral-execution.md +37 -27
  28. package/registry/rules/local-development.md +253 -251
  29. package/registry/rules/successful-debugging-patterns.md +491 -482
  30. package/registry/scripts/prep-issue.sh +468 -468
  31. package/registry/workflows/bootstrap/evaluate-code-quality.md +8 -2
  32. package/registry/workflows/bootstrap/verify-test-coverage.md +8 -2
  33. package/registry/workflows/customer-development/thank-customers.md +203 -193
  34. package/registry/workflows/customer-development/weekly-newsletter.md +366 -362
  35. package/registry/workflows/performance/analyze-performance.md +65 -63
  36. package/registry/workflows/product-building/implement.md +6 -2
  37. package/registry/workflows/product-building/prep-issue.md +11 -24
  38. package/registry/workflows/product-building/resolve.md +5 -1
  39. package/registry/workflows/replicate/replicate-discovery.md +336 -0
  40. package/registry/workflows/replicate/replicate-to-issues.md +319 -0
  41. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
  42. package/.windsurf/rules/windsurf-rules.md +0 -7
  43. package/.windsurf/workflows/resolve-issue.md +0 -6
  44. package/.windsurf/workflows/retrospect.md +0 -6
  45. package/.windsurf/workflows/start-design.md +0 -6
  46. package/.windsurf/workflows/start-impl.md +0 -6
  47. package/.windsurf/workflows/start-spec.md +0 -6
  48. package/.windsurf/workflows/start-tests.md +0 -6
  49. package/registry/scripts/build-scripts-generator.ts +0 -216
  50. package/registry/scripts/cleanup-branch.ts +0 -303
  51. package/registry/scripts/fraim-config.ts +0 -63
  52. package/registry/scripts/generate-engagement-emails.ts +0 -744
  53. package/registry/scripts/generic-issues-api.ts +0 -110
  54. package/registry/scripts/newsletter-helpers.ts +0 -874
  55. package/registry/scripts/openapi-generator.ts +0 -695
  56. package/registry/scripts/performance/profile-server.ts +0 -370
  57. package/registry/scripts/run-thank-you-workflow.ts +0 -122
  58. package/registry/scripts/send-newsletter-simple.ts +0 -104
  59. package/registry/scripts/send-thank-you-emails.ts +0 -57
  60. package/registry/workflows/replicate/re-implementation-strategy.md +0 -226
  61. package/registry/workflows/replicate/use-case-extraction.md +0 -135
  62. package/registry/workflows/replicate/visual-analysis.md +0 -154
  63. package/registry/workflows/replicate/website-discovery-analysis.md +0 -231
  64. package/sample_package.json +0 -18
  65. /package/registry/scripts/{replicate/comprehensive-explorer.py → comprehensive-explorer.py} +0 -0
  66. /package/registry/scripts/{replicate/interactive-explorer.py → interactive-explorer.py} +0 -0
  67. /package/registry/scripts/{replicate/scrape-site.py → scrape-site.py} +0 -0
@@ -1,468 +1,468 @@
1
- #!/bin/bash
2
-
3
- set -e # Exit on any error
4
-
5
- # Default flags (can be overridden by command line)
6
- SKIP_INSTALL=false
7
- SKIP_SERENA=true
8
- SKIP_EDITOR=true
9
- USE_DEFAULT_BRANCH=false
10
-
11
- # Function to display usage
12
- usage() {
13
- echo "Usage: $0 <issue_number> [editor] [flags]"
14
- echo "Example: $0 123"
15
- echo "Example: $0 123 windsurf"
16
- echo "Example: $0 123 claude --skip-install"
17
- echo "Example: $0 123 cursor --use-default --skip-serena"
18
- echo ""
19
- echo "Editor options: windsurf, claude, claudecode, cursor (default)"
20
- echo "If no editor specified, will try to detect from GitHub issue labels"
21
- echo ""
22
- echo "Flags:"
23
- echo " --skip-install Skip npm install step"
24
- echo " --skip-serena Skip Serena indexing step"
25
- echo " --skip-editor Skip opening editor"
26
- echo " --use-default Force branch creation from default branch (ignore current branch)"
27
- echo " --help Show this help message"
28
- exit 1
29
- }
30
-
31
- # Function to get editor from GitHub issue labels
32
- get_editor_from_issue() {
33
- local issue_num=$1
34
-
35
- # Try to get the issue labels using GitHub CLI or curl
36
- if command -v gh &> /dev/null; then
37
- # Use GitHub CLI if available
38
- local labels=$(gh issue view $issue_num -R "$REPO_OWNER/$REPO_NAME" --json labels --jq '.labels[].name' 2>/dev/null)
39
- else
40
- # Fallback to curl (requires GitHub token in GITHUB_TOKEN env var)
41
- if [ -n "$GITHUB_TOKEN" ]; then
42
- local labels=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
43
- "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_num" \
44
- | grep -o '"name":"ai-agent:[^"]*"' | sed 's/"name":"ai-agent://' | sed 's/"//' 2>/dev/null)
45
- else
46
- echo "Warning: No GitHub CLI or GITHUB_TOKEN found. Cannot auto-detect editor from issue labels." >&2
47
- return 1
48
- fi
49
- fi
50
-
51
- # Look for ai-agent labels
52
- for label in $labels; do
53
- if [[ $label == ai-agent:* ]]; then
54
- local editor=${label#ai-agent:}
55
- echo "Found ai-agent label: $label -> editor: $editor" >&2
56
- echo "$editor"
57
- return 0
58
- fi
59
- done
60
-
61
- echo "No ai-agent label found in issue #$issue_num, will continue without editor" >&2
62
- return 0
63
- }
64
-
65
- # Parse command line arguments
66
- if [ $# -eq 0 ]; then
67
- echo "Error: Issue number is required"
68
- usage
69
- fi
70
-
71
- ISSUE_NUMBER=$1
72
- EDITOR="" # Will be set later if not provided
73
-
74
- # Parse remaining arguments (editor and flags)
75
- shift 1 # Remove issue_number
76
- while [[ $# -gt 0 ]]; do
77
- case $1 in
78
- --skip-install)
79
- SKIP_INSTALL=true
80
- shift
81
- ;;
82
- --skip-serena)
83
- SKIP_SERENA=true
84
- shift
85
- ;;
86
- --skip-editor)
87
- SKIP_EDITOR=true
88
- shift
89
- ;;
90
- --use-default)
91
- USE_DEFAULT_BRANCH=true
92
- shift
93
- ;;
94
- --help)
95
- usage
96
- ;;
97
- windsurf|claude|claudecode|cursor)
98
- # Valid editor names
99
- if [ -z "$EDITOR" ]; then
100
- EDITOR="$1"
101
- else
102
- echo "Error: Multiple editors specified: $EDITOR and $1"
103
- usage
104
- fi
105
- shift
106
- ;;
107
- *)
108
- echo "Unknown argument: $1"
109
- echo "Valid editors: windsurf, claude, claudecode, cursor"
110
- echo "Valid flags: --skip-install, --skip-serena, --skip-editor, --use-default"
111
- usage
112
- ;;
113
- esac
114
- done
115
-
116
- # Load repository config from config.json
117
- CONFIG_FILE=".fraim/config.json"
118
- if [ ! -f "$CONFIG_FILE" ]; then
119
- echo "Error: Config file not found at $CONFIG_FILE" >&2
120
- exit 1
121
- fi
122
-
123
- if ! command -v node &> /dev/null; then
124
- echo "Error: node is required but not installed." >&2
125
- exit 1
126
- fi
127
-
128
- # Extract values using node reading from stdin to avoid path issues
129
- NODE_SCRIPT="
130
- const fs = require('fs');
131
- try {
132
- const input = fs.readFileSync(0, 'utf-8');
133
- const config = JSON.parse(input);
134
-
135
- // Support both 'repository' (new) and 'git' (legacy/current) schemas
136
- let repo = config.repository;
137
-
138
- if (!repo) {
139
- if (config.git) {
140
- repo = {
141
- owner: config.git.repoOwner,
142
- name: config.git.repoName,
143
- url: config.git.repoUrl || \`https://github.com/\${config.git.repoOwner}/\${config.git.repoName}.git\`
144
- };
145
- }
146
- }
147
-
148
- if (!repo || !repo.owner || !repo.name || !repo.url) {
149
- process.exit(1);
150
- }
151
- console.log(\`\${repo.owner}:\${repo.name}:\${repo.url}\`);
152
- } catch (e) {
153
- process.exit(1);
154
- }
155
- "
156
-
157
- REPO_INFO=$(cat "$CONFIG_FILE" | node -e "$NODE_SCRIPT")
158
- if [ $? -ne 0 ]; then
159
- echo "Error: Failed to parse repository config from $CONFIG_FILE" >&2
160
- echo "Required: repository.owner, repository.name, repository.url" >&2
161
- exit 1
162
- fi
163
-
164
- # Split the result into variables
165
- IFS=':' read -r REPO_OWNER REPO_NAME REPO_URL <<< "$REPO_INFO"
166
-
167
- echo "Repository Configuration:"
168
- echo " Owner: $REPO_OWNER"
169
- echo " Name: $REPO_NAME"
170
- echo " URL: $REPO_URL"
171
- echo
172
-
173
- echo "=== $REPO_NAME - Issue Preparation ==="
174
- echo "Preparing for Issue #$ISSUE_NUMBER"
175
- echo
176
-
177
- # Auto-detect editor from GitHub issue labels if not provided
178
- if [ -z "$EDITOR" ]; then
179
- echo "No editor specified, attempting to detect from GitHub issue labels..."
180
- echo "Fetching issue #$ISSUE_NUMBER labels from GitHub..."
181
- DETECTED_EDITOR=$(get_editor_from_issue $ISSUE_NUMBER)
182
- if [ $? -eq 0 ] && [ -n "$DETECTED_EDITOR" ]; then
183
- EDITOR="$DETECTED_EDITOR"
184
- echo "Auto-detected editor: $EDITOR"
185
- else
186
- EDITOR="cursor" # Default fallback
187
- echo "Using default editor: $EDITOR"
188
- fi
189
- else
190
- echo "Using specified editor: $EDITOR"
191
- fi
192
-
193
- # Step 1: Capture current branch BEFORE cloning
194
- echo "Step 1: Determining base branch from current repository..."
195
-
196
- # Determine base branch from the ORIGINAL repository (before cloning)
197
- if [ "$USE_DEFAULT_BRANCH" = true ]; then
198
- # Detect the default branch
199
- DEFAULT_BRANCH=""
200
- for candidate in main master develop; do
201
- if git ls-remote --exit-code --heads "$REPO_URL" "$candidate" >/dev/null 2>&1; then
202
- DEFAULT_BRANCH="$candidate"
203
- echo "✓ Detected default branch: $DEFAULT_BRANCH"
204
- break
205
- fi
206
- done
207
-
208
- # Fallback to master if nothing else worked
209
- if [ -z "$DEFAULT_BRANCH" ]; then
210
- DEFAULT_BRANCH="master"
211
- echo "⚠️ Could not detect default branch, defaulting to: $DEFAULT_BRANCH"
212
- fi
213
-
214
- BASE_BRANCH="$DEFAULT_BRANCH"
215
- echo "Using detected default branch as base: $BASE_BRANCH (--use-default flag)"
216
- else
217
- # Get current branch from the original repo
218
- ORIGINAL_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
219
-
220
- if [ -z "$ORIGINAL_BRANCH" ]; then
221
- echo "Warning: Detached HEAD detected in original repo, falling back to default branch detection"
222
-
223
- # Detect the default branch
224
- DEFAULT_BRANCH=""
225
- for candidate in main master develop; do
226
- if git ls-remote --exit-code --heads "$REPO_URL" "$candidate" >/dev/null 2>&1; then
227
- DEFAULT_BRANCH="$candidate"
228
- echo "✓ Detected default branch: $DEFAULT_BRANCH"
229
- break
230
- fi
231
- done
232
-
233
- # Fallback to master if nothing else worked
234
- if [ -z "$DEFAULT_BRANCH" ]; then
235
- DEFAULT_BRANCH="master"
236
- echo "⚠️ Could not detect default branch, defaulting to: $DEFAULT_BRANCH"
237
- fi
238
-
239
- BASE_BRANCH="$DEFAULT_BRANCH"
240
- else
241
- BASE_BRANCH="$ORIGINAL_BRANCH"
242
- echo "Using current branch from original repo as base: $BASE_BRANCH"
243
- fi
244
- fi
245
-
246
- echo "Issue number = $ISSUE_NUMBER, Editor = $EDITOR, Base branch = $BASE_BRANCH"
247
- echo
248
-
249
- # Step 2: Clone the repo
250
- echo "Step 2: Cloning repository..."
251
-
252
- # Get current workspace path
253
- CURRENT_WORKSPACE=$(pwd)
254
- echo "Current workspace: $CURRENT_WORKSPACE"
255
-
256
- # Change to parent directory
257
- echo "Changing to parent directory..."
258
- cd ..
259
- PARENT_DIR=$(pwd)
260
- echo "Parent directory: $PARENT_DIR"
261
-
262
- # Define clone directory name
263
- CLONE_DIR="$REPO_NAME - Issue $ISSUE_NUMBER"
264
- CLONE_PATH="$PARENT_DIR/$CLONE_DIR"
265
-
266
- # Check if directory already exists
267
- if [ -d "$CLONE_PATH" ]; then
268
- echo "Warning: Directory '$CLONE_PATH' already exists"
269
- read -p "Do you want to remove it and re-clone? (y/N): " -n 1 -r
270
- echo
271
- if [[ $REPLY =~ ^[Yy]$ ]]; then
272
- echo "Removing existing directory..."
273
- rm -rf "$CLONE_PATH"
274
- else
275
- echo "Aborting. Please remove the existing directory manually or choose a different issue number."
276
- exit 1
277
- fi
278
- fi
279
-
280
- # Clone the repository
281
- echo "Cloning to: $CLONE_PATH"
282
- git clone "$REPO_URL" "$CLONE_DIR"
283
-
284
- # Change into the cloned repository
285
- cd "$CLONE_DIR"
286
- echo "Changed into cloned repository: $(pwd)"
287
-
288
- echo
289
- echo "Step 3: Safety Checklist Verification"
290
- echo "====================================="
291
-
292
- # Safety checklist verification
293
- echo -n "✓ Confirmed current workspace path with pwd: "
294
- pwd
295
-
296
- echo -n "✓ Verified we are in the cloned repository: "
297
- if [[ "$(pwd)" = "$CLONE_PATH" ]]; then
298
- echo "YES"
299
- else
300
- echo "NO - ERROR!"
301
- exit 1
302
- fi
303
-
304
- echo -n "✓ Successfully changed into cloned repository: "
305
- if [ -d ".git" ]; then
306
- echo "YES"
307
- else
308
- echo "NO - ERROR!"
309
- exit 1
310
- fi
311
-
312
- echo
313
- echo "🎉 Huzzah! Issue #$ISSUE_NUMBER preparation completed successfully!"
314
- echo "Repository cloned to: $(pwd)"
315
- echo
316
-
317
- # Step 4: Create and checkout the feature branch
318
- echo "Step 4: Creating and checking out feature branch..."
319
-
320
- # Get issue title for branch naming
321
- ISSUE_TITLE=$(gh issue view $ISSUE_NUMBER -R "$REPO_OWNER/$REPO_NAME" --json title --jq '.title' 2>/dev/null || echo "issue-$ISSUE_NUMBER")
322
- BRANCH_NAME="feature/${ISSUE_NUMBER}-$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')"
323
-
324
- echo "Issue title: $ISSUE_TITLE"
325
- echo "Branch name: $BRANCH_NAME"
326
- echo "Base branch (from original repo): $BASE_BRANCH"
327
-
328
- # Create and checkout the branch
329
- if git show-ref --verify --quiet refs/heads/$BRANCH_NAME; then
330
- echo "Branch $BRANCH_NAME already exists locally, checking it out..."
331
- git checkout $BRANCH_NAME
332
- git pull origin $BRANCH_NAME 2>/dev/null || echo "No remote branch to pull from"
333
- else
334
- echo "Creating new branch $BRANCH_NAME from $BASE_BRANCH..."
335
-
336
- # Ensure we're on the base branch and it's up to date
337
- git checkout $BASE_BRANCH
338
- git pull origin $BASE_BRANCH 2>/dev/null || echo "Warning: Could not update $BASE_BRANCH from remote"
339
-
340
- # Create the new branch
341
- git checkout -b $BRANCH_NAME
342
-
343
- # Push branch without any commits
344
- # PR will be created by phase-change.yml when a phase label is applied
345
- echo "Pushing empty branch to origin..."
346
- git push -u origin $BRANCH_NAME
347
- fi
348
-
349
- echo "✓ Successfully on branch: $BRANCH_NAME (created from: $BASE_BRANCH)"
350
-
351
- # Install dependencies (optional)
352
- if [ "$SKIP_INSTALL" = false ]; then
353
- echo "Installing npm dependencies..."
354
- npm install
355
- if [ $? -eq 0 ]; then
356
- echo "✓ npm install completed successfully"
357
- else
358
- echo "⚠️ npm install failed - you may need to run it manually"
359
- fi
360
- else
361
- echo "⏭️ Skipping npm install (--skip-install flag)"
362
- fi
363
-
364
- # Step 5: Branch Ready for Phase Workflow
365
- echo "Step 5: Branch ready for phase workflow..."
366
- echo "✓ Branch $BRANCH_NAME created and pushed (no commits yet)"
367
- echo "✓ PR will be created when you apply a phase label to the issue"
368
- echo " (phase:spec, phase:design, phase:tests, or phase:impl)"
369
-
370
- # Step 6: Index the codebase with Serena (optional)
371
- if [ "$SKIP_SERENA" = false ]; then
372
- echo "Step 6: Indexing codebase with Serena..."
373
- echo "Running: uvx --from git+https://github.com/oraios/serena serena project index"
374
-
375
- if command -v uvx &> /dev/null; then
376
- uvx --from git+https://github.com/oraios/serena serena project index
377
- if [ $? -eq 0 ]; then
378
- echo "✓ Serena indexing completed successfully"
379
- else
380
- echo "⚠️ Serena indexing failed, but continuing..."
381
- fi
382
- else
383
- echo "⚠️ uvx command not found, skipping Serena indexing"
384
- echo " You may want to install uvx or run the indexing manually"
385
- fi
386
- else
387
- echo "⏭️ Skipping Serena indexing (--skip-serena flag)"
388
- fi
389
-
390
- # Step 7: Open the specified editor in the new directory (optional)
391
- if [ "$SKIP_EDITOR" = false ]; then
392
- echo "Step 7: Opening $EDITOR in the new workspace..."
393
- echo "Opening: $(pwd)"
394
-
395
- # Open the specified editor
396
- case $EDITOR in
397
- windsurf)
398
- if command -v windsurf &> /dev/null; then
399
- echo "Opening with 'windsurf' command..."
400
- windsurf .
401
- else
402
- echo "Could not find 'windsurf' command."
403
- echo "Please manually open Windsurf in: $(pwd)"
404
- fi
405
- ;;
406
- claude|claudecode)
407
- if command -v claude &> /dev/null; then
408
- echo "Opening with 'claude' command..."
409
- claude --dangerously-skip-permissions .
410
- else
411
- echo "Could not find 'claude' command."
412
- echo "Please manually open Claude in: $(pwd)"
413
- fi
414
- ;;
415
- cursor)
416
- if command -v cursor &> /dev/null; then
417
- echo "Opening with 'cursor' command..."
418
- cursor .
419
- elif command -v code &> /dev/null; then
420
- echo "Opening with 'code' command (VS Code fallback)..."
421
- code .
422
- else
423
- echo "Could not find 'cursor' or 'code' command."
424
- echo "Please manually open Cursor in: $(pwd)"
425
- fi
426
- ;;
427
- *)
428
- echo "Unknown editor: $EDITOR"
429
- echo "Supported editors: windsurf, claude, claudecode, cursor"
430
- echo "Please manually open your editor in: $(pwd)"
431
- ;;
432
- esac
433
- else
434
- echo "⏭️ Skipping editor launch (--skip-editor flag)"
435
- echo "Manually open your editor in: $(pwd)"
436
- fi
437
-
438
- echo
439
- echo "🎉 Setup Complete! Ready for agent workflow."
440
- echo
441
- echo "What was prepared:"
442
- echo "- ✅ Repository cloned and ready"
443
- echo "- ✅ Branch created: $BRANCH_NAME (from: $BASE_BRANCH)"
444
- echo "- ✅ Branch checked out and pushed"
445
- if [ "$SKIP_INSTALL" = false ]; then
446
- echo "- ✅ Dependencies installed"
447
- else
448
- echo "- ⏭️ Dependencies skipped"
449
- fi
450
- if [ "$SKIP_SERENA" = false ]; then
451
- echo "- ✅ Codebase indexed with Serena"
452
- else
453
- echo "- ⏭️ Serena indexing skipped"
454
- fi
455
- if [ "$SKIP_EDITOR" = false ]; then
456
- echo "- ✅ Editor opened: $EDITOR"
457
- else
458
- echo "- ⏭️ Editor launch skipped"
459
- fi
460
- echo
461
- echo "Agent workflow:"
462
- echo "1. Agent can start working immediately in this environment"
463
- echo "2. No need to create branches - already done!"
464
- echo "3. AGENT: Before starting work, you MUST read all documents in the retrospectives/ folder to learn from previous issues and avoid repeating mistakes."
465
- echo "4. Agent labels the issue with the appropriate phase (e.g., phase:spec, phase:design)"
466
- echo "5. GitHub Actions will automatically create the PR when phase label is applied"
467
- echo "6. Agent can commit and push changes directly"
468
- echo "7. GitHub Actions will update the PR when phase changes"
1
+ #!/bin/bash
2
+
3
+ set -e # Exit on any error
4
+
5
+ # Default flags (can be overridden by command line)
6
+ SKIP_INSTALL=false
7
+ SKIP_SERENA=true
8
+ SKIP_EDITOR=true
9
+ USE_DEFAULT_BRANCH=true
10
+
11
+ # Function to display usage
12
+ usage() {
13
+ echo "Usage: $0 <issue_number> [editor] [flags]"
14
+ echo "Example: $0 123"
15
+ echo "Example: $0 123 windsurf"
16
+ echo "Example: $0 123 claude --skip-install"
17
+ echo "Example: $0 123 cursor --use-default --skip-serena"
18
+ echo ""
19
+ echo "Editor options: windsurf, claude, claudecode, cursor (default)"
20
+ echo "If no editor specified, will try to detect from GitHub issue labels"
21
+ echo ""
22
+ echo "Flags:"
23
+ echo " --skip-install Skip npm install step"
24
+ echo " --skip-serena Skip Serena indexing step"
25
+ echo " --skip-editor Skip opening editor"
26
+ echo " --use-default Force branch creation from default branch (ignore current branch)"
27
+ echo " --help Show this help message"
28
+ exit 1
29
+ }
30
+
31
+ # Function to get editor from GitHub issue labels
32
+ get_editor_from_issue() {
33
+ local issue_num=$1
34
+
35
+ # Try to get the issue labels using GitHub CLI or curl
36
+ if command -v gh &> /dev/null; then
37
+ # Use GitHub CLI if available
38
+ local labels=$(gh issue view $issue_num -R "$REPO_OWNER/$REPO_NAME" --json labels --jq '.labels[].name' 2>/dev/null)
39
+ else
40
+ # Fallback to curl (requires GitHub token in GITHUB_TOKEN env var)
41
+ if [ -n "$GITHUB_TOKEN" ]; then
42
+ local labels=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
43
+ "https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/issues/$issue_num" \
44
+ | grep -o '"name":"ai-agent:[^"]*"' | sed 's/"name":"ai-agent://' | sed 's/"//' 2>/dev/null)
45
+ else
46
+ echo "Warning: No GitHub CLI or GITHUB_TOKEN found. Cannot auto-detect editor from issue labels." >&2
47
+ return 1
48
+ fi
49
+ fi
50
+
51
+ # Look for ai-agent labels
52
+ for label in $labels; do
53
+ if [[ $label == ai-agent:* ]]; then
54
+ local editor=${label#ai-agent:}
55
+ echo "Found ai-agent label: $label -> editor: $editor" >&2
56
+ echo "$editor"
57
+ return 0
58
+ fi
59
+ done
60
+
61
+ echo "No ai-agent label found in issue #$issue_num, will continue without editor" >&2
62
+ return 0
63
+ }
64
+
65
+ # Parse command line arguments
66
+ if [ $# -eq 0 ]; then
67
+ echo "Error: Issue number is required"
68
+ usage
69
+ fi
70
+
71
+ ISSUE_NUMBER=$1
72
+ EDITOR="" # Will be set later if not provided
73
+
74
+ # Parse remaining arguments (editor and flags)
75
+ shift 1 # Remove issue_number
76
+ while [[ $# -gt 0 ]]; do
77
+ case $1 in
78
+ --skip-install)
79
+ SKIP_INSTALL=true
80
+ shift
81
+ ;;
82
+ --skip-serena)
83
+ SKIP_SERENA=true
84
+ shift
85
+ ;;
86
+ --skip-editor)
87
+ SKIP_EDITOR=true
88
+ shift
89
+ ;;
90
+ --use-default)
91
+ USE_DEFAULT_BRANCH=true
92
+ shift
93
+ ;;
94
+ --help)
95
+ usage
96
+ ;;
97
+ windsurf|claude|claudecode|cursor)
98
+ # Valid editor names
99
+ if [ -z "$EDITOR" ]; then
100
+ EDITOR="$1"
101
+ else
102
+ echo "Error: Multiple editors specified: $EDITOR and $1"
103
+ usage
104
+ fi
105
+ shift
106
+ ;;
107
+ *)
108
+ echo "Unknown argument: $1"
109
+ echo "Valid editors: windsurf, claude, claudecode, cursor"
110
+ echo "Valid flags: --skip-install, --skip-serena, --skip-editor, --use-default"
111
+ usage
112
+ ;;
113
+ esac
114
+ done
115
+
116
+ # Load repository config from config.json
117
+ CONFIG_FILE=".fraim/config.json"
118
+ if [ ! -f "$CONFIG_FILE" ]; then
119
+ echo "Error: Config file not found at $CONFIG_FILE" >&2
120
+ exit 1
121
+ fi
122
+
123
+ if ! command -v node &> /dev/null; then
124
+ echo "Error: node is required but not installed." >&2
125
+ exit 1
126
+ fi
127
+
128
+ # Extract values using node reading from stdin to avoid path issues
129
+ NODE_SCRIPT="
130
+ const fs = require('fs');
131
+ try {
132
+ const input = fs.readFileSync(0, 'utf-8');
133
+ const config = JSON.parse(input);
134
+
135
+ // Support both 'repository' (new) and 'git' (legacy/current) schemas
136
+ let repo = config.repository;
137
+
138
+ if (!repo) {
139
+ if (config.git) {
140
+ repo = {
141
+ owner: config.git.repoOwner,
142
+ name: config.git.repoName,
143
+ url: config.git.repoUrl || \`https://github.com/\${config.git.repoOwner}/\${config.git.repoName}.git\`
144
+ };
145
+ }
146
+ }
147
+
148
+ if (!repo || !repo.owner || !repo.name || !repo.url) {
149
+ process.exit(1);
150
+ }
151
+ console.log(\`\${repo.owner}:\${repo.name}:\${repo.url}\`);
152
+ } catch (e) {
153
+ process.exit(1);
154
+ }
155
+ "
156
+
157
+ REPO_INFO=$(cat "$CONFIG_FILE" | node -e "$NODE_SCRIPT")
158
+ if [ $? -ne 0 ]; then
159
+ echo "Error: Failed to parse repository config from $CONFIG_FILE" >&2
160
+ echo "Required: repository.owner, repository.name, repository.url" >&2
161
+ exit 1
162
+ fi
163
+
164
+ # Split the result into variables
165
+ IFS=':' read -r REPO_OWNER REPO_NAME REPO_URL <<< "$REPO_INFO"
166
+
167
+ echo "Repository Configuration:"
168
+ echo " Owner: $REPO_OWNER"
169
+ echo " Name: $REPO_NAME"
170
+ echo " URL: $REPO_URL"
171
+ echo
172
+
173
+ echo "=== $REPO_NAME - Issue Preparation ==="
174
+ echo "Preparing for Issue #$ISSUE_NUMBER"
175
+ echo
176
+
177
+ # Auto-detect editor from GitHub issue labels if not provided
178
+ if [ -z "$EDITOR" ]; then
179
+ echo "No editor specified, attempting to detect from GitHub issue labels..."
180
+ echo "Fetching issue #$ISSUE_NUMBER labels from GitHub..."
181
+ DETECTED_EDITOR=$(get_editor_from_issue $ISSUE_NUMBER)
182
+ if [ $? -eq 0 ] && [ -n "$DETECTED_EDITOR" ]; then
183
+ EDITOR="$DETECTED_EDITOR"
184
+ echo "Auto-detected editor: $EDITOR"
185
+ else
186
+ EDITOR="cursor" # Default fallback
187
+ echo "Using default editor: $EDITOR"
188
+ fi
189
+ else
190
+ echo "Using specified editor: $EDITOR"
191
+ fi
192
+
193
+ # Step 1: Capture current branch BEFORE cloning
194
+ echo "Step 1: Determining base branch from current repository..."
195
+
196
+ # Determine base branch from the ORIGINAL repository (before cloning)
197
+ if [ "$USE_DEFAULT_BRANCH" = true ]; then
198
+ # Detect the default branch
199
+ DEFAULT_BRANCH=""
200
+ for candidate in main master develop; do
201
+ if git ls-remote --exit-code --heads "$REPO_URL" "$candidate" >/dev/null 2>&1; then
202
+ DEFAULT_BRANCH="$candidate"
203
+ echo "✓ Detected default branch: $DEFAULT_BRANCH"
204
+ break
205
+ fi
206
+ done
207
+
208
+ # Fallback to master if nothing else worked
209
+ if [ -z "$DEFAULT_BRANCH" ]; then
210
+ DEFAULT_BRANCH="master"
211
+ echo "⚠️ Could not detect default branch, defaulting to: $DEFAULT_BRANCH"
212
+ fi
213
+
214
+ BASE_BRANCH="$DEFAULT_BRANCH"
215
+ echo "Using detected default branch as base: $BASE_BRANCH (--use-default flag)"
216
+ else
217
+ # Get current branch from the original repo
218
+ ORIGINAL_BRANCH=$(git branch --show-current 2>/dev/null || echo "")
219
+
220
+ if [ -z "$ORIGINAL_BRANCH" ]; then
221
+ echo "Warning: Detached HEAD detected in original repo, falling back to default branch detection"
222
+
223
+ # Detect the default branch
224
+ DEFAULT_BRANCH=""
225
+ for candidate in main master develop; do
226
+ if git ls-remote --exit-code --heads "$REPO_URL" "$candidate" >/dev/null 2>&1; then
227
+ DEFAULT_BRANCH="$candidate"
228
+ echo "✓ Detected default branch: $DEFAULT_BRANCH"
229
+ break
230
+ fi
231
+ done
232
+
233
+ # Fallback to master if nothing else worked
234
+ if [ -z "$DEFAULT_BRANCH" ]; then
235
+ DEFAULT_BRANCH="master"
236
+ echo "⚠️ Could not detect default branch, defaulting to: $DEFAULT_BRANCH"
237
+ fi
238
+
239
+ BASE_BRANCH="$DEFAULT_BRANCH"
240
+ else
241
+ BASE_BRANCH="$ORIGINAL_BRANCH"
242
+ echo "Using current branch from original repo as base: $BASE_BRANCH"
243
+ fi
244
+ fi
245
+
246
+ echo "Issue number = $ISSUE_NUMBER, Editor = $EDITOR, Base branch = $BASE_BRANCH"
247
+ echo
248
+
249
+ # Step 2: Clone the repo
250
+ echo "Step 2: Cloning repository..."
251
+
252
+ # Get current workspace path
253
+ CURRENT_WORKSPACE=$(pwd)
254
+ echo "Current workspace: $CURRENT_WORKSPACE"
255
+
256
+ # Change to parent directory
257
+ echo "Changing to parent directory..."
258
+ cd ..
259
+ PARENT_DIR=$(pwd)
260
+ echo "Parent directory: $PARENT_DIR"
261
+
262
+ # Define clone directory name
263
+ CLONE_DIR="$REPO_NAME - Issue $ISSUE_NUMBER"
264
+ CLONE_PATH="$PARENT_DIR/$CLONE_DIR"
265
+
266
+ # Check if directory already exists
267
+ if [ -d "$CLONE_PATH" ]; then
268
+ echo "Warning: Directory '$CLONE_PATH' already exists"
269
+ read -p "Do you want to remove it and re-clone? (y/N): " -n 1 -r
270
+ echo
271
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
272
+ echo "Removing existing directory..."
273
+ rm -rf "$CLONE_PATH"
274
+ else
275
+ echo "Aborting. Please remove the existing directory manually or choose a different issue number."
276
+ exit 1
277
+ fi
278
+ fi
279
+
280
+ # Clone the repository
281
+ echo "Cloning to: $CLONE_PATH"
282
+ git clone "$REPO_URL" "$CLONE_DIR"
283
+
284
+ # Change into the cloned repository
285
+ cd "$CLONE_DIR"
286
+ echo "Changed into cloned repository: $(pwd)"
287
+
288
+ echo
289
+ echo "Step 3: Safety Checklist Verification"
290
+ echo "====================================="
291
+
292
+ # Safety checklist verification
293
+ echo -n "✓ Confirmed current workspace path with pwd: "
294
+ pwd
295
+
296
+ echo -n "✓ Verified we are in the cloned repository: "
297
+ if [[ "$(pwd)" = "$CLONE_PATH" ]]; then
298
+ echo "YES"
299
+ else
300
+ echo "NO - ERROR!"
301
+ exit 1
302
+ fi
303
+
304
+ echo -n "✓ Successfully changed into cloned repository: "
305
+ if [ -d ".git" ]; then
306
+ echo "YES"
307
+ else
308
+ echo "NO - ERROR!"
309
+ exit 1
310
+ fi
311
+
312
+ echo
313
+ echo "🎉 Huzzah! Issue #$ISSUE_NUMBER preparation completed successfully!"
314
+ echo "Repository cloned to: $(pwd)"
315
+ echo
316
+
317
+ # Step 4: Create and checkout the feature branch
318
+ echo "Step 4: Creating and checking out feature branch..."
319
+
320
+ # Get issue title for branch naming
321
+ ISSUE_TITLE=$(gh issue view $ISSUE_NUMBER -R "$REPO_OWNER/$REPO_NAME" --json title --jq '.title' 2>/dev/null || echo "issue-$ISSUE_NUMBER")
322
+ BRANCH_NAME="feature/${ISSUE_NUMBER}-$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-\|-$//g')"
323
+
324
+ echo "Issue title: $ISSUE_TITLE"
325
+ echo "Branch name: $BRANCH_NAME"
326
+ echo "Base branch (from original repo): $BASE_BRANCH"
327
+
328
+ # Create and checkout the branch
329
+ if git show-ref --verify --quiet refs/heads/$BRANCH_NAME; then
330
+ echo "Branch $BRANCH_NAME already exists locally, checking it out..."
331
+ git checkout $BRANCH_NAME
332
+ git pull origin $BRANCH_NAME 2>/dev/null || echo "No remote branch to pull from"
333
+ else
334
+ echo "Creating new branch $BRANCH_NAME from $BASE_BRANCH..."
335
+
336
+ # Ensure we're on the base branch and it's up to date
337
+ git checkout $BASE_BRANCH
338
+ git pull origin $BASE_BRANCH 2>/dev/null || echo "Warning: Could not update $BASE_BRANCH from remote"
339
+
340
+ # Create the new branch
341
+ git checkout -b $BRANCH_NAME
342
+
343
+ # Push branch without any commits
344
+ # PR will be created by phase-change.yml when a phase label is applied
345
+ echo "Pushing empty branch to origin..."
346
+ git push -u origin $BRANCH_NAME
347
+ fi
348
+
349
+ echo "✓ Successfully on branch: $BRANCH_NAME (created from: $BASE_BRANCH)"
350
+
351
+ # Install dependencies (optional)
352
+ if [ "$SKIP_INSTALL" = false ]; then
353
+ echo "Installing npm dependencies..."
354
+ npm install
355
+ if [ $? -eq 0 ]; then
356
+ echo "✓ npm install completed successfully"
357
+ else
358
+ echo "⚠️ npm install failed - you may need to run it manually"
359
+ fi
360
+ else
361
+ echo "⏭️ Skipping npm install (--skip-install flag)"
362
+ fi
363
+
364
+ # Step 5: Branch Ready for Phase Workflow
365
+ echo "Step 5: Branch ready for phase workflow..."
366
+ echo "✓ Branch $BRANCH_NAME created and pushed (no commits yet)"
367
+ echo "✓ PR will be created when you apply a phase label to the issue"
368
+ echo " (phase:spec, phase:design, phase:tests, or phase:impl)"
369
+
370
+ # Step 6: Index the codebase with Serena (optional)
371
+ if [ "$SKIP_SERENA" = false ]; then
372
+ echo "Step 6: Indexing codebase with Serena..."
373
+ echo "Running: uvx --from git+https://github.com/oraios/serena serena project index"
374
+
375
+ if command -v uvx &> /dev/null; then
376
+ uvx --from git+https://github.com/oraios/serena serena project index
377
+ if [ $? -eq 0 ]; then
378
+ echo "✓ Serena indexing completed successfully"
379
+ else
380
+ echo "⚠️ Serena indexing failed, but continuing..."
381
+ fi
382
+ else
383
+ echo "⚠️ uvx command not found, skipping Serena indexing"
384
+ echo " You may want to install uvx or run the indexing manually"
385
+ fi
386
+ else
387
+ echo "⏭️ Skipping Serena indexing (--skip-serena flag)"
388
+ fi
389
+
390
+ # Step 7: Open the specified editor in the new directory (optional)
391
+ if [ "$SKIP_EDITOR" = false ]; then
392
+ echo "Step 7: Opening $EDITOR in the new workspace..."
393
+ echo "Opening: $(pwd)"
394
+
395
+ # Open the specified editor
396
+ case $EDITOR in
397
+ windsurf)
398
+ if command -v windsurf &> /dev/null; then
399
+ echo "Opening with 'windsurf' command..."
400
+ windsurf .
401
+ else
402
+ echo "Could not find 'windsurf' command."
403
+ echo "Please manually open Windsurf in: $(pwd)"
404
+ fi
405
+ ;;
406
+ claude|claudecode)
407
+ if command -v claude &> /dev/null; then
408
+ echo "Opening with 'claude' command..."
409
+ claude --dangerously-skip-permissions .
410
+ else
411
+ echo "Could not find 'claude' command."
412
+ echo "Please manually open Claude in: $(pwd)"
413
+ fi
414
+ ;;
415
+ cursor)
416
+ if command -v cursor &> /dev/null; then
417
+ echo "Opening with 'cursor' command..."
418
+ cursor .
419
+ elif command -v code &> /dev/null; then
420
+ echo "Opening with 'code' command (VS Code fallback)..."
421
+ code .
422
+ else
423
+ echo "Could not find 'cursor' or 'code' command."
424
+ echo "Please manually open Cursor in: $(pwd)"
425
+ fi
426
+ ;;
427
+ *)
428
+ echo "Unknown editor: $EDITOR"
429
+ echo "Supported editors: windsurf, claude, claudecode, cursor"
430
+ echo "Please manually open your editor in: $(pwd)"
431
+ ;;
432
+ esac
433
+ else
434
+ echo "⏭️ Skipping editor launch (--skip-editor flag)"
435
+ echo "Manually open your editor in: $(pwd)"
436
+ fi
437
+
438
+ echo
439
+ echo "🎉 Setup Complete! Ready for agent workflow."
440
+ echo
441
+ echo "What was prepared:"
442
+ echo "- ✅ Repository cloned and ready"
443
+ echo "- ✅ Branch created: $BRANCH_NAME (from: $BASE_BRANCH)"
444
+ echo "- ✅ Branch checked out and pushed"
445
+ if [ "$SKIP_INSTALL" = false ]; then
446
+ echo "- ✅ Dependencies installed"
447
+ else
448
+ echo "- ⏭️ Dependencies skipped"
449
+ fi
450
+ if [ "$SKIP_SERENA" = false ]; then
451
+ echo "- ✅ Codebase indexed with Serena"
452
+ else
453
+ echo "- ⏭️ Serena indexing skipped"
454
+ fi
455
+ if [ "$SKIP_EDITOR" = false ]; then
456
+ echo "- ✅ Editor opened: $EDITOR"
457
+ else
458
+ echo "- ⏭️ Editor launch skipped"
459
+ fi
460
+ echo
461
+ echo "Agent workflow:"
462
+ echo "1. Agent can start working immediately in this environment"
463
+ echo "2. No need to create branches - already done!"
464
+ echo "3. AGENT: Before starting work, you MUST read all documents in the retrospectives/ folder to learn from previous issues and avoid repeating mistakes."
465
+ echo "4. Agent labels the issue with the appropriate phase (e.g., phase:spec, phase:design)"
466
+ echo "5. GitHub Actions will automatically create the PR when phase label is applied"
467
+ echo "6. Agent can commit and push changes directly"
468
+ echo "7. GitHub Actions will update the PR when phase changes"