agentic-loop 3.4.7 → 3.5.2

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/ralph/setup.sh CHANGED
@@ -2,6 +2,80 @@
2
2
  # shellcheck shell=bash
3
3
  # setup.sh - Set up agentic-loop in a project
4
4
 
5
+ # Append missing sections from template to existing PROMPT.md
6
+ # Usage: append_prompt_sections <template_file> <target_file>
7
+ append_prompt_sections() {
8
+ local template="$1"
9
+ local target="$2"
10
+ local added=false
11
+
12
+ # Key sections to check and append if missing
13
+ local sections=(
14
+ "### 3. Verify It Actually Works"
15
+ "**Playwright MCP**"
16
+ "**Chrome DevTools MCP**"
17
+ "## Verification Checklist"
18
+ "## Code Quality Standards"
19
+ "## Architecture Rules"
20
+ "## Scalability Rules"
21
+ )
22
+
23
+ for section in "${sections[@]}"; do
24
+ if ! grep -qF "$section" "$target" 2>/dev/null; then
25
+ # Section missing - extract and append it
26
+ case "$section" in
27
+ "### 3. Verify It Actually Works"|"**Playwright MCP**"|"**Chrome DevTools MCP**")
28
+ # Extract the browser verification block
29
+ if ! grep -qF "Playwright MCP" "$target" 2>/dev/null; then
30
+ echo "" >> "$target"
31
+ echo "### 3. Verify It Actually Works" >> "$target"
32
+ echo "" >> "$target"
33
+ echo "**You have browser tools - USE THEM to verify your work:**" >> "$target"
34
+ echo "" >> "$target"
35
+ echo "**Playwright MCP** (testing & automation):" >> "$target"
36
+ echo "- Navigate to URLs and verify page content" >> "$target"
37
+ echo "- Take screenshots to verify UI renders correctly" >> "$target"
38
+ echo "- Click elements and fill forms to test interactions" >> "$target"
39
+ echo "- Get accessibility snapshots for a11y testing" >> "$target"
40
+ echo "" >> "$target"
41
+ echo "**Chrome DevTools MCP** (debugging & inspection):" >> "$target"
42
+ echo "- Inspect DOM elements and check console for errors" >> "$target"
43
+ echo "- Debug network requests and responses" >> "$target"
44
+ echo "- Check element styles and computed properties" >> "$target"
45
+ echo "" >> "$target"
46
+ echo "**Do NOT say you're done until:**" >> "$target"
47
+ echo "- All unit tests pass" >> "$target"
48
+ echo "- You've opened the browser and visually verified the feature works" >> "$target"
49
+ echo "- Console has no errors" >> "$target"
50
+ echo "- Error states are handled gracefully" >> "$target"
51
+ added=true
52
+ break # Don't re-add for the other MCP checks
53
+ fi
54
+ ;;
55
+ "## Verification Checklist")
56
+ echo "" >> "$target"
57
+ echo "## Verification Checklist" >> "$target"
58
+ echo "" >> "$target"
59
+ echo "Before considering any story complete:" >> "$target"
60
+ echo "" >> "$target"
61
+ echo "- [ ] All acceptance criteria are met" >> "$target"
62
+ echo "- [ ] All error handling from story is implemented" >> "$target"
63
+ echo "- [ ] TypeScript/code compiles without errors" >> "$target"
64
+ echo "- [ ] Unit tests written and passing" >> "$target"
65
+ echo "- [ ] **Browser verified** - used MCP tools to visually confirm it works" >> "$target"
66
+ echo "- [ ] No console errors" >> "$target"
67
+ echo "- [ ] Linting passes" >> "$target"
68
+ added=true
69
+ ;;
70
+ esac
71
+ fi
72
+ done
73
+
74
+ if [[ "$added" == "true" ]]; then
75
+ echo " Updated PROMPT.md with new sections"
76
+ fi
77
+ }
78
+
5
79
  ralph_setup() {
6
80
  echo ""
7
81
  echo " _ _ _ _ "
@@ -83,7 +157,7 @@ setup_ralph_dir() {
83
157
  echo " Created .ralph/config.json"
84
158
  fi
85
159
 
86
- # Copy signs template
160
+ # Copy or merge signs template
87
161
  if [[ ! -f ".ralph/signs.json" ]]; then
88
162
  if [[ -f "$pkg_root/templates/signs.json" ]]; then
89
163
  cp "$pkg_root/templates/signs.json" ".ralph/signs.json"
@@ -91,12 +165,41 @@ setup_ralph_dir() {
91
165
  echo '{"signs": []}' > ".ralph/signs.json"
92
166
  fi
93
167
  echo " Created .ralph/signs.json"
168
+ else
169
+ # Merge new default signs into existing file
170
+ if [[ -f "$pkg_root/templates/signs.json" ]] && command -v jq &>/dev/null; then
171
+ local existing_ids new_signs_added=0
172
+ existing_ids=$(jq -r '.signs[].id // empty' ".ralph/signs.json" 2>/dev/null | tr '\n' '|')
173
+
174
+ # Add signs from template that don't exist locally
175
+ while IFS= read -r sign; do
176
+ local sign_id
177
+ sign_id=$(echo "$sign" | jq -r '.id // empty')
178
+ if [[ -n "$sign_id" && ! "$existing_ids" =~ "$sign_id" ]]; then
179
+ # Add this sign to local file
180
+ local tmp_file
181
+ tmp_file=$(mktemp)
182
+ jq --argjson new_sign "$sign" '.signs += [$new_sign]' ".ralph/signs.json" > "$tmp_file"
183
+ mv "$tmp_file" ".ralph/signs.json"
184
+ ((new_signs_added++))
185
+ fi
186
+ done < <(jq -c '.signs[]' "$pkg_root/templates/signs.json" 2>/dev/null)
187
+
188
+ if [[ $new_signs_added -gt 0 ]]; then
189
+ echo " Merged $new_signs_added new sign(s) into .ralph/signs.json"
190
+ fi
191
+ fi
94
192
  fi
95
193
 
96
- # Create PROMPT.md if missing
97
- if [[ ! -f "PROMPT.md" ]] && [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
98
- cp "$pkg_root/templates/PROMPT.md" "PROMPT.md"
99
- echo " Created PROMPT.md"
194
+ # Create or update PROMPT.md
195
+ if [[ -f "$pkg_root/templates/PROMPT.md" ]]; then
196
+ if [[ ! -f "PROMPT.md" ]]; then
197
+ cp "$pkg_root/templates/PROMPT.md" "PROMPT.md"
198
+ echo " Created PROMPT.md"
199
+ else
200
+ # Append missing sections to existing PROMPT.md
201
+ append_prompt_sections "$pkg_root/templates/PROMPT.md" "PROMPT.md"
202
+ fi
100
203
  fi
101
204
 
102
205
  # Auto-detect project settings
@@ -468,18 +571,12 @@ EOF
468
571
  # Set up GitHub Actions CI/CD
469
572
  setup_github_ci() {
470
573
  local pkg_root="$1"
471
- local template_dir="$pkg_root/templates/github/workflows"
472
574
 
473
575
  # Skip if not a git repo
474
576
  if [[ ! -d ".git" ]]; then
475
577
  return 0
476
578
  fi
477
579
 
478
- # Skip if templates don't exist
479
- if [[ ! -d "$template_dir" ]]; then
480
- return 0
481
- fi
482
-
483
580
  # Skip if already set up
484
581
  if [[ -f ".github/workflows/pr.yml" ]] && [[ -f ".github/workflows/nightly.yml" ]]; then
485
582
  return 0
@@ -493,7 +590,7 @@ setup_github_ci() {
493
590
  read -r -p " Set up GitHub Actions? [Y/n] " response
494
591
 
495
592
  if [[ "$response" =~ ^[Nn]$ ]]; then
496
- echo " Skipped GitHub Actions (run 'ralph ci install' later)"
593
+ echo " Skipped GitHub Actions (run 'npx agentic-loop ci install' later)"
497
594
  return 0
498
595
  fi
499
596
 
@@ -502,15 +599,200 @@ setup_github_ci() {
502
599
  # Create workflows directory
503
600
  mkdir -p .github/workflows
504
601
 
505
- # Install PR workflow
602
+ # Read config values for dynamic generation
603
+ local backend_dir frontend_dir test_cmd
604
+ backend_dir=$(get_config '.directories.backend' "")
605
+ frontend_dir=$(get_config '.directories.frontend' "")
606
+ test_cmd=$(get_config '.checks.testCommand' "")
607
+
608
+ # Generate PR workflow
506
609
  if [[ ! -f ".github/workflows/pr.yml" ]]; then
507
- cp "$template_dir/pr.yml" .github/workflows/pr.yml
610
+ _generate_pr_workflow "$backend_dir" "$frontend_dir"
508
611
  echo " Created .github/workflows/pr.yml (fast PR checks)"
509
612
  fi
510
613
 
511
- # Install nightly workflow
614
+ # Generate nightly workflow
512
615
  if [[ ! -f ".github/workflows/nightly.yml" ]]; then
513
- cp "$template_dir/nightly.yml" .github/workflows/nightly.yml
616
+ _generate_nightly_workflow "$backend_dir" "$frontend_dir" "$test_cmd"
514
617
  echo " Created .github/workflows/nightly.yml (nightly full tests)"
515
618
  fi
516
619
  }
620
+
621
+ # Generate PR workflow based on project structure
622
+ _generate_pr_workflow() {
623
+ local backend_dir="$1"
624
+ local frontend_dir="$2"
625
+
626
+ cat > .github/workflows/pr.yml << 'HEADER'
627
+ # Fast PR checks - lint only, no tests
628
+ name: PR Check
629
+
630
+ on:
631
+ pull_request:
632
+ branches: [main, master]
633
+
634
+ jobs:
635
+ lint:
636
+ runs-on: ubuntu-latest
637
+ steps:
638
+ - uses: actions/checkout@v4
639
+ HEADER
640
+
641
+ # Detect and add Python steps
642
+ if [[ -f "pyproject.toml" ]] || [[ -f "requirements.txt" ]] || [[ -n "$backend_dir" && -f "$backend_dir/pyproject.toml" ]]; then
643
+ local py_dir="${backend_dir:-.}"
644
+ cat >> .github/workflows/pr.yml << EOF
645
+
646
+ - name: Set up Python
647
+ uses: actions/setup-python@v5
648
+ with:
649
+ python-version: '3.11'
650
+
651
+ - name: Install Python dependencies
652
+ run: |
653
+ pip install ruff uv
654
+ cd $py_dir && uv pip install -e . --system 2>/dev/null || pip install -e . 2>/dev/null || true
655
+
656
+ - name: Ruff lint
657
+ run: cd $py_dir && ruff check .
658
+ EOF
659
+ fi
660
+
661
+ # Detect and add Node.js steps
662
+ if [[ -f "package.json" ]] || [[ -n "$frontend_dir" && -f "$frontend_dir/package.json" ]]; then
663
+ local node_dir="${frontend_dir:-.}"
664
+ cat >> .github/workflows/pr.yml << EOF
665
+
666
+ - name: Set up Node.js
667
+ uses: actions/setup-node@v4
668
+ with:
669
+ node-version: '20'
670
+
671
+ - name: Install Node dependencies
672
+ run: cd $node_dir && npm ci
673
+
674
+ - name: Lint
675
+ run: cd $node_dir && npm run lint 2>/dev/null || true
676
+
677
+ - name: TypeScript check
678
+ run: cd $node_dir && npx tsc --noEmit 2>/dev/null || true
679
+
680
+ - name: Build
681
+ run: cd $node_dir && npm run build 2>/dev/null || true
682
+ EOF
683
+ fi
684
+ }
685
+
686
+ # Generate nightly workflow based on project structure
687
+ _generate_nightly_workflow() {
688
+ local backend_dir="$1"
689
+ local frontend_dir="$2"
690
+ local test_cmd="$3"
691
+
692
+ cat > .github/workflows/nightly.yml << 'HEADER'
693
+ # Nightly comprehensive test suite
694
+ name: Nightly Tests
695
+
696
+ on:
697
+ schedule:
698
+ - cron: '0 3 * * *' # 3am UTC daily
699
+ workflow_dispatch:
700
+
701
+ jobs:
702
+ test:
703
+ runs-on: ubuntu-latest
704
+ HEADER
705
+
706
+ # Add services if backend exists (likely needs DB)
707
+ if [[ -n "$backend_dir" ]] || [[ -f "pyproject.toml" ]]; then
708
+ cat >> .github/workflows/nightly.yml << 'EOF'
709
+
710
+ services:
711
+ postgres:
712
+ image: postgres:15
713
+ env:
714
+ POSTGRES_USER: test
715
+ POSTGRES_PASSWORD: test
716
+ POSTGRES_DB: test
717
+ ports:
718
+ - 5432:5432
719
+ options: >-
720
+ --health-cmd pg_isready
721
+ --health-interval 10s
722
+ --health-timeout 5s
723
+ --health-retries 5
724
+
725
+ env:
726
+ DATABASE_URL: postgresql://test:test@localhost:5432/test
727
+ EOF
728
+ fi
729
+
730
+ cat >> .github/workflows/nightly.yml << 'EOF'
731
+
732
+ steps:
733
+ - uses: actions/checkout@v4
734
+ EOF
735
+
736
+ # Add Python setup and tests
737
+ if [[ -f "pyproject.toml" ]] || [[ -n "$backend_dir" && -f "$backend_dir/pyproject.toml" ]]; then
738
+ local py_dir="${backend_dir:-.}"
739
+ local py_test_cmd="${test_cmd:-pytest -v --tb=short}"
740
+
741
+ cat >> .github/workflows/nightly.yml << EOF
742
+
743
+ - name: Set up Python
744
+ uses: actions/setup-python@v5
745
+ with:
746
+ python-version: '3.11'
747
+
748
+ - name: Install Python dependencies
749
+ run: |
750
+ pip install uv
751
+ cd $py_dir && uv pip install -e ".[dev]" --system 2>/dev/null || pip install -e . 2>/dev/null || true
752
+
753
+ - name: Run migrations
754
+ run: cd $py_dir && alembic upgrade head 2>/dev/null || true
755
+ continue-on-error: true
756
+
757
+ - name: Python tests
758
+ run: cd $py_dir && $py_test_cmd
759
+ continue-on-error: true
760
+ EOF
761
+ fi
762
+
763
+ # Add Node.js setup and tests
764
+ if [[ -f "package.json" ]] || [[ -n "$frontend_dir" && -f "$frontend_dir/package.json" ]]; then
765
+ local node_dir="${frontend_dir:-.}"
766
+ cat >> .github/workflows/nightly.yml << EOF
767
+
768
+ - name: Set up Node.js
769
+ uses: actions/setup-node@v4
770
+ with:
771
+ node-version: '20'
772
+
773
+ - name: Install Node dependencies
774
+ run: cd $node_dir && npm ci
775
+
776
+ - name: Node tests
777
+ run: cd $node_dir && npm test 2>/dev/null || true
778
+ continue-on-error: true
779
+ EOF
780
+ fi
781
+
782
+ # Add PRD tests
783
+ cat >> .github/workflows/nightly.yml << 'EOF'
784
+
785
+ - name: Run PRD tests
786
+ if: hashFiles('.ralph/prd.json') != ''
787
+ run: npx agentic-loop test prd 2>/dev/null || true
788
+ continue-on-error: true
789
+
790
+ notify:
791
+ needs: test
792
+ runs-on: ubuntu-latest
793
+ if: failure()
794
+ steps:
795
+ - name: Notify on failure
796
+ run: echo "Nightly tests failed!"
797
+ EOF
798
+ }
@@ -223,6 +223,57 @@ run_unit_tests() {
223
223
  fi
224
224
  }
225
225
 
226
+ # Expand config placeholders in a string
227
+ # Usage: expand_config_vars "curl {config.urls.backend}/api"
228
+ # Expands: {config.urls.backend}, {config.urls.frontend}, {config.directories.*}
229
+ _expand_config_vars() {
230
+ local input="$1"
231
+ local config="$RALPH_DIR/config.json"
232
+
233
+ # No config file, return as-is
234
+ [[ ! -f "$config" ]] && echo "$input" && return
235
+
236
+ local result="$input"
237
+
238
+ # Expand {config.urls.backend}
239
+ if [[ "$result" == *"{config.urls.backend}"* ]]; then
240
+ local backend_url
241
+ backend_url=$(jq -r '.urls.backend // .api.baseUrl // empty' "$config" 2>/dev/null)
242
+ if [[ -n "$backend_url" ]]; then
243
+ result="${result//\{config.urls.backend\}/$backend_url}"
244
+ fi
245
+ fi
246
+
247
+ # Expand {config.urls.frontend}
248
+ if [[ "$result" == *"{config.urls.frontend}"* ]]; then
249
+ local frontend_url
250
+ frontend_url=$(jq -r '.urls.frontend // .testUrlBase // empty' "$config" 2>/dev/null)
251
+ if [[ -n "$frontend_url" ]]; then
252
+ result="${result//\{config.urls.frontend\}/$frontend_url}"
253
+ fi
254
+ fi
255
+
256
+ # Expand {config.directories.backend}
257
+ if [[ "$result" == *"{config.directories.backend}"* ]]; then
258
+ local backend_dir
259
+ backend_dir=$(jq -r '.directories.backend // empty' "$config" 2>/dev/null)
260
+ if [[ -n "$backend_dir" ]]; then
261
+ result="${result//\{config.directories.backend\}/$backend_dir}"
262
+ fi
263
+ fi
264
+
265
+ # Expand {config.directories.frontend}
266
+ if [[ "$result" == *"{config.directories.frontend}"* ]]; then
267
+ local frontend_dir
268
+ frontend_dir=$(jq -r '.directories.frontend // empty' "$config" 2>/dev/null)
269
+ if [[ -n "$frontend_dir" ]]; then
270
+ result="${result//\{config.directories.frontend\}/$frontend_dir}"
271
+ fi
272
+ fi
273
+
274
+ echo "$result"
275
+ }
276
+
226
277
  # Verify PRD acceptance criteria / test steps
227
278
  verify_prd_criteria() {
228
279
  local story="$1"
@@ -247,9 +298,13 @@ verify_prd_criteria() {
247
298
  while IFS= read -r step; do
248
299
  [[ -z "$step" ]] && continue
249
300
 
250
- echo -n " $step... "
301
+ # Expand config placeholders (e.g., {config.urls.backend})
302
+ local expanded_step
303
+ expanded_step=$(_expand_config_vars "$step")
304
+
305
+ echo -n " $expanded_step... "
251
306
 
252
- if safe_exec "$step" "$log_file"; then
307
+ if safe_exec "$expanded_step" "$log_file"; then
253
308
  print_success "passed"
254
309
  else
255
310
  print_error "failed"
@@ -260,7 +315,7 @@ verify_prd_criteria() {
260
315
  # Save failure details for retry context
261
316
  {
262
317
  echo "PRD test step $step_index failed for $story:"
263
- echo " Command: $step"
318
+ echo " Command: $expanded_step"
264
319
  echo " Error output:"
265
320
  tail -30 "$log_file" | sed 's/^/ /'
266
321
  echo ""