Skip to content

Adapters

Aditya Gupta edited this page Mar 30, 2026 · 4 revisions

Adapters are how Coggle knows what external tools exist and what they can do. Each adapter wraps a single tool — mv, ffmpeg, convert — and declares its capabilities in a self-describing way that the NLP pipeline can query at runtime.

Structure

An adapter is a TOML file with three top-level sections: the adapter identity, its domain definitions, and its capabilities.

adapter/
  [adapter]       — name and aliases
  [[domains]]     — what file types this adapter operates on
  [[capabilities]]— what operations it can perform

Adapter

[adapter]
name    = "UNIX File Management"
aliases = ["unix", "filesystem", "fs"]

aliases are the names a user can use to force this adapter explicitly — e.g. "move these files using unix".

Domains

A domain defines what kinds of files a capability operates on. Each adapter declares one or more domains, and each capability references exactly one by name.

[[domains]]
name        = "video"
description = "Video files"
extensions  = [".mp4", ".mkv", ".webm", ".mov"]
mimetypes   = ["video/mp4", "video/x-matroska", "video/webm"]

A domain must define exactly one of the following matching strategies:

Field Type Description
match "any" Wildcard — matches any file regardless of type
extensions array<string> Matched against the TARGET file extension
mimetypes array<string> Matched against the detected file MIME type

extensions and mimetypes may be used together. match = "any" is mutually exclusive with both.

# Filesystem domain — applies to any file type
[[domains]]
name        = "filesystem"
description = "Any file or directory regardless of type"
match       = "any"

Domain matching acts as a filter on the candidate capability set, not a hard selector. A match = "any" domain never eliminates candidates — intent triggers and slot fitting handle disambiguation when multiple adapters cover the same file type.

Capabilities

A capability is a single operation the adapter can perform. It maps to exactly one intent and defines the full parameter signature needed to construct the command.

[[capabilities]]
domain      = "filesystem"
name        = "move"
triggers    = ["move", "put", "shift", "transfer", "relocate"]
description = "Move one or more files or directories to a destination"
destructive = true
command     = { base = "mv", positional_order = ["source", "destination"], execution = "single" }
 
[capabilities.slots]
source      = { category = "TARGET",      type = "filepath", required = true,  cardinality = "many", expansion = "inline", render = "positional", desc = "Files to move" }
destination = { category = "DESTINATION", type = "filepath", required = true,  cardinality = 1,       render = "positional", desc = "Location to move files into" }
no_clobber  = { category = "ARGUMENT",    type = "boolean",  required = false, default = false, render = "flag", flag = "-n", desc = "Do not overwrite existing files" }
verbose     = { category = "ARGUMENT",    type = "boolean",  required = false, default = false, render = "flag", flag = "-v", desc = "Print each file as it is moved" }

Capability fields

Field Type Required Description
domain string yes Must match a domain name defined in [[domains]]
name string yes Unique identifier within this adapter
triggers array yes Intent keywords that activate this capability
description string yes Used by the slot mapper for confidence scoring
destructive boolean yes If true, requires dry-run confirmation before execution
command object yes Describes how to build the shell invocation

Command

Field Type Description
base string The binary to invoke e.g. "mv", "ffmpeg"
positional_order array<string> Slot names rendered as positional args, in order
execution "single" | "loop" single = one invocation for all targets; loop = one invocation per target

Slots

Slots are the parameters of a capability. Each slot is an inline table keyed by its name. Every capability must have at least one slot with category = "TARGET".

Slot fields

Field Type Required Description
category TARGET | DESTINATION | CONSTRAINT | ARGUMENT yes The span category that fills this slot
type see Slot Types yes Semantic type of the value
required boolean yes Hard-rejects the capability during selection if unfillable and no default is set
desc string yes Natural language description used for semantic slot matching
render "positional" | "flag" yes How the slot is rendered in the command
cardinality integer | "many" no Defaults to 1. Use "many" for unbounded inputs
expansion "inline" | "loop" if cardinality = "many" How multiple values are passed to the tool
flag string if render = "flag" The flag string e.g. "-n", "--verbose"
default any no Only valid when required = false
keywords array<string> no Fast-path synonyms for ARGUMENT slot matching
values array<string> if type = "enum" The set of valid values for enum slots

Span categories

Category Role
TARGET The file(s) being operated on
DESTINATION Where the result goes
CONSTRAINT Filtering criteria — narrows which files the operation applies to
ARGUMENT Operation modifiers — changes how the operation runs

Slot types

Slot types are broad enough to cover many domains without needing new types per adapter, but specific enough that each type encodes a distinct parsing behaviour and produces a distinct structure for the command builder.

Type Parsed as Examples
filepath Path or glob, resolved at runtime *.mp4, /home/user/file.txt
boolean Presence or absence of a flag true, false
integer Dimensionless whole number 30, 2, 100
quantity Scalar number + unit 10MB, 30fps, 2Mbps, 128kbps, 2x
dimensions Width × height compound value 1920x1080, 1080p, 800x600
timestamp Time offset or point 00:01:30, 90s, 1:30:00
enum Single select from a defined set h264, lanczos, nearest
string Free text, last resort metadata title, output label

quantity covers any slot that combines a number with a unit — file sizes, bitrates, framerates, scale factors. The parser always produces {value, unit} and the command builder formats it per tool.

dimensions is kept distinct from quantity because width × height is a two-number compound with its own parsing rules and shorthand (1080p1920x1080). Tools also consume it differently — ffmpeg wants -vf scale=1920:1080, not a single token.

enum replaces any slot that selects from a predefined set of values — codecs, interpolation filters, output modes, and so on. The valid options are declared on the slot itself via the values field. Multi-select is just enum + cardinality = "many".

codec = { category = "ARGUMENT", type = "enum", values = ["h264", "h265", "vp9", "av1"], required = false, render = "flag", flag = "-c:v", desc = "Video codec to encode with" }

string is an explicit last resort for genuinely free-text inputs. If you find yourself reaching for it frequently it is a signal that a more specific type is missing.

Cardinality and expansion

When a slot accepts multiple values (cardinality = "many"), expansion defines how those values are passed to the tool at command construction time:

Expansion Behaviour
inline All values in one invocation: mv a.txt b.txt dest/
loop One invocation per value: ffmpeg -i a.mp4 ..., then ffmpeg -i b.mp4 ...

Note that execution = "loop" on the command level and expansion = "loop" on a slot are distinct: command-level loop iterates the entire invocation per target, slot-level expansion only affects how that slot's values are passed within a single invocation.

Capability Selection

When a query is processed, the pipeline selects the best capability using three mechanisms in order:

1. Intent triggers — narrows candidates to capabilities whose triggers list matches the classified intent.

2. Adapter forcing — if the user specifies "using <alias>", the candidate set is filtered to that adapter only before scoring. The forcing phrase is stripped from the query before slot extraction.

3. Slot fitting — each remaining candidate is scored by how well the extracted spans fill its slots. The candidate with the highest confidence wins. When a required slot cannot be filled and no default is specified, that candidate is disqualified.

Destructive Operations

Capabilities with destructive = true require a dry-run confirmation step before execution. This applies to any operation that cannot be trivially undone — moves, deletes, truncations.

The dry-run prints the resolved command and affected files and waits for explicit confirmation before proceeding.

Writing an Adapter

  1. Create a .toml file under adapters/
  2. Define the [adapter] block with a name and aliases
  3. Declare one or more [[domains]] blocks
  4. Declare one or more [[capabilities]] blocks, each referencing a domain by name
  5. Validate against the schema — see schemas/adapter.schema.json

If you are using VSCode with the Even Better TOML extension, schema validation and autocomplete are enabled automatically for files under adapters/. See .vscode/settings.json.