- #954: strip non-spec source/attribution keys from all 39 plugin.json manifests so Claude Code's validator accepts them; metadata preserved in new .claude-plugin/authoring-notes.json sidecars; check_plugin_json.py now hard-fails manifests carrying those keys and sanity-checks the sidecar; CLAUDE.md ClawHub schema section updated to the new rule. - #949: move the c-level-agents plugin out of c-level-advisor/ to a top-level directory so the two marketplace sources no longer overlap; updated marketplace.json source, homepage, descriptions, all cross-references, docs, harness manifest, mirror-tree symlinks/indexes, and rebased the moved files' relative links; domain counters trued up (18 -> 19 domains). - #933: replace dead links to the gitignored maintainer-local megaprompts/ tree with annotated plain-text references (44 files: SKILL.md, READMEs, agents, commands). - #931: DynamoDB on-demand pricing updated to post-Nov-2024 rates ($0.625/M writes, $0.125/M strongly consistent reads). - #969: skill_security_auditor.py and the three dossier scripts reconfigure stdout/stderr to UTF-8 (errors=replace) so legacy Windows codepages no longer crash at print time; PYTHONUTF8=1 documented. - #968: Windows Notes section in INSTALLATION.md + README pointer for the core.symlinks mirror-tree checkout caveat. - #924/#885 residuals: hook commands quote "${CLAUDE_PLUGIN_ROOT}" paths in all plugin hooks.json/settings.json (space-safe roots); removed the stale pre-rename status/review mirror symlinks and index entries left over from the memory-status/memory-review rename. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qgc6RYXWJPr5oW9DHU7zR4 |
||
|---|---|---|
| .. | ||
| .claude-plugin | ||
| commands | ||
| hooks | ||
| scripts | ||
| skillopt_sleep | ||
| skills/skillopt-sleep | ||
| LICENSE | ||
| README.md | ||
SkillOpt-Sleep (vendored plugin)
This folder started as a verbatim copy of the skillopt_sleep engine and
the Claude Code plugin surface from
microsoft/SkillOpt
(skillopt_sleep/, plugins/claude-code/, plugins/run-sleep.sh), only
relocated so path resolution (CLAUDE_PLUGIN_ROOT-relative lookups in
scripts/sleep.sh / scripts/run-sleep.sh) resolves correctly at this
folder's location. Licensed under the MIT License © Microsoft
Corporation. A small number of targeted patches were made afterward to close
gaps between this plugin's own safety claims and what the code actually did
— see Deviations from upstream below. Re-apply all of these on re-vendor;
they are not upstream yet.
Deviations from upstream
- Cosmetic —
check_paths.pywording.skills/skillopt-sleep/SKILL.md's frontmatterdescriptionsaid "...consolidate validated CLAUDE.md/SKILL.md behind a held-out gate" — theCLAUDE.md/SKILL.mdsubstring reads as a broken relative path to this repo's[A-Za-z0-9_\-./]+/SKILL\.mdlinter regex. Reworded to "CLAUDE.md and SKILL.md"; no behavior or meaning changed. - Safety — secrets weren't redacted in the files that actually go live.
staging.py'sredact_secrets()was applied todiagnostics.jsonand CLI error logging, but not toproposed_SKILL.md/proposed_CLAUDE.md— the exact filesadopt()copies over your liveCLAUDE.md/ managedSKILL.md(with--auto-adopt, with no human in the loop). Sincereflect()'s prompt is built from real harvested session text, a secret pasted into a real debugging session could have landed in your live memory file unredacted, despite the "secrets are redacted from prompts" claim below. Fixed:write_staging()now runs both throughredact_secrets()before writing. - Safety — the crontab line was built via unescaped f-string
interpolation.
scheduler.py's_runner_cmd()wrappedproject(an arbitrary filesystem path) in manual"..."quoting, then wrote the result straight into your real crontab — which cron runs throughsh -con every fire. A path containing",`,$( ), or;could break out of the quoting and inject an arbitrary command into your crontab. Fixed:project,logdir,log, and the repo root are nowshlex.quote()-d before interpolation. - Safety —
max_tokens_per_nightwas a dead config key.config.pydeclared it inDEFAULTS, andbudget.pyalready had aBudget/plan_depthheuristic built for exactly this purpose, but nothing in the productionrun_sleep_cycle()path ever read it — a--backend claude/--backend codexnight had no real ceiling on API spend. Fixed:cycle.pynow starts aBudgetright after backend construction (so harvest/mine spend counts too), sizesdream_rolloutsdown viaplan_depth()when the remaining budget is tight, and appends areportnote whenever it caps rollouts or the budget is exhausted at night's end — no silent truncation. This caps rollout depth per task, not a hard mid-call abort inside a singledream_consolidate()call; a night can still overshoot the cap somewhat if an individual rollout is unusually token-heavy. That residual gap is real and not yet closed. - Cosmetic — dead hardcoded path.
backend.py'sresolve_codex_path()listed~/.nvm/versions/node/v22.22.3/bin/codexas a candidate ahead of the generic "any nvm node version" scan a few lines later, which already covers it. Removed; no behavior change for anyone not on that exact nvm version, one less leftover-looking line for everyone else. - Safety —
redact_secretswas a dead config key.config.pydeclared"redact_secrets": TrueinDEFAULTS, butwrite_staging()calledredact_secrets()unconditionally — the safe direction, but the knob had no effect either way. Fixed:cycle.pynow readscfg.get("redact_secrets", True)and threads it throughwrite_staging()and thediagnostics.jsonfields; disabling it is honored (it's the user's config) but never silently — areportnote fires whenever it's off. - Hardening —
adopt()now re-redacts as defense-in-depth. Previouslyadopt()copied the stagedproposed_SKILL.md/proposed_CLAUDE.mdstraight to the live path withshutil.copy2. Sincewrite_staging()already redacts, this was redundant for the common case, but didn't cover a human hand-editing the staged proposal betweenstageandadopt(the exact workflow staging exists to allow).adopt()now reads, re-runsredact_secrets(), and writes each file rather than a raw byte copy;_backup()of the prior live file is unaffected (that's a backup of what already existed, not the incoming content). - Safety —
scheduler.py'sextraparam wasn't shell-quoted.project/logdir/log/repo-root wereshlex.quote()-d in fix #3 above, butextra(today only ever""or the literal"--auto-adopt"from__main__.py) was appended raw. Not exploitable today since it's a hardcoded flag literal, but reopens the same class of bug if a future change letsextracarry anything else. Fixed:extrais nowshlex.split()into tokens and each tokenshlex.quote()-d before joining, so a multi-token or attacker-influencedextracan't break out of the command the wayprojectused to. - Safety — the cross-night task archive (
state.json) was never redacted.state.py'sadd_to_archive()persists rawTaskRecordcontent (intent/context_excerpt/attempted_solution— real harvested prompt/response text) to~/.skillopt-sleep/state.jsonindefinitely, forrecall_kassociative recall across nights. Unlikeproposed_SKILL.md/proposed_CLAUDE.md/diagnostics.json(fixes #2, #6), this file lives entirely outside the staging dir a user is ever told to review — a secret pasted into a real debugging session would have landed there and stayed. Fixed:cycle.pynow redacts each task dict before archiving, using the sameredact_enabledflag (and loud report-note-on-disable) as everything else. - Safety —
report.md/report.jsonwere never redacted. These are the two artifacts a human is told to read first (the SKILL.md's own workflow says "Read the generatedreport.md... show the user the exact proposed edits"), yetEditRecord.content/.rationale— sourced from the optimizer'sreflect()output over real failing task responses — were written unredacted, while the siblingproposed_SKILL.md/proposed_CLAUDE.md/diagnostics.jsongot fixed in earlier rounds. Fixed:write_staging()now redacts the renderedreport_mdstring andreport.to_dict()(viaredact_secrets's existing recursive dict/list/ str handling) before writingreport.md/report.json. - Safety/cosmetic — dead config knob:
replay_mode.config.pydeclared"replay_mode": "mock" | "fresh" (worktree), but the only production code path that reads it prints a cosmetic label in the report — there is no worktree-replay implementation anywhere in this engine. Implementing real worktree isolation was judged too invasive for a vendored copy (a substantial new feature in code this repo doesn't otherwise maintain); instead,cycle.pynow appends a loud report note wheneverreplay_modeis set to anything but"mock", so the report never implies real worktree isolation that isn't happening. - Sensitive — hardcoded internal Azure OpenAI infrastructure removed.
backend.pyshipped anAzureOpenAIBackend/AzureResponsesBackendpair with 5 internal-looking Azure endpoint hostnames and a hardcoded Managed Identity client ID, explicitly commented as sourced from "the intern's avail_api.md" — reads like leaked internal Microsoft dev-infra topology. This backend was already unreachable from this plugin's documentedmock/claude/codex/copilot--backendchoices (only settable by hand-editing~/.skillopt-sleep/config.jsondirectly) and requiresazure-identity/openaithird-party packages this repo never claims to depend on. Removed entirely (classes, constants,get_backend()/build_backend()dispatch branches and the now-unusedazure_endpointparameter);get_backend("azure")now safely falls back toMockBackendinstead of raising or wiring internal infra. Same class of "leftover internal-looking detail" as fix #5's deadnvmpath, but materially more sensitive, so it was removed rather than just flagged. - Safety — unvalidated tool names reachable via
--tasks-file.attempt_with_tools()(all three CLI backends) used a task's tool name both as a shim filename (os.path.join(work, tname)) and interpolated unescaped into the shim's generated shell body (f'echo "{tname}" >> ...'). Tool names originate from a hand-authored--tasks-file'sjudge.checks[].arg(op=="tool_called"), never validated as a safe identifier — a crafted name containing../,", backticks, or$( )could traverse out of the working dir or inject a command into the generated shim. Not reachable via the harvest/mine path today (the LLM miner excludes this check type), but--tasks-fileis a documented, user-facing input. Fixed: a shared_sanitize_tool_names()helper filters to a safe-identifier allowlist (^[A-Za-z0-9_-]{1,64}$) before any name is used as a filename or shell text, in all three backends. - Cosmetic — dead cross-reference in
SKILL.md.skills/skillopt-sleep/SKILL.mdpointed todocs/superpowers/specs/2026-06-07-skillopt-sleep-claude-code-plugin-design.md"for the full design" — that path is not in this repo (see "What was and wasn't vendored" below;docs/was deliberately excluded). An agent following the pointer hits a missing file. Fixed: points to the real upstream guide URL (microsoft.github.io/SkillOpt/docs/guideline.html#sleep) instead, with a note on why the local path is absent. - Safety —
commands/skillopt-sleep.mddidn't signal thatscheduleinstalls immediately. The command doc's action table listedscheduleas an ordinary action alongsidestatus/dry-run/run(all of which are safe previews or explicitly staged), while its own "Safety reminders" section separately said to point users at the print-onlyinstall-cron.shinstead — two different, uncoordinated stories about the same action.scheduler.schedule()writes directly to the user's real crontab the moment it runs, with no confirmation step in between. Fixed: "Steps to follow" now has an explicit step 1 telling the agent to confirm with the user before runningschedule(what project/hour/ minute/backend will be scheduled), withinstall-cron.shoffered as the print-only alternative; "Safety reminders" now says the same thing instead of contradicting the action table. - Hardening — state/staging directories and files had no restrictive
permissions.
state.json(the cross-night task archive) and.skillopt-sleep/staging/<ts>/'s proposal/report/diagnostics files contain real harvested session content in plaintext, created via plainos.makedirs/open(..., "w")— world-readable-by-default on a typical multi-user Linux box (subject to the process umask).redact_secrets()scrubs known secret patterns, but the files still carry real task intents, code excerpts, and project context otherwise. Fixed: bothstate.pyandstaging.pynowos.chmod()every directory they create to0o700and every file they write to0o600(best-effort, silently skipped on platforms without POSIX permission bits). LiveCLAUDE.md/SKILL.mdfiles themselves are intentionally left at their existing permissions — those are the user's own, often-committed files, not new output this plugin introduces. - Cosmetic — misleading redaction placeholder label.
staging.py'ssk-[A-Za-z0-9_-]{10,}pattern matched anysk--prefixed key (OpenAI, Anthropic'ssk-ant-..., and other vendors sharing the convention) but labeled every match[REDACTED_OPENAI_KEY]regardless of which vendor's key shape it actually was. Redaction itself was unaffected — the text was scrubbed either way — but the placeholder implied a narrower match than the pattern actually has. Fixed: relabeled to[REDACTED_API_KEY]. - Cosmetic/hardening — stale fallback paths in the shell launchers.
scripts/sleep.sh's header comment and itsSKILLOPT_SLEEP_REPOfallback branch referenced upstream's<repo>/plugins/run-sleep.shlayout;scripts/run-sleep.sh's header comment and itsCLAUDE_PLUGIN_ROOTbranch likewise assumed the upstream three-level<repo>/plugins/claude-code/structure (CLAUDE_PLUGIN_ROOT/../../ skillopt_sleep). In this vendored copyscripts/andskillopt_sleep/are siblings directly under the plugin root (engineering/skillopt-sleep/), soCLAUDE_PLUGIN_ROOT(when Claude Code sets it) points straight at a dir containingskillopt_sleep/, not two levels above one. Both scripts' primary co-located/repo-relative resolution branches happen to still succeed for this layout regardless (so this was unreachable in normal operation), but the documentedSKILLOPT_SLEEP_REPOandCLAUDE_PLUGIN_ROOTescape hatches would have silently failed to resolve for anyone actually relying on them — e.g. after a future re-vendor that missed copyingrun-sleep.shintoscripts/. Fixed:sleep.sh's explicit-env branch now checks$SKILLOPT_SLEEP_REPO/scripts/run-sleep.sh;run-sleep.shnow checks$CLAUDE_PLUGIN_ROOT/skillopt_sleep(this repo's actual layout) ahead of the upstream two-levels-up check (kept for portability if this script is ever reused in that shape again). Verified both fixed branches resolve correctly in isolation from the co-located fallback. - Safety — the CLI's own stdout/
--json/--outputbypassedredact_secrets()entirely. Rounds 1-3 covered every filewrite_staging()/state.pywrite, but__main__.py'scmd_run()reads the same in-memoryReportobject and printsEditRecord.content(raw, fromreflect()'s output over real task responses) directly to the console, and_report_payload()serializes it unredacted for--json—write_staging()'s redaction runs on a copy (redact_secrets(report.to_dict())) used only for the on-disk JSON, it never touchesreport.editsitself. Concretely:scheduler.py's cron entry redirectsrun's stdout/stderr straight into<project>/.skillopt-sleep/cron.log— a secret that leaked into a proposed edit's content would land there in plaintext on every scheduled night, in a file that (unlikestate.json/staged files) also had nochmodprotection (see the second fix below).cmd_harvest()'s debug output (--json,--output <file>, and the plain-text loop) has the same shape: it prints raw minedTaskRecord.intenttext so a human can review it before setting"reviewed": trueon a--tasks-file, which means redacting it doesn't reduce what's reviewable (only secret-shaped substrings are stripped) while closing the same leak path. Fixed:_report_payload()andcmd_run()'s plain-text edit printing, andcmd_harvest()'s payload (covering its--outputfile,--jsonstdout, and plain-text loop uniformly), all now run throughredact_secrets(), gated on the sameredact_secretsconfig flag as everywhere else. Also hardenedscheduler.py's generated cron line tochmod 700the.skillopt-sleeplog dir andchmod 600cron.logitself (best-effort,2>/dev/null) before each run appends to it — that file was never covered by the state/staging chmod pass in fix #16. Verified: a synthetic secret seeded into a task's intent no longer appears incmd_run's--jsonpayload or plain-text edit output, or incmd_harvest's redacted payload; executing the actual generated cron line end-to-end produces a0700log dir and0600log file on disk. - Bug —
scheduler.py's per-project marker match used unanchored substring comparison.schedule()/unschedule()both located "this project's" managed cron line viamarker not in ln, a bare substring test, not an exact-match or delimiter-anchored check. Failure scenario: two projects scheduled where one path is a literal prefix of the other (e.g./home/user/appand/home/user/app-v2) —_project_markerproduces# project=/home/user/app, which is itself a substring of# project=/home/user/app-v2's line. Runningschedule()orunschedule()for/home/user/appwould silently drop/home/user/app-v2's cron entry too, with no error or warning — the user's other project's nightly job just disappears.harvest.py's_project_matches()(added in this same PR) already gets this right a few hundred lines away (a == b or a.startswith(b + os.sep) or b.startswith(a + os.sep));scheduler.py's marker matching didn't follow the same discipline. Fixed: added_line_matches_project(), which anchors onln.rstrip().endswith(marker)since the marker is always the last token of a generated line (seeschedule()'scron_lineconstruction) — used at both call sites. Verified two ways: a standalone reproduction confirmed the bug before the fix and its absence after, and a fullschedule()/unschedule()round-trip through the actual public API (withcrontab -l/crontab -swapped for an in-memory fake) confirmed scheduling both/home/user/appand/home/user/app-v2, then unscheduling onlyapp, correctly leavesapp-v2's line intact. - Cosmetic —
install-cron.sh's printed--backendvalue was unquoted.scheduler.py::_runner_cmdquotes every interpolated value withshlex.quote(), but the standaloneinstall-cron.shscript (which only prints a crontab line for the user to copy intocrontab -e— nothing is executed automatically) interpolated--backend ${BACKEND}unquoted in its heredoc, next to otherwise-quoted${RUNNER}/${PROJECT}. Low risk sinceBACKENDis normally one of a fixed small set of values and the script never executes anything itself, but inconsistent with the quoting discipline applied everywhere else. Fixed: quoted as"${BACKEND}". - Hardening —
schedulehad no confirmation gate at the CLI layer. The deviation #15 "confirm with the user beforeschedule" safeguard lived only incommands/skillopt-sleep.md's agent-facing instructions —cmd_schedule()itself calledscheduler.schedule()directly and installed a real crontab entry immediately. That's fine for the documented Claude Code agent workflow (which confirms in chat first), but anyone invokingpython -m skillopt_sleep scheduledirectly bypassed it entirely, with no gate in the CLI itself. Fixed:schedulenow requires--yes; without it, an interactive terminal gets a[y/N]prompt and a non-interactive one (no TTY) refuses outright with an exit code (2) pointing at--yes.commands/skillopt-sleep.mdupdated so the driving agent passes--yesonce it has confirmed with the user in chat — that chat confirmation is what--yesrecords, not a redundant re-prompt (which would hang forever with no TTY to answer from inside a non-interactive Bash tool call anyway). Verified: non-interactiveschedulewithout--yesrefuses with exit 2; with--yesit proceeds to the samescheduler.schedule()call as before. - Hardening — mkdir-then-chmod wasn't atomic.
write_staging()(staging.py) andSleepState.save()(state.py) calledos.makedirs(path, exist_ok=True)and onlychmod'd afterward, leaving a brief window where a freshly-created sensitive directory sat at the process's default umask — exactly what this plugin's chmod hardening (deviation #16) exists to close. Fixed: theos.makedirs()calls that create the state dir, the per-run staging leaf dir, and the adopt-time backup dir now passmode=0o700directly, closing the window for the common first-creation case. The existing post-creationchmodcalls are kept, not removed —mode=only governs the leaf directorymkdir()itself creates (intermediate parent directories still fall back to the umask default) and is itself still subject to umask, so it narrows the window rather than eliminating every case (e.g. a directory that already existed from before this fix, or an intermediate parent). The equivalent race for individual files (open(path, "w")thenchmodafter) is a smaller, harder-to-close window — closing it fully would mean rewriting every file-write call site to useos.open()with an explicit mode instead of the builtinopen(), which felt like a larger rewrite than this specific, low-severity (requires a local attacker with precise timing) finding warranted; left as a known, narrower residual gap rather than silently claimed as fully closed.
What this plugin is
SkillOpt-Sleep gives a local Claude Code agent a nightly sleep cycle: it
reviews real past sessions in this repo, replays recurring tasks offline on
your own API budget, and consolidates what it learns into this repo's
CLAUDE.md memory and SKILL.md skills — but only through a held-out
validation gate, and only after you explicitly adopt the staged proposal.
It is the deployment-time companion to the (not vendored) skillopt training
package: SkillOpt trains a skill offline against a labeled benchmark;
SkillOpt-Sleep applies the same bounded-edit + held-out-gate discipline to
actual usage of this repo instead, so it needs no benchmark dataset.
harvest ~/.claude transcripts (read-only)
→ mine recurring tasks
→ replay offline
→ consolidate (reflect → bounded edit → GATE)
→ stage proposal (nothing live changes)
→ you review and run "adopt" (backs up first)
Why this is a fit for a skills library with no test harness
This repo's CLAUDE.md intentionally has no build system or
test framework, and skill scripts/ are stdlib-only with no LLM calls, so the
full microsoft/SkillOpt training package (benchmark-driven, requires
labeled train/val/test data per task, needs numpy/openai/azure-*) was
not vendored — there is no natural ground-truth benchmark for something
like finance/dcf-valuation or c-level-advisor/vpe-advisor.
skillopt_sleep, by contrast, has zero third-party dependencies (stdlib
only), its default mock backend spends no API budget, and it mines its
"benchmark" from how the skills in this repo actually get used in real
sessions rather than a pre-labeled dataset. That matches the repo's
deterministic-first, portable-first philosophy far better than the training
package does.
Use in this repo
# from the repo root:
engineering/skillopt-sleep/scripts/sleep.sh status # what's happened (read-only)
engineering/skillopt-sleep/scripts/sleep.sh dry-run --project "$(pwd)" # safe preview, stages nothing
engineering/skillopt-sleep/scripts/sleep.sh run --project "$(pwd)" # full cycle, stages a proposal
engineering/skillopt-sleep/scripts/sleep.sh adopt --project "$(pwd)" # apply staged proposal (backs up first)
Or, once the plugin is installed via Claude Code's plugin marketplace, use
the bundled /skillopt-sleep [run|dry-run|status|adopt|harvest|schedule|unschedule]
slash command (see commands/skillopt-sleep.md and skills/skillopt-sleep/SKILL.md).
Default backend is mock (deterministic, no API spend — safe to try
immediately). Add --backend claude to spend real budget replaying this
repo's own recurring tasks and get genuine lift on CLAUDE.md / a target
SKILL.md.
Safety model
- Harvest is read-only over
~/.claudesession transcripts. - Edits are proposed, gated against a held-out replay slice, and staged
under
.skillopt-sleep/staging/<date>/— nothing live is touched. adoptis explicit and backs up the prior file first (unless you opt into--auto-adopt).max_tasks_per_nightis a hard cap (mining stops there).max_tokens_per_nightsizesdream_rolloutsdown viaplan_depth()and is reported when hit, but is not a hard mid-call abort — see deviation #4 above for the exact scope.- Secrets (API keys, bearer tokens, private-key blocks) are redacted before
anything is written to the staging dir, including
proposed_SKILL.md/proposed_CLAUDE.md(deviation #2 above) — not just diagnostics — and re-redacted again atadopt()time as defense-in-depth against a hand-edited staged proposal (deviation #7). Disabling this viaredact_secrets: falseis honored but never silent — it logs a report note (deviation #6). The same flag now also covers the cross-night task archive (state.json, deviation #9),report.md/report.json(deviation #10) — the two files a human is actually told to read first — and every CLI console/--json/--outputcode path (cmd_run,cmd_harvest), not just what gets written to disk (deviation #19).cron.log(the CLI's redirected stdout/stderr) is nowchmod 600too, matching state/staging (deviation #19). - The generated crontab line, including the
extraflags parameter, is fullyshlex.quote()-d, not just the path arguments (deviations #3, #8, #21).schedule/unschedulelocate a project's own line via an anchored end-of-line match, not a bare substring test, so scheduling/unscheduling one project can't silently drop a sibling project whose path happens to be a prefix of it (deviation #20). replay_mode: "fresh"(worktree replay) is not implemented — every replay runs as"mock"regardless, and the report says so explicitly rather than implying isolation that isn't happening (deviation #11).- Tool names reachable via a hand-authored
--tasks-fileare validated against a safe-identifier allowlist before being used as a shim filename or interpolated into generated shell text (deviation #13). - Only
mock/claude/codex/copilotbackends are supported — a Microsoft-internal Azure OpenAI backend was removed rather than carried forward (deviation #12). scheduleinstalls a real crontab entry immediately — unlike every other action, it is not a preview or a staged proposal. An agent driving/skillopt-sleep schedulemust confirm with the user first (deviation #15);install-cron.shremains available as a print-only alternative.- Every directory
state.py/staging.pycreate ischmod 0700and every file they write ischmod 0600(best-effort), sostate.jsonand staged proposals/reports/diagnostics aren't left at the world-readable process umask default on a shared machine (deviation #16).
What was and wasn't vendored
| Vendored | Not vendored |
|---|---|
skillopt_sleep/ engine (stdlib-only) |
skillopt/ training package (needs numpy/openai/azure-* + labeled benchmarks) |
plugins/claude-code/skills|hooks|commands|scripts/ |
plugins/codex/, plugins/copilot/, plugins/devin/, plugins/openclaw/ (other-agent plugin variants) |
plugins/run-sleep.sh shared launcher |
skillopt_webui/ (optional Gradio dashboard) |
LICENSE |
docs/, ckpt/, data/, index.html (training-package docs/site/checkpoints) |
AzureOpenAIBackend/AzureResponsesBackend from backend.py — removed post-vendor, see deviation #12 (Microsoft-internal endpoints/client ID, unreachable from this plugin's supported --backend choices, needs deps not vendored here) |
Updating
Re-vendor from upstream when the plugin changes:
git clone --depth 1 https://github.com/microsoft/SkillOpt.git /tmp/skillopt-upstream
cp -r /tmp/skillopt-upstream/skillopt_sleep engineering/skillopt-sleep/skillopt_sleep
cp -r /tmp/skillopt-upstream/plugins/claude-code/skills/skillopt-sleep engineering/skillopt-sleep/skills/skillopt-sleep
cp -r /tmp/skillopt-upstream/plugins/claude-code/hooks engineering/skillopt-sleep/hooks
cp -r /tmp/skillopt-upstream/plugins/claude-code/commands engineering/skillopt-sleep/commands
cp /tmp/skillopt-upstream/plugins/claude-code/scripts/sleep.sh /tmp/skillopt-upstream/plugins/claude-code/scripts/install-cron.sh engineering/skillopt-sleep/scripts/
cp /tmp/skillopt-upstream/plugins/run-sleep.sh engineering/skillopt-sleep/scripts/run-sleep.sh
cp /tmp/skillopt-upstream/LICENSE engineering/skillopt-sleep/LICENSE