@jesdi/skills 0.0.49 → 0.0.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jesdi/skills",
3
- "version": "0.0.49",
3
+ "version": "0.0.50",
4
4
  "description": "jesdi's agent skills (content package consumed by @jesdi/skills-cli)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -117,14 +117,32 @@ def command(role: str, entry: str, report: str) -> list[str]:
117
117
  """The CLI call for one agent; the prompt goes to its stdin."""
118
118
  provider, model, effort = split(entry)
119
119
  if provider == "openai":
120
- # No --ephemeral: the rollout on disk is the trace. A read-only agent cannot write its report, so -o saves it.
121
- tail = ["--sandbox", "read-only", "-o", report] if role in READ_ONLY else ["--sandbox", "workspace-write"]
120
+ # No --ephemeral: the rollout on disk is the trace. A read-only agent may not write its report, so -o saves
121
+ # the last message next to it; settle_report picks one. -o on the report itself overwrites a written report.
122
+ tail = ["--sandbox", "read-only", "-o", last_message(report)] if role in READ_ONLY else [
123
+ "--sandbox", "workspace-write"]
122
124
  return ["codex", "exec", "--model", model, "-c", f'model_reasoning_effort="{effort}"', *tail]
123
125
  # stream-json keeps tool output in the log and ends with a result that has total_cost_usd.
124
126
  return ["claude", "-p", "--model", model, "--effort", effort, "--permission-mode", "auto",
125
127
  "--output-format", "stream-json", "--verbose"]
126
128
 
127
129
 
130
+ def last_message(report: str | Path) -> str:
131
+ """The file where codex saves the last message of a read-only agent."""
132
+ return f"{report}.last"
133
+
134
+
135
+ def settle_report(report: Path) -> None:
136
+ """The report is the one the agent wrote when it parses, else the saved last message, when there is one."""
137
+ last = Path(last_message(report))
138
+ if not last.exists():
139
+ return
140
+ with contextlib.suppress(OSError, ValueError):
141
+ json.loads(report.read_text())
142
+ return
143
+ report.write_text(last.read_text())
144
+
145
+
128
146
  def render(name: str, values: dict[str, str]) -> str:
129
147
  return re.sub(r"\{([a-z_]+)\}", lambda m: values[m.group(1)], (SKILL / "prompts" / f"{name}.md").read_text())
130
148
 
@@ -291,9 +291,11 @@ def _attempts(run: Run, role: str, number: str, slot: str, values: dict[str, str
291
291
  for _ in range(2):
292
292
  prompt.write_text(text)
293
293
  report.unlink(missing_ok=True)
294
+ Path(driver_agents.last_message(report)).unlink(missing_ok=True)
294
295
  start = time.monotonic()
295
296
  code = driver_agents.launch(argv, prompt, log, minutes * 60, slot)
296
297
  seconds = time.monotonic() - start
298
+ driver_agents.settle_report(report)
297
299
  error, rerun_lines = _rejection(role, code, minutes, report, context, alone, detached)
298
300
  _record_attempt(run, role, number, entry, seconds, log, error)
299
301
  if not error:
@@ -211,6 +211,29 @@ def test_reviewing_roles_launch_codex_exec_read_only(env, role):
211
211
  assert value_after(argv, "--model", "-m") == "gpt-sol"
212
212
 
213
213
 
214
+ @pytest.mark.parametrize("writes", ["file", "message"])
215
+ def test_a_read_only_codex_report_is_the_file_it_wrote_else_its_last_message(env, writes):
216
+ # A read-only sandbox can still let the agent write its report; codex then writes the last message to the -o file.
217
+ w = World(env, refactor=True, correctness=["openai/gpt-sol@medium"])
218
+ report = {"status": "DONE", "commits": [], "files": [], "concerns": [], "findings": []}
219
+ (w.tmp / "bin/codex").write_text(f"""#!{sys.executable}
220
+ import re, sys
221
+ from pathlib import Path
222
+ prompt, argv = sys.stdin.read(), sys.argv[1:]
223
+ last = Path(argv[argv.index("-o") + 1])
224
+ if "implement-spec preflight" in prompt:
225
+ last.write_text("OK")
226
+ elif {writes!r} == "file":
227
+ Path(re.search(r"JSON report at (\\S+?)\\.? ", prompt).group(1)).write_text({json.dumps(report)!r})
228
+ last.write_text("Wrote and validated the report.\\n")
229
+ else:
230
+ last.write_text({json.dumps(report)!r})
231
+ """)
232
+ ok(w.run(None, "--only", "correctness", runner=False))
233
+ assert json.loads((w.repo / ".agent/implement/01/correctness-0.json").read_text()) == report
234
+ assert "correctness ran on openai/gpt-sol@medium" in w.ledger() and "report accepted" in w.ledger()
235
+
236
+
214
237
  @pytest.mark.parametrize("entry", ["claude-opus-5@medium", "anthropic/claude-opus-5@medium"])
215
238
  def test_claude_entry_launches_claude_p_and_bare_means_anthropic(env, entry):
216
239
  w = World(env, refactor=True, implementer=[entry])
@@ -342,7 +342,7 @@ def test_command_lines_per_provider():
342
342
  assert driver_agents.command("implementer", "openai/gpt-sol@high", "r.json") == [
343
343
  "codex", "exec", "--model", "gpt-sol", "-c", 'model_reasoning_effort="high"', "--sandbox", "workspace-write"]
344
344
  assert driver_agents.command("reviewer", "openai/gpt-sol@high", "r.json")[-4:] == [
345
- "--sandbox", "read-only", "-o", "r.json"]
345
+ "--sandbox", "read-only", "-o", "r.json.last"]
346
346
  assert driver_agents.command("reviewer", "opus@low", "r.json")[:6] == [
347
347
  "claude", "-p", "--model", "opus", "--effort", "low"]
348
348
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-18T20:38:07.695Z",
3
+ "generatedAt": "2026-10-19T09:42:49.911Z",
4
4
  "skills": [
5
5
  {
6
6
  "name": "backlog",
@@ -46,8 +46,8 @@
46
46
  },
47
47
  {
48
48
  "name": "implement-spec",
49
- "version": "0.1.25",
50
- "hash": "sha256-644814949264c14d8ce10419e2b71af1c6f00e872c328ff31e47b4441eac5365",
49
+ "version": "0.1.26",
50
+ "hash": "sha256-40637f9172d94edb3ef009f91f58e27f7cd400ed15935c5a62654822a57208c5",
51
51
  "description": "Implement a specification in code."
52
52
  },
53
53
  {