Skip to content

Fix task/tasks skill: fast single-call lookup, run, and create-offer#33

Merged
skapoor8 merged 1 commit into
mainfrom
fix/task-speedup
Jul 19, 2026
Merged

Fix task/tasks skill: fast single-call lookup, run, and create-offer#33
skapoor8 merged 1 commit into
mainfrom
fix/task-speedup

Conversation

@skapoor8

@skapoor8 skapoor8 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Intent

Make /task (and its /tasks alias) fast. This skill sits on a hot path — the spec, em, dev, and gcp skills all call /task detect / /task list before running any build/test/lint. Each call was taking ~2 minutes for a job that is really ~1ms of shell. This branch collapses that into a single deterministic script call, and makes no-arg invocations list tasks, query invocations run the matching task, and unmatched queries offer to create a runner-native task.

Bug

Detecting the project's task runner took ~2 minutes per /task call. The detection work itself is trivial (a few test -f marker checks plus one jq on package.json, ~1ms). The slowness was structural: the model did the work by transcribing-and-executing bash across four markdown files. SKILL.md dispatched to references/workflows/detect.md; list.md re-inlined the same detection and its list command; run.md did it a third time. Every "read this file, transcribe its bash, run it, read the output, move to the next file" hop is a slow LLM round-trip. Four files × round-trips = minutes for a millisecond job.

Fix

Move the deterministic part into one shipped script and reduce the model's role to "run one command, read its output."

  • New skills/task/scripts/task.sh — single POSIX-sh source of truth for detect/list/run and the no-match offer, one process per call:
    • no-arg / list → detect runner, print RUNNER: <name>, then the task listing (or a no-runner message with a mise.toml suggestion).
    • detect → runner name plus run/list commands in stable KEY=value form.
    • run <query> [args…] → detect, resolve query (exact → unique prefix/substring), exec the runner on match; on no match, exit distinctly and print the task list plus a NO_MATCH:<query> marker.
  • skills/task/SKILL.md rewritten to a thin dispatcher: every verb maps to one bash scripts/task.sh … call. No-arg → list; an unrecognized first token → run <query> (so /task test, /task build --release just work); explicit detect/list/run still take precedence. On NO_MATCH, it offers a runner-native entry (mise [tasks.*], just recipe, Taskfile.yml task, package.json script — or a scaffolded mise.toml if no runner), gated behind user confirmation.
  • detect.md / list.md / run.md thinned to short reference docs describing the script contract — no executable bash on the hot path.
  • /tasks alias reconciled and kept in sync: .mise-tasks/gen-task-alias regenerates skills/tasks/ from skills/task/ (identical except the name/trigger token), wired via mise run skills:sync-task-alias with a drift --check folded into skills:validate. docs/skills/task.md updated for the new behaviour.

Detection order (mise → just → Taskfile → package.json → none) and cross-skill references (/task detect, /task list, /tasks list) are unchanged — no other skill needs to change.

Fixes:

Validation

  • mise run skills:validate passes — 44/44 SKILL.md checks plus the alias drift --check
  • /task with no args lists tasks in a single Bash call (no multi-file dispatch)
  • /task <known-task> runs the matching runner command; extra args pass through
  • /task <unknown> offers a runner-native create snippet for the detected runner
  • Existing /task detect|list|run verbs and aliases produce equivalent output

@skapoor8 skapoor8 self-assigned this Jul 19, 2026

@skapoor8 skapoor8 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: 7 inline comments (3 BLOCKING run-path bugs). See details inline.

Comment thread skills/task/scripts/task.sh Outdated
Comment thread skills/task/scripts/task.sh Outdated
Comment thread skills/task/scripts/task.sh Outdated
Comment thread skills/task/scripts/task.sh
Comment thread skills/task/scripts/task.sh Outdated
Comment thread .mise-tasks/gen-task-alias Outdated
Comment thread skills/task/scripts/task.sh
skapoor8 added a commit that referenced this pull request Jul 19, 2026
Model-driven dispatch (SKILL.md -> detect.md -> list.md -> run.md)
made `/task detect` and `/task list` take ~2 minutes: every verb
walked several reference docs and ran ad-hoc bash the model
assembled per call. Replace that hot path with one shipped POSIX-sh
script so detection, listing, running, and the no-match offer happen
in a single deterministic process.

**Single shipped dispatcher**
- Add scripts/task.sh: one source of truth for runner detection
  (mise -> just -> Taskfile -> package.json -> none), listing, and
  running.
- No argument lists tasks; an unrecognised first token is treated as
  a run query and matched against the runner's task names; a query
  with no match returns NO_MATCH so the skill can offer to create a
  runner-native task entry instead of failing.
- SKILL.md maps every verb to a single Bash call; the three workflow
  docs shrink to thin references pointing at the script, so no
  executable bash remains on the hot path.

**Correct lookup signals**
- List names via `mise tasks ls --no-header` so the first task is no
  longer dropped by a header-skipping awk (mise 2026.3.x emits no
  header); dropping it made `/task <first>` wrongly offer to create
  an existing task.
- Capture enumeration stderr: a runner present but unable to
  enumerate (e.g. untrusted mise on a fresh clone) is reported as
  ENUM_FAILED, surfacing the trust error rather than offering to
  create the task.
- Two or more prefix/substring candidates return AMBIGUOUS with the
  candidate list so the query is disambiguated, not treated as
  no-match.
- Match literally via `grep -F` and a lowercased case-glob prefix;
  the query is never interpreted as a regex.

**/tasks alias with a drift guard**
- Ship skills/tasks/ as a generated mirror of canonical skills/task/
  (identical but for the name, `/tasks` trigger token, and SKILL.md
  H1), so both triggers share the same fast script.
- Add .mise-tasks/gen-task-alias to regenerate it and a
  skills:sync-task-alias task; fold a --check drift guard (content
  and exec-bit modes) into skills:validate so CI fails if the two
  diverge.
- Update docs/skills/task.md for no-arg list, bare-query run, and the
  create-offer.

Closes #33
Model-driven dispatch (SKILL.md -> detect.md -> list.md -> run.md)
made `/task detect` and `/task list` take ~2 minutes: every verb
walked several reference docs and ran ad-hoc bash the model
assembled per call. Replace that hot path with one shipped POSIX-sh
script so detection, listing, running, and the no-match offer happen
in a single deterministic process.

**Single shipped dispatcher**
- Add scripts/task.sh: one source of truth for runner detection
  (mise -> just -> Taskfile -> package.json -> none), listing, and
  running.
- No argument lists tasks; an unrecognised first token is treated as
  a run query and matched against the runner's task names; a query
  with no match returns NO_MATCH so the skill can offer to create a
  runner-native task entry instead of failing.
- SKILL.md maps every verb to a single Bash call; the three workflow
  docs shrink to thin references pointing at the script, so no
  executable bash remains on the hot path.

**Correct lookup signals**
- List names via `mise tasks ls --no-header` so the first task is no
  longer dropped by a header-skipping awk (mise 2026.3.x emits no
  header); dropping it made `/task <first>` wrongly offer to create
  an existing task.
- Capture enumeration stderr: a runner present but unable to
  enumerate (e.g. untrusted mise on a fresh clone) is reported as
  ENUM_FAILED, surfacing the trust error rather than offering to
  create the task.
- Two or more prefix/substring candidates return AMBIGUOUS with the
  candidate list so the query is disambiguated, not treated as
  no-match.
- Match literally via `grep -F` and a lowercased case-glob prefix;
  the query is never interpreted as a regex.

**/tasks alias with a drift guard**
- Ship skills/tasks/ as a generated mirror of canonical skills/task/
  (identical but for the name, `/tasks` trigger token, and SKILL.md
  H1), so both triggers share the same fast script.
- Add .mise-tasks/gen-task-alias to regenerate it and a
  skills:sync-task-alias task; fold a --check drift guard (content
  and exec-bit modes) into skills:validate so CI fails if the two
  diverge.
- Update docs/skills/task.md for no-arg list, bare-query run, and the
  create-offer.

Closes #33
@skapoor8
skapoor8 marked this pull request as ready for review July 19, 2026 19:41
@skapoor8
skapoor8 merged commit c95361b into main Jul 19, 2026
2 checks passed
@skapoor8
skapoor8 deleted the fix/task-speedup branch July 19, 2026 19:41
github-actions Bot pushed a commit that referenced this pull request Jul 19, 2026
## [1.66.1](v1.66.0...v1.66.1) (2026-07-19)

### Performance Improvements

* **task:** deterministic single-script runner dispatch + /tasks alias ([#33](#33))

Model-driven dispatch (SKILL.md -> detect.md -> list.md -> run.md)
made `/task detect` and `/task list` take ~2 minutes: every verb
walked several reference docs and ran ad-hoc bash the model
assembled per call. Replace that hot path with one shipped POSIX-sh
script so detection, listing, running, and the no-match offer happen
in a single deterministic process.

**Single shipped dispatcher**
- Add scripts/task.sh: one source of truth for runner detection
  (mise -> just -> Taskfile -> package.json -> none), listing, and
  running.
- No argument lists tasks; an unrecognised first token is treated as
  a run query and matched against the runner's task names; a query
  with no match returns NO_MATCH so the skill can offer to create a
  runner-native task entry instead of failing.
- SKILL.md maps every verb to a single Bash call; the three workflow
  docs shrink to thin references pointing at the script, so no
  executable bash remains on the hot path.

**Correct lookup signals**
- List names via `mise tasks ls --no-header` so the first task is no
  longer dropped by a header-skipping awk (mise 2026.3.x emits no
  header); dropping it made `/task <first>` wrongly offer to create
  an existing task.
- Capture enumeration stderr: a runner present but unable to
  enumerate (e.g. untrusted mise on a fresh clone) is reported as
  ENUM_FAILED, surfacing the trust error rather than offering to
  create the task.
- Two or more prefix/substring candidates return AMBIGUOUS with the
  candidate list so the query is disambiguated, not treated as
  no-match.
- Match literally via `grep -F` and a lowercased case-glob prefix;
  the query is never interpreted as a regex.

**/tasks alias with a drift guard**
- Ship skills/tasks/ as a generated mirror of canonical skills/task/
  (identical but for the name, `/tasks` trigger token, and SKILL.md
  H1), so both triggers share the same fast script.
- Add .mise-tasks/gen-task-alias to regenerate it and a
  skills:sync-task-alias task; fold a --check drift guard (content
  and exec-bit modes) into skills:validate so CI fails if the two
  diverge.
- Update docs/skills/task.md for no-arg list, bare-query run, and the
  create-offer.

Closes #33
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.66.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant