Skip to content

feat: explicit skill invocation in TUI and desktop, aligned on a shared runtime contract #1148

Closed

Description

@Astro-Han

Problem

Skill support today is model-driven only: the agent may call the Skill tool when a request looks relevant, but users have no reliable way to explicitly invoke a skill. Three verified gaps:

  1. TUI has no manual trigger at all. The prompt fragment advertises the skill catalog and the Skill tool is wired with host gating (packages/cli/src/runtime-bootstrap.ts), but a user who wants a specific skill can only ask for it in prose and hope.
  2. Desktop / popup lists only workspace-installed skills. It is fed by skills:list → scanWorkspaceSkills (single-root, {workspaceRoot}/skills/), while the runtime injects from five discovery paths (resolveSkillDiscoveryPaths: {cwd}/.maka/skills, {cwd}/.agents/skills, {workspaceRoot}/skills, ~/.maka/skills, ~/.agents/skills). Agent-visible skills are invisible to users.
  3. Desktop skill injection never applies the host-capability gate. Both call sites pass host === undefined (system-prompt-main.ts → buildSkillsPromptFragment, main.ts → buildSkillAgentTool), which the runtime explicitly treats as legacy no-gating. required-tools / required-capabilities metadata is inert on desktop, and the prompt can advertise skills the session cannot load (the desktop tool set is conditionally assembled, so this is not theoretical). The CLI already does this correctly.

Design (agreed)

One shared contract in @maka/runtime (skill-invocation module), consumed by both clients:

  • listInvocableSkills(source, host) — five-path scan + host gate + enablement → slim entries; the single source for TUI autocomplete/highlight validation and the desktop discoverable IPC.
  • resolveSkillInvocations + composeSkillInvocationMessage — load via the existing loadSkillInstructions semantics (truncation, cleaning, gating) and compose the final user message (instruction blocks carry the existing user-provided trust framing; no permission escalation).

TUI (packages/cli)

  • /skill:<name> tokens inline in the input, anywhere in the text, multiple allowed (deduped, order-preserving). /skill: triggers dynamic autocomplete from listInvocableSkills; bare /skill opens a picker (the /model pattern) that only inserts the token.
  • Valid tokens are highlighted in the editor in the CLI brand accent color; invalid tokens are simply not highlighted — absence of the affordance is the inactive state (no separate inactive styling).
  • Tokens are parsed at submit time; the CLI (not the model) deterministically loads and injects instructions. Failed tokens stay as literal text and the send proceeds with at most a non-blocking notice — sending is the high-frequency path and must never be blocked by skill resolution.

Desktop (apps/desktop + packages/ui)

  • The / popup and its inserted text (使用 X 技能:) stay as-is; selecting a skill additionally creates an invocation chip {id, name}.
  • Chips render inline at the head of the input flow (flex-wrap container, textarea keeps all native behavior; no contenteditable, no attachment-lane elevation), using the existing Chip primitive (accent) + Blocks icon + name, multiple allowed, hover-to-remove. Chips that are no longer invocable are silently dropped on skills-list refresh — a chip is the active-invocation indicator, so an invalid one must not exist.
  • sessions:send gains an optional skillIds field; main resolves and injects via the shared module before runtime.sendMessage. Races resolve silently in favor of sending.
  • The discoverable IPC (popup data source) switches to listInvocableSkills, closing gap 2; desktop passes a real HostCapabilities built from the assembled tool list to both injection call sites, closing gap 3.

Delivery plan

PR Scope Depends on
1 runtime skill-invocation module + TUI end-to-end (TUI as first consumer proves the contract) —
2 desktop host-capability gating for skill injection (standalone bug fix) —
3 desktop discoverable IPC + inline chips + main-side injection 1, 2

Non-goals

  • contenteditable rewrite or overlay-highlight tricks in the desktop composer
  • forking/patching pi-tui's Editor (highlighting is a render post-process in our subclass)
  • registering each skill as a top-level slash command
  • any special "inactive" visual state on either client
  • desktop skill management (panel stays workspace-scoped)

Activity

  1. Nyvo-io commented on Jul 18, 2026

    @Nyvo-io
    Contributor

    I would like to claim Part 2 of #1148: Desktop host-capability gating for skill discovery and injection.

    The TUI/runtime contract work in #1150 is already merged, so I will focus on the standalone Desktop slice described in the issue:

    • build a real HostCapabilities from the conditionally assembled tool set;
    • pass that host surface to both Desktop skill-injection call sites;
    • ensure requiredTools and requiredCapabilities are enforced consistently with the CLI;
    • keep the existing Desktop tool assembly, IPC behavior, and skill UI unchanged in this slice.

    I will add focused regression coverage for unavailable required tools/capabilities and verify that eligible skills continue to load with the existing instruction semantics. The Desktop discovery IPC and inline invocation chips remain Part 3 and are out of scope for this PR. I will wait for maintainer confirmation before creating the implementation branch.

  2. Astro-Han commented on Jul 18, 2026

    @Astro-Han
    ContributorAuthor

    Assigned!

  3. Astro-Han commented on Jul 25, 2026

    @Astro-Han
    ContributorAuthor

    Closing as completed/superseded.

    The shared runtime and TUI invocation path landed in #1150. Desktop capability gating and invocation correctness landed through #1224, #1240, #1282, and #1386. The remaining TUI /skills product decision is isolated in #1279, while Desktop Skills visual simplification is tracked by #1452.

    There is no remaining implementation unique to this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions