Skip to content

[Bug] mavis skill show returns empty description for skills whose YAML frontmatter was added after daemon boot #110

Description

@jordelmir

Summary

mavis skill show <name> returns "description": "" even when the on-disk SKILL.md has a clean YAML frontmatter block, the corresponding row exists in ~/.minimax/sqlite.db with the right description, and the agent tool can load the skill once prompted. The desktop daemon's in-memory skillIndex is populated only once at boot and is never refreshed when an already-indexed skill's file is rewritten.

Steps to reproduce

  1. Open MiniMax Code.app so the desktop daemon (PID 53290 in our environment) is up.

  2. Pick any skill whose SKILL.md originally lacked a YAML frontmatter block. The shipped bundle ships at least one (e.g. secure-by-design-review) and the legacy # Skill: header shape is easy to introduce by importing a marketplace bundle written before the YAML contract landed.

  3. Rewrite the file to add a canonical frontmatter block, save the file.

  4. Re-run the same query without restarting the app:

    $ mavis skill show secure-by-design-review | jq .description
    ""
    
    $ mavis skill ls mavis | jq '.skills[] | select(.name == "secure-by-design-review").description'
    null
    
    $ sqlite3 ~/.minimax/sqlite.db \
        "SELECT length(description) FROM skills WHERE name='secure-by-design-review';"
    142
    
    $ # But the agent tool *can* load it:
    $ # claude /skill secure-by-design-review  ->  works

Expected

mavis skill show should return the on-disk description (or the row description, whichever wins) without a daemon restart.

Actual

show keeps returning "" until the daemon is restarted (launchctl kickstart -k gui/$UID/com.mavis.daemon on macOS). A clean restart sometimes still fails with Cannot find module 'better-sqlite3' in ~/.minavis/logs/launchd-stderr.log until MAVIS_SQLITE3_MODULE_PATH is exported to the app.asar.unpacked directory.

Root cause (reverse-engineered from MiniMax Code.app/Contents/Resources/resources/daemon/daemon.js)

In packages/skill/dist/skill.js (extracted from the bundled daemon.js):

async readSkill(req) {
  const { skill_name, agent_name } = req;
  // ... (fs lookup chain)
  const meta = this.skillIndex.get(skill_name, actualAgentName);  // in-memory cache
  const fallback = meta ? null : this.frontmatterFallback(content);
  const description2 = meta?.description ?? fallback?.description ?? "";
  // ...
}

skillIndex is hydrated at daemon boot from ~/.minimax/sqlite.db. Two failure modes compound:

  1. Boot-scan skip. If the file lacks valid YAML frontmatter at boot time, the indexer silently drops the row (the description is empty string). Without a row in skills, every later API hit falls through to frontmatterFallback(content), which also yields "".
  2. Cache staleness. Even when a row is later INSERT-ed by hand into skills, the running daemon never refreshes its in-memory skillIndex. The skill is invisible to the agent tool until restart.

Workaround (validated end-to-end on 2026-07-04)

A community skill patch is already in flight — see MiniMax-AI/skills#107 which adds skill-hygiene plus a reference doc with three recovery paths. The cleanest one is:

mavis skill update <name> -a mavis --file <absolute-path-to-SKILL.md>

This invokes the file-watcher's onSkillFileChange handler in the daemon, which calls this.skillIndex.upsert(...) and brings the in-memory cache back in sync without restarting the desktop app.

A launchctl kickstart -k gui/$UID/com.mavis.daemon also works but forces the user to relaunch the whole UI; see workaround doc for MAVIS_SQLITE3_MODULE_PATH export if you go that route.

Proposed fix in the daemon

Two minimum-touch changes:

  1. Boot-scan must retry on frontmatterFallback. If parsed?.description is empty and the file has a # Skill: style header, attempt a tolerant fallback parse (regex over the first five lines, same algorithm as the community script) before giving up on the file.
  2. File-watch must upsert on every change. The current onSkillFileChange already calls this.skillIndex.upsert(...) when content changes — but the upsert guard rejects rows whose description is empty. Drop that guard, or better, fall back to the tolerant parse above whenever parsed?.description is empty.

A third, larger change is a mavis skill reindex [--agent <name>] [--skill <ref>] CLI command so contributors can recover from cache drift without a restart at all.

Environment

  • Product: MiniMax Code.app (bundle id com.minimax.agent)
  • Daemon PID 53290 launched by the Electron app with --port 15321 --owner electron --skip-pid-port
  • macOS 15.x, Apple Silicon
  • ~/.minimax/sqlite.db size ~39 MB, schema includes skills (PK name+agent_name), skill_hub, skill_hub_added
  • Reproduced on secure-by-design-review and 48 other SKILL.md files in the bundled ~/.minimax/skills/

Severity

Medium — the agent tool still loads the skill through available_skills once the user mentions it, but any tooling built on top of mavis skill show (publishing, marketplace linting, contribution checks) is silently broken until restart.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions