ralph-cli 2.2.0__tar.gz

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 (101) hide show
  1. ralph_cli-2.2.0/.editorconfig +22 -0
  2. ralph_cli-2.2.0/.github/dependabot.yml +25 -0
  3. ralph_cli-2.2.0/.github/workflows/ci.yml +141 -0
  4. ralph_cli-2.2.0/.github/workflows/publish.yml +178 -0
  5. ralph_cli-2.2.0/.github/workflows/security.yml +35 -0
  6. ralph_cli-2.2.0/.gitignore +53 -0
  7. ralph_cli-2.2.0/AGENTS.md +154 -0
  8. ralph_cli-2.2.0/CHANGELOG.md +268 -0
  9. ralph_cli-2.2.0/CLAUDE.md +215 -0
  10. ralph_cli-2.2.0/CONTRIBUTING.md +103 -0
  11. ralph_cli-2.2.0/LICENSE +21 -0
  12. ralph_cli-2.2.0/PKG-INFO +280 -0
  13. ralph_cli-2.2.0/PUBLISHING.md +105 -0
  14. ralph_cli-2.2.0/README.md +249 -0
  15. ralph_cli-2.2.0/docs/.gitkeep +0 -0
  16. ralph_cli-2.2.0/plans/PROGRESS.txt +849 -0
  17. ralph_cli-2.2.0/plans/SPEC.md +99 -0
  18. ralph_cli-2.2.0/plans/TASKS.json +145 -0
  19. ralph_cli-2.2.0/pyproject.toml +73 -0
  20. ralph_cli-2.2.0/scripts/setup-branch-protection.sh +69 -0
  21. ralph_cli-2.2.0/src/ralph/__init__.py +3 -0
  22. ralph_cli-2.2.0/src/ralph/cli.py +61 -0
  23. ralph_cli-2.2.0/src/ralph/commands/__init__.py +11 -0
  24. ralph_cli-2.2.0/src/ralph/commands/init_cmd.py +358 -0
  25. ralph_cli-2.2.0/src/ralph/commands/loop.py +725 -0
  26. ralph_cli-2.2.0/src/ralph/commands/once.py +239 -0
  27. ralph_cli-2.2.0/src/ralph/commands/prd.py +281 -0
  28. ralph_cli-2.2.0/src/ralph/commands/review.py +330 -0
  29. ralph_cli-2.2.0/src/ralph/commands/sync.py +137 -0
  30. ralph_cli-2.2.0/src/ralph/commands/tasks.py +507 -0
  31. ralph_cli-2.2.0/src/ralph/models/__init__.py +51 -0
  32. ralph_cli-2.2.0/src/ralph/models/config.py +97 -0
  33. ralph_cli-2.2.0/src/ralph/models/finding.py +134 -0
  34. ralph_cli-2.2.0/src/ralph/models/manifest.py +77 -0
  35. ralph_cli-2.2.0/src/ralph/models/review_state.py +97 -0
  36. ralph_cli-2.2.0/src/ralph/models/reviewer.py +165 -0
  37. ralph_cli-2.2.0/src/ralph/models/tasks.py +96 -0
  38. ralph_cli-2.2.0/src/ralph/services/__init__.py +53 -0
  39. ralph_cli-2.2.0/src/ralph/services/claude.py +360 -0
  40. ralph_cli-2.2.0/src/ralph/services/fix_loop.py +294 -0
  41. ralph_cli-2.2.0/src/ralph/services/git.py +177 -0
  42. ralph_cli-2.2.0/src/ralph/services/language.py +96 -0
  43. ralph_cli-2.2.0/src/ralph/services/review_loop.py +461 -0
  44. ralph_cli-2.2.0/src/ralph/services/reviewer_config_writer.py +136 -0
  45. ralph_cli-2.2.0/src/ralph/services/reviewer_detector.py +147 -0
  46. ralph_cli-2.2.0/src/ralph/services/scaffold.py +531 -0
  47. ralph_cli-2.2.0/src/ralph/services/skill_loader.py +185 -0
  48. ralph_cli-2.2.0/src/ralph/services/skills.py +520 -0
  49. ralph_cli-2.2.0/src/ralph/skills/REVIEWER_TEMPLATE.md +228 -0
  50. ralph_cli-2.2.0/src/ralph/skills/SKILL_TEMPLATE.md +192 -0
  51. ralph_cli-2.2.0/src/ralph/skills/__init__.py +5 -0
  52. ralph_cli-2.2.0/src/ralph/skills/ralph/__init__.py +1 -0
  53. ralph_cli-2.2.0/src/ralph/skills/ralph/iteration/SKILL.md +210 -0
  54. ralph_cli-2.2.0/src/ralph/skills/ralph/iteration/__init__.py +1 -0
  55. ralph_cli-2.2.0/src/ralph/skills/ralph/prd/SKILL.md +201 -0
  56. ralph_cli-2.2.0/src/ralph/skills/ralph/prd/__init__.py +1 -0
  57. ralph_cli-2.2.0/src/ralph/skills/ralph/tasks/SKILL.md +250 -0
  58. ralph_cli-2.2.0/src/ralph/skills/ralph/tasks/__init__.py +1 -0
  59. ralph_cli-2.2.0/src/ralph/skills/reviewers/__init__.py +1 -0
  60. ralph_cli-2.2.0/src/ralph/skills/reviewers/code_simplifier/SKILL.md +298 -0
  61. ralph_cli-2.2.0/src/ralph/skills/reviewers/code_simplifier/__init__.py +1 -0
  62. ralph_cli-2.2.0/src/ralph/skills/reviewers/github_actions/SKILL.md +333 -0
  63. ralph_cli-2.2.0/src/ralph/skills/reviewers/github_actions/__init__.py +1 -0
  64. ralph_cli-2.2.0/src/ralph/skills/reviewers/language/__init__.py +1 -0
  65. ralph_cli-2.2.0/src/ralph/skills/reviewers/language/bicep/SKILL.md +394 -0
  66. ralph_cli-2.2.0/src/ralph/skills/reviewers/language/bicep/__init__.py +1 -0
  67. ralph_cli-2.2.0/src/ralph/skills/reviewers/language/python/SKILL.md +363 -0
  68. ralph_cli-2.2.0/src/ralph/skills/reviewers/language/python/__init__.py +1 -0
  69. ralph_cli-2.2.0/src/ralph/skills/reviewers/release/SKILL.md +323 -0
  70. ralph_cli-2.2.0/src/ralph/skills/reviewers/release/__init__.py +1 -0
  71. ralph_cli-2.2.0/src/ralph/skills/reviewers/repo_structure/SKILL.md +295 -0
  72. ralph_cli-2.2.0/src/ralph/skills/reviewers/repo_structure/__init__.py +1 -0
  73. ralph_cli-2.2.0/src/ralph/skills/reviewers/test_quality/SKILL.md +498 -0
  74. ralph_cli-2.2.0/src/ralph/skills/reviewers/test_quality/__init__.py +1 -0
  75. ralph_cli-2.2.0/src/ralph/utils/__init__.py +39 -0
  76. ralph_cli-2.2.0/src/ralph/utils/console.py +126 -0
  77. ralph_cli-2.2.0/src/ralph/utils/files.py +111 -0
  78. ralph_cli-2.2.0/src/ralph/utils/prompt.py +44 -0
  79. ralph_cli-2.2.0/tests/__init__.py +1 -0
  80. ralph_cli-2.2.0/tests/conftest.py +36 -0
  81. ralph_cli-2.2.0/tests/test_already_implemented.py +479 -0
  82. ralph_cli-2.2.0/tests/test_commands_integration.py +1775 -0
  83. ralph_cli-2.2.0/tests/test_finding_model.py +227 -0
  84. ralph_cli-2.2.0/tests/test_fix_loop.py +406 -0
  85. ralph_cli-2.2.0/tests/test_language_detection.py +87 -0
  86. ralph_cli-2.2.0/tests/test_manifest.py +75 -0
  87. ralph_cli-2.2.0/tests/test_nofix_and_resume.py +512 -0
  88. ralph_cli-2.2.0/tests/test_quality_checks.py +149 -0
  89. ralph_cli-2.2.0/tests/test_review_command.py +746 -0
  90. ralph_cli-2.2.0/tests/test_review_force.py +292 -0
  91. ralph_cli-2.2.0/tests/test_review_loop.py +912 -0
  92. ralph_cli-2.2.0/tests/test_review_state.py +272 -0
  93. ralph_cli-2.2.0/tests/test_reviewer_config.py +149 -0
  94. ralph_cli-2.2.0/tests/test_reviewer_config_writer.py +261 -0
  95. ralph_cli-2.2.0/tests/test_reviewer_detector.py +202 -0
  96. ralph_cli-2.2.0/tests/test_scaffold.py +105 -0
  97. ralph_cli-2.2.0/tests/test_skill_loader.py +65 -0
  98. ralph_cli-2.2.0/tests/test_skills_service.py +242 -0
  99. ralph_cli-2.2.0/tests/test_tasks_codebase_context.py +465 -0
  100. ralph_cli-2.2.0/tests/test_tasks_model.py +140 -0
  101. ralph_cli-2.2.0/uv.lock +571 -0
@@ -0,0 +1,22 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 4
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.py]
12
+ indent_size = 4
13
+ max_line_length = 100
14
+
15
+ [*.{json,yaml,yml,toml}]
16
+ indent_size = 2
17
+
18
+ [*.md]
19
+ trim_trailing_whitespace = false
20
+
21
+ [Makefile]
22
+ indent_style = tab
@@ -0,0 +1,25 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies (uv/pip ecosystem)
4
+ - package-ecosystem: "pip"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ open-pull-requests-limit: 10
9
+ commit-message:
10
+ prefix: "chore(deps)"
11
+ groups:
12
+ dev-dependencies:
13
+ patterns:
14
+ - "pytest*"
15
+ - "ruff"
16
+ - "pyright"
17
+
18
+ # GitHub Actions
19
+ - package-ecosystem: "github-actions"
20
+ directory: "/"
21
+ schedule:
22
+ interval: "weekly"
23
+ open-pull-requests-limit: 5
24
+ commit-message:
25
+ prefix: "chore(ci)"
@@ -0,0 +1,141 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ['v*']
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ version-check:
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 5
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+
20
+ - name: Extract version from pyproject.toml
21
+ id: pyproject
22
+ run: |
23
+ version=$(python3 -c "
24
+ import re, pathlib
25
+ text = pathlib.Path('pyproject.toml').read_text()
26
+ m = re.search(r'^version\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
27
+ print(m.group(1) if m else '')
28
+ ")
29
+ echo "version=$version" >> "$GITHUB_OUTPUT"
30
+
31
+ - name: Extract version from __init__.py
32
+ id: init
33
+ run: |
34
+ version=$(python3 -c "
35
+ import re, pathlib
36
+ text = pathlib.Path('src/ralph/__init__.py').read_text()
37
+ m = re.search(r'^__version__\s*=\s*\"([^\"]+)\"', text, re.MULTILINE)
38
+ print(m.group(1) if m else '')
39
+ ")
40
+ echo "version=$version" >> "$GITHUB_OUTPUT"
41
+
42
+ - name: Check CHANGELOG.md contains version entry
43
+ id: changelog
44
+ run: |
45
+ version="${{ steps.pyproject.outputs.version }}"
46
+ if grep -qF "## [$version]" CHANGELOG.md; then
47
+ echo "found=true" >> "$GITHUB_OUTPUT"
48
+ else
49
+ echo "found=false" >> "$GITHUB_OUTPUT"
50
+ fi
51
+
52
+ - name: Verify version consistency
53
+ run: |
54
+ pyproject="${{ steps.pyproject.outputs.version }}"
55
+ init="${{ steps.init.outputs.version }}"
56
+ changelog="${{ steps.changelog.outputs.found }}"
57
+ errors=0
58
+
59
+ if [ -z "$pyproject" ]; then
60
+ echo "::error file=pyproject.toml::Could not extract version from pyproject.toml"
61
+ errors=1
62
+ fi
63
+
64
+ if [ "$pyproject" != "$init" ]; then
65
+ echo "::error file=src/ralph/__init__.py::Version mismatch: pyproject.toml has '$pyproject' but __init__.py has '$init'"
66
+ errors=1
67
+ fi
68
+
69
+ if [ "$changelog" != "true" ]; then
70
+ echo "::error file=CHANGELOG.md::CHANGELOG.md is missing an entry for version '$pyproject' (expected '## [$pyproject]')"
71
+ errors=1
72
+ fi
73
+
74
+ if [ "$errors" -ne 0 ]; then
75
+ echo ""
76
+ echo "Version consistency check failed. Ensure these files agree:"
77
+ echo " - pyproject.toml version = \"$pyproject\""
78
+ echo " - src/ralph/__init__.py __version__ = \"$init\""
79
+ echo " - CHANGELOG.md ## [$pyproject] ($changelog)"
80
+ exit 1
81
+ fi
82
+
83
+ echo "Version consistency check passed: $pyproject"
84
+
85
+ - name: Verify tag matches pyproject.toml version
86
+ if: startsWith(github.ref, 'refs/tags/v')
87
+ run: |
88
+ tag="${GITHUB_REF#refs/tags/v}"
89
+ pyproject="${{ steps.pyproject.outputs.version }}"
90
+
91
+ if [ "$tag" != "$pyproject" ]; then
92
+ echo "::error::Git tag 'v$tag' does not match pyproject.toml version '$pyproject'"
93
+ exit 1
94
+ fi
95
+
96
+ echo "Tag validation passed: v$tag matches pyproject.toml version $pyproject"
97
+
98
+ test:
99
+ runs-on: ${{ matrix.os }}
100
+ timeout-minutes: 15
101
+ strategy:
102
+ matrix:
103
+ os: [ubuntu-latest, windows-latest]
104
+ python-version: ["3.11", "3.12", "3.13"]
105
+ defaults:
106
+ run:
107
+ shell: bash
108
+
109
+ steps:
110
+ - uses: actions/checkout@v6
111
+
112
+ - name: Install uv
113
+ uses: astral-sh/setup-uv@v7
114
+ with:
115
+ version: "0.9.26"
116
+ enable-cache: true
117
+
118
+ - name: Set up Python ${{ matrix.python-version }}
119
+ run: uv python install ${{ matrix.python-version }}
120
+
121
+ - name: Install dependencies
122
+ run: uv sync --all-extras --dev
123
+
124
+ - name: Run type checking
125
+ run: uv run pyright
126
+
127
+ - name: Run linting
128
+ run: uv run ruff check .
129
+
130
+ - name: Check formatting
131
+ run: uv run ruff format --check .
132
+
133
+ - name: Run tests
134
+ run: uv run pytest --cov=ralph --cov-report=xml
135
+
136
+ - name: Upload coverage
137
+ uses: codecov/codecov-action@v5
138
+ if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
139
+ with:
140
+ files: coverage.xml
141
+ fail_ci_if_error: false
@@ -0,0 +1,178 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+
7
+ permissions:
8
+ id-token: write
9
+ contents: write
10
+
11
+ jobs:
12
+ ci-check:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 15
15
+ steps:
16
+ - name: Wait for CI workflow to succeed
17
+ env:
18
+ GH_TOKEN: ${{ github.token }}
19
+ run: |
20
+ commit_sha="${GITHUB_SHA}"
21
+ repo="${GITHUB_REPOSITORY}"
22
+ workflow_name="CI"
23
+ max_attempts=30
24
+ wait_seconds=30
25
+
26
+ echo "Checking CI workflow status for commit ${commit_sha}..."
27
+
28
+ for attempt in $(seq 1 "$max_attempts"); do
29
+ # Query CI workflow runs for this exact commit
30
+ run_data=$(gh api "repos/${repo}/actions/workflows/ci.yml/runs?head_sha=${commit_sha}&per_page=1" \
31
+ --jq '.workflow_runs[0] // empty')
32
+
33
+ if [ -z "$run_data" ]; then
34
+ if [ "$attempt" -eq "$max_attempts" ]; then
35
+ echo "::error::CI workflow has not run for commit ${commit_sha}. Ensure CI is triggered before publishing."
36
+ exit 1
37
+ fi
38
+ echo "Attempt ${attempt}/${max_attempts}: CI workflow not found yet. Waiting ${wait_seconds}s..."
39
+ sleep "$wait_seconds"
40
+ continue
41
+ fi
42
+
43
+ status=$(echo "$run_data" | jq -r '.status')
44
+ conclusion=$(echo "$run_data" | jq -r '.conclusion')
45
+ run_url=$(echo "$run_data" | jq -r '.html_url')
46
+
47
+ echo "CI run: status=${status}, conclusion=${conclusion}"
48
+ echo " URL: ${run_url}"
49
+
50
+ if [ "$status" = "completed" ]; then
51
+ if [ "$conclusion" = "success" ]; then
52
+ echo "CI workflow passed for commit ${commit_sha}."
53
+ exit 0
54
+ else
55
+ echo "::error::CI workflow failed for commit ${commit_sha} (conclusion: ${conclusion}). See: ${run_url}"
56
+ exit 1
57
+ fi
58
+ fi
59
+
60
+ if [ "$attempt" -eq "$max_attempts" ]; then
61
+ echo "::error::CI workflow still running after $((max_attempts * wait_seconds))s. See: ${run_url}"
62
+ exit 1
63
+ fi
64
+
65
+ echo "Attempt ${attempt}/${max_attempts}: CI still running (status=${status}). Waiting ${wait_seconds}s..."
66
+ sleep "$wait_seconds"
67
+ done
68
+
69
+ build:
70
+ needs: ci-check
71
+ runs-on: ubuntu-latest
72
+ timeout-minutes: 10
73
+ steps:
74
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
75
+
76
+ - name: Install uv
77
+ uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7
78
+ with:
79
+ version: "0.9.26"
80
+ enable-cache: true
81
+
82
+ - name: Build package
83
+ run: uv build
84
+
85
+ - name: Upload dist artifacts
86
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
87
+ with:
88
+ name: python-package-distributions
89
+ path: dist/
90
+
91
+ testpypi:
92
+ needs: build
93
+ runs-on: ubuntu-latest
94
+ timeout-minutes: 10
95
+ environment:
96
+ name: testpypi
97
+ url: https://test.pypi.org/p/ralph-cli
98
+ permissions:
99
+ id-token: write
100
+ steps:
101
+ - name: Download dist artifacts
102
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
103
+ with:
104
+ name: python-package-distributions
105
+ path: dist/
106
+
107
+ - name: Publish to TestPyPI
108
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
109
+ with:
110
+ repository-url: https://test.pypi.org/legacy/
111
+
112
+ pypi:
113
+ needs: testpypi
114
+ runs-on: ubuntu-latest
115
+ timeout-minutes: 10
116
+ environment:
117
+ name: pypi
118
+ url: https://pypi.org/p/ralph-cli
119
+ permissions:
120
+ id-token: write
121
+ steps:
122
+ - name: Download dist artifacts
123
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
124
+ with:
125
+ name: python-package-distributions
126
+ path: dist/
127
+
128
+ - name: Publish to PyPI
129
+ uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
130
+
131
+ release:
132
+ needs: pypi
133
+ runs-on: ubuntu-latest
134
+ timeout-minutes: 10
135
+ permissions:
136
+ contents: write
137
+ steps:
138
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
139
+
140
+ - name: Extract version from tag
141
+ id: version
142
+ run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
143
+
144
+ - name: Extract release notes from CHANGELOG.md
145
+ id: notes
146
+ run: |
147
+ version="${{ steps.version.outputs.version }}"
148
+ # Extract content between this version header and the next version header
149
+ awk "/^## \\[${version}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md > release_notes.txt
150
+ # Remove leading/trailing blank lines
151
+ sed -i '/./,$!d' release_notes.txt
152
+ sed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}' release_notes.txt
153
+
154
+ - name: Download dist artifacts
155
+ uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
156
+ with:
157
+ name: python-package-distributions
158
+ path: dist/
159
+
160
+ - name: Determine pre-release flag
161
+ id: prerelease
162
+ run: |
163
+ version="${{ steps.version.outputs.version }}"
164
+ if echo "$version" | grep -qE '(a|b|rc|dev|alpha|beta|pre)'; then
165
+ echo "flag=--prerelease" >> "$GITHUB_OUTPUT"
166
+ else
167
+ echo "flag=--latest" >> "$GITHUB_OUTPUT"
168
+ fi
169
+
170
+ - name: Create GitHub Release
171
+ env:
172
+ GH_TOKEN: ${{ github.token }}
173
+ run: |
174
+ gh release create "${{ github.ref_name }}" \
175
+ --title "${{ github.ref_name }}" \
176
+ --notes-file release_notes.txt \
177
+ ${{ steps.prerelease.outputs.flag }} \
178
+ dist/*
@@ -0,0 +1,35 @@
1
+ name: Security
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ # Run weekly on Monday at 00:00 UTC
10
+ - cron: "0 0 * * 1"
11
+
12
+ permissions:
13
+ contents: read
14
+ security-events: write
15
+
16
+ jobs:
17
+ codeql:
18
+ name: CodeQL Analysis
19
+ runs-on: ubuntu-latest
20
+ timeout-minutes: 15
21
+
22
+ steps:
23
+ - name: Checkout repository
24
+ uses: actions/checkout@v6
25
+
26
+ - name: Initialize CodeQL
27
+ uses: github/codeql-action/init@v4
28
+ with:
29
+ languages: python
30
+ queries: security-and-quality
31
+
32
+ - name: Perform CodeQL Analysis
33
+ uses: github/codeql-action/analyze@v4
34
+ with:
35
+ category: "/language:python"
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # Environment files
29
+ .env
30
+ .env.*
31
+ !.env.example
32
+
33
+ # IDE
34
+ .idea/
35
+ .vscode/
36
+ *.swp
37
+ *.swo
38
+
39
+ # Testing
40
+ .coverage
41
+ htmlcov/
42
+ .pytest_cache/
43
+
44
+ # Type checking
45
+ .pyright/
46
+ .mypy_cache/
47
+
48
+ # OS
49
+ .DS_Store
50
+ Thumbs.db
51
+
52
+ # Ruff
53
+ .ruff_cache/
@@ -0,0 +1,154 @@
1
+ # Agent Instructions
2
+
3
+ ## Overview
4
+
5
+ Ralph CLI - A Python CLI tool that implements the Ralph autonomous iteration pattern for Claude Code. This project wraps the entire autonomous coding workflow with proper project structure, intuitive commands, and a polished terminal experience.
6
+
7
+ ## Ralph Workflow
8
+
9
+ **Key files:**
10
+ - `plans/SPEC.md` - Feature specification
11
+ - `plans/TASKS.json` - Task list with completion status
12
+ - `plans/PROGRESS.txt` - Append-only iteration log
13
+
14
+ **Iteration process:**
15
+ 1. Read `plans/TASKS.json` and pick highest-priority incomplete story
16
+ 2. Read `plans/PROGRESS.txt` for context and patterns
17
+ 3. Implement the story following acceptance criteria
18
+ 4. Run quality checks defined in `CLAUDE.md`
19
+ 5. Commit on success: `feat: [Story ID] - [Story Title]`
20
+ 6. Update TASKS.json (`passes: true`) and append to PROGRESS.txt
21
+ 7. Update CLAUDE.md and AGENTS.md with discovered patterns
22
+
23
+ ## Technology Stack
24
+
25
+ | Component | Technology |
26
+ |-----------|------------|
27
+ | Language | Python 3.11+ |
28
+ | CLI Framework | Typer |
29
+ | Terminal UI | Rich |
30
+ | Data Validation | Pydantic |
31
+ | Package Management | uv |
32
+ | Linting/Formatting | ruff |
33
+ | Type Checking | pyright |
34
+ | Testing | pytest |
35
+
36
+ ## Project Structure
37
+
38
+ ```
39
+ ralph_cli/
40
+ ├── pyproject.toml
41
+ ├── README.md
42
+ ├── CLAUDE.md
43
+ ├── AGENTS.md
44
+ ├── plans/
45
+ │ ├── SPEC.md
46
+ │ ├── TASKS.json
47
+ │ └── PROGRESS.txt
48
+ ├── src/
49
+ │ └── ralph/
50
+ │ ├── __init__.py
51
+ │ ├── cli.py # Typer app, command definitions
52
+ │ ├── commands/ # Command implementations
53
+ │ ├── models/ # Pydantic models
54
+ │ ├── services/ # Business logic (Claude, Git, etc.)
55
+ │ ├── skills/ # Bundled skill definitions (packaged)
56
+ │ │ ├── ralph/ # Core workflow skills
57
+ │ │ │ ├── iteration/
58
+ │ │ │ ├── prd/
59
+ │ │ │ └── tasks/
60
+ │ │ └── reviewers/ # Review pipeline skills
61
+ │ │ ├── code_simplifier/
62
+ │ │ ├── github_actions/
63
+ │ │ ├── language/
64
+ │ │ │ ├── bicep/
65
+ │ │ │ └── python/
66
+ │ │ ├── release/
67
+ │ │ ├── repo_structure/
68
+ │ │ └── test_quality/
69
+ │ └── utils/ # Console and file utilities
70
+ └── tests/
71
+ ```
72
+
73
+ **Note:** Skills are bundled with the package for immediate use after `pip install ralph-cli`. Use `ralph sync` to copy them to `~/.claude/skills/` for native Claude Code integration.
74
+
75
+ ## Codebase Patterns
76
+
77
+ (Keep in sync with CLAUDE.md)
78
+
79
+ - Use Pydantic `BaseModel` for all classes, NOT stdlib `@dataclass` - this is a Pydantic project
80
+ - Use `ConfigDict(arbitrary_types_allowed=True)` when fields use types like `Path` or `TextIO`
81
+ - Use Pydantic `alias` with `populate_by_name=True` when JSON uses camelCase but Python should use snake_case
82
+ - Use `by_alias=True` in `model_dump_json()` to serialize back to the original JSON format
83
+ - Import `Iterator`, `Sequence`, etc. from `collections.abc` instead of `typing` (ruff UP035)
84
+ - Use `X | None` instead of `Optional[X]` for type annotations (ruff UP045)
85
+ - Use `ClassVar[T]` from `typing` for class-level constants in Pydantic `BaseModel` classes to avoid them being treated as fields
86
+ - When calling Typer command functions directly (not through CLI), pass all Option parameters explicitly since Typer defaults are not applied programmatically
87
+ - **Prompt construction**: Use `@filepath` notation with `build_skill_prompt()` to reference bundled skill files directly, reducing token usage. The function uses `SkillLoader.get_path()` to get the actual filesystem path to SKILL.md files. Example: `build_skill_prompt("ralph/prd", context)` produces `@/path/to/skills/ralph/prd/SKILL.md`
88
+
89
+ ## Project-Specific Instructions
90
+
91
+ ### Commands
92
+
93
+ The CLI provides these commands:
94
+ - `ralph init` - Scaffold a project for Ralph workflow
95
+ - `ralph prd` - Interactive PRD creation with Claude
96
+ - `ralph tasks <spec>` - Convert spec to TASKS.json
97
+ - `ralph once` - Execute single iteration
98
+ - `ralph loop [n]` - Run n iterations (default: 10)
99
+ - `ralph review` - Run the review pipeline with automatic configuration
100
+ - `ralph sync` - Sync skills to ~/.claude/skills/
101
+
102
+ ### Key Design Decisions
103
+
104
+ 1. **Subprocess for Claude Code**: Invoke `claude` CLI via subprocess, not as a library
105
+ 2. **Streaming output**: Default shows text output; `--verbose` shows full JSON
106
+ 3. **Branch management**: Auto-create/checkout branches from TASKS.json branchName
107
+ 4. **Memory system**: PROGRESS.txt is append-only; CLAUDE.md/AGENTS.md get pattern updates
108
+
109
+ ### Linear Integration
110
+
111
+ This project tracks work in [Linear](https://linear.app/jmcptest) under the **Ralph CLI** team (identifier: `RAL`).
112
+
113
+ **Hierarchy:** Project (release) > Parent Issue (feature/epic) > Sub-issue (user story)
114
+
115
+ **GitHub integration is configured with:**
116
+ - **Branch/PR linking** — PRs referencing `RAL-XX` identifiers auto-link to Linear issues
117
+ - **Magic words** — Commits with `RAL-XX` get linked via webhook
118
+ - **Public repo linkbacks** — Linear adds comments on linked GitHub PRs
119
+ - **Bidirectional GitHub Issues sync** — Issues sync between `jackemcpherson/ralph-cli` and the Ralph CLI team (note: syncs new activity, does not back-import existing issues)
120
+
121
+ **When creating PRs:**
122
+ - Reference Linear identifiers in the PR body (e.g., `RAL-1`, `RAL-15`) for automatic linking
123
+ - Use `Closes #XX` for GitHub issues to auto-close on merge
124
+ - Linear issues should be updated to Done when their stories pass
125
+
126
+ **When completing a feature branch:**
127
+ 1. Mark all completed stories as Done in Linear
128
+ 2. Mark parent issues as Done when all sub-issues complete
129
+ 3. Update the Linear project status to Completed after release
130
+
131
+ ### Release Process
132
+
133
+ When merging a release PR:
134
+ 1. Update version in `pyproject.toml` and `src/ralph/__init__.py`
135
+ 2. Add changelog entry to `CHANGELOG.md`
136
+ 3. Merge the PR to main
137
+ 4. Create and push a git tag: `git tag vX.Y.Z && git push origin vX.Y.Z`
138
+ 5. Create a GitHub Release from the tag with notes from CHANGELOG.md
139
+ 6. Update the Linear project description (link to release) and set status to Completed
140
+ 7. Clean up local and remote feature branches: `git branch -d <branch> && git fetch --prune`
141
+
142
+ Tags must match the version in pyproject.toml (e.g., `v1.2.6` for version `1.2.6`).
143
+
144
+ ### File Formats
145
+
146
+ **TASKS.json schema:**
147
+ - `project`: string
148
+ - `branchName`: string (e.g., "ralph/feature-name")
149
+ - `description`: string
150
+ - `userStories`: array of {id, title, description, acceptanceCriteria[], priority, passes, notes}
151
+
152
+ **Quality checks in CLAUDE.md:**
153
+ - Parsed from YAML between `<!-- RALPH:CHECKS:START -->` and `<!-- RALPH:CHECKS:END -->`
154
+ - Each check has: name, command, required (boolean)