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.
- ralph_cli-2.2.0/.editorconfig +22 -0
- ralph_cli-2.2.0/.github/dependabot.yml +25 -0
- ralph_cli-2.2.0/.github/workflows/ci.yml +141 -0
- ralph_cli-2.2.0/.github/workflows/publish.yml +178 -0
- ralph_cli-2.2.0/.github/workflows/security.yml +35 -0
- ralph_cli-2.2.0/.gitignore +53 -0
- ralph_cli-2.2.0/AGENTS.md +154 -0
- ralph_cli-2.2.0/CHANGELOG.md +268 -0
- ralph_cli-2.2.0/CLAUDE.md +215 -0
- ralph_cli-2.2.0/CONTRIBUTING.md +103 -0
- ralph_cli-2.2.0/LICENSE +21 -0
- ralph_cli-2.2.0/PKG-INFO +280 -0
- ralph_cli-2.2.0/PUBLISHING.md +105 -0
- ralph_cli-2.2.0/README.md +249 -0
- ralph_cli-2.2.0/docs/.gitkeep +0 -0
- ralph_cli-2.2.0/plans/PROGRESS.txt +849 -0
- ralph_cli-2.2.0/plans/SPEC.md +99 -0
- ralph_cli-2.2.0/plans/TASKS.json +145 -0
- ralph_cli-2.2.0/pyproject.toml +73 -0
- ralph_cli-2.2.0/scripts/setup-branch-protection.sh +69 -0
- ralph_cli-2.2.0/src/ralph/__init__.py +3 -0
- ralph_cli-2.2.0/src/ralph/cli.py +61 -0
- ralph_cli-2.2.0/src/ralph/commands/__init__.py +11 -0
- ralph_cli-2.2.0/src/ralph/commands/init_cmd.py +358 -0
- ralph_cli-2.2.0/src/ralph/commands/loop.py +725 -0
- ralph_cli-2.2.0/src/ralph/commands/once.py +239 -0
- ralph_cli-2.2.0/src/ralph/commands/prd.py +281 -0
- ralph_cli-2.2.0/src/ralph/commands/review.py +330 -0
- ralph_cli-2.2.0/src/ralph/commands/sync.py +137 -0
- ralph_cli-2.2.0/src/ralph/commands/tasks.py +507 -0
- ralph_cli-2.2.0/src/ralph/models/__init__.py +51 -0
- ralph_cli-2.2.0/src/ralph/models/config.py +97 -0
- ralph_cli-2.2.0/src/ralph/models/finding.py +134 -0
- ralph_cli-2.2.0/src/ralph/models/manifest.py +77 -0
- ralph_cli-2.2.0/src/ralph/models/review_state.py +97 -0
- ralph_cli-2.2.0/src/ralph/models/reviewer.py +165 -0
- ralph_cli-2.2.0/src/ralph/models/tasks.py +96 -0
- ralph_cli-2.2.0/src/ralph/services/__init__.py +53 -0
- ralph_cli-2.2.0/src/ralph/services/claude.py +360 -0
- ralph_cli-2.2.0/src/ralph/services/fix_loop.py +294 -0
- ralph_cli-2.2.0/src/ralph/services/git.py +177 -0
- ralph_cli-2.2.0/src/ralph/services/language.py +96 -0
- ralph_cli-2.2.0/src/ralph/services/review_loop.py +461 -0
- ralph_cli-2.2.0/src/ralph/services/reviewer_config_writer.py +136 -0
- ralph_cli-2.2.0/src/ralph/services/reviewer_detector.py +147 -0
- ralph_cli-2.2.0/src/ralph/services/scaffold.py +531 -0
- ralph_cli-2.2.0/src/ralph/services/skill_loader.py +185 -0
- ralph_cli-2.2.0/src/ralph/services/skills.py +520 -0
- ralph_cli-2.2.0/src/ralph/skills/REVIEWER_TEMPLATE.md +228 -0
- ralph_cli-2.2.0/src/ralph/skills/SKILL_TEMPLATE.md +192 -0
- ralph_cli-2.2.0/src/ralph/skills/__init__.py +5 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/iteration/SKILL.md +210 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/iteration/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/prd/SKILL.md +201 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/prd/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/tasks/SKILL.md +250 -0
- ralph_cli-2.2.0/src/ralph/skills/ralph/tasks/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/code_simplifier/SKILL.md +298 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/code_simplifier/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/github_actions/SKILL.md +333 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/github_actions/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/language/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/language/bicep/SKILL.md +394 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/language/bicep/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/language/python/SKILL.md +363 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/language/python/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/release/SKILL.md +323 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/release/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/repo_structure/SKILL.md +295 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/repo_structure/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/test_quality/SKILL.md +498 -0
- ralph_cli-2.2.0/src/ralph/skills/reviewers/test_quality/__init__.py +1 -0
- ralph_cli-2.2.0/src/ralph/utils/__init__.py +39 -0
- ralph_cli-2.2.0/src/ralph/utils/console.py +126 -0
- ralph_cli-2.2.0/src/ralph/utils/files.py +111 -0
- ralph_cli-2.2.0/src/ralph/utils/prompt.py +44 -0
- ralph_cli-2.2.0/tests/__init__.py +1 -0
- ralph_cli-2.2.0/tests/conftest.py +36 -0
- ralph_cli-2.2.0/tests/test_already_implemented.py +479 -0
- ralph_cli-2.2.0/tests/test_commands_integration.py +1775 -0
- ralph_cli-2.2.0/tests/test_finding_model.py +227 -0
- ralph_cli-2.2.0/tests/test_fix_loop.py +406 -0
- ralph_cli-2.2.0/tests/test_language_detection.py +87 -0
- ralph_cli-2.2.0/tests/test_manifest.py +75 -0
- ralph_cli-2.2.0/tests/test_nofix_and_resume.py +512 -0
- ralph_cli-2.2.0/tests/test_quality_checks.py +149 -0
- ralph_cli-2.2.0/tests/test_review_command.py +746 -0
- ralph_cli-2.2.0/tests/test_review_force.py +292 -0
- ralph_cli-2.2.0/tests/test_review_loop.py +912 -0
- ralph_cli-2.2.0/tests/test_review_state.py +272 -0
- ralph_cli-2.2.0/tests/test_reviewer_config.py +149 -0
- ralph_cli-2.2.0/tests/test_reviewer_config_writer.py +261 -0
- ralph_cli-2.2.0/tests/test_reviewer_detector.py +202 -0
- ralph_cli-2.2.0/tests/test_scaffold.py +105 -0
- ralph_cli-2.2.0/tests/test_skill_loader.py +65 -0
- ralph_cli-2.2.0/tests/test_skills_service.py +242 -0
- ralph_cli-2.2.0/tests/test_tasks_codebase_context.py +465 -0
- ralph_cli-2.2.0/tests/test_tasks_model.py +140 -0
- 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)
|