Agent skill

Intent Router

by angel291592 in angel291592/Intent-Router

Turns an underspecified do-something request into a typed IntentSpec by looking things up, asking only necessary questions or halting, before any planning or coding.

MITAuto-check passedAgent Workflows

Install Intent Router

skills CLI
$ npx skills add angel291592/Intent-Router --skill intent-router -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install angel291592/Intent-Router intent-router --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/angel291592/Intent-Router.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/intent-router .claude/skills/intent-router && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
intent-router
GitHub stars
1k
Token cost
~7.9k tokens
SKILL.md length
4,423 words
Files
12 (incl. references)
Skills in repo
1
Repo updated
First seen
Licence
MIT

At a glance

Turns an underspecified do-something request into a typed IntentSpec by looking things up, asking only necessary questions or halting, before any planning or coding.

  • Works in 9 steps: Purpose and when this fires → Vocabulary → Pass 1 — Parse → …
  • A request leaves open which approach or which objects to change
  • SKILL.md covers 1. Purpose and when this fires, 2. Vocabulary, 3. Pass 1 — Parse and 4. Pass 2 — Resolve, plus 5 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Intent-router works one step ahead of planning. It parses the request into a draft spec, resolves each open question by checking what its sources already hold (code, history, decision records, tickets, policies) or by asking you, then type-checks the spec and either routes it onward, asks, or halts. It does not implement anything itself.

It starts only when the request asks for action and a quick scan finds at least one unknown that would change the files touched, the approach, how failures behave or what counts as done. Questions are limited to preference or irreversible choices, asked one at a time with a recommended default. Saved contracts in a .intent folder are reused by source: asked answers carry over, probed facts are re-checked, and inferences are decided again.

Bundled files include reference notes (ask protocol, domains, the IntentSpec format, probe surfaces, harness compatibility) and a JSON schema with example specs for route, ask and halt outcomes. The skill stays silent on fully stated tasks and on requests that only ask for an explanation of existing state.

When your agent uses it

  • A request leaves open which approach or which objects to change
  • A ticket or task contradicts itself or allows two readings
  • Before planning a migration whose failure behavior is unspecified
  • When you want the agent to clarify the intent before it starts

Example prompts

  • “Add caching to the product API. Clarify the intent before you plan anything.”
  • “Sort out the failed payment tickets in the queue and tell me what you need from me.”
  • “Migrate our config loader to the new format, and check what the repo already decides before asking me.”

Workflow steps

9 steps, taken from the step headings in SKILL.md.

  1. Purpose and when this fires
  2. Vocabulary
  3. Pass 1 — Parse
  4. Pass 2 — Resolve
  5. Pass 3 — Typecheck and emit
  6. Output format
  7. Ungrillable questions
  8. Anti-patterns
  9. References

What it can do on your machine

Read from SKILL.md and the folder at commit 00b54f2. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are yaml).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Intent Router loads about 7.9k tokens when it runs, and up to ~20k if it reads all its reference files. Until then it costs about 258 tokens; SKILL.md has 4,423 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~258
When it runs · the whole SKILL.md, loaded when a task matches
~7.9k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~20k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from angel291592/Intent-Router at commit 00b54f2, republished under its MIT licence (© angel291592). 4,423 words, ~7,890 tokens.

Download SKILL.mdSave it as .claude/skills/intent-router/SKILL.md (or your agent's skills folder). This skill also uses 11 other files; get the full folder from GitHub.
name
intent-router
description
Converges an underspecified request into a typed IntentSpec before planning or acting. Use when the user asks to implement, add, change, refactor, fix, migrate, configure, handle, triage, sort out, look into or decide something and the request leaves decisions open (which objects, which approach, what happens on failure, which trade-off), contradicts itself, admits two readings, or rests on an approach its sources may rule out — in a codebase, a ticket queue, a research brief or a runbook — or when the user says "clarify the intent", "what do you need from me", or invokes intent-router. Looks up what its sources hold (code, history, decision records, a ticket log, an order record, the policy in force) before asking; asks only preference or irreversible questions, one at a time, with a recommended default; halts instead of guessing; checks the delivered work against the spec. Silent on a fully stated task its sources do not contradict. Not for explaining existing state ("what does X do", "why is Y slow").
license
MIT
metadata.version
1.3.0
metadata.author
angel291592
metadata.homepage
https://github.com/angel291592/Intent-Router

Intent-Router

A compiler does not guess the address of an undefined symbol. Do not guess the user's intent.

This runs one layer before planning, routing or coding. It converges a request into an IntentSpec — a typed, machine-readable statement of what is actually being asked — and hands that off. It does not implement anything itself.

1. Purpose and when this fires

Three passes, in compiler order: Parse the request into a draft spec, Resolve every open question by looking it up or asking, then Typecheck and emit — route, ask, or halt.

Start when both hold:

  1. The request is a do-something request: implement, add, change, refactor, fix, migrate, configure, handle, triage, sort out, look into, decide, write, set up, wire up, rename, remove, upgrade, optimise.
  2. A quick scan finds at least one decision-bearing unknown — an answer that would change which files are touched, which approach is taken, how failures behave, or what counts as done.

Do not start when: the request asks you to explain existing state ("what does X do", "why is Y slow") rather than to find something out and produce a deliverable; the silence check below passes and its premise check finds no contradiction — every decision-bearing item is already stated, so there is nothing to converge; or the request is trivially scoped and reversible (fix a typo).

Look for a saved contract first. Before probing, look for earlier work on this same request: <workspace>/.intent/<intent>.intent.yaml, stem = the intent slug, matching on request/objects. Its verification_status decides:

  • asked — its source: asked constraints are settled decisions: carry them in and never ask those questions again; only answers received now count in resolution.asked.
  • routed — premise-check it (below), carry it out, then verify it. verified — carried out already: do not reopen unless the user asks for a redo, which marks the old file superseded and starts a new intent. superseded, or absent on an older file — ignore it, or use it only to know what was asked and done.

Inherit by source, never wholesale: the file is not a probe surface (section 2). asked carries over; probed is re-checked at its evidence pointer and drops to unknown (kind: probe) when it no longer holds; inferred is never inherited — decide it again, and if it is still an inference keep source: inferred and mark it visibly again. Never relabel an inherited value explicit or probed.

The silence check. Run it once, against the request text alone, before probing anything. The request is fully specified when all four hold:

  1. Objects — it names what is acted on, precisely enough to enumerate: endpoints, files, records, tickets. A collective noun ("the user API", "this customer") does not qualify.
  2. Approach — it names the method or dependency to use, or rules the alternatives out.
  3. Failure behaviour — for every step it asks for that can fail, a rule is stated. A rule stated once covers the cases it subsumes: "on invalidation failure serve uncached" is stated — you do not reopen it for read failures, write failures or timeouts the request did not separately enumerate. A step whose failure the request is silent about is unstated.
  4. Acceptance — it names what counts as done: an observable end state one can check to declare the work complete ("done when both endpoints serve from cache and the suite passes"). A parameter the work must use is not a done condition — a TTL, a limit or a response shape bounds the work without saying when it is done, and naming one does not close this item.

If all four hold, make one premise check before staying out of the way: at most two lookups at the sources most likely to rule out what the request states — a decision record or history entry about the named approach, the manifest entry for a named dependency, the definition of a named object. If nothing you open contradicts the request, do not run: emit no spec, no fence, no announcement that you considered this skill — carry the request out as ordinary. A spec emitted after a passed silence check is a false positive, costing more than this skill saves. If a source does contradict it (tried and reverted, pinned or removed, does not exist), run, with that contradiction as a premise issue (section 3); an approach you merely prefer is never one.

If any one of the four is unstated, run — and do not downgrade an unstated item to "inferable" because a plausible default exists: if the user had to be trusted with the outcome, or the spec could be wrong without contradicting the request, it is unstated.

No-ask mode. If the user says "no questions", "just do it", "don't ask me anything", keep looking things up but never ask: whatever stays open is recorded with source: inferred and its reasoning. If an inferred item touches an irreversible boundary, halt and name the field rather than guessing it — the one thing worse than a question is a silent irreversible choice. Inference may fill in how something is done; it may never invent what is being asked for. If the request itself is unclear — it names neither the object nor an observable outcome ("make it better" with nothing to make better) — no-ask mode halts with cause: underspecified and the open fields, rather than inferring three concrete improvements and routing them as the user's intent. This halt is specific to no-ask mode: when asking is allowed, the same unclear request is answered with a question, not a halt.

Explicit invocation. When the user names this skill in any way, start unconditionally and treat the text after the name as the request.

2. Vocabulary

  • unknown — a field of the spec whose value is not yet established.
  • decision-bearing — an unknown whose answer changes the files touched, the approach, the failure behaviour, or the acceptance criteria. Everything else is an implementation detail and is left to whoever executes.
  • required field — a field the spec cannot be emitted without: intent, objects, and every constraint needed to act without guessing.
  • inferred — a value the model supplied itself. Always visible, always evidenced, always cheap for the user to veto in one line.
  • evidence — a pointer to where a value came from: one token with no whitespace, naming something you actually opened, in one of these forms only: path, path:line, path:line-line, path#heading, git:<short-sha>, git:#<pr-number>, user:delegated (a decision handed back), record:<system>/<id>, doc:<slug>#<section>. Not evidence: the repository root (.), anything under .git/, .claude/, .agents/ or .intent/, the harness config file, and above all a reasoning sentence — that goes in text. Anything else is a defect, even if it exists.
  • probe surface — a place in the workspace where objective answers live: manifests, route definitions, configuration, tests, CI, version history, decision records.
  • ASK budget — the hard cap on questions for one request. Default 3.
  • irreversible — a decision that cannot be walked back once shipped, because clients, data or users will depend on it.

3. Pass 1 — Parse

Produce a draft spec. Do not emit it, do not act on it.

  1. Record the user's own words in request, truncated to 500 characters. Never paraphrase: the point is that a reviewer can compare the spec against what was actually said.
  2. Normalise the action into intent, a snake_case verb-object identifier: add_caching, migrate_auth, rate_limit_signup.
  3. List what the intent acts on in objects — files, endpoints, modules, jobs, records. If the request does not name them, leave it empty for now; this is an unknown, not a licence to pick.
  4. Copy every constraint the user stated into constraints with source: explicit. A stated constraint is never re-derived, and is questioned for one of two reasons only — it collides with another stated constraint, or a source you opened contradicts it (the conflict and premise issues below).
  5. Enumerate the unknowns. Each gets a stable field name, a category from the table below, and a judgement: decision-bearing or not. In the emitted spec an unknown item carries exactly field, kind, and optionally category, issue and note — never a decision_bearing flag, a free-form detail, or any other key; the judgement itself is expressed by keeping the item out of unknown when it is not decision-bearing.
categorythe question it asksexample in codeexample outside code
scopewhat is acted on, and what is explicitly outwhich endpoints get cachedwhich orders in this account are in scope
approachwhich method or dependency; what is mandatory or forbiddenexisting Redis client or a new in-process cachegoodwill credit, or a carrier claim
data_compatibilitydata shape, interface contract, backward compatibilitymay the response shape changemay the reply change the date already promised
failure_behaviorbehaviour on error, degradation or empty stateon invalidation failure, serve stale or uncachedif the refund is declined, hold the ticket or escalate
acceptancewhat counts as done, measurablywhich TTL matches the repository's conventionwhat closes the ticket — customer confirmation, or the SLA timer
non_goals_constraintsexplicit exclusions, hard limits on time, cost, complianceno new dependenciesno commitment beyond the policy in force

When the request touches any step that can fail, be rejected, or half-complete — a cache write, a refund, a backfill, a notification, an approval — and states no rule for that failure, enumerate the failure path as its own unknown: not "what should the feature do" but "what should happen when the feature's own step fails" — a cache write that errors, an invalidation that misses, a dependency that times out. Skipping it while emitting a happy-path spec is the guess this pass exists to prevent; and when the request does state the failure rule, that is an explicit constraint, never an unknown — re-opening it to split sub-cases the user did not distinguish is the over-asking this skill exists to remove.

Three defects that filling in cannot fix. Check the request for these before resolving anything; each one found becomes an ask unknown carrying issue, asked before any other unknown, because these are what the user would veto the work over.

  • ambiguous — the request reads two ways that change the deliverable, and no source settles which ("make it cheaper for mobile": fewer bytes, or fewer requests). Look first — an inventory often settles it; if it does not, the options are the readings, not ways to carry out one.
  • conflict — two things the user stated cannot both hold ("cache it", "always serve the latest write", "add no invalidation"). The options say which one yields.
  • premise — a source you opened contradicts something the user stated: the approach was tried and reverted, a named dependency is pinned or removed, a named object does not exist. It surfaces while probing. Keep the user's words as the explicit constraint, add the contradicting fact as a probed constraint with its evidence, and ask whether to go ahead as stated or as the source says.

In no-ask mode an ambiguous or conflict issue halts with cause: underspecified: choosing a reading is choosing what, which inference never may. A premise issue does not halt: the user's words decide, the unknown closes as an inferred constraint whose evidence is the contradicting source, and one sentence after the fence names the contradiction — unless going ahead as stated crosses an irreversible boundary, which halts.

Unknowns that are not decision-bearing do not enter Pass 2. Note them in trace and move on; resolving them is the executor's job, not a reason to spend a question.

4. Pass 2 — Resolve

This pass is the whole point. Everything else is bookkeeping.

4.1 The iron law

If an objective answer exists and you have any means to reach it, PROBE. Never ASK. ASK is reserved for answers that live in a person's head (preferences, priorities) or decisions that cannot be walked back.

Classify every decision-bearing unknown as probe or ask before saying anything to the user. A question whose answer was sitting in the workspace is a defect, not a courtesy.

4.2 PROBE

Name the sources first. A code workspace → the surfaces below; anything else (a ticket queue, a records system, a policy archive, a notes collection, a candidate registry) → load references/domains.md and use that domain's table. Use whatever file-reading, search, or shell capability your environment provides, version-control history included; if it exposes none, see degraded below: an absent capability is a fact about the environment, never a gap in the request.

Work the probe surfaces in this order, stopping as soon as the unknown is settled:

  1. Dependency and package manifests — what is already available, and what was deliberately pinned or removed.
  2. Entry points, route, command and job definitions — the real inventory of what exists. For any scope unknown ("which endpoints / commands / jobs"), this surface is authoritative: read the definition file itself — an inventory reconstructed from commit messages, docs or another module's imports is not evidence of what exists today.
  3. Existing implementations of the same kind — the pattern the repository already chose.
  4. Configuration, constants and environment templates — values that are conventions, not opinions.
  5. Tests and CI configuration — the contract that is already enforced.
  6. Version history — commits, reverts and pull-request numbers carry reasons no current file shows. Through a shell, try the command once before calling it unavailable and record the try in trace; with no shell, read the history files on disk (reference log, stored commit message). Evidence is git:<short-sha> or git:#<number>, never a path in the history store.

Decision records, changelogs, and any agent instruction file the project ships are covered in references/probe-surfaces.md, together with the surfaces for other ecosystems.

Budget: at most 3 probe actions per unknown, plus the one reserved history query below, which does not count against it. Do not read the whole repository. If an unknown survives its budget, escalate it: to ask if a person could answer it, otherwise leave it in unknown with kind: probe so the halt names it.

A dangling reference is not a settled unknown. A what-only answer — a changelog line, comment, config value or record field naming a pull-request number, "revert", "pin" or "workaround" with no reason — has not settled it. Follow the reference once as the reserved history query, outside the 3-action budget, then stop; if the reason is still missing, reclassify it ask.

Every probed value carries evidence. A probed or inferred constraint needs an evidence pointer — one per field, never two comma-joined (the second gets its own constraint or trace entry). Record facts you were not looking for when they constrain the work, and point at every probed object.

Degraded. When a probe fails for an environmental reason — no capability, a command error, a timeout — record it in trace as a failed lookup and keep the field's kind: probe. If the run ends without enough information and the cause is failed lookups rather than an underspecified request, halt with cause: degraded and an error that names what failed. Never present a broken environment as a vague request, or the reverse.

An empty probe surface is not a failed lookup. degraded means a lookup was attempted and failed; an empty repository, or a request naming no existing code, failed nothing. Reclassify the affected unknown as kind: ask and ASK: a person can still say what this should become.

Show full SKILL.md (1,910 more words)Show less
4.3 ASK

Only for answers that live in a person's head, or decisions that cannot be walked back.

Ask order. When several unknowns are askable, ask the one whose answer the user would veto the work over first — an observable behaviour or contract (what happens on failure, what the response looks like, what is in scope) before internal placement (which layer, which file, which module), because internal placement is the executor's call and may dissolve once the behavioural answer is known. Never pick a question for being easy to answer. The section 3 issues come before all of these.

What is never worth a question — internal structure: which layer or file hosts the logic, which function names to use, how to organise the code. These are reversible implementation details; whoever executes decides them. If the only remaining unknown is internal, the spec is sufficient — infer it visibly and route.

Eliminating options is not resolving the unknown. If only one option remains because you ruled the others out by reasoning — "stale reads are unacceptable, so fall through is the only choice" — the surviving option is itself the preference the user should confirm (fail fast with 5xx, serve uncached, queue and retry…). Infer it only when the user's own words or the workspace state it; otherwise ASK.

  • Budget: 3 questions per request by default. The user may override ("ask up to 5"); record whatever cap is in force as resolution.ask_budget. In no-ask mode the budget is 0.

  • One question at a time. Wait for the answer before asking the next; do not preview what else you might ask, and do not batch — a list puts the sorting work back on the user.

  • Each question has exactly this structure:

    • Question — the full question, answerable without scrolling back.
    • Why you, not me — one sentence on why this could not be looked up: a preference, a priority, or a decision that cannot be undone.
    • Recommended: <option id> — <reason> — the option you would take if the user delegates.
    • Options — two or three mutually exclusive, concrete choices, labelled A, B, C. Not restatements of the question, and not "other".
    • A free-form answer of up to five words is always acceptable; interpret it against the options.
  • Delegation. On "you decide", "whatever", "your call", take the recommended option and record it with source: inferred and evidence: user:delegated — unless it is irreversible: true: then restate the risk in one sentence and ask once more, which counts against the budget.

  • Language. Ask in the language the user wrote in — a hard rule, whatever language the workspace or your runtime instructions use (section 6, check 3). Spec keys stay English.

  • When the budget is exhausted and the spec is still not sufficient, stop asking and halt with cause: underspecified. Do not squeeze in "one more" question, and do not paper over the gap with a guess.

Templates, worked good-versus-bad questions, and the delegation and override rules in full are in references/ask-protocol.md.

5. Pass 3 — Typecheck and emit

The stopping condition is computed, not felt:

sufficient ⟺ unknown is empty ∧ no constraint has both source: inferred and irreversible: true.

Then exactly one of three outcomes:

  • ROUTE — sufficient. Emit the complete IntentSpec with decision.state: ROUTE and a target: implement, plan, research, respond, escalate, prototype, or whatever the user named. Name the saved spec file in the hand-off and ask for every constraint to be checked before the work is called done, then stop — do not implement inside this skill. When the handed-off work is carried out in this conversation, verify it and report (below).
  • ASK — not sufficient and the budget still allows a question. Emit the current spec snapshot with decision.state: ASK and decision.question set to the one question you are asking, then the question itself, then wait. The unresolved field stays in unknown and resolution.asked still counts only answers received, not questions sent. Nothing inferred in an ASK snapshot may depend on the pending answer: "done means the chosen remedy is executed" is the open decision itself, not an inference — it belongs to the question's options, not to constraints.
  • HALT — not sufficient and no way forward. cause: underspecified when the request is not yet decidable, with open_fields naming what is still dangling; cause: degraded when lookups failed, with error naming the failure. The two causes are mutually exclusive and must never be merged: one is the user's next move, the other is an operations signal. Underspecified is not a halt while a question is still possible: if an askable unknown remains and the budget allows a question, the outcome is ASK, not HALT. HALT underspecified is reserved for when asking is impossible — no-ask mode, or the budget spent. An empty workspace is ASK, never HALT (see 4.2).

A spec that reaches ROUTE with an inferred constraint in it is fine — that is the design. One that reaches ROUTE with an inferred irreversible constraint is a bug.

After the hand-off: verify

When the work a ROUTEd spec handed off is carried out in this same conversation, verify it before reporting it done. Reread the saved spec — or the fenced block, if it could not be saved — and check the finished work against every constraint on what it must or must not do. Report one line per constraint, after the work summary, under a line reading exactly Intent check, which stays in English whatever the language; the lines under it use the user's language:

  • met — with the one pointer (path:line) where the delivered work satisfies it;
  • not met — then fix it before reporting, or say why it stays unmet;
  • not checkable here — with the reason (it needs a running service, a person, live data).

Never mark a constraint met without a pointer into the delivered work — and the pointer must be the line that satisfies that constraint, not a neighbouring branch or a similar handler. Work that does not implement the constraint is not met, however plausible the file looks. The check reads the spec; it does not reopen it — a decided constraint is checked, not asked again.

Write the result back into the same .intent/ file, replacing its earlier snapshot: verification_status: verified, plus verification with its status, a checked_at, and one results line per constraint carrying constraint, verdict and — when met — pointer. The reply keeps the same lines under Intent check: the file is for the next session, not a substitute for telling this user. A delegated check carries the spec path and runs in a fresh context — a check sharing the implementer's is not a check.

6. Output format

Lead with the human part. Before the fence, 3–6 lines of prose: what you looked up, above all the finding that changed the approach; what you decided for the user, one line each, so any line can be vetoed; and the one question, if any. Then the fenced yaml block, whole spec, in field order.

The fence stays. Some environments keep only a turn's final message, and the eval reads the spec from the reply, so the block is never dropped in favour of the prose; a workspace that cannot be written to still gets one sentence after it (below).

Quote every dirty scalar. Any value containing :, #, {, }, [, ], ,, " or ', or starting with a character that is not a letter, must be wrapped in single quotes — an inner ' is written ''. Multi-line text uses the block scalar > instead. Single quotes, not double: the offending characters are usually double quotes themselves ("ioredis": "^5.4.1"), and single-quoting needs no escaping. The dangerous case is a # after a space (a reference like reverted in #412): unquoted, YAML reads it as an inline comment and silently truncates the value — the fence still parses, so a corrupted spec survives a review a hard error would catch.

yaml
spec_version: "0.1"
request: add rate limiting to the public endpoints
intent: add_rate_limiting
objects:
  - POST /api/login
  - POST /api/signup
constraints:
  - source: explicit
    text: apply it to the public endpoints only
    category: scope
  - source: probed
    text: 'the entry point already registers a rate-limit middleware: mounted before the routes'
    evidence: src/app.ts:24
    category: approach
  - source: asked
    text: 'reject over-limit requests with 429 and the body { error: "rate_limited" }, not by queueing them'
    irreversible: true
    category: failure_behavior
unknown: []
decision:
  state: ROUTE
  confidence: 0.82
  target: implement
resolution:
  unknowns_found: 2
  resolved_by_probe: 1
  asked: 1
  inferred: 0
  ask_budget: 3
trace:
  - step: parse
    detail: two decision-bearing unknowns — middleware choice and over-limit behaviour
  - step: probe
    detail: the entry point already registers a rate-limit middleware, so no new dependency
    evidence: src/app.ts:24
  - step: ask
    detail: asked the one irreversible choice; the user chose rejection over queueing
  - step: emit
    detail: unknown is empty and no inferred constraint is irreversible — sufficient; handing off

confidence is a self-reported ordinal, not a calibrated probability; resolution is a diagnostic, not a score to push up. Before emitting, reread the block and check it against these three:

  1. Counts, by attribution and not from memory. Attribute each decision-bearing unknown — those listed in Pass 1 plus any probing turned up — to exactly one outcome: closed by a lookup, by an answer, by a value you supplied, or still open. The four partition unknowns_found, which counts the closed ones too, so unknowns_found = resolved_by_probe + asked + inferred + len(unknown) must hold. Count unknowns, not constraints: two probed constraints settling one unknown add 1, not 2, and a fact recorded because it constrains the work but closing no unknown adds nothing to any counter — it is still a constraint with its evidence. A probe that settled something unlisted does count, in both resolved_by_probe and unknowns_found; the attributed unknowns belong in the parse and probe trace steps, so the scorecard can be checked rather than trusted.
  2. Evidence — every probed and inferred constraint carries an evidence token; one missing pointer invalidates the spec — an unsourced claim is indistinguishable from a guess.
  3. Language — the question, its why_human and every option in the user's language (4.3).

Every trace step is exactly one of parse, probe, ask, typecheck, emit — there is no infer, resolve or decide step; an unknown closed by inference is a constraint with source: inferred, not a new trace step.

For the other two states, decision carries different fields and nothing else changes:

yaml
decision:
  state: ASK
  confidence: 0.55
  question:
    text: <the question, in the user's language>
    why_human: <why this cannot be looked up>
    recommended: A
    options:
      - id: A
        text: <concrete option>
      - id: B
        text: <concrete option>
yaml
decision:
  state: HALT
  confidence: 0.2
  cause: underspecified      # open_fields required; error forbidden
  open_fields: [scope, acceptance]

Every emitted spec is also saved. Before replying, write it to .intent/<intent>.intent.yaml — on ASK, ROUTE and HALT alike, replacing this request's earlier snapshot, with verification_status: asked on ASK and HALT and routed on ROUTE — and touch no other file for it. Nothing is written when you stay silent (section 1); a workspace you cannot write to gets one sentence after the fence, never a halt. The field reference and the .intent/ convention are in references/intentspec.md; the machine-checkable contract is schema/intentspec.schema.json.

7. Ungrillable questions

Some questions cannot be resolved by probing or asking, because the user cannot answer them in the abstract either ("make it feel modern", "make the onboarding delightful"): the signature is an aesthetic or experiential target with no observable acceptance criterion, which no conceivable file in the workspace could name. Recognise this in Pass 1, before spending probe budget — probing it never converges, and it is not worth a question either. Name the ungrillable field, say it needs something to react to rather than more discussion, and hand off to a throwaway artifact as the target — a prototype or mock, a draft reply, one sample record — or halt with the field in open_fields. Never quietly pick a direction and present it as the user's intent.

8. Anti-patterns

  1. Asking what the workspace could answer: treat a violation as a bug, not a style preference.
  2. Batching questions, or previewing the questions you might ask next.
  3. Reporting a failed lookup as missing information, or an underspecified request as a failure.
  4. A probed or inferred constraint with no evidence.
  5. Continuing to ask after the budget is spent, or substituting a guess for a halt.
  6. Starting to implement inside this skill instead of handing off at ROUTE.
  7. Running for a question, an explanation, or a fully specified task its sources do not contradict.
  8. Passivity: the run grinds on while the user keeps agreeing. Converge and route once sufficient.

9. References

Load on demand; each is self-contained.

  • references/probe-surfaces.md — probe surfaces per ecosystem, evidence formats, degraded cases.
  • references/ask-protocol.md — question templates and examples, delegation, budgets, language.
  • references/intentspec.md — every field, the invariants, the worked examples, .intent/ files.
  • references/domains.md — probe surfaces and questions outside code, routing-registry criteria.
  • references/harness-compat.md — which environments load this skill, from where, how to invoke.

© angel291592, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 11 other files (references) in skills/intent-router of angel291592/Intent-Router.

  • SKILL.md
  • references/ask-protocol.md
  • references/domains.md
  • references/harness-compat.md
  • references/intentspec.md
  • references/probe-surfaces.md
  • schema/examples/ask.yaml
  • schema/examples/halt-degraded.yaml
  • schema/examples/halt-underspecified.yaml
  • schema/examples/route-support.yaml
  • schema/examples/route.yaml
  • schema/intentspec.schema.json

Open the folder on GitHubat commit 00b54f2

Compare with similar skills

Intent Router next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Intent Router compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Intent Router this skillangel291592/Intent-Router1k—~7.9kAutomated safety check: PassMIT
Grill With UIjasonku09/grill-with-ui258—~7.9kAutomated safety check: PassMIT
Interview Meaddyosmani/agent-skills104k6 repos~3.8kAutomated safety check: PassMIT
Ask User QuestionMemTensor/MemOS12k—~1kAutomated safety check: PassApache-2.0
Brainstorming Before BuildingjnMetaCode/superpowers-zh8.3k—~1.8kAutomated safety check: PassMIT
Plannotator Planning Analysisbacknotprop/plannotator9.3k—~6.7kAutomated safety check: PassApache-2.0

Similar skills

  • Grill With UI

    jasonku09/grill-with-ui

    Moves a design-grilling interview from the terminal to a local web page, where each question, recommendation and discussion thread can be handled in any order.

    258 GitHub stars~7.9k tokensUpdated 2 days ago
    Agent WorkflowsAuto-check passed
  • Interview Me

    addyosmani/agent-skills

    Asks one question at a time, each with a best guess attached, until the agent is about 95 percent sure what you really want, before any plan, spec or code.

    104k GitHub starsUsed in 6 repos~3.8k tokens
    Agent WorkflowsAuto-check passed
  • Ask User Question

    MemTensor/MemOS

    Shows a question as a modal in the interface to clarify a task, collect a preference or get approval, since the user cannot see terminal output.

    12k GitHub stars~1k tokensUpdated yesterday
    Agent WorkflowsAuto-check passed
  • Brainstorming Before Building

    jnMetaCode/superpowers-zh

    Turns a rough idea into an approved design before any code is written, sorting the request into spike, bounded or architectural and enforcing an approval gate.

    8.3k GitHub stars~1.8k tokensUpdated 2 days ago
    Agent WorkflowsAuto-check passed
  • Plannotator Planning Analysis

    backnotprop/plannotator

    Mines a Plannotator archive of denied plans for feedback patterns and prompt improvements, then writes an HTML dashboard report, with a Claude Code fallback.

    9.3k GitHub stars~6.7k tokensUpdated today
    Agent WorkflowsAuto-check passed
  • ULW Plan Workflow

    code-yeongyu/oh-my-openagent

    Explore-first planning that turns a vague or large request into one decision-complete work plan, written only after your approval and executed by a separate worker.

    70k GitHub stars~3.9k tokensUpdated today
    Agent WorkflowsAuto-check passed

Categories

Questions about Intent Router

What does Intent Router do?

Turns an underspecified do-something request into a typed IntentSpec by looking things up, asking only necessary questions or halting, before any planning or coding. Intent-router works one step ahead of planning. It parses the request into a draft spec, resolves each open question by checking what its sources already hold (code, history, decision records, tickets, policies) or by asking you, then type-checks the spec and either routes it onward, asks, or halts.

When should I use Intent Router?

Intent Router fits situations like: A request leaves open which approach or which objects to change; A ticket or task contradicts itself or allows two readings; before planning a migration whose failure behavior is unspecified; when you want the agent to clarify the intent before it starts.

How do I install Intent Router in Claude Code?

Run `npx skills add angel291592/Intent-Router --skill intent-router -a claude-code`. Or copy the skill folder (skills/intent-router in angel291592/Intent-Router) into .claude/skills/intent-router in your project. Claude Code loads it when a task matches its description.

How do I install Intent Router in Codex?

Run `npx skills add angel291592/Intent-Router --skill intent-router -a codex`. Or copy the skill folder (skills/intent-router in angel291592/Intent-Router) into .agents/skills/intent-router in your project. Codex loads it when a task matches its description.

Can I use Intent Router in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add angel291592/Intent-Router --skill intent-router -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/intent-router, .gemini/skills/intent-router, .github/skills/intent-router and .opencode/skills/intent-router in your project.

What does Intent Router need to run?

SKILL.md names no scripts, command-line tools or credentials: Intent Router is instructions for the agent only.

Does Intent Router access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Intent Router safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Intent Router use?

Intent Router is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Intent Router use?

About 7.9k tokens (SKILL.md is roughly 32k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 12k tokens, read only when the agent opens those files.

What are the alternatives to Intent Router?

Skills that share tags, products or a category with Intent Router: Grill With UI (jasonku09/grill-with-ui, 258 stars), Interview Me (addyosmani/agent-skills, 104k stars), Ask User Question (MemTensor/MemOS, 12k stars) and Brainstorming Before Building (jnMetaCode/superpowers-zh, 8.3k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Intent Router?

angel291592 (a GitHub user) maintains it in angel291592/Intent-Router, which has 1,048 GitHub stars. The repository was last updated on October 9, 2026.

Source: angel291592/Intent-Router on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.