-
Notifications
You must be signed in to change notification settings - Fork 2
Embed agent skills synced from muxinc/mux-skills #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
49ceea4
82c6ab1
0b2a803
08c4415
ecd67db
38aea94
8eaaf91
a2819b9
dd2fbf4
e9efd79
dda5b08
355edb4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| name: Sync agent skills | ||
|
|
||
| # Keeps skill/ in sync with the agent skills published in muxinc/skills. | ||
| # Triggered by a repository_dispatch from that repo on skill changes, with a | ||
| # weekly cron as a backstop and workflow_dispatch for manual runs. Opens a PR | ||
| # only when the synced content actually changed. | ||
| # | ||
| # EMBEDDED_SKILLS is an allowlist: muxinc/skills also publishes skills with | ||
| # vendored reference docs (skills/mux), which the CLI intentionally does not | ||
| # embed — the CLI ships routing skills only, never docs content. | ||
|
|
||
| on: | ||
| repository_dispatch: | ||
| types: [skills-updated] | ||
| workflow_dispatch: | ||
| schedule: | ||
| - cron: '17 6 * * 1' | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
|
|
||
| env: | ||
| EMBEDDED_SKILLS: mux-docs | ||
|
|
||
| jobs: | ||
| sync: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Check out muxinc/skills | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: muxinc/skills | ||
| path: .skills-src | ||
|
|
||
| - uses: oven-sh/setup-bun@v2 | ||
|
|
||
| - name: Copy skills into skill/ | ||
| run: | | ||
| set -euo pipefail | ||
| rm -rf skill | ||
| mkdir -p skill | ||
| for name in $EMBEDDED_SKILLS; do | ||
| test -f ".skills-src/skills/$name/SKILL.md" | ||
| cp -R ".skills-src/skills/$name" "skill/$name" | ||
| done | ||
| SKILLS_SHA=$(git -C .skills-src log -1 --format=%H -- $(printf "skills/%s " $EMBEDDED_SKILLS)) | ||
| printf '{\n "source": "muxinc/skills",\n "commit": "%s"\n}\n' "$SKILLS_SHA" > skill/manifest.json | ||
| rm -rf .skills-src | ||
|
|
||
| - name: Regenerate embedded skills module | ||
| run: bun scripts/generate-embedded-skills.ts | ||
|
|
||
| - name: Verify embedded module matches skill/ | ||
| run: bun test src/lib/embedded-skills.test.ts | ||
|
|
||
| - name: Open pull request | ||
| uses: peter-evans/create-pull-request@v6 | ||
| with: | ||
| commit-message: 'chore: sync agent skills from muxinc/skills' | ||
| title: 'Sync agent skills from muxinc/skills' | ||
| body: | | ||
| Automated sync of the agent skills embedded in the CLI from | ||
| [muxinc/skills](https://github.com/muxinc/skills). The source | ||
| commit is recorded in `skill/manifest.json`. | ||
|
|
||
| The next tagged release will embed these files in the compiled | ||
| binaries via `src/lib/embedded-skills.gen.ts`. | ||
| branch: chore/sync-agent-skills | ||
| delete-branch: true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sync PRs skip CIMedium Severity
Reviewed by Cursor Bugbot for commit a2819b9. Configure here. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| #!/usr/bin/env bun | ||
| /** | ||
| * Generates src/lib/embedded-skills.gen.ts from the contents of skill/. | ||
| * | ||
| * The skill/ directory is synced from muxinc/skills by the sync-skills | ||
| * workflow. Embedding the files as string constants means every build target | ||
| * (bun run, dist bundle, compiled binary) ships the skills with no runtime | ||
| * filesystem or network dependency. | ||
| * | ||
| * Run with: pnpm run generate:skills | ||
| */ | ||
| import { readdir } from 'node:fs/promises'; | ||
| import { join } from 'node:path'; | ||
|
|
||
| const repoRoot = join(import.meta.dir, '..'); | ||
| const skillDir = join(repoRoot, 'skill'); | ||
| const outPath = join(repoRoot, 'src', 'lib', 'embedded-skills.gen.ts'); | ||
|
|
||
| const entries = await readdir(skillDir, { | ||
| recursive: true, | ||
| withFileTypes: true, | ||
| }); | ||
|
|
||
| const files: Record<string, string> = {}; | ||
| for (const entry of entries) { | ||
| if (!entry.isFile()) continue; | ||
| const absolute = join(entry.parentPath, entry.name); | ||
| const relative = absolute.slice(skillDir.length + 1); | ||
| files[relative] = await Bun.file(absolute).text(); | ||
| } | ||
|
|
||
| const sortedPaths = Object.keys(files).sort(); | ||
| if (sortedPaths.length === 0) { | ||
| console.error( | ||
| 'No files found in skill/ — refusing to generate an empty module.', | ||
| ); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const lines = [ | ||
| '// Generated by scripts/generate-embedded-skills.ts from skill/ — do not edit.', | ||
| '// Regenerate with: pnpm run generate:skills', | ||
| '', | ||
| 'export const EMBEDDED_SKILLS: Record<string, string> = {', | ||
| ...sortedPaths.map( | ||
| (path) => ` ${JSON.stringify(path)}: ${JSON.stringify(files[path])},`, | ||
| ), | ||
| '};', | ||
| '', | ||
| ]; | ||
|
|
||
| await Bun.write(outPath, lines.join('\n')); | ||
| console.log(`Embedded ${sortedPaths.length} files into ${outPath}`); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "source": "muxinc/skills", | ||
| "commit": "99ff3093cf76a57a6745d04f2e45622175034411" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| --- | ||
| name: mux-docs | ||
| description: Use for any question about Mux APIs, SDKs, the mux CLI, webhooks, assets, uploads, playback, live streaming, Mux Data, or Mux Robots. Finds the current Mux documentation — Mux publishes every docs page as LLM-ready markdown indexed at mux.com/llms.txt — so answers come from today's published docs instead of web search or model memory. | ||
| --- | ||
|
|
||
| # Mux docs discovery | ||
|
|
||
| Mux publishes its entire documentation as agent-ready markdown, auto-generated from the same source as the real docs site. For any Mux question, fetch the relevant page and answer from it — never answer Mux API questions from memory, because API shapes and guidance change. | ||
|
|
||
| ## Workflow | ||
|
|
||
| 1. **Route — try these in order and stop at the first hit.** The order is cost-ordered: each step reads fewer tokens than the next. | ||
| 1. If the `mux` CLI is installed, run `mux docs find "<topic>" --agent` — it searches the docs index CLI-side and returns only the matching page URLs, so you never read an index into context. (Check `mux docs --help`; older versions lack it.) | ||
| 2. Construct the URL from a known pattern: the tables below (collections, Robots `robots-<task>.md`, API references) cover most topics. | ||
| 3. Fetch the smallest collection index for the topic area (a few dozen lines). | ||
| 4. Fetch https://www.mux.com/llms.txt — the master index of every docs page — only when the steps above fail. | ||
| 2. **Fetch ONE page** — the page's markdown version: append `.md` to its URL (e.g. `https://www.mux.com/docs/guides/start-live-streaming.md`). Only add an API reference bundle when you need exact request/response shapes; do not fetch extra pages "for context." | ||
| 3. **Answer from the fetched content** and cite the page URL. | ||
|
|
||
| **Never fetch https://www.mux.com/llms-full.txt.** It is the entire documentation in a single file and will flood the context window; every page in it is reachable individually through the steps above. | ||
|
|
||
| ## Collection indexes | ||
|
|
||
| | Collection | URL | | ||
| | --- | --- | | ||
| | Core concepts | https://www.mux.com/docs/core.txt | | ||
| | Video: upload, encode, manage assets | https://www.mux.com/docs/guides/video.txt | | ||
| | Uploader: handle user uploads | https://www.mux.com/docs/guides/uploader.txt | | ||
| | Player: embed video playback | https://www.mux.com/docs/guides/player.txt | | ||
| | Data: playback quality and analytics | https://www.mux.com/docs/guides/data.txt | | ||
| | Data integrations: third-party player monitoring | https://www.mux.com/docs/guides/data-integrations.txt | | ||
| | Framework integrations and SDKs | https://www.mux.com/docs/integrations.txt | | ||
| | Example implementations | https://www.mux.com/docs/examples.txt | | ||
| | Pricing and billing | https://www.mux.com/docs/pricing.txt | | ||
|
|
||
|
|
||
| ## Mux Robots (AI video workflows) | ||
|
|
||
| Robots — Mux's AI-powered video workflows — don't have their own collection index; their guides are part of the video collection and the master llms.txt index. Start here: | ||
|
|
||
| | Resource | URL | | ||
| | --- | --- | | ||
| | Robots overview | https://www.mux.com/docs/guides/robots.md | | ||
| | Robots directives reference | https://www.mux.com/docs/guides/robots-directives.md | | ||
|
|
||
| Task-specific guides follow the pattern `https://www.mux.com/docs/guides/robots-<task>.md`, where `<task>` is one of: `summarize`, `moderate`, `generate-chapters`, `ask-questions`, `find-key-moments`, `find-scenes`, `find-best-thumbnails`, `translate-captions`, `translate-audio`, `generate-premium-captions`, `edit-captions`, `generate-engagement-insights`. | ||
|
|
||
| ## API references and specs | ||
|
|
||
| For exact endpoint, parameter, and webhook shapes, prefer these over prose guides: | ||
|
|
||
| | Resource | URL | | ||
| | --- | --- | | ||
| | Video API reference | https://www.mux.com/docs/api-reference/video.txt | | ||
| | Data API reference | https://www.mux.com/docs/api-reference/data.txt | | ||
| | System API reference | https://www.mux.com/docs/api-reference/system.txt | | ||
| | Full API spec (OpenAPI JSON) | https://www.mux.com/api-spec.json | | ||
| | Webhook spec (JSON) | https://www.mux.com/webhook-spec.json | | ||
| | Mux Player web component API | https://raw.githubusercontent.com/muxinc/elements/refs/heads/main/packages/mux-player/REFERENCE.md | | ||
| | Mux Player React component API | https://raw.githubusercontent.com/muxinc/elements/refs/heads/main/packages/mux-player-react/REFERENCE.md | | ||
|
|
||
| All of these resources are also discoverable from the docs site itself: every docs page links to "Docs for LLMs" (https://www.mux.com/docs/core/llms-txt), the canonical page describing Mux's llms.txt files and machine-readable bundles. If this skill's URL lists ever drift from reality, that page is the source of truth. | ||
|
|
||
| ## Self-healing | ||
|
|
||
| If a docs URL 404s, the docs have likely been reorganized. Do not give up after one 404: run `mux docs find "<topic>" --agent` (or fetch https://www.mux.com/llms.txt and search it) to get the page's current URL. | ||
|
|
||
| ## Staying in sync | ||
|
|
||
| - This skill is distributed from the `muxinc/skills` repository and ships embedded in the `mux` CLI. Updating the CLI (`brew upgrade mux` / `npm update -g @mux/cli`) updates the embedded skills, and `mux skills update` refreshes all local copies (including `~/.claude/skills`) to match the installed version. For skills decoupled from CLI releases, install the `mux@mux` Claude Code plugin or pull directly from https://github.com/muxinc/skills. | ||
| - The CLI embeds only skill instruction files, never docs content — always fetch docs live using the workflow above. Check `mux skills --help` for what the installed version offers. | ||
|
|
||
| ## Guardrails | ||
|
|
||
| - Cite the docs page URL you answered from. | ||
| - If the network is unavailable, say you could not verify against current docs — never guess API request/response shapes from memory. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||
| import { Command } from '@cliffy/command'; | ||||||
| import { handleCommandError } from '@/lib/errors.ts'; | ||||||
| import { | ||||||
| fetchDocsIndex, | ||||||
| parseDocsIndex, | ||||||
| searchDocsIndex, | ||||||
| } from '../../lib/docs-index.ts'; | ||||||
|
|
||||||
| interface DocsFindOptions { | ||||||
| json?: boolean; | ||||||
| limit: number; | ||||||
| } | ||||||
|
|
||||||
| export const docsFindCommand = new Command() | ||||||
| .description( | ||||||
| 'Search the live Mux docs index (mux.com/llms.txt) and print matching page URLs.\n\nSearches CLI-side so agents get page URLs without reading the index into context. Fetch the returned URL for the page content; no docs are stored locally.', | ||||||
| ) | ||||||
| .arguments('<query...:string>') | ||||||
| .option('--limit <n:number>', 'Maximum number of results', { default: 5 }) | ||||||
| .option('--json', 'Output JSON instead of pretty format') | ||||||
| .action(async (options: DocsFindOptions, ...query: string[]) => { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Variadic query args mishandledHigh Severity Cliffy passes a named variadic
Suggested change
Reviewed by Cursor Bugbot for commit 355edb4. Configure here. |
||||||
| try { | ||||||
| const text = await fetchDocsIndex(); | ||||||
| const entries = parseDocsIndex(text); | ||||||
| const results = searchDocsIndex(entries, query.join(' '), options.limit); | ||||||
|
|
||||||
| if (options.json) { | ||||||
| console.log( | ||||||
| JSON.stringify({ query: query.join(' '), results }, null, 2), | ||||||
| ); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| if (results.length === 0) { | ||||||
| console.log( | ||||||
| 'No matching docs pages. Try different terms, or browse https://www.mux.com/llms.txt', | ||||||
| ); | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| for (const result of results) { | ||||||
| console.log(result.url); | ||||||
| if (result.description) { | ||||||
| console.log(` ${result.description}`); | ||||||
| } | ||||||
| } | ||||||
| } catch (error) { | ||||||
| await handleCommandError(error, 'docs', 'find', options); | ||||||
| } | ||||||
| }); | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { Command } from '@cliffy/command'; | ||
| import { docsFindCommand } from './find.ts'; | ||
|
|
||
| // biome-ignore lint/suspicious/noExplicitAny: Cliffy's chained types are too complex for TS to infer | ||
| export const docsCommand: Command<any> = new Command() | ||
| .description( | ||
| 'Search the live Mux documentation index.\n\nNo docs are stored locally — `find` searches mux.com/llms.txt and prints current page URLs.', | ||
| ) | ||
| .action(function () { | ||
| this.showHelp(); | ||
| }) | ||
| .command('find', docsFindCommand); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { Command } from '@cliffy/command'; | ||
| import { skillsInstallCommand } from './install.ts'; | ||
| import { skillsPathCommand } from './path.ts'; | ||
| import { skillsUpdateCommand } from './update.ts'; | ||
|
|
||
| // biome-ignore lint/suspicious/noExplicitAny: Cliffy's chained types are too complex for TS to infer | ||
| export const skillsCommand: Command<any> = new Command() | ||
| .description( | ||
| 'Locate or install the agent skills embedded in this CLI build.\n\nFor agents and other tooling, start with `mux skills path --json`.', | ||
| ) | ||
| .action(function () { | ||
| this.showHelp(); | ||
| }) | ||
| .command('path', skillsPathCommand) | ||
| .command('install', skillsInstallCommand) | ||
| .command('update', skillsUpdateCommand); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { join } from 'node:path'; | ||
| import { Command } from '@cliffy/command'; | ||
| import { handleCommandError } from '@/lib/errors.ts'; | ||
| import { | ||
| getDefaultAgentSkillsDir, | ||
| getSkillsManifest, | ||
| installSkills, | ||
| listSkills, | ||
| } from '../../lib/embedded-skills.ts'; | ||
|
|
||
| interface DocsInstallOptions { | ||
| dir: string; | ||
| json?: boolean; | ||
| } | ||
|
|
||
| export const skillsInstallCommand = new Command() | ||
| .description( | ||
| 'Install the embedded agent skills into an agent skills directory.\n\nDefaults to ~/.claude/skills, which Claude Code loads automatically — no CLAUDE.md or AGENTS.md changes needed. Run `mux skills update` after upgrading the CLI to refresh the installed copy.', | ||
| ) | ||
| .option('--dir <path:string>', 'Target skills directory', { | ||
| default: getDefaultAgentSkillsDir(), | ||
| }) | ||
| .option('--json', 'Output JSON instead of pretty format') | ||
| .action(async (options: DocsInstallOptions) => { | ||
| try { | ||
| const { dir } = await installSkills(options.dir); | ||
| const manifest = getSkillsManifest(); | ||
| const skills = listSkills().map((skill) => ({ | ||
| name: skill.name, | ||
| path: join(dir, skill.path), | ||
| })); | ||
|
|
||
| if (options.json) { | ||
| console.log( | ||
| JSON.stringify( | ||
| { skills_dir: dir, source: manifest, skills }, | ||
| null, | ||
| 2, | ||
| ), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| console.log(`Installed ${skills.length} agent skills to ${dir}`); | ||
| console.log( | ||
| `Synced from: ${manifest.source}@${manifest.commit.slice(0, 7)}`, | ||
| ); | ||
| console.log(''); | ||
| for (const skill of skills) { | ||
| console.log(` ${skill.name}`); | ||
| } | ||
| console.log(''); | ||
| console.log( | ||
| 'Claude Code loads these automatically in new sessions. Run `mux skills update` after upgrading the CLI to refresh this copy.', | ||
| ); | ||
| } catch (error) { | ||
| await handleCommandError(error, 'skills', 'install', options); | ||
| } | ||
| }); |


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shallow clone empties skills SHA
High Severity
actions/checkoutdefaults tofetch-depth: 1, but the sync step runsgit logwith a pathspec to find the last commit that touched the allowlisted skills. In a shallow clone that returns empty whenever HEAD did not touch those paths, soskill/manifest.jsongets"commit": ""and the embedded-skills test fails. Weekly cron and manual dispatch are especially exposed.Additional Locations (1)
.github/workflows/sync-skills.yml#L48-L50Reviewed by Cursor Bugbot for commit 355edb4. Configure here.