ralph-code 0.6.2__tar.gz → 0.6.3__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 (43) hide show
  1. {ralph_code-0.6.2/ralph_code.egg-info → ralph_code-0.6.3}/PKG-INFO +7 -1
  2. {ralph_code-0.6.2 → ralph_code-0.6.3}/README.md +6 -0
  3. {ralph_code-0.6.2 → ralph_code-0.6.3}/pyproject.toml +1 -1
  4. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/__init__.py +1 -1
  5. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/git_manager.py +9 -4
  6. {ralph_code-0.6.2 → ralph_code-0.6.3/ralph_code.egg-info}/PKG-INFO +7 -1
  7. {ralph_code-0.6.2 → ralph_code-0.6.3}/setup.py +1 -1
  8. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_git_manager.py +55 -0
  9. {ralph_code-0.6.2 → ralph_code-0.6.3}/LICENSE +0 -0
  10. {ralph_code-0.6.2 → ralph_code-0.6.3}/MANIFEST.in +0 -0
  11. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/__main__.py +0 -0
  12. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/app.py +0 -0
  13. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/claude_runner.py +0 -0
  14. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/colors.py +0 -0
  15. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/config.py +0 -0
  16. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/harness.py +0 -0
  17. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/harness_runner.py +0 -0
  18. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/prd_manager.py +0 -0
  19. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/schemas/ralph_tasks_schema.json +0 -0
  20. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/schemas/task_schema.json +0 -0
  21. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/spinner.py +0 -0
  22. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/storage.py +0 -0
  23. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/tasks.py +0 -0
  24. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/user_stories.py +0 -0
  25. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph/workflow.py +0 -0
  26. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph_code.egg-info/SOURCES.txt +0 -0
  27. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph_code.egg-info/dependency_links.txt +0 -0
  28. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph_code.egg-info/entry_points.txt +0 -0
  29. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph_code.egg-info/requires.txt +0 -0
  30. {ralph_code-0.6.2 → ralph_code-0.6.3}/ralph_code.egg-info/top_level.txt +0 -0
  31. {ralph_code-0.6.2 → ralph_code-0.6.3}/setup.cfg +0 -0
  32. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_app.py +0 -0
  33. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_app_integration.py +0 -0
  34. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_colors.py +0 -0
  35. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_config.py +0 -0
  36. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_harness.py +0 -0
  37. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_harness_runner.py +0 -0
  38. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_prd_manager.py +0 -0
  39. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_spinner.py +0 -0
  40. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_storage.py +0 -0
  41. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_tasks.py +0 -0
  42. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_user_stories.py +0 -0
  43. {ralph_code-0.6.2 → ralph_code-0.6.3}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ralph-code
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Automated task implementation with Claude Code and Codex
5
5
  Author: Ralph Coding
6
6
  License: MIT
@@ -61,6 +61,12 @@ ralph [OPTIONS] [DIRECTORY]
61
61
 
62
62
  ## Recent changes
63
63
 
64
+ Version `0.6.3` includes:
65
+ - Safer git integration for staged-file commit checks
66
+ - Correct staged diff generation
67
+ - Deduplicated unstaged file reporting
68
+ - Clearer errors when `git` is not available
69
+
64
70
  Version `0.6.2` includes:
65
71
  - Bounded non-interactive harness execution with timeout and turn limits
66
72
  - Structured `tasks.json` generation for more reliable PRD conversion
@@ -24,6 +24,12 @@ ralph [OPTIONS] [DIRECTORY]
24
24
 
25
25
  ## Recent changes
26
26
 
27
+ Version `0.6.3` includes:
28
+ - Safer git integration for staged-file commit checks
29
+ - Correct staged diff generation
30
+ - Deduplicated unstaged file reporting
31
+ - Clearer errors when `git` is not available
32
+
27
33
  Version `0.6.2` includes:
28
34
  - Bounded non-interactive harness execution with timeout and turn limits
29
35
  - Structured `tasks.json` generation for more reliable PRD conversion
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ralph-code"
7
- version = "0.6.2"
7
+ version = "0.6.3"
8
8
  description = "Automated task implementation with Claude Code and Codex"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -1,6 +1,6 @@
1
1
  """ralph-code: Automated task implementation with Claude Code and Codex."""
2
2
 
3
- __version__ = "0.6.2"
3
+ __version__ = "0.6.3"
4
4
  __author__ = "Ralph Coding"
5
5
 
6
6
  from .app import RalphApp, main
@@ -26,6 +26,8 @@ class GitManager:
26
26
  check=check,
27
27
  )
28
28
  return result
29
+ except FileNotFoundError as e:
30
+ raise GitError("Git executable not found on PATH") from e
29
31
  except subprocess.CalledProcessError as e:
30
32
  raise GitError(f"Git command failed: git {' '.join(args)}\n{e.stderr}")
31
33
 
@@ -241,19 +243,22 @@ class GitManager:
241
243
  Returns files that have changes not yet added to the staging area.
242
244
  This includes both modified tracked files and new untracked files.
243
245
  """
244
- files = []
246
+ files: list[str] = []
247
+ seen: set[str] = set()
245
248
 
246
249
  # Get modified but unstaged files
247
250
  result = self._run_git("diff", "--name-only")
248
251
  for f in result.stdout.strip().split("\n"):
249
- if f:
252
+ if f and f not in seen:
250
253
  files.append(f)
254
+ seen.add(f)
251
255
 
252
256
  # Get untracked files
253
257
  result = self._run_git("ls-files", "--others", "--exclude-standard")
254
258
  for f in result.stdout.strip().split("\n"):
255
- if f:
259
+ if f and f not in seen:
256
260
  files.append(f)
261
+ seen.add(f)
257
262
 
258
263
  return files
259
264
 
@@ -264,7 +269,7 @@ class GitManager:
264
269
 
265
270
  def get_diff(self, staged: bool = False) -> str:
266
271
  """Get the current diff for stageable files only."""
267
- files = self.get_stageable_files()
272
+ files = self.get_staged_files() if staged else self.get_stageable_files()
268
273
  if not files:
269
274
  return ""
270
275
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ralph-code
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Automated task implementation with Claude Code and Codex
5
5
  Author: Ralph Coding
6
6
  License: MIT
@@ -61,6 +61,12 @@ ralph [OPTIONS] [DIRECTORY]
61
61
 
62
62
  ## Recent changes
63
63
 
64
+ Version `0.6.3` includes:
65
+ - Safer git integration for staged-file commit checks
66
+ - Correct staged diff generation
67
+ - Deduplicated unstaged file reporting
68
+ - Clearer errors when `git` is not available
69
+
64
70
  Version `0.6.2` includes:
65
71
  - Bounded non-interactive harness execution with timeout and turn limits
66
72
  - Structured `tasks.json` generation for more reliable PRD conversion
@@ -8,7 +8,7 @@ from setuptools import setup, find_packages # type: ignore[import-untyped]
8
8
 
9
9
  setup(
10
10
  name="ralph-code",
11
- version="0.6.2",
11
+ version="0.6.3",
12
12
  description="Automated task implementation with Claude Code and Codex",
13
13
  author="Ralph Coding",
14
14
  packages=find_packages(),
@@ -18,6 +18,18 @@ class TestGitManagerInit:
18
18
  assert manager.project_dir == tmp_path
19
19
 
20
20
 
21
+ class TestRunGit:
22
+ """Tests for low-level git command execution."""
23
+
24
+ @patch("ralph.git_manager.subprocess.run", side_effect=FileNotFoundError())
25
+ def test_run_git_handles_missing_git(self, _mock_run: MagicMock, tmp_path: Path) -> None:
26
+ """Test a missing git executable raises GitError."""
27
+ manager = GitManager(tmp_path)
28
+
29
+ with pytest.raises(GitError, match="Git executable not found on PATH"):
30
+ manager._run_git("status")
31
+
32
+
21
33
  class TestIsGitRepo:
22
34
  """Tests for is_git_repo method."""
23
35
 
@@ -386,6 +398,49 @@ class TestGetDiff:
386
398
 
387
399
  assert "modified content here" in diff
388
400
 
401
+ def test_get_diff_staged_only_uses_staged_files(self, tmp_path: Path) -> None:
402
+ """Test staged diffs ignore separate unstaged changes."""
403
+ subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True)
404
+ subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=tmp_path, capture_output=True)
405
+ subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, capture_output=True)
406
+
407
+ staged_file = tmp_path / "staged.py"
408
+ unstaged_file = tmp_path / "unstaged.py"
409
+ staged_file.write_text("before staged")
410
+ unstaged_file.write_text("before unstaged")
411
+ subprocess.run(["git", "add", "."], cwd=tmp_path, capture_output=True)
412
+ subprocess.run(["git", "commit", "-m", "Initial"], cwd=tmp_path, capture_output=True)
413
+
414
+ staged_file.write_text("after staged")
415
+ unstaged_file.write_text("after unstaged")
416
+ subprocess.run(["git", "add", "staged.py"], cwd=tmp_path, capture_output=True)
417
+
418
+ manager = GitManager(tmp_path)
419
+ diff = manager.get_diff(staged=True)
420
+
421
+ assert "after staged" in diff
422
+ assert "after unstaged" not in diff
423
+
424
+
425
+ class TestGetUnstagedFiles:
426
+ """Tests for get_unstaged_files method."""
427
+
428
+ def test_get_unstaged_files_deduplicates_results(self, tmp_path: Path) -> None:
429
+ """Test duplicate file names are collapsed while preserving order."""
430
+ manager = GitManager(tmp_path)
431
+
432
+ with patch.object(
433
+ manager,
434
+ "_run_git",
435
+ side_effect=[
436
+ MagicMock(stdout="a.py\nb.py\n"),
437
+ MagicMock(stdout="b.py\nc.py\n"),
438
+ ],
439
+ ):
440
+ files = manager.get_unstaged_files()
441
+
442
+ assert files == ["a.py", "b.py", "c.py"]
443
+
389
444
 
390
445
  class TestGetLastCommitMessage:
391
446
  """Tests for get_last_commit_message method."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes