Skip to content

feat(skills): automatically discover and refresh project-scoped skills - #42

Merged
enowdev merged 9 commits into
enowdev:mainfrom
elliottophellia:feat/automatic-skill-discovery
Sep 18, 2026
Merged

enowdev merged 9 commits into
enowdev:mainfrom
elliottophellia:feat/automatic-skill-discovery

Conversation

@elliottophellia

Copy link
Copy Markdown
Collaborator

Summary

  • Discover six conventional user directories and six project directories while preserving configured Antares sources and bundled security skills. Automatic sources accept only SKILL.md, follow symlinks with cycle detection, and never create missing roots.
  • Resolve duplicate names by configured sources > selected project > user sources > bundled pack. Scope prompts, skill tools, and /skills to persisted session project bindings; keep the dashboard on the startup catalog.
  • Refresh every five seconds without replacing service pointers. Cache source-neutral parsed content, force a parse every twelve ticks for metadata-preserving edits, and cancel/join the worker during shutdown.
  • Poll the dashboard every five seconds, display imported skills in a read-only viewer with source paths, and preserve open writable drafts.

Permission boundary

Automatic sources cannot be edited, toggled, or deleted through skill management; those writes return HTTP 403 without changing source bytes or creating an override. Explicitly configured sources remain writable. Hub installs and /learn keep their existing Antares-copy destinations.

The toggle restriction remains in this PR because the existing toggle rewrites enabled in the source file. A separate Antares-local enabled/disabled preference for borrowed skills is not implemented here.

Verified checkpoints

  1. edb8258 — discovery, formats, precedence, symlinks, and read-only guards.
  2. 3b41c23 — startup/A/B prompt, tool, and command isolation.
  3. c1d5191 — refresh, reconfiguration, cancellation, and stable consumers.
  4. 3dbcab0 — live dashboard, imported viewer, and HTTP 403 responses.

Verification

Passed:

  • go test ./...
  • go test -race ./internal/skills ./internal/agent ./internal/commands ./internal/server ./cmd/antares
  • make build build-fixture
  • Focused mutation proofs for filtering, all three read-only guards, project selection, tick refresh, forced parsing, canceled-scan publication, and HTTP 403 mapping. Mutations produced expected behavioral failures, followed by passing restored runs.
  • Actual-binary smoke with scrubbed temporary HOME/ANTARES_HOME and a loopback OpenAI-compatible SSE provider: twelve roots, external additions/edits/removals, fallback precedence, reconfiguration, initial/resumed project A/B sessions, real skill tool results, and /skills isolation.
  • Browser checks at 1440×900 and 390×844: cards refreshed in 5–7 seconds; imported viewer controls correct; close/reopen read refreshed content; writable drafts survived polling and saved; library polling stopped when unmounted; GET failures appeared in the editor. Clean interaction probe recorded no console/page errors.
  • Both smoke servers exited with code 0 after SIGTERM.

Unmodified make smoke was not used as feature proof: its fixture disables skills and preserves the developer HOME.

@elliottophellia

Copy link
Copy Markdown
Collaborator Author

don't merge it yet

@enowdev
enowdev merged commit 1d88777 into enowdev:main Sep 18, 2026
3 checks passed
@enowdev

enowdev commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Merged into main. This also delivers #44 in full, which was closed as superseded — all 22 of its files are here, with internal/config/skills.go and internal/tools/skill.go byte-identical.

The architecture holds up well under review: layered source snapshots with precedence resolved under lock, symlink cycle detection via canonical-path ancestors, a cache keyed on parser output only, cancel/join on shutdown (idempotent stopSkillRefresh, covered by tests), and read-only guards returning 403 that are thoroughly tested. readPersistedConfig deliberately bypassing Load/Reload so env overrides are never written back is a good catch.

One scaling issue worth a follow-up, not a blocker. state.scopes in internal/skills/scope.go is never pruned. I probed it directly: 250 distinct session project_dir values produce 250 permanent scope entries, and scanAndPublishLocked rescans every registered scope on each 5s tick — 6 conventional roots each, so ~1500 stat calls per tick at that size. Every session carrying a project_dir registers one via skillsForSession, so a long-running daemon accumulates them for the process lifetime. Worth an eviction policy, or scoping the refresh to recently-used projects, before anyone runs this with many projects.

Verified on the merged tree: go build ./..., go vet ./..., full go test ./..., go test -race over config/skills/agent/server/commands/tui/tools/cmd, make build, and frontend bun test (86 passed) + typecheck + build.

One note on the verification in the description: a full-suite run surfaced a failure in TestPluginTimeoutHonoursAVerdictPrintedBeforeTheDeadline. It is unrelated to this PR — that test races a shell subprocess against a 500ms deadline and touches no skills code. It passes in isolation, passes on a repeat full run, and the package passes on main under the same parallel load.

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.

2 participants