codex-ralph 0.1.3__py3-none-any.whl

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.
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ __all__ = ["main"]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
codex_ralph/cli.py ADDED
@@ -0,0 +1,238 @@
1
+ import argparse
2
+ import json
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ import textwrap
8
+ from pathlib import Path
9
+
10
+
11
+ SUBPROCESS_PROMPT_TEMPLATE = textwrap.dedent(
12
+ """\
13
+ You are an autonomous coding agent running in iteration {iteration}.
14
+
15
+ Global goal:
16
+ {goal}
17
+
18
+ Current workspace:
19
+ {workspace}
20
+
21
+ Previous iteration notes:
22
+ {history}
23
+
24
+ Instructions for this iteration:
25
+ 1) Check the current state quickly (files, git status, test state when needed).
26
+ 2) Make or update a short plan for the remaining work.
27
+ 3) Execute the next smallest meaningful chunk of work.
28
+ 4) Validate only what is relevant for the executed chunk.
29
+ 5) Decide if the global goal is fully complete.
30
+
31
+ Hard rules:
32
+ - Keep momentum; do not stop at analysis if code changes are needed.
33
+ - Do not ask the user for input unless absolutely blocked.
34
+ - Return valid JSON only, matching the provided schema.
35
+ - Be explicit about what changed and what remains.
36
+ """
37
+ )
38
+
39
+
40
+ OUTPUT_SCHEMA = {
41
+ "type": "object",
42
+ "properties": {
43
+ "done": {"type": "boolean"},
44
+ "summary": {"type": "string"},
45
+ "actions_taken": {"type": "array", "items": {"type": "string"}},
46
+ "remaining_work": {"type": "array", "items": {"type": "string"}},
47
+ "next_focus": {"type": "string"},
48
+ },
49
+ "required": ["done", "summary", "actions_taken", "remaining_work", "next_focus"],
50
+ "additionalProperties": False,
51
+ }
52
+
53
+
54
+ def default_codex_bin() -> str:
55
+ return "codex.cmd" if os.name == "nt" else "codex"
56
+
57
+
58
+ def build_prompt(goal: str, workspace: str, history: list[str], iteration: int) -> str:
59
+ history_text = "\n".join(f"- {item}" for item in history[-12:]) if history else "- (none)"
60
+ return SUBPROCESS_PROMPT_TEMPLATE.format(
61
+ iteration=iteration,
62
+ goal=goal.strip(),
63
+ workspace=workspace,
64
+ history=history_text,
65
+ )
66
+
67
+
68
+ def run_codex_once(
69
+ codex_bin: str,
70
+ prompt: str,
71
+ workspace: Path,
72
+ schema_path: Path,
73
+ output_path: Path,
74
+ extra_args: list[str],
75
+ ) -> dict:
76
+ cmd = [
77
+ codex_bin,
78
+ "exec",
79
+ "--dangerously-bypass-approvals-and-sandbox",
80
+ "--skip-git-repo-check",
81
+ "--cd",
82
+ str(workspace),
83
+ "--color",
84
+ "never",
85
+ "--output-schema",
86
+ str(schema_path),
87
+ "--output-last-message",
88
+ str(output_path),
89
+ *extra_args,
90
+ prompt,
91
+ ]
92
+ result = subprocess.run(cmd, text=True, capture_output=True)
93
+ if result.returncode != 0:
94
+ raise RuntimeError(
95
+ "Codex execution failed.\n"
96
+ f"Command: {' '.join(cmd)}\n"
97
+ f"Exit code: {result.returncode}\n"
98
+ f"STDOUT:\n{result.stdout}\n"
99
+ f"STDERR:\n{result.stderr}"
100
+ )
101
+ raw = output_path.read_text(encoding="utf-8").strip()
102
+ try:
103
+ return json.loads(raw)
104
+ except json.JSONDecodeError as exc:
105
+ raise RuntimeError(f"Codex returned non-JSON output:\n{raw}") from exc
106
+
107
+
108
+ def print_panel(title: str, lines: list[str]) -> None:
109
+ width = 88
110
+ print("\n" + "=" * width)
111
+ print(title)
112
+ print("-" * width)
113
+ for line in lines:
114
+ print(line)
115
+ print("=" * width)
116
+
117
+
118
+ def parse_args() -> argparse.Namespace:
119
+ parser = argparse.ArgumentParser(
120
+ description="Iterative Codex orchestrator using subprocess + codex exec."
121
+ )
122
+ parser.add_argument("--goal", help="Global goal to complete.")
123
+ parser.add_argument(
124
+ "--workspace",
125
+ default=".",
126
+ help="Workspace directory passed to codex --cd. Default: current directory.",
127
+ )
128
+ parser.add_argument(
129
+ "--max-iterations",
130
+ type=int,
131
+ default=20,
132
+ help="Maximum iterations before stopping. Default: 20.",
133
+ )
134
+ parser.add_argument(
135
+ "--auto-continue",
136
+ action="store_true",
137
+ help="Continue iterations automatically without confirmation.",
138
+ )
139
+ parser.add_argument(
140
+ "--codex-bin",
141
+ default=default_codex_bin(),
142
+ help="Codex binary name/path. On Windows default is codex.cmd.",
143
+ )
144
+ parser.add_argument(
145
+ "--extra-arg",
146
+ action="append",
147
+ default=[],
148
+ help="Extra argument passed to codex exec (repeatable). Example: --extra-arg --search",
149
+ )
150
+ return parser.parse_args()
151
+
152
+
153
+ def main() -> int:
154
+ args = parse_args()
155
+ goal = args.goal or input("Enter your global goal: ").strip()
156
+ if not goal:
157
+ print("Goal is required.")
158
+ return 1
159
+
160
+ workspace = Path(args.workspace).resolve()
161
+ if not workspace.exists():
162
+ print(f"Workspace does not exist: {workspace}")
163
+ return 1
164
+
165
+ print_panel(
166
+ "Codex Iterative Orchestrator",
167
+ [
168
+ f"Workspace: {workspace}",
169
+ f"Max iterations: {args.max_iterations}",
170
+ f"Auto-continue: {args.auto_continue}",
171
+ f"Codex bin: {args.codex_bin}",
172
+ "Danger flag: --dangerously-bypass-approvals-and-sandbox",
173
+ ],
174
+ )
175
+
176
+ history: list[str] = []
177
+ with tempfile.TemporaryDirectory(prefix="codex-orch-") as td:
178
+ schema_path = Path(td) / "schema.json"
179
+ output_path = Path(td) / "last_message.json"
180
+ schema_path.write_text(json.dumps(OUTPUT_SCHEMA, ensure_ascii=True), encoding="utf-8")
181
+
182
+ for iteration in range(1, args.max_iterations + 1):
183
+ prompt = build_prompt(
184
+ goal=goal,
185
+ workspace=str(workspace),
186
+ history=history,
187
+ iteration=iteration,
188
+ )
189
+ print_panel(
190
+ f"Iteration {iteration}",
191
+ [
192
+ "Running codex exec...",
193
+ "This may take a while depending on task complexity.",
194
+ ],
195
+ )
196
+
197
+ payload = run_codex_once(
198
+ codex_bin=args.codex_bin,
199
+ prompt=prompt,
200
+ workspace=workspace,
201
+ schema_path=schema_path,
202
+ output_path=output_path,
203
+ extra_args=args.extra_arg,
204
+ )
205
+
206
+ done = bool(payload["done"])
207
+ summary = payload["summary"].strip()
208
+ actions = payload.get("actions_taken", [])
209
+ remaining = payload.get("remaining_work", [])
210
+ next_focus = payload["next_focus"].strip()
211
+
212
+ history.append(f"[iter {iteration}] {summary}")
213
+ print_panel(
214
+ f"Result {iteration} | done={done}",
215
+ [
216
+ f"Summary: {summary}",
217
+ f"Actions taken: {actions if actions else '[]'}",
218
+ f"Remaining: {remaining if remaining else '[]'}",
219
+ f"Next focus: {next_focus}",
220
+ ],
221
+ )
222
+
223
+ if done:
224
+ print("\nGlobal goal completed.")
225
+ return 0
226
+
227
+ if not args.auto_continue:
228
+ choice = input("Continue to next iteration? [Y/n]: ").strip().lower()
229
+ if choice in {"n", "no"}:
230
+ print("Stopped by user before completion.")
231
+ return 0
232
+
233
+ print("\nReached max iterations before completion.")
234
+ return 2
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex_ralph
3
+ Version: 0.1.3
4
+ Summary: Iterative Codex orchestrator CLI
5
+ Project-URL: Homepage, https://github.com/hakunamta00700/codex_ralph
6
+ Project-URL: Repository, https://github.com/hakunamta00700/codex_ralph
7
+ Project-URL: Issues, https://github.com/hakunamta00700/codex_ralph/issues
8
+ Project-URL: Documentation, https://github.com/hakunamta00700/codex_ralph#readme
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: uv-easy
@@ -0,0 +1,7 @@
1
+ codex_ralph/__init__.py,sha256=nyYbcAqf4zrlUMwQH9lT0TKpdmNMW-FpbYcjC55DXSg,43
2
+ codex_ralph/__main__.py,sha256=9XoVSW2QEE09aVSJKVs2Js1iUfQO5NFoC9JwAndJeVo,80
3
+ codex_ralph/cli.py,sha256=wMVjZZ4XEqCoxU0biTuD_asIzZ21mjZiTfReC5mudyk,7207
4
+ codex_ralph-0.1.3.dist-info/METADATA,sha256=T0rwtFcqJR-XJJDpMzBShsyVrHep-4ADM_NagkpmA9w,439
5
+ codex_ralph-0.1.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
6
+ codex_ralph-0.1.3.dist-info/entry_points.txt,sha256=1DM0l0fiG-nh7EwSFSx06YNpX2MTVZeeL46Ps-lkX-E,53
7
+ codex_ralph-0.1.3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ codex_ralph = codex_ralph.cli:main