Skip to content

Architecture: skills should be agent directives, not LLM tools #61

Description

@zjshen14

Background

Skill activation is currently implemented as a fake LLM tool named activate_skill. When the model calls it, the executor intercepts it, loads the skill body, injects it into context, and returns no tool result. This is documented as a special case throughout the codebase.

Problems this causes

1. Semantic mismatch — skills are instructions to the agent, not capabilities of the agent. Expressing them as tools inverts the relationship: the LLM thinks it invoked a capability; it actually received new instructions.

2. Pervasive special-casing:

  • executor.ts — separates skillCalls from toolCalls before execution
  • core.ts — yields a distinct skill_activated event; filters activate_skill out of plan mode tools inconsistently (it's in PLAN_MODE_TOOLS but skills can mutate context)
  • schema.tsactivateSkillDefinition is a separate export alongside toolToDefinition

3. Token costactivate_skill appears in every prompt's tool list, consuming tokens even when no skill activation is expected.

4. Model confusion — the model sees activate_skill as a peer of bash, read, edit. It may call it at unexpected times or conflate it with a filesystem operation.

Proposed design

Skills are directives: structured content injected into the agent's context by the agent infrastructure, not by the LLM. Two options:

Option A — Pre-loading (simpler): skills are activated before the agentic loop begins, via explicit user invocation (/skill-name) or a lightweight classifier. The activate_skill tool is removed entirely. The model never sees it; it only sees the resulting injected content.

Option B — Directive message type (preserves model-driven activation): introduce a DirectivePart message type that the agent strips before sending to the LLM but processes internally. The model can still request skill activation via a structured output field (not a tool call), but the mechanism is explicit.

Option A is the recommended starting point — model-driven skill activation is rarely needed in practice, and pre-loading covers the common cases with far less complexity.

Acceptance criteria

  • activate_skill tool is removed from schema.ts and the tool list
  • Skills activated via /skill-name are injected before the agentic loop begins
  • No special-casing of skill calls in executor.ts or core.ts
  • skill_activated event is emitted by the CLI layer (user invocation), not by the agent loop
  • Existing skill injection and context behaviour is preserved

Location

  • src/model/schema.tsactivateSkillDefinition
  • src/agent/executor.ts:113 — skill call separation
  • src/agent/core.ts:80,178activate_skill filtering and event emission

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureStructural design decisions and system-wide refactorsenhancementNew feature or requesthelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions