feat: skills listing, /-autocomplete palette, and skill invocation across harness + CLI + chat#756
Conversation
Builds on #748 (SKILL.md discovery). Makes Agent Skills listable, discoverable in the chat UI, and callable across harness + client + CLI + chat. Harness (miot-harness 0.1.0 -> 0.2.0): - GET /skills + ContextSkillsBundle.list_skills(tenant) -> SkillSummary (id, name, description, when_to_use, scope, source[skill_md|manifest]). - Skill invocation: UserRequest.skill_id; ContextSkillsBundle.activate_skill; the supervisor injects the chosen skill's SKILL.md body as a SystemMessage into prior_messages so every run path (direct/meta/data/agentic) follows it. Unknown/bodyless ids are ignored. @microboxlabs/miot-harness-client 0.2.0 -> 0.3.0: - skills.list() resource + SkillSummary type; skill_id on UserRequest. - sync HARNESS_EVENT_TYPES with the Python source of truth (approval.auto, steering.mode_denied) so the parity guard passes. @microboxlabs/miot-chat 0.1.1 -> 0.2.0: - Claude-Code-style "/" autocomplete palette wired into the Editor (scrolling window, wrapping descriptions, accent highlight; up/down select, Tab completes, Enter runs). - each harness skill registered as a /<skill-id> command, so "/" lists skills with descriptions and /<skill-id> [args] runs the skill. @microboxlabs/miot-cli 0.4.2 -> 0.5.0: - `miot harness skills` (table/json) and `miot harness create --skill <id>`. - dep range: @microboxlabs/miot-chat ^0.1.1 -> ^0.2.0. Closes #755 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 51 minutes and 11 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds skill listing and invocation plumbing across harness, client, chat, and CLI packages. The harness exposes ChangesSkills listing and invocation
Sequence Diagram(s)sequenceDiagram
participant App
participant UseSessionApi
participant RunsCreate as runs.create
participant HarnessSupervisor
participant ContextSkillsBundle
participant SystemMessage
App->>UseSessionApi: submit(prompt, { skillId })
UseSessionApi->>RunsCreate: POST with skill_id
RunsCreate->>HarnessSupervisor: build run request
HarnessSupervisor->>ContextSkillsBundle: activate_skill(tenant_id, skill_id)
ContextSkillsBundle-->>HarnessSupervisor: name, playbook_body / None
HarnessSupervisor->>SystemMessage: prepend active skill guidance
sequenceDiagram
participant App
participant HarnessClient
participant SkillsRoute as GET /skills
participant ContextSkillsBundle
App->>HarnessClient: skills.list(tenantId)
HarnessClient->>SkillsRoute: GET /skills?tenant=...
SkillsRoute->>ContextSkillsBundle: list_skills(tenant_id)
ContextSkillsBundle-->>SkillsRoute: SkillSummary[]
SkillsRoute-->>HarnessClient: JSON response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
App preview imageThe latest app preview image for this PR is ready.
|
…ript projector Adding these two literals to HARNESS_EVENT_TYPES (to fix the harness-client parity guard) made the `switch (event.type)` in transcript/project.ts non-exhaustive, so applyHarnessEvent could fall through without returning (TS2366, surfaced only on a clean check-types — local incremental cache hid it). Handle both as no-op status markers (like run.completed), restoring exhaustiveness. Verified with `turbo run check-types --force` (20/20). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
turbo-repo/packages/miot-chat/src/tui/__tests__/slash.skills.test.ts (1)
4-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace
unknown-typed test context with a concrete inferred shape.Line 4 uses
unknownin changed test helper types, which conflicts with the TS guideline and weakens type safety in this test path.As per coding guidelines, "turbo-repo/**/*.{ts,tsx}: Avoid
anyandunknowntypes; prefer type inference".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@turbo-repo/packages/miot-chat/src/tui/__tests__/slash.skills.test.ts` at line 4, The test helper mkCtx currently uses an unknown-typed parameter, which should be replaced with a concrete inferred shape to follow the TypeScript guideline. Update the helper and any related assertions in slash.skills.test.ts so the client/context type is derived from existing test data or local inference instead of unknown, keeping the test path type-safe without weakening the signature.Source: Coding guidelines
turbo-repo/packages/miot-chat/src/tui/input/Editor.tsx (1)
111-115: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSlash commands submitted via the palette aren't added to history.
While the palette is open (a
/commandtoken with no space), Enter takes this branch, which clears and submits but never callsappendHistory. Since the palette stays open for a fully-typed command like/help, submitting it no longer records it in history, so it can't be recalled with ↑ — a behavior change from the pre-palette SUBMIT path below (Line 122). Consider appending to history here for parity if command recall is desired.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@turbo-repo/packages/miot-chat/src/tui/input/Editor.tsx` around lines 111 - 115, The SUBMIT branch in Editor.tsx bypasses history recording when a slash command is chosen from the palette, so commands like /help cannot be recalled afterward. Update the SUBMIT handling in the Editor component to append the submitted command to history before clearing/submitting, matching the existing non-palette submit path and preserving command recall behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@miot-harness/src/miot_harness/context_skills/registry.py`:
- Around line 168-172: The source badge logic in the registry’s loaded-skill
handling can misclassify files because the current endswith check on
loaded.source_path matches any path whose name merely ends in skill.md. Update
the source selection near the loaded.source_path check to compare only the
basename, using the existing source badge branch in registry.py so that only a
real SKILL.md file is labeled skill_md and all other cases remain manifest.
In `@turbo-repo/packages/miot-chat/src/tui/__tests__/slash.skills.test.ts`:
- Around line 49-55: The empty-skill-set test in skillsCommand.handle currently
hides failures because the text assertion only runs inside an if block; update
the test to explicitly assert that r.output.kind is "system" before asserting
the output text, so it fails whenever handle returns a missing or non-system
output. Use the existing skillsCommand.handle and r.output checks in
slash.skills.test.ts to locate the test.
---
Nitpick comments:
In `@turbo-repo/packages/miot-chat/src/tui/__tests__/slash.skills.test.ts`:
- Line 4: The test helper mkCtx currently uses an unknown-typed parameter, which
should be replaced with a concrete inferred shape to follow the TypeScript
guideline. Update the helper and any related assertions in slash.skills.test.ts
so the client/context type is derived from existing test data or local inference
instead of unknown, keeping the test path type-safe without weakening the
signature.
In `@turbo-repo/packages/miot-chat/src/tui/input/Editor.tsx`:
- Around line 111-115: The SUBMIT branch in Editor.tsx bypasses history
recording when a slash command is chosen from the palette, so commands like
/help cannot be recalled afterward. Update the SUBMIT handling in the Editor
component to append the submitted command to history before clearing/submitting,
matching the existing non-palette submit path and preserving command recall
behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3a96b5e4-6098-46a9-aef4-190b1f79a200
⛔ Files ignored due to path filters (2)
miot-harness/uv.lockis excluded by!**/*.lockturbo-repo/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (32)
miot-harness/pyproject.tomlmiot-harness/src/miot_harness/api/server.pymiot-harness/src/miot_harness/context_skills/registry.pymiot-harness/src/miot_harness/context_skills/skill_models.pymiot-harness/src/miot_harness/runtime/context.pymiot-harness/src/miot_harness/runtime/supervisor.pymiot-harness/tests/api/test_skills.pymiot-harness/tests/context_skills/test_bundle_layering.pymiot-harness/tests/runtime/test_supervisor_skill.pyturbo-repo/packages/miot-chat/package.jsonturbo-repo/packages/miot-chat/src/tui/App.tsxturbo-repo/packages/miot-chat/src/tui/__tests__/components/Editor.test.tsxturbo-repo/packages/miot-chat/src/tui/__tests__/skillCommand.test.tsturbo-repo/packages/miot-chat/src/tui/__tests__/slash.skills.test.tsturbo-repo/packages/miot-chat/src/tui/__tests__/transcript.project.test.tsturbo-repo/packages/miot-chat/src/tui/input/Editor.tsxturbo-repo/packages/miot-chat/src/tui/input/keymap.tsturbo-repo/packages/miot-chat/src/tui/slash/Palette.tsxturbo-repo/packages/miot-chat/src/tui/slash/handlers/skills.tsturbo-repo/packages/miot-chat/src/tui/slash/skillCommand.tsturbo-repo/packages/miot-chat/src/tui/transcript/project.tsturbo-repo/packages/miot-chat/src/tui/useSession.tsturbo-repo/packages/miot-cli/package.jsonturbo-repo/packages/miot-cli/src/commands/harness/create.tsturbo-repo/packages/miot-cli/src/commands/harness/index.tsturbo-repo/packages/miot-cli/src/commands/harness/skills.tsturbo-repo/packages/miot-harness-client/package.jsonturbo-repo/packages/miot-harness-client/src/__tests__/skills.test.tsturbo-repo/packages/miot-harness-client/src/client.tsturbo-repo/packages/miot-harness-client/src/index.tsturbo-repo/packages/miot-harness-client/src/resources/skills.tsturbo-repo/packages/miot-harness-client/src/types.ts
- registry.py: match SKILL.md by basename, not suffix, so the `source` badge can't misclassify files like `foo-skill.md` as `skill_md`. - slash.skills.test.ts: assert `r.output?.kind === "system"` before the conditional so the empty-state test fails on missing/wrong output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a first-class “skills” flow across the harness API, TypeScript client, CLI, and chat TUI so discovered SKILL.md directory skills become listable, discoverable via / palette UI, and invokable per-run via skill_id.
Changes:
- Harness:
GET /skillsreturningSkillSummary, plus per-run skill activation that injects the skill’sSKILL.mdbody asSystemMessageguidance. - TS client + CLI: new
skills.list()resource andmiot harness skills/miot harness create --skill <id>support. - Chat TUI:
/autocomplete palette,/skillshandler, and/<skill-id> [args]skill invocation.
Reviewed changes
Copilot reviewed 32 out of 34 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| turbo-repo/packages/miot-harness-client/src/types.ts | Adds skill_id to UserRequest, extends event types, and introduces SkillSummary type. |
| turbo-repo/packages/miot-harness-client/src/resources/skills.ts | New client.skills.list() wrapper for GET /skills. |
| turbo-repo/packages/miot-harness-client/src/index.ts | Re-exports SkillSummary. |
| turbo-repo/packages/miot-harness-client/src/client.ts | Wires skills resource into the client. |
| turbo-repo/packages/miot-harness-client/src/tests/skills.test.ts | Tests skills.list() URL building and tenant query parameter. |
| turbo-repo/packages/miot-harness-client/package.json | Bumps harness-client version to 0.3.0. |
| turbo-repo/packages/miot-cli/src/commands/harness/skills.ts | Adds miot harness skills command (table/json output). |
| turbo-repo/packages/miot-cli/src/commands/harness/index.ts | Registers the new skills subcommand. |
| turbo-repo/packages/miot-cli/src/commands/harness/create.ts | Adds --skill <id> flag and forwards to skill_id in run create. |
| turbo-repo/packages/miot-cli/package.json | Bumps CLI version + updates miot-chat dependency range. |
| turbo-repo/packages/miot-chat/src/tui/useSession.ts | Allows submit(prompt, { skillId }) to send skill_id to harness. |
| turbo-repo/packages/miot-chat/src/tui/transcript/project.ts | Treats new harness event types as transcript no-ops (exhaustive switch). |
| turbo-repo/packages/miot-chat/src/tui/slash/skillCommand.ts | Adds parsing for /<skill-id> [args] into a skill run. |
| turbo-repo/packages/miot-chat/src/tui/slash/Palette.tsx | Implements scrollable, wrapped-description slash palette rendering. |
| turbo-repo/packages/miot-chat/src/tui/slash/handlers/skills.ts | Adds /skills handler that queries harness and prints formatted list. |
| turbo-repo/packages/miot-chat/src/tui/input/keymap.ts | Maps Tab to a COMPLETE action for slash autocomplete. |
| turbo-repo/packages/miot-chat/src/tui/input/Editor.tsx | Adds slash autocomplete interactions (open, navigate, tab-complete, enter-run). |
| turbo-repo/packages/miot-chat/src/tui/App.tsx | Fetches skills, registers them into the palette, and routes /<skill-id> to skill runs. |
| turbo-repo/packages/miot-chat/src/tui/tests/transcript.project.test.ts | Adds regression test for new no-op event types. |
| turbo-repo/packages/miot-chat/src/tui/tests/slash.skills.test.ts | Tests /skills handler behavior and error handling. |
| turbo-repo/packages/miot-chat/src/tui/tests/skillCommand.test.ts | Tests matchSkillRun parsing and fallthrough behavior. |
| turbo-repo/packages/miot-chat/src/tui/tests/components/Editor.test.tsx | Adds tests for palette open/state, enter-run, tab-complete, and no-registry behavior. |
| turbo-repo/packages/miot-chat/package.json | Bumps chat version to 0.2.0. |
| turbo-repo/package-lock.json | Updates workspace lockfile for new versions/deps. |
| miot-harness/uv.lock | Bumps harness Python package version to 0.2.0. |
| miot-harness/tests/runtime/test_supervisor_skill.py | Adds tests for supervisor skill injection behavior. |
| miot-harness/tests/context_skills/test_bundle_layering.py | Adds tests for list_skills and activate_skill. |
| miot-harness/tests/api/test_skills.py | Adds API tests for GET /skills including tenant scoping. |
| miot-harness/src/miot_harness/runtime/supervisor.py | Injects activated skill guidance as a SystemMessage before routing. |
| miot-harness/src/miot_harness/runtime/context.py | Adds skill_id to UserRequest model. |
| miot-harness/src/miot_harness/context_skills/skill_models.py | Introduces SkillSummary API model. |
| miot-harness/src/miot_harness/context_skills/registry.py | Adds list_skills() and activate_skill() to the skills bundle. |
| miot-harness/src/miot_harness/api/server.py | Adds GET /skills endpoint with tenant precedence rules. |
| miot-harness/pyproject.toml | Bumps harness version to 0.2.0. |
Files not reviewed (1)
- turbo-repo/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "node_modules/@eslint/plugin-kit": { | ||
| "version": "0.4.1", | ||
| "version": "0.4.2", | ||
| "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", | ||
| "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", |
- skills.ts: tighten isSkillsCtx to verify skills.list is callable and a tenantId is present (not just key existence), so a partial/older client yields the clean "not bound" error instead of a thrown TypeError. - package-lock.json: fix @eslint/plugin-kit entry — version was bumped to a nonexistent 0.4.2 by an audit-fix while resolved/integrity stayed at 0.4.1; pin version back to 0.4.1 so the entry is internally consistent (deterministic installs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
| name, body = activated | ||
| guidance = SystemMessage( | ||
| content=( | ||
| f"# Active skill: {name}\n\n" | ||
| f'The user invoked the "{name}" skill. Follow these ' | ||
| f"instructions for this run:\n\n{body}" | ||
| ) | ||
| ) | ||
| return [guidance, *prior_messages] |



What & why
Follow-up to #748 (SKILL.md discovery, merged). #748 made the harness discover Agent Skills; this makes them listable, discoverable in the chat UI, and callable — closing the gap that you couldn't actually call a loaded skill.
Changes
Harness (
miot-harness0.1.0 → 0.2.0)GET /skills+ContextSkillsBundle.list_skills(tenant)→SkillSummary(id,name,description,when_to_use,scope,source=skill_md|manifest).UserRequest.skill_id;activate_skill(tenant, id); the supervisor injects the chosen skill'sSKILL.mdbody as aSystemMessageintoprior_messages, so every run path (direct/meta/data/agentic) follows it. Unknown/bodyless ids are ignored.@microboxlabs/miot-harness-client0.2.0 → 0.3.0client.skills.list()resource +SkillSummarytype;skill_idonUserRequest.HARNESS_EVENT_TYPESwith the Python source of truth (approval.auto,steering.mode_denied) — fixes the pre-existing parity-guard failure so the package tests are green for release.@microboxlabs/miot-chat0.1.1 → 0.2.0/autocomplete palette wired into the Editor (scrolling window, wrapping descriptions, accent highlight; ↑/↓ select, Tab completes, Enter runs)./<skill-id>command →/lists skills with descriptions and/<skill-id> [args]runs the skill.@microboxlabs/miot-cli0.4.2 → 0.5.0miot harness skills(table/json) andmiot harness create --skill <id>. Dep rangemiot-chat^0.1.1→^0.2.0.Test plan
pytest249 passed / 1 skipped;ruff+mypyclean. New: bundlelist_skills/activate_skill,GET /skills(3), supervisor_inject_skill(4).miot-harness-client: 24 passed (incl.skills.list); parity restored.miot-chat: 402 passed (Palette, Editor palette nav,matchSkillRun,/skillshandler).miot-cli: 70 passed. tsc + lint clean across all three; dists build./lists skills with descriptions;/skill-creator <args>andmiot harness create --skill skill-creatorboth make the agent follow the skill's SKILL.md.Release
<pkg>@v*tags — push in dependency order after merge:miot-harness-client@v0.3.0→miot-chat@v0.2.0→miot-cli@v0.5.0.harness.yamlrebuildsghcr.io/microboxlabs/miot-harness:latest(+sha-) on trunk merge; a version tag produces:<version>.Closes #755
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
GET /skills) with tenant scoping.skillsCLI subcommand, plusharness create --skill <id>to start runs with a specific skill./<skill-id>slash routing.Bug Fixes
Chores