e0d80fd3aec3eb65e37de44b53d61a46b9376637
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8770f98dcd |
capabilities: read a plugin's where they live, stop copying them in
Four publishers copied a plugin's affordances, notifications and skills into capabilities/ at install. That directory is meant to be forked — core's work plus the owner's — so a fork accumulated files it did not author with nothing on disk saying which, and there was no good answer: commit them and carry another plugin's work, leave them and git status stays dirty. Copying was also wrong in two ways unrelated to git. A copy goes stale, because plugins:update pulls the plugin's directory and the copy keeps describing the old version. And a copy survived plugins:disable, so a disabled plugin's skill stayed in the agent's index — off, and still being announced. Now the loaders take a list of roots: the owner's store, plus each installed AND enabled plugin's own directory. capability-sources.ts resolves that list. No namespace is needed to keep them apart, because separate directories do it structurally rather than by convention. The owner's own win a skill name collision — their root is first and the merge keeps the first of each id — so a plugin cannot take over a name already in use. Affordances are deliberately NOT de-duplicated: several can be true at once. Notifications needed more than a root list, and it would have failed silently. Their codes were derived from the COPY'S PATH, <publisher>/<appName>/file.yaml. Read in place the path is just notifications/catalogue.yaml and carries no identity, so every plugin code would have vanished with nothing raised. loadPluginEntries takes the identity from the manifest instead, which is better provenance anyway — publisher and appName are validated at discovery, where a path was only ever a directory somebody chose. A yaml key still cannot claim another namespace, and that is pinned by test. createSession is async now, only because resolving enabled plugins reads the database. Once per session; its single caller already awaited. Deletes all four publishers including api/tasks/publish.ts from earlier today, superseded before it shipped. app-store/assets stays — images served over HTTP, where the copy is what makes them reachable. NOT done: tasks. That store is read-write, so reads must span sources while writes stay in the owner's directory, and a plugin's task has to be read-only in the UI. Next. Two things the design predicted as orphans turned out not to be, and both were checked before deleting rather than after. capabilities/extensions/tool-loader is written by the platform's own bootstrap every boot, not published by a plugin. And skills/email-db has no source to fall back on — the email plugin ships no skills directory, so that file exists in exactly one place and was hand-written. Neither was touched. 1422 pass, tsgo clean, officer restarts and serves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 |
||
|
|
49f7761f53 |
skills: publish the index, leave the body on disk
the inverse of an affordance, and deliberately so: affordance body always compiled in, description never sent skill description always compiled in, body read on demand an affordance is a fact about the surface — short, and every conversation needs it. a skill is reference material that is dead weight in a conversation about something else. so what reaches the prompt is the index: name, when to reach for it, and the path. measured on the first one: 608 characters in the prompt against 6,070 bytes left on disk. no protocol. progressive disclosure is normally a harness feature — claude code discovers .claude/skills, opencode has its own conventions, the next one will have a third — and pointing at a file needs none of that, because every harness worth running can already read a file. the cost is that the agent has to CHOOSE to open it, which is what the description is for and why a vague one makes a skill useless rather than merely verbose. the first skill is the email database, moved out of tools/email-db/TOOL.md where NOTHING has been reading it: the loader parses frontmatter and discards the body, so that schema and its eight queries have never reached any claude code session. written for pi, inert since pi left. it carries two things the schema alone gets wrong, both measured against the real 19,097-message database rather than copied from the old doc: `read` is populated on 93 rows, so the documented unread query returns essentially the whole mailbox; and `labels` is a comma-separated string with real values like `updates,inbox`, so `labels = 'inbox'` misses most of the inbox. plugins can ship skills, published and unpublished on install like affordances and notification catalogues. flat namespace unlike those two — a skill's directory name is the path an agent reads, and `officerdev/gitea/SKILL.md` in a prompt is noise. a skill has no authority to usurp, so nothing needs protecting. wired to claude code. opencode still gets NOTHING — its runner sends no system prompt at all, and session create takes only a directory. that needs its own channel and a guess would be worse than the gap. |