feat(skills): rebuild the skill system for the multi-user model
Nightly Build / build (push) Successful in 8m6s

Per blueprint/skill-project.md: the old single-namespace, hand-maintained
index is gone, replaced by a read-only, two-scope tree whose index is a
runtime function of its content.

- skills/ index generated at runtime (crates/skald-core/src/skills/:
  inventory, install, validate, watch), injected through the new
  <!-- SKILLS_LIST --> placeholder in AGENT.md (agents/common/skills.md);
  meta.json inject_skills flag removed. 11 chat/task agents carry the
  include, the 4 system agents do not.
- Two trees, both read-only in both directions: skills/shared/{id} (the
  group's) and skills/{username}/{id} (one member's own, on the stable
  userid). The root is closed too: UserFs::SkillMounts + RouteError (alias
  probe, plain-denied paths, no home fallback) and a per-user
  .skills-root/{userid} container mount with the two scope mounts nested
  inside, plus the fifth self-heal axis (skills_mounted).
- Agent verbs: skill_register/skill_delete (Config group, global scope
  behind the new skill.manage capability), fetch_repo for public repos,
  list_items(type="skills"); reads are plain read_file on the printed
  path. Seeded @fs_read skills/* allow.
- Freshness: a digest-gated watcher on the two trees emits
  SystemEvent::SkillsChanged, whose subscriber rebuilds the frozen prompt
  prefix via Skald::invalidate_prompt_prefix; in-process writers invalidate
  directly.
- The build ships no skills: the three bundled skills (ics2json,
  mcp-builder, skill-creator) and skills/index.md are removed, skills/ is
  instance data (gitignored, not packaged, no longer pruned by update.sh).
- Docs: skills.md, agents.md, shared-folders.md added; docs/index.md and
  agents/README.md updated.
This commit is contained in:
Daniele
2026-08-08 23:05:35 +01:00
parent 71e1a26b08
commit c27da4e6ab
88 changed files with 4624 additions and 9546 deletions
+2 -3
View File
@@ -17,7 +17,7 @@ pub struct RunContext {
#[serde(default)]
pub allow_fs_writes: Vec<String>,
/// Extra directories/files granted read-only access (beyond the working directory,
/// `docs/`, `skills/`, and everything in `allow_fs_writes`, which is readable too).
/// `docs/`, and everything in `allow_fs_writes`, which is readable too).
#[serde(default)]
pub allow_fs_reads: Vec<String>,
/// Project root (agent path `projects/{owner}/{slug}`) when this is a project
@@ -70,7 +70,7 @@ impl RunContext {
/// True if reading `path` is pre-authorized by this RunContext.
/// Read access is granted (no approval prompt) for: the process working directory
/// itself, its `docs/` and `skills/` subtrees (always-safe baseline), any
/// itself and its `docs/` subtree (always-safe baseline), any
/// `allow_fs_reads` entry, and anything writable (write implies read). All paths
/// are canonicalized first so `..`/symlink escapes cannot widen the grant.
///
@@ -84,7 +84,6 @@ impl RunContext {
let mut roots: Vec<std::path::PathBuf> = vec![
canonicalize_for_policy(".", &wd), // process working directory
canonicalize_for_policy("docs", &wd),
canonicalize_for_policy("skills", &wd),
];
roots.extend(self.allow_fs_reads.iter().map(|e| canonicalize_for_policy(e, &wd)));
roots.extend(self.allow_fs_writes.iter().map(|e| canonicalize_for_policy(e, &wd)));