feat(devin): expose handoff backend in MCP server enum (#3)
The Devin MCP server's backend enum only listed mock/claude/codex/copilot, excluding the handoff backend that was merged to the engine in #125. This made the subscription-friendly, no-API-key path unavailable to Devin users while Claude Code had a dedicated /skillopt-sleep-handoff command for it. Add "handoff" to the backend enum in _TOOL_SCHEMA so sleep_run accepts backend: "handoff". The engine already handles the prompt/answer file loop (exit code 3 + .skillopt-sleep-handoff/); the MCP server needs no special handling — it passes through the engine output showing pending prompts. Update the README, rules snippet, and test_backends_in_enum accordingly.
This commit is contained in:
@@ -62,9 +62,12 @@ Requires Python ≥ 3.10. No third-party packages — the server is pure stdlib.
|
|||||||
| `sleep_unschedule` | remove the nightly cron entry |
|
| `sleep_unschedule` | remove the nightly cron entry |
|
||||||
|
|
||||||
Default backend is `mock` (no API spend); the `claude`, `codex`, and `copilot`
|
Default backend is `mock` (no API spend); the `claude`, `codex`, and `copilot`
|
||||||
backends use the corresponding authenticated CLI and budget. The seven tools
|
backends use the corresponding authenticated CLI and budget. The `handoff`
|
||||||
call the same `python -m skillopt_sleep` actions as the other shared-engine
|
backend runs the cycle with no model subprocess or API key — the engine writes
|
||||||
integrations.
|
pending model calls to `.skillopt-sleep-handoff/PROMPTS.md` + `pending.json`
|
||||||
|
(exit code 3) and resumes after answers are placed in `answers/<id>.md`; re-run
|
||||||
|
`sleep_run` with the same arguments to resume. The seven tools call the same
|
||||||
|
`python -m skillopt_sleep` actions as the other shared-engine integrations.
|
||||||
|
|
||||||
## Data boundary
|
## Data boundary
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ Always pass the absolute Devin workspace as `project`, especially for
|
|||||||
`sleep_adopt`. Default backend is `mock` (no provider calls). The `claude`,
|
`sleep_adopt`. Default backend is `mock` (no provider calls). The `claude`,
|
||||||
`codex`, and `copilot` backend values use the corresponding installed and
|
`codex`, and `copilot` backend values use the corresponding installed and
|
||||||
authenticated CLI; they do not require this plugin to implement a separate
|
authenticated CLI; they do not require this plugin to implement a separate
|
||||||
API-key flow.
|
API-key flow. The `handoff` backend runs the cycle with no model subprocess
|
||||||
|
or API key — the engine writes pending model calls to
|
||||||
|
`.skillopt-sleep-handoff/` and exits; answer each prompt in a fresh context
|
||||||
|
and re-run `sleep_run` to resume (typically 3–6 rounds).
|
||||||
|
|
||||||
The Devin conversion and mock workflow stay local. A real backend sends
|
The Devin conversion and mock workflow stay local. A real backend sends
|
||||||
truncated transcript excerpts and derived tasks to the selected provider for
|
truncated transcript excerpts and derived tasks to the selected provider for
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ _TOOL_SCHEMA = {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"project": {"type": "string",
|
"project": {"type": "string",
|
||||||
"description": "Project dir to evolve (default: cwd)."},
|
"description": "Project dir to evolve (default: cwd)."},
|
||||||
"backend": {"type": "string", "enum": ["mock", "claude", "codex", "copilot"],
|
"backend": {"type": "string", "enum": ["mock", "claude", "codex", "copilot", "handoff"],
|
||||||
"description": "mock = no API spend (default); claude/codex/copilot = real."},
|
"description": "mock = no API spend (default); claude/codex/copilot = real; handoff = session answers prompts, no API subprocess."},
|
||||||
"scope": {"type": "string", "enum": ["invoked", "all"],
|
"scope": {"type": "string", "enum": ["invoked", "all"],
|
||||||
"description": "Harvest scope (default: invoked project only)."},
|
"description": "Harvest scope (default: invoked project only)."},
|
||||||
"source": {"type": "string", "enum": ["claude", "codex", "auto"],
|
"source": {"type": "string", "enum": ["claude", "codex", "auto"],
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class TestDevinMcpSchema(unittest.TestCase):
|
|||||||
|
|
||||||
def test_backends_in_enum(self):
|
def test_backends_in_enum(self):
|
||||||
backends = mcp_server._TOOL_SCHEMA["properties"]["backend"]["enum"]
|
backends = mcp_server._TOOL_SCHEMA["properties"]["backend"]["enum"]
|
||||||
for b in ["mock", "claude", "codex", "copilot"]:
|
for b in ["mock", "claude", "codex", "copilot", "handoff"]:
|
||||||
self.assertIn(b, backends)
|
self.assertIn(b, backends)
|
||||||
|
|
||||||
def test_schema_has_key_engine_params(self):
|
def test_schema_has_key_engine_params(self):
|
||||||
@@ -64,6 +64,10 @@ class TestClaudeHomeExpansion(unittest.TestCase):
|
|||||||
(the documented mcp-config sets SKILLOPT_DEVIN_CLAUDE_HOME="~/...")."""
|
(the documented mcp-config sets SKILLOPT_DEVIN_CLAUDE_HOME="~/...")."""
|
||||||
|
|
||||||
def test_env_tilde_is_expanded(self):
|
def test_env_tilde_is_expanded(self):
|
||||||
|
# Re-insert the devin plugin path at position 0 so importlib.reload
|
||||||
|
# picks up this module, not plugins/copilot/mcp_server.py when both
|
||||||
|
# test modules are loaded in the same process.
|
||||||
|
sys.path.insert(0, PLUGIN)
|
||||||
os.environ["SKILLOPT_DEVIN_CLAUDE_HOME"] = "~/.skillopt-sleep-devin"
|
os.environ["SKILLOPT_DEVIN_CLAUDE_HOME"] = "~/.skillopt-sleep-devin"
|
||||||
try:
|
try:
|
||||||
importlib.reload(mcp_server)
|
importlib.reload(mcp_server)
|
||||||
|
|||||||
Reference in New Issue
Block a user