convertible-cli 0.4.0__tar.gz → 0.6.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 (129) hide show
  1. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/CHANGELOG.md +19 -0
  2. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/CLAUDE.md +20 -4
  3. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/PKG-INFO +75 -3
  4. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/README.md +74 -2
  5. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/__init__.py +24 -0
  6. convertible_cli-0.6.0/convertible/cli/_commands/agents.py +109 -0
  7. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/drive.py +5 -2
  8. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/overview.py +2 -0
  9. convertible_cli-0.6.0/convertible/cli/_commands/skills.py +107 -0
  10. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/engine.py +18 -0
  11. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/engines/mock.py +6 -1
  12. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/engines/vllm_openai.py +6 -1
  13. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/explain/catalog.py +76 -5
  14. convertible_cli-0.6.0/convertible/layers.py +288 -0
  15. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/pyproject.toml +1 -1
  16. convertible_cli-0.6.0/tests/test_agents_cli.py +82 -0
  17. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_cli.py +23 -1
  18. convertible_cli-0.6.0/tests/test_layers.py +294 -0
  19. convertible_cli-0.6.0/tests/test_layers_engine_parity.py +83 -0
  20. convertible_cli-0.6.0/tests/test_skills_cli.py +87 -0
  21. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_zero_deps.py +1 -0
  22. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/uv.lock +1 -1
  23. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/agent-config/SKILL.md +0 -0
  24. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  25. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/agent-config/scripts/show.sh +0 -0
  26. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  27. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  28. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/SKILL.md +0 -0
  29. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  30. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  31. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  32. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  33. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  34. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/SKILL.md +0 -0
  35. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  36. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  37. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  38. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  39. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  40. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  41. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  42. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  43. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  44. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  45. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/run-tests/SKILL.md +0 -0
  46. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/run-tests/scripts/test.sh +0 -0
  47. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/sonarclaude/SKILL.md +0 -0
  48. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  49. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  50. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  51. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/think/SKILL.md +0 -0
  52. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/think/scripts/think.sh +0 -0
  53. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/version-bump/SKILL.md +0 -0
  54. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills/version-bump/scripts/bump.py +0 -0
  55. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.claude/skills.local.yaml.example +0 -0
  56. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/current +0 -0
  57. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/current_plan +0 -0
  58. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/frames/convertible-gains-an-extensibility-layer-like-clau.json +0 -0
  59. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/frames/convertible-v0-ships-point-it-at-a-repo-task-and-i.json +0 -0
  60. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/plans/convertible-gains-an-extensibility-layer-like-clau.json +0 -0
  61. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.devague/plans/convertible-v0-ships-point-it-at-a-repo-task-and-i.json +0 -0
  62. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.flake8 +0 -0
  63. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.github/workflows/publish.yml +0 -0
  64. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.github/workflows/tests.yml +0 -0
  65. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.gitignore +0 -0
  66. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/.markdownlint-cli2.yaml +0 -0
  67. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/LICENSE +0 -0
  68. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/__init__.py +0 -0
  69. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/__main__.py +0 -0
  70. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/artifact.py +0 -0
  71. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_banner-big.txt +0 -0
  72. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_banner.py +0 -0
  73. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_banner.txt +0 -0
  74. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/__init__.py +0 -0
  75. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/cli.py +0 -0
  76. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/commands.py +0 -0
  77. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/doctor.py +0 -0
  78. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/explain.py +0 -0
  79. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/hooks.py +0 -0
  80. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/learn.py +0 -0
  81. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/session.py +0 -0
  82. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/wheels.py +0 -0
  83. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_commands/whoami.py +0 -0
  84. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_errors.py +0 -0
  85. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/cli/_output.py +0 -0
  86. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/commands.py +0 -0
  87. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/config.py +0 -0
  88. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/configdir.py +0 -0
  89. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/contract.py +0 -0
  90. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/engines/__init__.py +0 -0
  91. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/explain/__init__.py +0 -0
  92. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/handoff.py +0 -0
  93. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/hooks.py +0 -0
  94. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/loop.py +0 -0
  95. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/registry.py +0 -0
  96. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/convertible/tools.py +0 -0
  97. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/culture.yaml +0 -0
  98. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/docs/plans/2026-05-26-convertible-v0-ships-point-it-at-a-repo-task-and-i.md +0 -0
  99. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/docs/plans/2026-05-27-convertible-gains-an-extensibility-layer-like-clau.md +0 -0
  100. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/docs/skill-sources.md +0 -0
  101. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/docs/specs/2026-05-26-convertible-v0-ships-point-it-at-a-repo-task-and-i.md +0 -0
  102. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/docs/specs/2026-05-27-convertible-gains-an-extensibility-layer-like-clau.md +0 -0
  103. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/sonar-project.properties +0 -0
  104. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/__init__.py +0 -0
  105. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_artifact.py +0 -0
  106. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_banner.py +0 -0
  107. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_boundary.py +0 -0
  108. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_cli_introspection.py +0 -0
  109. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_commands.py +0 -0
  110. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_commands_cli.py +0 -0
  111. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_config.py +0 -0
  112. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_configdir.py +0 -0
  113. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_contract.py +0 -0
  114. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_drive.py +0 -0
  115. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_e2e_extensibility.py +0 -0
  116. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_e2e_mock.py +0 -0
  117. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_engine.py +0 -0
  118. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_handoff.py +0 -0
  119. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_hooks.py +0 -0
  120. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_hooks_cli.py +0 -0
  121. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_loop.py +0 -0
  122. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_mock_engine.py +0 -0
  123. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_registry.py +0 -0
  124. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_review_fixes.py +0 -0
  125. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_session.py +0 -0
  126. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_tools.py +0 -0
  127. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_vllm_live.py +0 -0
  128. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_vllm_openai.py +0 -0
  129. {convertible_cli-0.4.0 → convertible_cli-0.6.0}/tests/test_wheels.py +0 -0
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.0] - 2026-05-28
9
+
10
+ ### Added
11
+
12
+ - Layered per-model config: AGENTS instructions (`AGENTS.md` -> `AGENTS.convertible.md` -> `AGENTS.convertible.<model>.md` at the repo root, with a `~/.convertible/` fallback) and skills (`.convertible/skills/*.md` -> `.convertible/<model>/skills/*.md`) compose into the engine system prompt via `convertible/layers.py`
13
+ - `convertible agents` and `convertible skills` introspection nouns (list + overview, `--json`, `--model`)
14
+ - `Engine.system_prompt()` base-class helper injects the layered prompt for every engine (all-engines rule)
15
+
16
+ ### Changed
17
+
18
+ - Both engines (mock, vllm-openai) now pass a model-specific `system_prompt` to the loop; behavior is byte-identical when no AGENTS/skills files exist
19
+
20
+ ## [0.5.0] - 2026-05-27
21
+
22
+ ### Changed
23
+
24
+ - Bare `convertible` (no subcommand) now opens the interactive harness (the `session` palette) when run at a terminal — the natural "get in and drive" gesture. Piped, redirected, or otherwise non-interactive, it still prints usage, preserving the discoverable surface for scripts and agents. `-h/--help` is unaffected.
25
+ - Reframed `convertible drive` help and `explain` text to lead with the goal/instruction ("drive toward a goal") rather than "run a repo task"; the repo is the target, not the headline. No behavior change.
26
+
8
27
  ## [0.4.0] - 2026-05-27
9
28
 
10
29
  ### Added
@@ -34,6 +34,16 @@ The car metaphor *is* the architecture:
34
34
  code path, no daemon.
35
35
  - **Config resolution** — `convertible/configdir.py`: repo-level
36
36
  `.convertible/` overrides user-level `~/.convertible/`.
37
+ - **Layered per-model config** — `convertible/layers.py`: AGENTS instructions
38
+ (`AGENTS.md` → `AGENTS.convertible.md` → `AGENTS.convertible.<model>.md`, at
39
+ the repo root with a `~/.convertible/` fallback) and skills
40
+ (`.convertible/skills/*.md` → `.convertible/<model>/skills/*.md`) compose into
41
+ the engine system prompt. Resolution builds exact paths for the current model
42
+ and never globs sibling models — per-model isolation is structural. Injected
43
+ once on the `Engine` base class (`system_prompt()`), so every engine inherits
44
+ it (all-engines rule). Surfaced via the `agents` / `skills` introspection
45
+ nouns. **MCP layering is not built** — convertible reads no `mcp.json` and has
46
+ no `mcp` verb; a live MCP client is a re-spec (see scope below).
37
47
 
38
48
  The buildable spec and plan this implementation converged from live in
39
49
  [`docs/specs/`](docs/specs/) and [`docs/plans/`](docs/plans/) (authored via the
@@ -43,14 +53,20 @@ The buildable spec and plan this implementation converged from live in
43
53
 
44
54
  In scope: the chassis, the entry-point wheel contract, exactly two engines
45
55
  (`mock`, `vllm-openai`), the git/PR handoff, command templates, lifecycle
46
- hooks, and the foreground interactive palette.
56
+ hooks, the foreground interactive palette, and layered per-model AGENTS/skills
57
+ config (`convertible/layers.py`).
47
58
 
48
59
  **Out of scope for v0** — do not add without re-speccing: a multi-engine
49
60
  router/policy "gearbox", an execution sandbox, a daemon/server mode,
50
- Codex/Claude/Gemini drivers, and a per-repo hook trust gate / `--no-hooks`
61
+ Codex/Claude/Gemini drivers, a per-repo hook trust gate / `--no-hooks`
51
62
  escape hatch (planned follow-up hardening — not yet built; document this gap
52
- honestly, never invent a `--no-hooks` flag). Adding an excluded feature means
53
- scope crept.
63
+ honestly, never invent a `--no-hooks` flag), and an **MCP execution runtime**
64
+ (a live MCP client — stdio/socket transport, tool discovery, dynamic tool
65
+ registration). The layered config ships AGENTS + skills only; `mcp.json` is
66
+ **not** read and there is no `mcp` verb. A live MCP client would breach the
67
+ no-deps / no-socket / no-daemon conventions and needs its own spec — document
68
+ this gap honestly, never invent an `mcp` surface. Adding an excluded feature
69
+ means scope crept.
54
70
 
55
71
  ## The all-engines rule
56
72
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: convertible-cli
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Convertible CLI is a swappable coder-agent harness that turns different models into repo workers behind one shared task contract.
5
5
  Project-URL: Homepage, https://github.com/agentculture/convertible
6
6
  Project-URL: Issues, https://github.com/agentculture/convertible/issues
@@ -17,6 +17,27 @@ Description-Content-Type: text/markdown
17
17
 
18
18
  # convertible
19
19
 
20
+ ```text
21
+ ,"^,::,:::::I::::::^
22
+ ^!'` `"^,. ::`
23
+ `,;;i;,,' .!^'`",. "<," " ;,^
24
+ ^ '"^";"^```,":::','"'^`"."'.,:^,i::,"".
25
+ .I. ' I-l '",I;!ii,I.,,l;;IlIl:i!:::::,,,^^;",,'
26
+ '^ ""::``':. .`";,"```"^.. '^^'":,''.
27
+ ::,`;!"..^..'^"^ .' .."" ..`"^ ..`"`'"".
28
+ ;;l!i:' ^ '^"`. `"`.. ',^.. `` '^
29
+ :i>I!lII". .' .''^` '`^' .`". ^. :;lI.
30
+ ",l!`;"':l;,,`. .' '",'. ^...'^`'"...^l'":;Ii!!l:!';.
31
+ ^''^., '`^^`:l;"^"',!~ .^^^,""..'`^. .^l,I''^.:l!;;;i>|+,!IlI::^
32
+ `;. :>i":` "`, ',^^" "; I.'. :::"^:;iil;'":^; '"
33
+ '^"'"IlI,.:,::;:' .`;"!l:``,,"``',,,^,,:ll!,,l^
34
+ ":^:,I^:"!;:: :I>l:::"`""::,:I>!<i^ " ;.
35
+ ",;"Il;i^: ',":;"`'`'^, ><[il```'
36
+ " ;;I"l:^.^;^''lI" :^::",:,'
37
+ ,`:;,I.^ ''
38
+ `. " "
39
+ ```
40
+
20
41
  > Convertible CLI is a swappable coder-agent harness that turns different models
21
42
  > into repo workers behind one shared task contract.
22
43
  >
@@ -106,10 +127,13 @@ engine, so it binds equally to `mock`, `vllm-openai`, and any future wheel.
106
127
  uv sync
107
128
  uv run pytest -n auto # full suite, no network needed
108
129
 
130
+ # Open the interactive harness (the session palette) at a terminal:
131
+ uv run convertible
132
+
109
133
  # Discover the engines installed in this environment:
110
134
  uv run convertible wheels list
111
135
 
112
- # Drive a task with the deterministic mock engine (no model, no network):
136
+ # Drive toward a goal with the deterministic mock engine (no model, no network):
113
137
  uv run convertible drive "add a CONTRIBUTING.md stub" --repo . --engine mock --no-pr
114
138
  ```
115
139
 
@@ -297,10 +321,54 @@ loop, hooks, and artifact — no parallel code path):
297
321
  uv run convertible session --repo /path/to/repo --engine vllm-openai
298
322
  ```
299
323
 
324
+ Running `convertible` with no arguments **at a terminal** opens this same palette
325
+ (with the default engine and repo) — the natural "get in and drive" gesture.
326
+ Piped, redirected, or otherwise non-interactive, bare `convertible` prints usage
327
+ instead, so scripts and agents keep a discoverable surface.
328
+
300
329
  The session loops until the user enters `q`, `quit`, or an empty line. Any
301
330
  driver flags accepted by `drive` (`--engine`, `--no-pr`, `--base-url`, etc.)
302
331
  are also accepted by `session`.
303
332
 
333
+ ## Per-model instructions & skills
334
+
335
+ Convertible composes a model-specific **system prompt** for every drive from two
336
+ layered families, resolved *relative to the model currently driving*. Strict
337
+ per-model isolation: driving model X reads only X's overlay plus the shared base
338
+ — it never even opens model Y's files (isolation is structural, built from exact
339
+ paths, not filtered).
340
+
341
+ **AGENTS instructions** cascade from the **repo root** (the cross-tool standard
342
+ location — sibling agent tools read `AGENTS.md` there too), general → specific,
343
+ with a `~/.convertible/` user-level fallback:
344
+
345
+ ```text
346
+ AGENTS.md # shared base
347
+ AGENTS.convertible.md # convertible overlay
348
+ AGENTS.convertible.<model>.md # model overlay
349
+ ```
350
+
351
+ **Skills** are markdown capability docs under `.convertible/`, folded into the
352
+ prompt as a compact name + one-line-summary catalog (a skill is instructional
353
+ text only — there is no skill *execution* in v0):
354
+
355
+ ```text
356
+ .convertible/skills/*.md # base
357
+ .convertible/<model>/skills/*.md # model overlay (shadows base by stem)
358
+ ```
359
+
360
+ `<model>` is sanitized to a filename-safe token (e.g. `Qwen/Qwen3-32B` →
361
+ `Qwen-Qwen3-32B`). Inspect what resolves for a model:
362
+
363
+ ```bash
364
+ uv run convertible agents list --model Qwen/Qwen3-32B --repo .
365
+ uv run convertible skills list --model Qwen/Qwen3-32B --repo .
366
+ ```
367
+
368
+ > **MCP layering is not built yet.** Convertible does not read `mcp.json` or
369
+ > connect to any MCP server today; a live MCP client needs its own spec. There
370
+ > is no `mcp` verb — don't rely on a non-existent surface.
371
+
304
372
  ## ⚠ Security: repo-shipped hooks run by default
305
373
 
306
374
  > **This is a code-execution risk. Read before driving an untrusted repo.**
@@ -333,12 +401,16 @@ rely on a non-existent flag.
333
401
 
334
402
  | Verb | What it does |
335
403
  |------|--------------|
336
- | `drive <instruction>` | Run a repo task through a coder engine; write the artifact; hand off. |
404
+ | `drive <goal>` | Drive toward a goal/instruction: work autonomously through a coder engine; write the artifact; hand off. |
337
405
  | `drive --command <name> [args…]` | Expand a saved command template and drive it. |
338
406
  | `commands list` | List discovered command templates for a repo. |
339
407
  | `commands overview` | Describe the commands surface. |
340
408
  | `hooks list` | List configured hook entries for a repo. |
341
409
  | `hooks overview` | Describe the hooks surface. |
410
+ | `agents list` | List resolved AGENTS instruction layers for a model. |
411
+ | `agents overview` | Describe the agents surface. |
412
+ | `skills list` | List resolved skill docs for a model. |
413
+ | `skills overview` | Describe the skills surface. |
342
414
  | `session` | Open a foreground interactive palette. |
343
415
  | `wheels list` | List discovered engine wheels (the garage). |
344
416
  | `whoami` | Report this agent's nick, version, backend, and model. |
@@ -1,5 +1,26 @@
1
1
  # convertible
2
2
 
3
+ ```text
4
+ ,"^,::,:::::I::::::^
5
+ ^!'` `"^,. ::`
6
+ `,;;i;,,' .!^'`",. "<," " ;,^
7
+ ^ '"^";"^```,":::','"'^`"."'.,:^,i::,"".
8
+ .I. ' I-l '",I;!ii,I.,,l;;IlIl:i!:::::,,,^^;",,'
9
+ '^ ""::``':. .`";,"```"^.. '^^'":,''.
10
+ ::,`;!"..^..'^"^ .' .."" ..`"^ ..`"`'"".
11
+ ;;l!i:' ^ '^"`. `"`.. ',^.. `` '^
12
+ :i>I!lII". .' .''^` '`^' .`". ^. :;lI.
13
+ ",l!`;"':l;,,`. .' '",'. ^...'^`'"...^l'":;Ii!!l:!';.
14
+ ^''^., '`^^`:l;"^"',!~ .^^^,""..'`^. .^l,I''^.:l!;;;i>|+,!IlI::^
15
+ `;. :>i":` "`, ',^^" "; I.'. :::"^:;iil;'":^; '"
16
+ '^"'"IlI,.:,::;:' .`;"!l:``,,"``',,,^,,:ll!,,l^
17
+ ":^:,I^:"!;:: :I>l:::"`""::,:I>!<i^ " ;.
18
+ ",;"Il;i^: ',":;"`'`'^, ><[il```'
19
+ " ;;I"l:^.^;^''lI" :^::",:,'
20
+ ,`:;,I.^ ''
21
+ `. " "
22
+ ```
23
+
3
24
  > Convertible CLI is a swappable coder-agent harness that turns different models
4
25
  > into repo workers behind one shared task contract.
5
26
  >
@@ -89,10 +110,13 @@ engine, so it binds equally to `mock`, `vllm-openai`, and any future wheel.
89
110
  uv sync
90
111
  uv run pytest -n auto # full suite, no network needed
91
112
 
113
+ # Open the interactive harness (the session palette) at a terminal:
114
+ uv run convertible
115
+
92
116
  # Discover the engines installed in this environment:
93
117
  uv run convertible wheels list
94
118
 
95
- # Drive a task with the deterministic mock engine (no model, no network):
119
+ # Drive toward a goal with the deterministic mock engine (no model, no network):
96
120
  uv run convertible drive "add a CONTRIBUTING.md stub" --repo . --engine mock --no-pr
97
121
  ```
98
122
 
@@ -280,10 +304,54 @@ loop, hooks, and artifact — no parallel code path):
280
304
  uv run convertible session --repo /path/to/repo --engine vllm-openai
281
305
  ```
282
306
 
307
+ Running `convertible` with no arguments **at a terminal** opens this same palette
308
+ (with the default engine and repo) — the natural "get in and drive" gesture.
309
+ Piped, redirected, or otherwise non-interactive, bare `convertible` prints usage
310
+ instead, so scripts and agents keep a discoverable surface.
311
+
283
312
  The session loops until the user enters `q`, `quit`, or an empty line. Any
284
313
  driver flags accepted by `drive` (`--engine`, `--no-pr`, `--base-url`, etc.)
285
314
  are also accepted by `session`.
286
315
 
316
+ ## Per-model instructions & skills
317
+
318
+ Convertible composes a model-specific **system prompt** for every drive from two
319
+ layered families, resolved *relative to the model currently driving*. Strict
320
+ per-model isolation: driving model X reads only X's overlay plus the shared base
321
+ — it never even opens model Y's files (isolation is structural, built from exact
322
+ paths, not filtered).
323
+
324
+ **AGENTS instructions** cascade from the **repo root** (the cross-tool standard
325
+ location — sibling agent tools read `AGENTS.md` there too), general → specific,
326
+ with a `~/.convertible/` user-level fallback:
327
+
328
+ ```text
329
+ AGENTS.md # shared base
330
+ AGENTS.convertible.md # convertible overlay
331
+ AGENTS.convertible.<model>.md # model overlay
332
+ ```
333
+
334
+ **Skills** are markdown capability docs under `.convertible/`, folded into the
335
+ prompt as a compact name + one-line-summary catalog (a skill is instructional
336
+ text only — there is no skill *execution* in v0):
337
+
338
+ ```text
339
+ .convertible/skills/*.md # base
340
+ .convertible/<model>/skills/*.md # model overlay (shadows base by stem)
341
+ ```
342
+
343
+ `<model>` is sanitized to a filename-safe token (e.g. `Qwen/Qwen3-32B` →
344
+ `Qwen-Qwen3-32B`). Inspect what resolves for a model:
345
+
346
+ ```bash
347
+ uv run convertible agents list --model Qwen/Qwen3-32B --repo .
348
+ uv run convertible skills list --model Qwen/Qwen3-32B --repo .
349
+ ```
350
+
351
+ > **MCP layering is not built yet.** Convertible does not read `mcp.json` or
352
+ > connect to any MCP server today; a live MCP client needs its own spec. There
353
+ > is no `mcp` verb — don't rely on a non-existent surface.
354
+
287
355
  ## ⚠ Security: repo-shipped hooks run by default
288
356
 
289
357
  > **This is a code-execution risk. Read before driving an untrusted repo.**
@@ -316,12 +384,16 @@ rely on a non-existent flag.
316
384
 
317
385
  | Verb | What it does |
318
386
  |------|--------------|
319
- | `drive <instruction>` | Run a repo task through a coder engine; write the artifact; hand off. |
387
+ | `drive <goal>` | Drive toward a goal/instruction: work autonomously through a coder engine; write the artifact; hand off. |
320
388
  | `drive --command <name> [args…]` | Expand a saved command template and drive it. |
321
389
  | `commands list` | List discovered command templates for a repo. |
322
390
  | `commands overview` | Describe the commands surface. |
323
391
  | `hooks list` | List configured hook entries for a repo. |
324
392
  | `hooks overview` | Describe the hooks surface. |
393
+ | `agents list` | List resolved AGENTS instruction layers for a model. |
394
+ | `agents overview` | Describe the agents surface. |
395
+ | `skills list` | List resolved skill docs for a model. |
396
+ | `skills overview` | Describe the skills surface. |
325
397
  | `session` | Open a foreground interactive palette. |
326
398
  | `wheels list` | List discovered engine wheels (the garage). |
327
399
  | `whoami` | Report this agent's nick, version, backend, and model. |
@@ -61,7 +61,19 @@ def _argv_has_json(argv: list[str] | None) -> bool:
61
61
  return any(t == "--json" or t.startswith("--json=") for t in tokens)
62
62
 
63
63
 
64
+ def _stdio_is_interactive() -> bool:
65
+ """Whether stdin and stdout are both interactive terminals.
66
+
67
+ Bare ``convertible`` opens the interactive harness only at a real terminal;
68
+ isolated as a module function so tests can force the interactive branch
69
+ without a TTY (mirrors :func:`convertible.cli._banner._isatty`). Both streams
70
+ must be a TTY: the palette reads from stdin and renders its chrome to stdout.
71
+ """
72
+ return sys.stdin.isatty() and sys.stdout.isatty()
73
+
74
+
64
75
  def _build_parser() -> argparse.ArgumentParser:
76
+ from convertible.cli._commands import agents as _agents_group
65
77
  from convertible.cli._commands import cli as _cli_group
66
78
  from convertible.cli._commands import commands as _commands_group
67
79
  from convertible.cli._commands import doctor as _doctor_cmd
@@ -71,6 +83,7 @@ def _build_parser() -> argparse.ArgumentParser:
71
83
  from convertible.cli._commands import learn as _learn_cmd
72
84
  from convertible.cli._commands import overview as _overview_cmd
73
85
  from convertible.cli._commands import session as _session_cmd
86
+ from convertible.cli._commands import skills as _skills_group
74
87
  from convertible.cli._commands import wheels as _wheels_group
75
88
  from convertible.cli._commands import whoami as _whoami_cmd
76
89
 
@@ -99,6 +112,9 @@ def _build_parser() -> argparse.ArgumentParser:
99
112
  # Extensibility layer: command templates + lifecycle hooks.
100
113
  _commands_group.register(sub)
101
114
  _hooks_group.register(sub)
115
+ # Layered per-model config: AGENTS instructions + skills.
116
+ _agents_group.register(sub)
117
+ _skills_group.register(sub)
102
118
  # Interactive foreground palette (c28/R8).
103
119
  _session_cmd.register(sub)
104
120
 
@@ -136,6 +152,14 @@ def main(argv: list[str] | None = None) -> int:
136
152
  args = parser.parse_args(argv)
137
153
 
138
154
  if args.command is None:
155
+ # Bare `convertible` opens the interactive harness at a terminal; piped /
156
+ # redirected / non-interactive it prints usage so scripts and agents keep
157
+ # a discoverable surface. `-h/--help` is handled by argparse before here,
158
+ # so the help surface (and the teken rubric, which probes --help) stay
159
+ # available either way. Re-parsing ["session"] reuses the session
160
+ # subparser's defaults and func wiring — no parallel code path.
161
+ if _stdio_is_interactive():
162
+ return _dispatch(parser.parse_args(["session"]))
139
163
  parser.print_help()
140
164
  return 0
141
165
 
@@ -0,0 +1,109 @@
1
+ """``convertible agents`` — inspect layered AGENTS instruction files.
2
+
3
+ ``agents list`` resolves the AGENTS instruction cascade for a model
4
+ (``AGENTS.md`` -> ``AGENTS.convertible.md`` -> ``AGENTS.convertible.<model>.md``;
5
+ repo root with a ``~/.convertible/`` fallback) and reports the layers that
6
+ exist, in general -> specific order. ``agents overview`` describes the noun
7
+ (satisfying the agent-first rubric: any noun with action-verbs must also expose
8
+ ``overview``).
9
+
10
+ These layers are composed (with the engine default and the skills catalog) into
11
+ the system prompt every drive sends — so what ``agents list`` reports for a model
12
+ is exactly what that model is instructed with. Per-model isolation is structural:
13
+ only the named model's overlay is read, never a sibling model's.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ from pathlib import Path
20
+
21
+ from convertible.cli._commands.overview import emit_overview
22
+ from convertible.cli._output import emit_result
23
+ from convertible.config import EngineConfig
24
+ from convertible.layers import resolve_agents
25
+
26
+
27
+ def _agents_sections() -> list[dict[str, object]]:
28
+ return [
29
+ {
30
+ "title": "What it does",
31
+ "items": [
32
+ "Resolves AGENTS instruction layers for the current model",
33
+ "Cascade (general -> specific): AGENTS.md, AGENTS.convertible.md, "
34
+ "AGENTS.convertible.<model>.md",
35
+ "Read from the repo root, with a ~/.convertible/ user-level fallback",
36
+ "Composed into the system prompt every drive sends to the engine",
37
+ ],
38
+ },
39
+ {
40
+ "title": "Per-model isolation",
41
+ "items": [
42
+ "<model> is sanitized (e.g. 'Qwen/Qwen3-32B' -> 'Qwen-Qwen3-32B')",
43
+ "Only the named model's overlay is read — never a sibling model's",
44
+ "MCP layering is not built yet (no mcp.json reader); tracked separately",
45
+ ],
46
+ },
47
+ {
48
+ "title": "Verbs",
49
+ "items": [
50
+ "agents list [--model M] [--repo PATH] — list resolved AGENTS layers",
51
+ "agents overview — describe the agents surface (this command)",
52
+ ],
53
+ },
54
+ ]
55
+
56
+
57
+ def cmd_agents_overview(args: argparse.Namespace) -> int:
58
+ emit_overview(
59
+ "convertible agents",
60
+ _agents_sections(),
61
+ json_mode=bool(getattr(args, "json", False)),
62
+ )
63
+ return 0
64
+
65
+
66
+ def cmd_agents_list(args: argparse.Namespace) -> int:
67
+ repo = Path(getattr(args, "repo", ".")).expanduser()
68
+ model = getattr(args, "model", None) or EngineConfig.resolve().model
69
+ json_mode = bool(getattr(args, "json", False))
70
+
71
+ layers = resolve_agents(repo, model)
72
+
73
+ if json_mode:
74
+ items = [{"scope": layer.scope, "path": str(layer.path)} for layer in layers]
75
+ emit_result({"model": model, "agents": items}, json_mode=True)
76
+ elif not layers:
77
+ emit_result("(no AGENTS layers found)", json_mode=False)
78
+ else:
79
+ lines = [f"{layer.scope}\t{layer.path}" for layer in layers]
80
+ emit_result("\n".join(lines), json_mode=False)
81
+ return 0
82
+
83
+
84
+ def _no_verb(args: argparse.Namespace) -> int:
85
+ return cmd_agents_overview(args)
86
+
87
+
88
+ def register(sub: argparse._SubParsersAction) -> None:
89
+ p = sub.add_parser(
90
+ "agents",
91
+ help="Inspect layered AGENTS instruction files (see 'convertible agents overview').",
92
+ )
93
+ p.add_argument("--json", action="store_true", help="Emit structured JSON.")
94
+ p.set_defaults(func=_no_verb, json=False)
95
+ noun_sub = p.add_subparsers(dest="agents_command", parser_class=type(p))
96
+
97
+ lst = noun_sub.add_parser("list", help="List resolved AGENTS instruction layers.")
98
+ lst.add_argument("--repo", default=".", help="Path to the target repository (default: cwd).")
99
+ lst.add_argument(
100
+ "--model",
101
+ default=None,
102
+ help="Model to resolve layers for (default: the resolved engine model).",
103
+ )
104
+ lst.add_argument("--json", action="store_true", help="Emit structured JSON.")
105
+ lst.set_defaults(func=cmd_agents_list)
106
+
107
+ ov = noun_sub.add_parser("overview", help="Describe the agents surface.")
108
+ ov.add_argument("--json", action="store_true", help="Emit structured JSON.")
109
+ ov.set_defaults(func=cmd_agents_overview)
@@ -224,7 +224,10 @@ def cmd_drive(args: argparse.Namespace) -> int:
224
224
  def register(sub: argparse._SubParsersAction) -> None:
225
225
  p = sub.add_parser(
226
226
  "drive",
227
- help="Run a repo task through a coder engine and hand off the result.",
227
+ help=(
228
+ "Drive toward a goal: work autonomously on a request or instruction "
229
+ "through a coder engine, then hand off the result."
230
+ ),
228
231
  )
229
232
  # ``instruction`` is now zero-or-more positional tokens (nargs="*") so
230
233
  # ``--command`` can be the sole input without argparse raising an error.
@@ -232,7 +235,7 @@ def register(sub: argparse._SubParsersAction) -> None:
232
235
  "instruction",
233
236
  nargs="*",
234
237
  help=(
235
- "What the engine should do in the repo. "
238
+ "A goal or instruction to pursue autonomously. "
236
239
  "Mutually exclusive with --command. "
237
240
  "When --command is used, any positional tokens are passed as template arguments."
238
241
  ),
@@ -27,6 +27,8 @@ _ARTIFACTS = [
27
27
  _VERBS = [
28
28
  "drive <instruction> — run a repo task through a coder engine",
29
29
  "wheels list — list discovered engine wheels",
30
+ "agents list — inspect layered AGENTS instruction files for a model",
31
+ "skills list — inspect layered skill docs for a model",
30
32
  "whoami — identity probe (nick, version, backend, model)",
31
33
  "learn — structured self-teaching prompt",
32
34
  "explain <path> — markdown docs for a topic",
@@ -0,0 +1,107 @@
1
+ """``convertible skills`` — inspect layered skill docs.
2
+
3
+ ``skills list`` resolves the skill docs for a model — ``.convertible/skills/*.md``
4
+ (base) overlaid by ``.convertible/<model>/skills/*.md`` (model overlay shadows
5
+ base by stem) — and reports them with their winning scope. ``skills overview``
6
+ describes the noun (satisfying the agent-first rubric).
7
+
8
+ A skill is purely instructional: convertible folds a compact name +
9
+ one-line-summary catalog of the resolved skills into the system prompt every
10
+ drive sends. There is no skill *execution* model (an execution sandbox is out of
11
+ v0 scope); invokable skills are a tracked follow-up.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ from pathlib import Path
18
+
19
+ from convertible.cli._commands.overview import emit_overview
20
+ from convertible.cli._output import emit_result
21
+ from convertible.config import EngineConfig
22
+ from convertible.layers import resolve_skills
23
+
24
+
25
+ def _skills_sections() -> list[dict[str, object]]:
26
+ return [
27
+ {
28
+ "title": "What it does",
29
+ "items": [
30
+ "Resolves skill docs for the current model",
31
+ "base: .convertible/skills/*.md",
32
+ "model overlay: .convertible/<model>/skills/*.md (shadows base by stem)",
33
+ "Folded into the system prompt as a name + one-line-summary catalog",
34
+ ],
35
+ },
36
+ {
37
+ "title": "Scope",
38
+ "items": [
39
+ "A skill is instructional text only — no execution model in v0",
40
+ "<model> is sanitized; only the named model's overlay is read",
41
+ "Invokable skills (skills as procedures) are a tracked follow-up",
42
+ ],
43
+ },
44
+ {
45
+ "title": "Verbs",
46
+ "items": [
47
+ "skills list [--model M] [--repo PATH] — list resolved skills",
48
+ "skills overview — describe the skills surface (this command)",
49
+ ],
50
+ },
51
+ ]
52
+
53
+
54
+ def cmd_skills_overview(args: argparse.Namespace) -> int:
55
+ emit_overview(
56
+ "convertible skills",
57
+ _skills_sections(),
58
+ json_mode=bool(getattr(args, "json", False)),
59
+ )
60
+ return 0
61
+
62
+
63
+ def cmd_skills_list(args: argparse.Namespace) -> int:
64
+ repo = Path(getattr(args, "repo", ".")).expanduser()
65
+ model = getattr(args, "model", None) or EngineConfig.resolve().model
66
+ json_mode = bool(getattr(args, "json", False))
67
+
68
+ skills = resolve_skills(repo, model)
69
+ ordered = [skills[name] for name in sorted(skills)]
70
+
71
+ if json_mode:
72
+ items = [{"name": s.name, "scope": s.scope} for s in ordered]
73
+ emit_result({"model": model, "skills": items}, json_mode=True)
74
+ elif not ordered:
75
+ emit_result("(no skills found)", json_mode=False)
76
+ else:
77
+ lines = [f"{s.scope}\t{s.name}" for s in ordered]
78
+ emit_result("\n".join(lines), json_mode=False)
79
+ return 0
80
+
81
+
82
+ def _no_verb(args: argparse.Namespace) -> int:
83
+ return cmd_skills_overview(args)
84
+
85
+
86
+ def register(sub: argparse._SubParsersAction) -> None:
87
+ p = sub.add_parser(
88
+ "skills",
89
+ help="Inspect layered skill docs (see 'convertible skills overview').",
90
+ )
91
+ p.add_argument("--json", action="store_true", help="Emit structured JSON.")
92
+ p.set_defaults(func=_no_verb, json=False)
93
+ noun_sub = p.add_subparsers(dest="skills_command", parser_class=type(p))
94
+
95
+ lst = noun_sub.add_parser("list", help="List resolved skill docs.")
96
+ lst.add_argument("--repo", default=".", help="Path to the target repository (default: cwd).")
97
+ lst.add_argument(
98
+ "--model",
99
+ default=None,
100
+ help="Model to resolve skills for (default: the resolved engine model).",
101
+ )
102
+ lst.add_argument("--json", action="store_true", help="Emit structured JSON.")
103
+ lst.set_defaults(func=cmd_skills_list)
104
+
105
+ ov = noun_sub.add_parser("overview", help="Describe the skills surface.")
106
+ ov.add_argument("--json", action="store_true", help="Emit structured JSON.")
107
+ ov.set_defaults(func=cmd_skills_overview)
@@ -36,3 +36,21 @@ class Engine(abc.ABC):
36
36
  same result shape regardless of the model underneath.
37
37
  """
38
38
  raise NotImplementedError
39
+
40
+ def system_prompt(self, task: Task, config: EngineConfig) -> str | None:
41
+ """Compose the model-specific system prompt (AGENTS + skills layers).
42
+
43
+ Resolved here on the base class — not in each ``drive`` — so *every*
44
+ engine wheel inherits the layered instruction injection for free (the
45
+ all-engines rule), mirroring how hooks are inherited via the loop.
46
+ Subclasses pass the return value as ``system_prompt=`` to
47
+ :func:`convertible.loop.run`. Returns ``None`` when no AGENTS/skills
48
+ layers exist for ``config.model``, so the loop falls back to its own
49
+ default and behavior is byte-identical to a layer-free run.
50
+ """
51
+ # Imported lazily to keep this module's import surface minimal and avoid
52
+ # pulling the whole loop in at engine import time.
53
+ from convertible.layers import system_prompt_for
54
+ from convertible.loop import _DEFAULT_SYSTEM
55
+
56
+ return system_prompt_for(task.repo_path, config.model, base=_DEFAULT_SYSTEM)
@@ -51,4 +51,9 @@ class MockEngine(Engine):
51
51
  name = "mock"
52
52
 
53
53
  def drive(self, task: Task, config: EngineConfig) -> TaskResult:
54
- return run(_script(task), task, max_steps=config.max_steps)
54
+ return run(
55
+ _script(task),
56
+ task,
57
+ max_steps=config.max_steps,
58
+ system_prompt=self.system_prompt(task, config),
59
+ )
@@ -94,4 +94,9 @@ class VllmOpenAIEngine(Engine):
94
94
  return complete
95
95
 
96
96
  def drive(self, task: Task, config: EngineConfig) -> TaskResult:
97
- return run(self._make_complete(config), task, max_steps=config.max_steps)
97
+ return run(
98
+ self._make_complete(config),
99
+ task,
100
+ max_steps=config.max_steps,
101
+ system_prompt=self.system_prompt(task, config),
102
+ )