v0.71.3
Patch Changes
moveon 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.jsonfollows it, and it is re-projected into the editor locations the destination level can host. History does not transfer, and everything else (theagentshub, 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 asdroppedLocationsso you can re-add the ones that level can place withinstall, and the tool's success text names theagents-hub equivalent for an editor that reads the hub there and says so when no destination-level placement exists. That reading ofdroppedLocationsholds 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-scopeaccepts an optionaltoName), 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 amoveStatecode 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 asdestination-retained-blocking, which replays the recordedsourceStatealongside it: onintactthe 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 carriesretentionLedger(unreadable,occupant-unverifiable):unreadablemeans the retained-destination ledger itself could not be read, andoccupant-unverifiablemeans 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 asourceStateother thanintactand compare it against the source before deleting it, including on anothing-writtenfailure that would otherwise read as an ordinary, safely-retryable collision.SKILL_RETENTION_LEDGER_CODES,SkillRetentionLedgerCode, andisSkillRetentionLedgerCodeship 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 returnwarningCodes(skill-name-vendor-word,skill-body-too-long) aligned 1:1 withwarnings, matchingPOST /api/skill/import. The MCPimporttool concatenates acquisition and placement codes in the same order as their display text, sowarningCodes[i]is always the code forwarnings[i]. MCPwriteandeditexpose 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 throughPUT /api/skillno longer returns each warning twice. A non-string warning inside awarningsarray is now rendered as text and keeps its paired code instead of being dropped from the structured list while still showing incontent[0].text, and awarningspayload 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
warningsmember 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 answered500 Failed to delete skill., and retrying it re-ran the same refusal indefinitely.installrefusesmodepassed withoutaddorconvert(`mode` needs `add` or `convert`) instead of silently ignoring it, and reports a missingmodeonconvertbefore contacting the server.