Skip to content

feat(skills): allow registering local skill directories at runtime - #608

Open
q2000s wants to merge 1 commit into
synthetic-sciences:mainfrom
q2000s:feature/custom-skill-paths
Open

q2000s wants to merge 1 commit into
synthetic-sciences:mainfrom
q2000s:feature/custom-skill-paths

Conversation

@q2000s

@q2000s q2000s commented Sep 12, 2026

Copy link
Copy Markdown

Feature proposal: plug arbitrary local skill directories into the skill library

Status: proposal + reference tooling · Target: synthetic-sciences/openscience

Problem

skills.paths already exists in openscience.json, so skills can be loaded from
extra directories — but only statically, at boot. There is no way for a user
or a tool to

  • ask the running server which skill roots are active and where each skill
    came from,
  • register or remove a local skill directory without editing a config file
    and restarting,
  • sync / diff / vendor a local skill collection against the server.

As a result anyone who keeps their own skill library (a private team pack, a
vendor pack, an air-gapped mirror) has to fork the project and patch the source,
which is exactly the situation this proposal removes.

Current API surface (v2.0.93, measured)

endpoint state
GET /skill lists name, description, location, origin, permission_action, recommended, enabledno content
PUT /skill/{name} write a skill (body: content)
DELETE /skill/{name} remove a skill
POST /settings/skills/install install from a url
GET/POST/DELETE /skill/paths absent (404)
POST /skill/reload absent (404)
GET /skill?withContent=1 parameter ignored

Proposal

1. Make skill roots first-class and introspectable

GET /skill/paths
-> { "paths": [
       { "path": "…/backend/cli/skills",        "kind": "builtin", "skills": 312 },
       { "path": "…/user-skills",               "kind": "user",    "skills": 295 },
       { "path": "D:/my-team-skills",           "kind": "custom",  "skills": 49 }
     ], "revision": 17 }

kind is builtin | user | custom, derived from how the root was registered.
revision increments on any change so clients can cache.

2. Register / unregister roots at runtime (no restart)

POST   /skill/paths   { "path": "D:/my-team-skills", "persist": true }
                      -> 201 { "revision": 18, "skills": 49 }
DELETE /skill/paths?path=D:/my-team-skills
                      -> 200 { "revision": 19 }
  • the directory is scanned immediately and its skills become usable without
    a restart (hot reload); a POST /skill/reload endpoint is also useful for
    re-scanning after an out-of-band file change;
  • persist: true appends the path to skills.paths in openscience.json, so it
    survives a restart (this is the only behaviour that needs a config write);
  • reject paths that do not exist or are not directories (400), and paths already
    registered (409) rather than silently duplicating skills.

3. Deterministic conflict resolution

Scanning must be recursive — a real skill library nests skills under category
folders (ml-training/unsloth-fine-tuning/SKILL.md). When two roots expose the
same skill name:

priority: custom  >  user  >  builtin

(later-registered custom roots win over earlier ones), and the shadowed entry is
still reported by GET /skill with "shadowed_by": "<path>" so a user can see
why their edit had no effect. This is the single most confusing failure mode
today: a locally edited skill silently loses to a same-named builtin one.

4. Let clients sync without filesystem access

GET /skill?withContent=1     # include content in each entry
GET /skill/{name}/content    # or a dedicated sub-resource

Today the only way to read a skill's text is to follow location on the local
filesystem, which breaks for a remote or containerised server. Either form is
enough to make a dumb client able to vendor a whole library.

Reference tooling (included here)

sync-skills.js — a dependency-free Node CLI that works against an unmodified
server today by reading each entry's location:

node sync-skills.js <url> list     # inventory with categories
node sync-skills.js <url> diff     # server vs local
node sync-skills.js <url> pull     # server -> local
node sync-skills.js <url> push     # local  -> server
node sync-skills.js <url> prune    # drop local skills removed upstream

Run it as .cjs (or outside the repo) when the surrounding package.json sets
"type": "module". Once the endpoints above exist the same CLI can drop its
filesystem dependency and work against a remote server.

Acceptance criteria

  • GET /skill/paths reports every active root with kind and skill count
  • POST /skill/paths makes a new directory's skills usable with no restart
  • DELETE /skill/paths removes only that root's skills
  • duplicate names resolve by the documented priority and are reported as shadowed
  • persist: true survives a restart; the default does not touch the config file
  • an empty / missing directory is a 400, not a silent no-op
  • withContent returns the text so a remote client can vendor the library

Backwards compatibility

skills.paths keeps its current meaning (a boot-time custom root list), so
existing configurations behave exactly as before; GET /skill without
withContent keeps its present shape.

skills.paths` could already load extra directories but only at boot, and there was no
way to see which roots actually contribute skills. This makes skill roots first-class:

- Skill.roots(): every active root with kind (builtin/user/custom) and count
- Skill.addPath(): register a directory; scanned immediately, no restart needed;
  persist=true also writes it to skills.paths
- Skill.removePath(); POST /settings/skills/reload re-scans in place
- GET/POST/DELETE /settings/skills/paths

A missing directory returns 400 instead of silently doing nothing.

Verified locally: tsc --noEmit clean; against a running server a POSTed root appears
in GET /paths with kind=custom, a bad path yields 400, reload and DELETE both work.
@vercel

vercel Bot commented Sep 12, 2026

Copy link
Copy Markdown

q2000s is attempting to deploy a commit to the InkVell Team on Vercel.

A member of the Team first needs to authorize it.

Ishaan Gangwani (ishaan1124) added a commit that referenced this pull request Sep 13, 2026
After the proposal in #608. Skill.roots() reports every directory feeding the
catalog with the skills it won and lost; addRoot/removeRoot register a local
directory per project without a restart (recursive scan, optional persist to
skills.paths, missing/empty rejected, duplicates refused); a winning skill
carries shadows; GET /skill/:name/content serves the text for remote clients.
Ishaan Gangwani (ishaan1124) added a commit that referenced this pull request Sep 13, 2026
…tiers (#609)

Autoresearch, the curated skill core, the / menu and Skills settings in the agent's tiers, parallel-only workers with deeper reasoning, and the skill roots API. Details are the Unreleased section of CHANGELOG.md, reproduced here.

### Added

- Science-benchmark campaigns over the existing headless Research loop: Harbor
  0.22.0 for Terminal-Bench Science, Terminal-Bench 4 science, and BiomniBench-DA
  50; native adapters for BixBench3 and ResearchClawBench. Bundled skills stay
  on unless `--ak skills=none`. See `evals/science-harness`.
- Autoresearch: a pane beside Files, Terminal and Compute with one tab per
  study, tracking metrics from every run. A script imports
  `openscience_track` (or `wandb`, shimmed) and logs numbers; inside a compute
  job the records ride the job log with no network or dependency, and land in a
  per-project SQLite store. A study reads as a score (best value and its move
  from the baseline), the climb across runs, the runs with a multi-run chart
  (shared hover, smoothing, log scale) and per-run configuration, summary and
  curves, then the queue, the lessons and the activity; local GPUs show in
  the bar.
- Studies: an autoresearch loop the agent drives with the `study` and
  `experiments` tools. One metric and direction, a baseline, a queue of ideas
  ranked by expected value, exactly one run per idea through the existing
  compute permissions, verdicts with analysis and lessons, kill criteria in
  plain words ("1 hour OR val_loss plateaus for 500 steps"), budgets by runs,
  hours, spend or target, and Pause, Resume, Halt and Write up beside the
  score. The driver follows each run, ends runs that break the criteria, and
  wakes the session with one "Study update" per batch of news, capped per
  hour; `study.md`, `ideas.md`, `results.tsv` and `lessons.md` are rendered
  into the working folder. An `autoresearch` skill carries the method.

- Core skills: fifteen research procedures authored for the Research agent and
  always on its index: `research-lookup`, `literature-review`, `brainstorming`,
  `hypotheses`, `reproduce`, `autoresearch`, `compute`, `delegation`, `figures`,
  `schematics`, `paper-writing`, `ml-paper-writing`, `citations`, `peer-review`
  and `sources`. Each is under 250 lines with a workflow, its checks and one
  level of references. `schematics` plans, styles from reference figures,
  renders with Nano Banana Pro and checks the image against the plan;
  `figures` ships a matplotlib style module and one reference per figure type;
  `citations` resolves every reference against Crossref, OpenAlex, arXiv or
  PubMed and ships a `.bib` validator. The retired K-Dense versions
  (`scientific-writing`, `citation-management`, `hypothesis-generation`,
  `scientific-schematics`, `venue-templates`, ...) resolve to their replacements.
- Specialists the agent can call: the Task tool's `specialist` takes `ml`,
  `biology`, `physics`, `chemistry` or the read-only `critique` reviewer. A
  specialist worker keeps the Research contract and gains its domain contract,
  the full index of its skill categories and its domain tools.
- Library sync: 46 more K-Dense scientific skills (`paper-lookup`,
  `database-lookup`, `experimental-design`, `statistical-power`, `nextflow`,
  `bulk-rnaseq`, `phylogenetics`, `molecular-dynamics`, `pkpd-modeling`,
  `pdf`, `docx`, `pptx`, `xlsx`, ...), 357 skills in total.
- `generate_image` takes `image_size` (1K, 2K, 4K) and up to 14
  `reference_paths`, and sends Gemini the documented `imageConfig` request.
- Autoresearch steering and loop discipline, after autoresearcherUI: a
  `steer` input on the study adds a standing directive that wakes the agent
  at once and stays in its study reminder until retired; the driver asks for
  more ideas when fewer than three are queued, for a change of kind after
  four runs without progress, and for a step-back review every six runs;
  `study create` requires a budget agreed for this study rather than one
  carried over; `study propose` rejects configurations already tried; and
  after a short run the agent is told to wait for it in the same turn rather
  than end the turn and be woken.

- Skill roots as an API, after the proposal in #608: `GET /settings/skills/paths`
  lists every directory feeding the catalog with the skills it won and lost,
  `POST` registers a local directory without a restart (scanned recursively,
  optionally persisted to `skills.paths` in the global or project config,
  missing or empty directories rejected, duplicates refused), `DELETE` removes
  it, `POST /settings/skills/reload` rescans, and `GET /skill/{name}/content`
  returns a skill's instructions for clients without filesystem access. A
  skill that shadows a same-named one now carries `shadows` with the losing
  paths, so a local edit that had no effect is explained.

### Removed

- Fusion, the delegation strategy that bound one persistent worker to the lead
  with per-turn handoff budgets. Workers are parallel only: a fresh child per
  Task call, on the Worker model from Customize → Models or the lead's model.
  The Workers switch in Tools, the Fusion badge and handoff count on task
  cards, the `delegation_strategy` preference and the binding store are gone;
  a stored `fusion` preference is ignored.

### Changed

- Reasoning runs deeper and shows more. The composer's effort defaults to
  **high** whenever a model offers it (the picker keeps every level), a worker
  running on the lead's model inherits that effort, and direct OpenAI, Azure and
  Codex OAuth requests for the GPT-5/GPT-6/o3/o4/codex families ask for
  `detailed` reasoning summaries instead of `auto`. A phase the provider kept
  private shows as a "Thought" row with its duration and nothing to open.
- The `/` menu is one list in the agent's own tiers. It opens on Core: `/plan`,
  `/goal`, the fifteen core skills in workflow order and `/compact`; pinned
  skills and the Session actions (`/stop` while a turn runs, `/init`,
  `/handoff`, `/checkpoint`, `/resume`) follow, then the whole library by
  subject. Typing filters everything at once, prefix matches first and core
  ahead on ties, with a library skill's subject on the right. Rows are one
  line: icon, name, purpose. The separate "Browse all skills" dialog is gone;
  the menu and Customize → Skills cover it.
- `/status`, `/context` and `/undo` are removed from the menu and the command
  catalog. The session header shows progress and context usage, and **Undo
  from here** on a finished response reverts a turn.
- Customize → Skills is organised the way the agent uses skills: Core first in
  workflow order, then the skills you wrote, installed or keep in the project
  (personal skills can be edited and deleted in place), then the library as
  folded shelves by subject with a per-shelf Activate all / Turn off all, and
  a Sources section listing every directory that feeds the catalog with the
  names that lost a collision. Views are All, Core, Library, Personal and
  Off; search is one flat list. Add skill gains "Add a local folder", which
  registers a directory of skills without a restart and can persist it to the
  global or project config. Badges, tags and the density toggle are gone; a
  prevailing ask-first permission reads once in the summary.
- A new session opens on the composer alone; the "What would you like to work
  on?" heading and starter buttons are gone.
- Delegation is scoped: a worker needs a clean boundary, a self-contained
  brief with a definition of done, and one worker per independent branch.
  Checking the lead's own output (compiling, reading the rendered pages,
  confirming a number or a reference) is never delegated, and a report on the
  session's own work is built from its evidence rather than a literature
  review. The header, the delegation reminder, the Task tool and the
  paper-writing skill all say so; built-in command descriptions are sentence
  case.
- A delegated worker is a closed line while it runs (title, agent, state,
  elapsed) and streams nothing; its handoff, outputs and **Open agent** appear
  when it finishes. The live operation list, activity groups, operation count
  and model provenance are gone from the card.
- Skills that declare `allowed-tools` unlock those tools for whichever agent
  loaded them; the biology database tools are no longer reserved for the
  biology agent.
- The composer no longer shows a separate Independence chip; Independence
  stays in Tools next to Delegation, where it was already set.

### Fixed

- Autoresearch, from the pre-release audit: `study start` refuses a run once
  the study's run budget is spent (live runs count, so parallel starts cannot
  overshoot it) and refuses to share a GPU when every local GPU already has a
  live run; a run whose compute job record disappears is marked failed after
  two minutes instead of holding its slot forever; a study wake that fails to
  reach the session keeps its news and spends neither the hourly cap nor the
  turn tally; dispatch failures no longer count against the run budget; the
  pane reads a study's complete run list from its overview rather than the
  project-wide cap, and its charts release their resize observers.
- A PDF opened from Results filled a fixed 560px box inside a scrolling pane,
  so a page showed clipped with blank space below it. The viewer now fills the
  pane and scrolls its pages itself, as in the Files tab.

---
Verification: `bun run check` green except the pre-existing `desktop-updater` environment flake; Playwright slash/skills/file-viewer specs; a Bugbot pass on the branch with its four findings fixed and covered by tests; the landing replica rebuilt on the current theme.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant