diff --git a/plugins/devin/README.md b/plugins/devin/README.md index 84bb970..b5417b6 100644 --- a/plugins/devin/README.md +++ b/plugins/devin/README.md @@ -16,7 +16,10 @@ source into the Claude Code-compatible JSONL the engine reads. | `harvest_devin.py` | converts Devin ATIF-v1.7 transcripts + agentmemory + `.devin/skills` into JSONL, with `taskKey` + outcome envelopes | | `judge.py` | reference judge for the deferred/judge branch of the validation gate | | `mcp-config.example.json` | drop-in MCP server config | -| `devin-rules.snippet.md` | paste into `.devin/rules/skillopt-sleep.md` | +| `install.sh` | copies hooks + rules into a project's `.devin/` and prints the MCP registration command | +| `devin-rules.snippet.md` | copied to `.devin/rules/skillopt-sleep.md` by `install.sh` | +| `hooks/hooks.v1.json` | SessionEnd hook config — copied to `.devin/hooks.v1.json` by `install.sh` | +| `hooks/on-session-end.sh` | best-effort activity marker script (called by the hook) | ## What it harvests @@ -33,10 +36,20 @@ After `sleep_adopt`, the evolved skill is synced to `.devin/skills/skillopt-slee Requires Python ≥ 3.10. No third-party packages — the server is pure stdlib. -1. **Register the MCP server.** Use `mcp-config.example.json` as a template; set - `args` to the absolute path of this `mcp_server.py`. The engine is found - automatically (this plugin lives inside the SkillOpt repo). Or via the Devin - CLI: +1. **Install hooks + rules into your project.** From the repo root: + + ```bash + bash plugins/devin/install.sh /path/to/your/project + ``` + + This copies the SessionEnd hook and rules snippet into the project's + `.devin/` directory and prints the MCP registration command. The hook is + on by default — it logs a cheap activity marker when each session ends so + the next nightly cycle knows there is fresh data to harvest. It is + non-blocking and spends no API budget. Re-run the script to update. + +2. **Register the MCP server.** Use `mcp-config.example.json` as a template, or + run the command printed by `install.sh`: ```bash devin mcp add skillopt-sleep \ @@ -44,9 +57,6 @@ Requires Python ≥ 3.10. No third-party packages — the server is pure stdlib. -- python3 /abs/path/to/SkillOpt/plugins/devin/mcp_server.py ``` -2. **(Optional)** copy `devin-rules.snippet.md` to `.devin/rules/skillopt-sleep.md` - so Devin proactively offers the tools. - 3. Ask Devin: *"run the sleep cycle"*, *"what did the last sleep propose?"*, *"adopt it"*. ## Tools diff --git a/plugins/devin/hooks/hooks.v1.json b/plugins/devin/hooks/hooks.v1.json new file mode 100644 index 0000000..5bca19f --- /dev/null +++ b/plugins/devin/hooks/hooks.v1.json @@ -0,0 +1,14 @@ +{ + "SessionEnd": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "\"${DEVIN_PROJECT_DIR}/.devin/hooks/on-session-end.sh\"", + "timeout": 5 + } + ] + } + ] +} diff --git a/plugins/devin/hooks/on-session-end.sh b/plugins/devin/hooks/on-session-end.sh new file mode 100755 index 0000000..1f886d9 --- /dev/null +++ b/plugins/devin/hooks/on-session-end.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# SkillOpt-Sleep SessionEnd hook for Devin (best-effort, NON-BLOCKING). +# +# This does NOT run the optimizer. It only appends a tiny marker so the next +# nightly cycle knows there is fresh activity to harvest, and (optionally) +# nudges the user once that a sleep cycle is available. It must never fail the +# session or spend API budget. +# +# Install: copy this file and hooks.v1.json into your project's .devin/hooks/ +# directory. Devin CLI reads .devin/hooks.v1.json automatically. +set -uo pipefail + +STATE_DIR="${HOME}/.skillopt-sleep" +mkdir -p "$STATE_DIR" 2>/dev/null || exit 0 + +# Record that a session just ended (cheap; used for "is there new data?"). +printf '%s\t%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${DEVIN_PROJECT_DIR:-${PWD}}" \ + >> "$STATE_DIR/session-end.log" 2>/dev/null || true + +exit 0 diff --git a/plugins/devin/install.sh b/plugins/devin/install.sh new file mode 100755 index 0000000..79ab0fd --- /dev/null +++ b/plugins/devin/install.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Install the SkillOpt-Sleep Devin integration into a project. +# Copies the SessionEnd hook and rules snippet into .devin/, and prints +# the MCP server registration command. Idempotent. +set -euo pipefail + +PLUGIN_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$PLUGIN_DIR/../.." && pwd)" +PROJECT="${1:-$(pwd)}" + +echo "[install] repo: $REPO_ROOT" +echo "[install] project: $PROJECT" + +DEVIN_DIR="$PROJECT/.devin" +mkdir -p "$DEVIN_DIR/hooks" "$DEVIN_DIR/rules" + +# 1) SessionEnd hook (on by default — provides activity signal for nightly harvest) +# Merge into existing hooks.v1.json instead of overwriting, so we don't +# destroy other project hooks. +HOOK_SCRIPT_SRC="$PLUGIN_DIR/hooks/on-session-end.sh" +HOOK_SCRIPT_DST="$DEVIN_DIR/hooks/on-session-end.sh" +cp "$HOOK_SCRIPT_SRC" "$HOOK_SCRIPT_DST" +chmod +x "$HOOK_SCRIPT_DST" +echo "[install] hook script -> $HOOK_SCRIPT_DST" + +HOOK_CONFIG="$DEVIN_DIR/hooks.v1.json" +if [ -f "$HOOK_CONFIG" ]; then + # Merge our SessionEnd hook into the existing config (jq deep-merge) + if command -v jq >/dev/null 2>&1; then + jq -s '.[0] * .[1]' "$HOOK_CONFIG" "$PLUGIN_DIR/hooks/hooks.v1.json" > "$HOOK_CONFIG.tmp" + mv "$HOOK_CONFIG.tmp" "$HOOK_CONFIG" + echo "[install] session-end hook -> $HOOK_CONFIG (merged)" + else + echo "[install] WARNING: jq not found; cannot merge into existing $HOOK_CONFIG" + echo "[install] Merge this SessionEnd hook manually or install jq:" + echo "[install] cat $PLUGIN_DIR/hooks/hooks.v1.json" + echo "[install] Skipping hook config to avoid overwriting existing hooks." + fi +else + cp "$PLUGIN_DIR/hooks/hooks.v1.json" "$HOOK_CONFIG" + echo "[install] session-end hook -> $HOOK_CONFIG" +fi + +# 2) Rules snippet so Devin proactively offers the tools +cp "$PLUGIN_DIR/devin-rules.snippet.md" "$DEVIN_DIR/rules/skillopt-sleep.md" +echo "[install] rules snippet -> $DEVIN_DIR/rules/skillopt-sleep.md" + +# 3) Print the MCP server registration command +cat <