OpenKnowledge
Changelog

v0.71.3

Patch Changes

  • move on a skill across the Project and Global levels (skill: { from, to, scope, toScope }) now uses the same scope-move endpoint as the Skills Studio. The whole bundle moves, binary files included, its entry in .ok/skills-lock.json follows it, and it is re-projected into the editor locations the destination level can host. History does not transfer, and everything else (the agents hub, custom roots, and any editor with no skills root at that level) is removed at the source and not re-created at the destination; the response returns them as droppedLocations so you can re-add the ones that level can place with install, and the tool's success text names the agents-hub equivalent for an editor that reads the hub there and says so when no destination-level placement exists. That reading of droppedLocations holds only on a success: on a reported failure the field lists source-side placements the call had already removed, so installing them at the destination would spread a retained copy rather than restore anything. Before, the MCP tool copied text files one at a time, dropped binary files and the lock entry, landed the skill with no locations, and still reported that it had been re-projected. A move that also renames the skill lands directly under the new name (POST /api/skill/move-scope accepts an optional toName), and nothing is written when that name is already taken at the destination. If the destination name patch fails before source removal, cleanup removes only the new copy when possible and reports any cleanup failure; the source stays intact. If source removal fails after deleting some files, the complete destination copy is retained for recovery; unrelated skills with the old name are untouched. Errors identify the partial source and retained destination so callers can inspect both before retrying, and the endpoint stamps a moveState code on the failures it reports (nothing-written, destination-removed, destination-stray, destination-retained, destination-retained-blocking, destination-unreadable, partially-applied) so a caller can tell what is on disk without reading the English. When a retained destination is reported, sourceState (intact, lossy, unknown) says what the source looked like when its removal failed, so a caller can tell a safe-to-remove duplicate from a copy that may hold the only surviving files. A retained destination is marked on disk, so a retry against it is refused as destination-retained-blocking, which replays the recorded sourceState alongside it: on intact the retained copy is a redundant duplicate and removing it is what unblocks the retry, and otherwise the caller reconciles rather than deletes. A failure that could not clear the destination name also carries retentionLedger (unreadable, occupant-unverifiable): unreadable means the retained-destination ledger itself could not be read, and occupant-unverifiable means the directory at the destination could not be read, so the server could not confirm whether it is a copy an earlier failed move retained. Either way, treat that occupant the way you would a sourceState other than intact and compare it against the source before deleting it, including on a nothing-written failure that would otherwise read as an ordinary, safely-retryable collision. SKILL_RETENTION_LEDGER_CODES, SkillRetentionLedgerCode, and isSkillRetentionLedgerCode ship from core beside the existing move-state and source-state guards. Copying, source removal, lock transfer, and editor projection remain separate steps, not an all-or-nothing transaction.

    PUT /api/skill, POST /api/skill/reimport, and the bulk import and update endpoints now return warningCodes (skill-name-vendor-word, skill-body-too-long) aligned 1:1 with warnings, matching POST /api/skill/import. The MCP import tool concatenates acquisition and placement codes in the same order as their display text, so warningCodes[i] is always the code for warnings[i]. MCP write and edit expose these arrays in their structured SKILL.md results and declare them in their output schemas, including empty arrays when there are no warnings. Creating a skill through PUT /api/skill no longer returns each warning twice. A non-string warning inside a warnings array is now rendered as text and keeps its paired code instead of being dropped from the structured list while still showing in content[0].text, and a warnings payload that is not an array at all now yields one explicit "treat this result as unverified" warning instead of an empty, code-paired all-clear.

    The three records under .ok/ that track installs (installed-skills.json, .ok/skills-lock.json, and the placements store) now refuse to rewrite a file they could not read, rather than replacing every entry it held with the one being written. A corrupt or truncated file is reported instead of silently clobbered.

    Because that refusal can land after the change it accompanies has already been made, the operations it can interrupt no longer report a completed change as a failure. Deleting, renaming, or moving a skill across levels whose bookkeeping fails now answers 200 with a warnings member naming what could not be recorded, so a caller can tell "the skill moved and one record is stale" from "the move did not happen". Before, a delete that had already removed the skill answered 500 Failed to delete skill., and retrying it re-ran the same refusal indefinitely.

    install refuses mode passed without add or convert (`mode` needs `add` or `convert`) instead of silently ignoring it, and reports a missing mode on convert before contacting the server.

View v0.71.3 on GitHub