diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index c1e475b41..b7960fbba 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added + +- Added built-in skills shipped with prime-agent, starting with `prime-intellect`: ecosystem knowledge and prime CLI workflows for verifiers environments, evaluations, Hosted Training, sandboxes, inference, and compute. Built-in skills have the lowest precedence (user, project, and package skills with the same name win) and can be disabled with the `enableBuiltinSkills` setting or `--no-skills`. + ### Changed - Changed interactive `Ctrl+C` to interrupt the current operation first and exit only on a second press while the exit hint is visible; `Escape` now clears the input bar without interrupting the agent. diff --git a/packages/coding-agent/docs/settings.md b/packages/coding-agent/docs/settings.md index b83605a6a..a22246638 100644 --- a/packages/coding-agent/docs/settings.md +++ b/packages/coding-agent/docs/settings.md @@ -211,6 +211,7 @@ Paths in `~/.pi/agent/settings.json` resolve relative to `~/.pi/agent`. Paths in | `prompts` | string[] | `[]` | Local prompt template paths or directories | | `themes` | string[] | `[]` | Local theme file paths or directories | | `enableSkillCommands` | boolean | `true` | Register skills as `/skill:name` commands | +| `enableBuiltinSkills` | boolean | `true` | Load built-in skills shipped with prime-agent | Arrays support glob patterns and exclusions. Use `!pattern` to exclude. Use `+path` to force-include an exact path and `-path` to force-exclude an exact path. diff --git a/packages/coding-agent/docs/skills.md b/packages/coding-agent/docs/skills.md index 283cb4488..7a749d335 100644 --- a/packages/coding-agent/docs/skills.md +++ b/packages/coding-agent/docs/skills.md @@ -9,6 +9,7 @@ Prime Agent implements the [Agent Skills standard](https://agentskills.io/specif ## Table of Contents - [Locations](#locations) +- [Built-in Skills](#built-in-skills) - [How Skills Work](#how-skills-work) - [Python-Backed Skills](#python-backed-skills) - [Skill Commands](#skill-commands) @@ -33,6 +34,7 @@ Prime Agent loads skills from: - Packages: `skills/` directories or `pi.skills` entries in `package.json` - Settings: `skills` array with files or directories - CLI: `--skill ` (repeatable, additive even with `--no-skills`) +- Built-in: `skills/` shipped with the prime-agent package (lowest precedence) Discovery rules: - In `~/.prime/agent/skills/` and `.prime/agent/skills/`, direct root `.md` files are discovered as individual skills @@ -41,6 +43,30 @@ Discovery rules: Disable discovery with `--no-skills` (explicit `--skill` paths still load). +## Built-in Skills + +Prime Agent ships with built-in skills that teach the agent the Prime Intellect ecosystem: + +- `prime-intellect` - Prime Intellect products and workflows via the prime CLI: verifiers environments and the Environments Hub, evaluations (local and hosted), Hosted Training and prime-rl, sandboxes, tunnels, Prime Inference, GPU compute, and storage. Reference docs for each area load on demand from the skill's `references/` directory. + +Built-in skills behave like any other skill but have the lowest precedence: a user, project, or package skill with the same name overrides the built-in one. + +To disable all built-in skills, set `enableBuiltinSkills` to `false` in `settings.json` (or toggle "Built-in skills" in `/settings`): + +```json +{ + "enableBuiltinSkills": false +} +``` + +`--no-skills` also excludes them. To disable a single built-in skill, force-exclude it in the global `skills` array (patterns resolve against the built-in skills directory): + +```json +{ + "skills": ["-prime-intellect/SKILL.md"] +} +``` + ### Using Skills from Other Harnesses To use skills from Claude Code or OpenAI Codex, add their directories to settings: diff --git a/packages/coding-agent/package.json b/packages/coding-agent/package.json index d966b98bf..b921a1c67 100644 --- a/packages/coding-agent/package.json +++ b/packages/coding-agent/package.json @@ -26,6 +26,7 @@ "dist", "docs", "examples", + "skills", "postinstall.cjs", "CHANGELOG.md" ], @@ -35,7 +36,7 @@ "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js && npm run copy-assets", "build:binary": "npm --prefix ../tui run build && npm --prefix ../ai run build && npm --prefix ../agent run build && npm run build && bun build --compile ./dist/bun/cli.js --outfile dist/pi && npm run copy-binary-assets", "copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/modes/interactive/assets && shx cp src/modes/interactive/assets/*.png dist/modes/interactive/assets/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/ && shx rm -rf dist/prime-agent-runtime && shx cp -r ../../prime-agent-runtime dist/prime-agent-runtime", - "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/", + "copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/assets && shx cp src/modes/interactive/assets/*.png dist/assets/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp -r skills dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/", "test": "vitest --run", "postinstall": "node postinstall.cjs", "prepublishOnly": "npm run clean && npm run build" diff --git a/packages/coding-agent/skills/prime-intellect/SKILL.md b/packages/coding-agent/skills/prime-intellect/SKILL.md new file mode 100644 index 000000000..6b0517d44 --- /dev/null +++ b/packages/coding-agent/skills/prime-intellect/SKILL.md @@ -0,0 +1,87 @@ +--- +name: prime-intellect +description: Work with Prime Intellect products via the prime CLI and Python SDKs - verifiers environments and the Environments Hub, evaluations (local and hosted), Hosted Training and prime-rl, code sandboxes, Prime Inference, GPU compute (pods and clusters), storage, and tunnels. Use when a task involves Prime Intellect, the prime CLI, verifiers, RL environments, evals, training, sandboxes, renting GPUs, Prime Inference models, or when the user asks what Prime Intellect is or what it offers. +--- + +# Prime Intellect + +Prime Intellect is an open superintelligence lab building open-source AGI infrastructure: a platform for RL environments, evaluations, post-training, inference, and globally distributed GPU compute. Prime Agent (this agent) is built by Prime Intellect, and the `prime` CLI is the default way to interface with every product below. + +## Product Map + +| Product | What it is | Details | +|---|---|---| +| verifiers | Python library for building LLM environments and evaluations | [environments.md](references/environments.md) | +| Environments Hub | Platform library of community RL environments (`prime env`) | [environments.md](references/environments.md) | +| Hosted Evaluations | Run evals on Prime-managed infra (`prime eval run --hosted`) | [environments.md](references/environments.md) | +| Hosted Training | Post-train models against environments (`prime train`, Lab) | [environments.md](references/environments.md) | +| prime-rl | Large-scale async RL framework for self-managed training | [environments.md](references/environments.md) | +| Sandboxes | Secure disposable Docker environments for AI-generated code | [sandboxes.md](references/sandboxes.md) | +| Tunnels | Public HTTPS URLs for local/sandboxed services | [sandboxes.md](references/sandboxes.md) | +| Inference | OpenAI-compatible API for frontier models | [inference.md](references/inference.md) | +| Compute | Rent single GPU pods or multi-node clusters | [compute.md](references/compute.md) | +| Storage | Persistent disks shared between instances | [compute.md](references/compute.md) | + +## Prime CLI Setup + +Default to the `prime` CLI for all Prime Intellect operations. If it is not installed: + +```bash +uv tool install prime # or: pip install prime +prime login # browser auth; or: prime config set-api-key +prime config view # verify configuration +``` + +The same package provides the Python SDKs (e.g. `prime_sandboxes`, `prime_tunnel`). Source: https://github.com/PrimeIntellect-ai/prime + +## Live Documentation + +Authoritative, current docs live at https://docs.primeintellect.ai. Any docs page is fetchable as Markdown by appending `.md` to its URL, and the full index is at https://docs.primeintellect.ai/llms.txt. When you need details not covered here (exact flags, API schemas, pricing, new features), fetch the live docs instead of guessing: + +```bash +curl -s https://docs.primeintellect.ai/llms.txt # discover pages +curl -s https://docs.primeintellect.ai/sandboxes/overview.md # fetch a page as markdown +``` + +The REST API is documented under `api-reference/` pages (OpenAPI spec: https://api.primeintellect.ai/openapi.json), with `https://api.primeintellect.ai` as the base URL. + +## Command Quick Reference + +```bash +# Environments Hub +prime env list --search "math" # discover environments +prime env info owner/name # inspect one +prime env install owner/name # install locally +prime env init my-env --v1 # scaffold a new environment +prime env push # publish to the Hub + +# Evaluations +prime eval run my-env -m openai/gpt-4.1-mini -n 5 # local smoke eval +prime eval run owner/env --hosted --follow # hosted eval with logs + +# Training +prime lab setup # set up a Lab workspace (Hosted Training) +prime train models # models, capacity, pricing +prime train init && prime train rl.toml # configure + launch a run + +# Sandboxes +prime sandbox create python:3.11-slim --timeout-minutes 120 +prime sandbox run "python --version" +prime sandbox delete + +# Inference +prime inference models # list available models + +# Compute +prime availability list # GPU availability + pricing +prime pods create # provision a pod +prime pods ssh # SSH in (needs: prime config set-ssh-key-path) +``` + +## Working Conventions + +- Prefer ecosystem-native paths (`prime env init`, `prime eval run`, `prime lab setup`) over custom scaffolding. +- Smoke-test small (`-n 5`) before scaling evals or training; keep default result uploads unless the user explicitly opts out. +- For non-trivial eval/training work, ask whether the user wants instruct models (`gpt-4.1` series, `qwen3` instruct) or reasoning models (`gpt-5` series, `qwen3` thinking, `glm` series). +- Hosted Training launches from a CPU machine; self-managed `prime-rl` requires local GPU access and is a power-user path. +- The dashboard at https://app.primeintellect.ai covers API keys, billing, teams, and anything the CLI does not. diff --git a/packages/coding-agent/skills/prime-intellect/references/compute.md b/packages/coding-agent/skills/prime-intellect/references/compute.md new file mode 100644 index 000000000..19b6fe21e --- /dev/null +++ b/packages/coding-agent/skills/prime-intellect/references/compute.md @@ -0,0 +1,45 @@ +# Compute & Storage + +Prime Intellect aggregates GPU capacity across providers: rent single GPU pods on demand or deploy multi-node clusters, with persistent network-attached storage. + +Live docs: `cli-reference/check-gpu-availability.md`, `cli-reference/provision-gpu.md`, `cli-reference/managing-disks.md`, `tutorials-multi-node-cluster/deploy-multi-node.md`, `tutorials-storage/create-persistent-storage.md` under https://docs.primeintellect.ai/ + +## GPU Availability + +```bash +prime availability list # all GPUs with pricing +prime availability list --gpu-type H100_80GB # filter by GPU type +``` + +## Pods (single instances) + +```bash +prime config set-ssh-key-path # one-time: SSH key for pod access (generate on the dashboard profile page) +prime pods create # interactive provisioning from availability data +prime pods list +prime pods ssh +``` + +Pods support custom Docker images (`tutorials-on-demand-cloud/deploy-custom-docker-image.md`). Monitor, terminate, and inspect pods from the CLI or the dashboard. + +## Multi-Node Clusters + +Deploy multi-node clusters (with optional Slurm orchestration) from the platform; see `tutorials-multi-node-cluster/deploy-multi-node.md` and `tutorials-multi-node-cluster/slurm-orchestration.md`. Cluster monitoring guidance lives at `tutorials-reserved-clusters/monitoring.md`. + +## Storage + +Persistent disks outlive individual instances and can be attached to different instances as needed: + +- Create disks from the Storage tab of the Instances page (choose provider, datacenter, size), then attach them to pods or clusters. +- Manage disks from the CLI as well — see `cli-reference/managing-disks.md`. +- For multi-node shared and ephemeral storage patterns, see `tutorials-storage/cluster-storage.md`. + +## Teams + +Manage team membership and run resources against team billing: + +```bash +prime teams list +``` + +Most provisioning commands accept a `--team-id` flag; the REST API equivalent is the `X-Prime-Team-ID` header. diff --git a/packages/coding-agent/skills/prime-intellect/references/environments.md b/packages/coding-agent/skills/prime-intellect/references/environments.md new file mode 100644 index 000000000..d0e4cf2e3 --- /dev/null +++ b/packages/coding-agent/skills/prime-intellect/references/environments.md @@ -0,0 +1,73 @@ +# Environments, Evaluations & Training + +verifiers is Prime Intellect's Python library for LLM environments: packages that expose `load_environment` and bundle datasets, rollout logic, and reward rubrics. Environments power evaluations (local and hosted) and RL training (Hosted Training or self-managed prime-rl). + +Live docs: `verifiers/overview.md`, `tutorials-environments/getting-started.md`, `hosted-training/getting-started.md`, `prime-rl/overview.md` under https://docs.primeintellect.ai/ (append `.md` for raw markdown). Source: https://github.com/PrimeIntellect-ai/verifiers and https://github.com/PrimeIntellect-ai/prime-rl + +## Discovering Environments (Hub) + +```bash +prime env list --search "math" --owner primeintellect --show-actions +prime env list --tag tools --tag sandbox +prime env list --mine +prime env list --starred +prime env info owner/name # metadata, version, dependencies +prime env status owner/name # CI/action status +prime env pull owner/name -t ./tmp-env # pull source for inspection +prime env install owner/name # install locally +``` + +When picking candidates, prefer: `primeintellect`-owned, passing latest actions, updated within ~2 months, recent verifiers versions. Compare task type (single-turn, multi-turn, tool, sandbox, agent), reward type (binary, continuous, judge-based), and dependency/secret requirements. + +## Creating Environments + +```bash +prime env init my-env --v1 # scaffold (add --with-harness for an explicit reusable harness) +prime env install my-env +prime eval run my-env -m openai/gpt-4.1-mini -n 5 # smoke test immediately +prime env push # publish to the Hub +``` + +Build guidance: + +- Define the task contract first: prompt shape, allowed tools, stop conditions, rubric outputs, metrics. +- Prefer starting from an existing Hub environment (`prime env list --search`, then `prime env install owner/name`) over building from scratch. +- v1 environments expose `load_environment(config: vf.EnvConfig) -> vf.Env`; Taskset + Harness environments additionally expose `load_taskset(config)` and optionally `load_harness(config)` with `load_environment` delegating through `vf.load_taskset`/`vf.load_harness`. +- The environment must install, load, evaluate, and train without hidden setup. + +## Evaluations + +`prime eval run` is the canonical eval path. Runs save automatically (visible in the Evaluations tab and `prime eval view`); do not add `--skip-upload` unless the user explicitly asks. + +```bash +prime eval run my-env -m openai/gpt-4.1-mini -n 5 # smoke +prime eval run owner/env -m openai/gpt-4.1-mini -n 200 -r 3 --shuffle -s # scaled +prime eval run owner/env --hosted --follow # hosted, streaming logs +``` + +- Smoke-test first; scale only after a pass. Use `--shuffle` (seed defaults to 0; set `--shuffle-seed` for reproducible reports). +- Hosted evals require the environment to be published (`prime env push`) and support TOML configs and temporary sandbox/tunnel permissions (`--allow-tunnel-access`). +- Prime Inference models include estimated run cost in eval output automatically. +- Keep reusable model endpoints as aliases in `configs/endpoints.toml` (fields: `endpoint_id`, `model`, `url`, key) and reference them with `-m `. + +## Training + +Default to Hosted Training unless the user explicitly wants self-managed infrastructure. + +```bash +prime lab setup # Lab workspace for environments, evals, GEPA, Hosted Training +prime train models # supported models, capacity, pricing +prime train init # generate a training config +prime train rl.toml # launch the run +``` + +- Hosted Training launches from a CPU machine; no local GPUs needed. +- Validate the environment with an eval before training (e.g. `-n 20 -r 3 -s`) and confirm reward diversity exists at baseline. +- Self-managed path: `prime lab setup --prime-rl`, then follow prime-rl's own configs and launch commands. Treat prime-rl as a power-user path requiring local GPU access; see https://docs.primeintellect.ai/prime-rl/overview.md. + +## Model Family Choice + +Ask the user whether they want instruct or reasoning models before non-trivial runs: + +- Instruct (quick behavior checks): `gpt-4.1` series, `qwen3` instruct series. +- Reasoning (harder probes, deeper coverage): `gpt-5` series, `qwen3` thinking series, `glm` series. diff --git a/packages/coding-agent/skills/prime-intellect/references/inference.md b/packages/coding-agent/skills/prime-intellect/references/inference.md new file mode 100644 index 000000000..0839d4825 --- /dev/null +++ b/packages/coding-agent/skills/prime-intellect/references/inference.md @@ -0,0 +1,47 @@ +# Prime Inference + +Prime Inference is an OpenAI-compatible API for frontier and open models, routed across providers and built for large-scale evaluations. + +Live docs: `inference/overview.md`, `inference/usage.md`, `inference/adapter-deployments.md`, `inference/troubleshooting.md` under https://docs.primeintellect.ai/ + +## Setup + +1. Create an API key on https://app.primeintellect.ai (account settings → API Keys) with the **Inference** permission enabled — without it, requests fail with authentication errors. +2. Export it: + +```bash +export PRIME_API_KEY="your-api-key-here" +``` + +## Via the CLI (recommended for evaluations) + +```bash +prime inference models # list available models +prime eval run gsm8k -m meta-llama/llama-3.1-70b-instruct -n 25 # evals route through Prime Inference +``` + +Eval runs against Prime Inference models report estimated USD cost automatically. + +## Direct API (OpenAI-compatible) + +Base URL: `https://api.pinference.ai/api/v1` + +```python +import openai, os + +client = openai.OpenAI( + api_key=os.environ["PRIME_API_KEY"], + base_url="https://api.pinference.ai/api/v1", +) +response = client.chat.completions.create( + model="meta-llama/llama-3.1-70b-instruct", + messages=[{"role": "user", "content": "Hello!"}], +) +``` + +Anything that speaks the OpenAI API works — just point it at the base URL above. Streaming and advanced parameters are covered in `inference/usage.md`. + +## Teams & Adapters + +- Team billing: send the `X-Prime-Team-ID` header (find the ID via `prime teams list` or the Team Profile page) to use team credits instead of personal balance. +- LoRA adapters trained with Hosted Training can be deployed and queried through the same OpenAI-compatible API (`inference/adapter-deployments.md`). diff --git a/packages/coding-agent/skills/prime-intellect/references/sandboxes.md b/packages/coding-agent/skills/prime-intellect/references/sandboxes.md new file mode 100644 index 000000000..93010beee --- /dev/null +++ b/packages/coding-agent/skills/prime-intellect/references/sandboxes.md @@ -0,0 +1,75 @@ +# Sandboxes & Tunnels + +Prime Sandboxes are disposable Docker environments for running AI-generated or untrusted code in the cloud: isolated, fast to create, billed only while running (CPU $0.05/core/hr, memory $0.01/GB/hr, disk $0.001/GB/hr). + +Live docs: `sandboxes/overview.md`, `sandboxes/cli.md`, `sandboxes/sdk.md`, `sandboxes/images.md`, `sandboxes/tunnel.md` under https://docs.primeintellect.ai/ + +## CLI Lifecycle + +```bash +prime sandbox create python:3.11-slim --timeout-minutes 120 # any Docker image +prime sandbox list +prime sandbox run "python --version" +prime sandbox get +prime sandbox delete +``` + +Useful create flags: + +```bash +prime sandbox create python:3.11-slim \ + --name analytics-lab \ + --cpu-cores 2 --memory-gb 4 --disk-size-gb 20 \ + --timeout-minutes 240 \ + --idle-timeout-minutes 15 \ + --env APP_ENV=staging \ + --secret API_KEY=sk-abc123 \ + --start-command "python serve.py --port 8000" \ + --yes +``` + +- `--timeout-minutes` caps total lifetime; `--idle-timeout-minutes` reaps the sandbox early when no exec/upload/download/file-read arrives (1 ≤ idle ≤ timeout ≤ 1440; not supported with `--vm`). +- `--env` values are plain text; `--secret` values are encrypted at rest and obfuscated in output. Both become environment variables inside the container. +- Default start command is `tail -f /dev/null` (idle, ready for `prime sandbox run`); `--start-command` replaces the image ENTRYPOINT. +- Outbound internet is on by default; use `--no-network-access` for isolation when running untrusted code. +- `--team-id` bills a team; `--yes` skips confirmation in automation. + +Custom images: build and push your own via Prime Images (`sandboxes/images.md`). + +## Python SDK + +`prime_sandboxes` ships `SandboxClient` (sync) and `AsyncSandboxClient` (async) with identical methods: + +```python +from prime_sandboxes import SandboxClient, CreateSandboxRequest, APIClient + +client = SandboxClient(APIClient()) +sandbox = client.create(CreateSandboxRequest( + name="sdk-demo", + docker_image="python:3.11-slim", + timeout_minutes=120, + environment_vars={"LOG_LEVEL": "debug"}, + secrets={"API_KEY": "sk-abc123"}, +)) +client.wait_for_creation(sandbox.id) +result = client.execute_command(sandbox.id, "python -c 'print(42)'") +client.upload_file(sandbox.id, "/workspace/data.csv", "./data.csv") +client.download_file(sandbox.id, "/workspace/output.csv", "./output.csv") +exposed = client.expose(sandbox.id, port=8000, name="web") # public URL +client.delete(sandbox.id) +``` + +Async variant: `async with AsyncSandboxClient() as sandboxes: await sandboxes.create(...)` — same methods, useful for fanning out many sandboxes concurrently. + +## Tunnels + +Prime Tunnel exposes a local (or in-sandbox) service through a secure public HTTPS URL — for dev servers, webhooks, and sharing work in progress: + +```python +from prime_tunnel import Tunnel + +tunnel = Tunnel(local_port=8000) +await tunnel.start() # prints the public URL +``` + +Hosted evaluations can create tunnels too: launch the run with `--allow-tunnel-access` (grants tunnel permission to the temporary `PRIME_API_KEY`), then use the same flow inside the hosted sandbox. diff --git a/packages/coding-agent/src/config.ts b/packages/coding-agent/src/config.ts index f96195088..aae605318 100644 --- a/packages/coding-agent/src/config.ts +++ b/packages/coding-agent/src/config.ts @@ -434,6 +434,15 @@ export function getBundledInteractiveAssetPath(name: string): string { return join(getInteractiveAssetsDir(), name); } +/** + * Get the directory containing built-in skills shipped with the package. + * - For Bun binary: skills/ next to executable (copied by copy-binary-assets) + * - For Node.js (dist/) and tsx (src/): /skills + */ +export function getBundledSkillsDir(): string { + return join(getPackageDir(), "skills"); +} + // ============================================================================= // App Config (from package.json piConfig) // ============================================================================= diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts index 981f2d922..f7ed1d843 100644 --- a/packages/coding-agent/src/core/package-manager.ts +++ b/packages/coding-agent/src/core/package-manager.ts @@ -27,7 +27,7 @@ import type { Readable } from "node:stream"; import { globSync } from "glob"; import ignore from "ignore"; import { minimatch } from "minimatch"; -import { CONFIG_DIR_NAME } from "../config.js"; +import { CONFIG_DIR_NAME, getBundledSkillsDir } from "../config.js"; import { shouldUseWindowsShell } from "../utils/child-process.js"; import { type GitSource, parseGitUrl } from "../utils/git.js"; import { canonicalizePath, isLocalPath } from "../utils/paths.js"; @@ -111,6 +111,8 @@ interface PackageManagerOptions { cwd: string; agentDir: string; settingsManager: SettingsManager; + /** Directory of built-in skills shipped with the package. Defaults to the bundled skills dir; pass null to disable. */ + bundledSkillsDir?: string | null; } type SourceScope = "user" | "project" | "temporary"; @@ -169,8 +171,10 @@ interface ResourceAccumulator { * 2 user + settings entry (source: "local", scope: "user") * 3 user + auto-discovered (source: "auto", scope: "user") * 4 package resource (origin: "package") + * 5 built-in resource shipped with prime-agent (source: "builtin") */ function resourcePrecedenceRank(m: PathMetadata): number { + if (m.source === "builtin") return 5; if (m.origin === "package") return 4; const scopeBase = m.scope === "project" ? 0 : 2; return scopeBase + (m.source === "local" ? 0 : 1); @@ -758,6 +762,7 @@ export class DefaultPackageManager implements PackageManager { private cwd: string; private agentDir: string; private settingsManager: SettingsManager; + private bundledSkillsDir: string | null; private globalNpmRoot: string | undefined; private globalNpmRootCommandKey: string | undefined; private progressCallback: ProgressCallback | undefined; @@ -766,6 +771,7 @@ export class DefaultPackageManager implements PackageManager { this.cwd = options.cwd; this.agentDir = options.agentDir; this.settingsManager = options.settingsManager; + this.bundledSkillsDir = options.bundledSkillsDir === undefined ? getBundledSkillsDir() : options.bundledSkillsDir; } setProgressCallback(callback: ProgressCallback | undefined): void { @@ -2228,6 +2234,22 @@ export class DefaultPackageManager implements PackageManager { userOverrides.skills, globalBaseDir, ); + + if (this.bundledSkillsDir && this.settingsManager.getEnableBuiltinSkills()) { + const builtinMetadata: PathMetadata = { + source: "builtin", + scope: "user", + origin: "top-level", + baseDir: this.bundledSkillsDir, + }; + addResources( + "skills", + collectAutoSkillEntries(this.bundledSkillsDir, "pi"), + builtinMetadata, + userOverrides.skills, + this.bundledSkillsDir, + ); + } addResources( "prompts", collectAutoPromptEntries(userDirs.prompts), diff --git a/packages/coding-agent/src/core/resource-loader.ts b/packages/coding-agent/src/core/resource-loader.ts index 338daf7e9..8ac486737 100644 --- a/packages/coding-agent/src/core/resource-loader.ts +++ b/packages/coding-agent/src/core/resource-loader.ts @@ -128,6 +128,8 @@ export interface DefaultResourceLoaderOptions { noPromptTemplates?: boolean; noThemes?: boolean; noContextFiles?: boolean; + /** Directory of built-in skills shipped with the package. Defaults to the bundled skills dir; pass null to disable. */ + bundledSkillsDir?: string | null; systemPrompt?: string; appendSystemPrompt?: string[]; extensionsOverride?: (base: LoadExtensionsResult) => LoadExtensionsResult; @@ -213,6 +215,7 @@ export class DefaultResourceLoader implements ResourceLoader { cwd: this.cwd, agentDir: this.agentDir, settingsManager: this.settingsManager, + bundledSkillsDir: options.bundledSkillsDir, }); this.additionalExtensionPaths = options.additionalExtensionPaths ?? []; this.additionalSkillPaths = options.additionalSkillPaths ?? []; diff --git a/packages/coding-agent/src/core/settings-manager.ts b/packages/coding-agent/src/core/settings-manager.ts index 804f25ecb..dc342c0f4 100644 --- a/packages/coding-agent/src/core/settings-manager.ts +++ b/packages/coding-agent/src/core/settings-manager.ts @@ -96,6 +96,7 @@ export interface Settings { prompts?: string[]; // Array of local prompt template paths or directories themes?: string[]; // Array of local theme file paths or directories enableSkillCommands?: boolean; // default: true - register skills as /skill:name commands + enableBuiltinSkills?: boolean; // default: true - load built-in skills shipped with prime-agent terminal?: TerminalSettings; images?: ImageSettings; enabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag) @@ -876,6 +877,16 @@ export class SettingsManager { this.save(); } + getEnableBuiltinSkills(): boolean { + return this.settings.enableBuiltinSkills ?? true; + } + + setEnableBuiltinSkills(enabled: boolean): void { + this.globalSettings.enableBuiltinSkills = enabled; + this.markModified("enableBuiltinSkills"); + this.save(); + } + getThinkingBudgets(): ThinkingBudgetsSettings | undefined { return this.settings.thinkingBudgets; } diff --git a/packages/coding-agent/src/modes/interactive/components/config-selector.ts b/packages/coding-agent/src/modes/interactive/components/config-selector.ts index 1c825e0af..208456f9b 100644 --- a/packages/coding-agent/src/modes/interactive/components/config-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/config-selector.ts @@ -60,6 +60,9 @@ function getGroupLabel(metadata: PathMetadata): string { return `${metadata.source} (${metadata.scope})`; } // Top-level resources + if (metadata.source === "builtin") { + return "Built-in"; + } if (metadata.source === "auto") { return metadata.scope === "user" ? `User (~/${CONFIG_DIR_NAME}/)` : `Project (${CONFIG_DIR_NAME}/)`; } @@ -534,6 +537,11 @@ class ResourceList implements Component, Focusable { } private getResourcePattern(item: ResourceItem): string { + // Built-in resources live under the package install dir; their override + // patterns are matched relative to metadata.baseDir, not the config dir. + if (item.metadata.source === "builtin" && item.metadata.baseDir) { + return relative(item.metadata.baseDir, item.path); + } const scope = item.metadata.scope as "user" | "project"; const baseDir = this.getTopLevelBaseDir(scope); return relative(baseDir, item.path); diff --git a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts index 138b29e4c..019b9822a 100644 --- a/packages/coding-agent/src/modes/interactive/components/settings-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/settings-selector.ts @@ -36,6 +36,7 @@ export interface SettingsConfig { autoResizeImages: boolean; blockImages: boolean; enableSkillCommands: boolean; + enableBuiltinSkills: boolean; steeringMode: "all" | "one-at-a-time"; followUpMode: "all" | "one-at-a-time"; transport: Transport; @@ -61,6 +62,7 @@ export interface SettingsCallbacks { onAutoResizeImagesChange: (enabled: boolean) => void; onBlockImagesChange: (blocked: boolean) => void; onEnableSkillCommandsChange: (enabled: boolean) => void; + onEnableBuiltinSkillsChange: (enabled: boolean) => void; onSteeringModeChange: (mode: "all" | "one-at-a-time") => void; onFollowUpModeChange: (mode: "all" | "one-at-a-time") => void; onTransportChange: (transport: Transport) => void; @@ -368,8 +370,18 @@ export class SettingsSelectorComponent extends Container { values: ["true", "false"], }); - // Hardware cursor toggle (insert after skill-commands) - const skillCommandsIndex = items.findIndex((item) => item.id === "skill-commands"); + // Built-in skills toggle (insert after skill-commands) + const skillCommandsItemIndex = items.findIndex((item) => item.id === "skill-commands"); + items.splice(skillCommandsItemIndex + 1, 0, { + id: "builtin-skills", + label: "Built-in skills", + description: "Load built-in skills shipped with prime-agent (takes effect after reload)", + currentValue: config.enableBuiltinSkills ? "true" : "false", + values: ["true", "false"], + }); + + // Hardware cursor toggle (insert after builtin-skills) + const skillCommandsIndex = items.findIndex((item) => item.id === "builtin-skills"); items.splice(skillCommandsIndex + 1, 0, { id: "show-hardware-cursor", label: "Show hardware cursor", @@ -445,6 +457,9 @@ export class SettingsSelectorComponent extends Container { case "skill-commands": callbacks.onEnableSkillCommandsChange(newValue === "true"); break; + case "builtin-skills": + callbacks.onEnableBuiltinSkillsChange(newValue === "true"); + break; case "steering-mode": callbacks.onSteeringModeChange(newValue as "all" | "one-at-a-time"); break; diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 2553447f7..316b6b86b 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -588,6 +588,10 @@ export class InteractiveMode { const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t"; const source = sourceInfo.source.trim(); + if (source === "builtin") { + return "builtin"; + } + if (source === "auto" || source === "local" || source === "cli") { return scopePrefix; } @@ -4486,6 +4490,7 @@ export class InteractiveMode { autoResizeImages: this.settingsManager.getImageAutoResize(), blockImages: this.settingsManager.getBlockImages(), enableSkillCommands: this.settingsManager.getEnableSkillCommands(), + enableBuiltinSkills: this.settingsManager.getEnableBuiltinSkills(), steeringMode: this.session.steeringMode, followUpMode: this.session.followUpMode, transport: this.settingsManager.getTransport(), @@ -4534,6 +4539,10 @@ export class InteractiveMode { this.settingsManager.setEnableSkillCommands(enabled); this.setupAutocompleteProvider(); }, + onEnableBuiltinSkillsChange: (enabled) => { + this.settingsManager.setEnableBuiltinSkills(enabled); + void this.handleReloadCommand(); + }, onSteeringModeChange: (mode) => { this.session.setSteeringMode(mode); }, diff --git a/packages/coding-agent/test/builtin-skills.test.ts b/packages/coding-agent/test/builtin-skills.test.ts new file mode 100644 index 000000000..c1273a5c9 --- /dev/null +++ b/packages/coding-agent/test/builtin-skills.test.ts @@ -0,0 +1,165 @@ +import { mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { getBundledSkillsDir } from "../src/config.js"; +import { DefaultPackageManager } from "../src/core/package-manager.js"; +import { DefaultResourceLoader } from "../src/core/resource-loader.js"; +import { SettingsManager } from "../src/core/settings-manager.js"; +import { loadSkillsFromDir } from "../src/core/skills.js"; + +function writeSkill(dir: string, name: string, description = `Description for ${name}`): void { + const skillDir = join(dir, name); + mkdirSync(skillDir, { recursive: true }); + writeFileSync( + join(skillDir, "SKILL.md"), + `--- +name: ${name} +description: ${description} +--- +Content for ${name}.`, + ); +} + +describe("builtin skills", () => { + let tempDir: string; + let agentDir: string; + let cwd: string; + let bundledDir: string; + let settingsManager: SettingsManager; + + beforeEach(() => { + tempDir = join(tmpdir(), `builtin-skills-test-${Date.now()}-${Math.random().toString(36).slice(2)}`); + agentDir = join(tempDir, "agent"); + cwd = join(tempDir, "project"); + bundledDir = join(tempDir, "bundled-skills"); + mkdirSync(agentDir, { recursive: true }); + mkdirSync(cwd, { recursive: true }); + mkdirSync(bundledDir, { recursive: true }); + settingsManager = SettingsManager.inMemory(); + }); + + afterEach(() => { + rmSync(tempDir, { recursive: true, force: true }); + }); + + describe("DefaultPackageManager", () => { + it("resolves bundled skills with builtin source at lowest precedence", async () => { + writeSkill(bundledDir, "builtin-skill"); + + const packageManager = new DefaultPackageManager({ + cwd, + agentDir, + settingsManager, + bundledSkillsDir: bundledDir, + }); + const result = await packageManager.resolve(); + + const builtin = result.skills.find((r) => r.path === join(bundledDir, "builtin-skill", "SKILL.md")); + expect(builtin).toBeDefined(); + expect(builtin?.enabled).toBe(true); + expect(builtin?.metadata.source).toBe("builtin"); + expect(builtin?.metadata.scope).toBe("user"); + expect(builtin?.metadata.baseDir).toBe(bundledDir); + }); + + it("sorts builtin skills after user and project skills so collisions favor local skills", async () => { + writeSkill(bundledDir, "shared-skill"); + writeSkill(join(agentDir, "skills"), "shared-skill"); + + const packageManager = new DefaultPackageManager({ + cwd, + agentDir, + settingsManager, + bundledSkillsDir: bundledDir, + }); + const result = await packageManager.resolve(); + + const paths = result.skills.map((r) => r.path); + const userIndex = paths.indexOf(join(agentDir, "skills", "shared-skill", "SKILL.md")); + const builtinIndex = paths.indexOf(join(bundledDir, "shared-skill", "SKILL.md")); + expect(userIndex).toBeGreaterThanOrEqual(0); + expect(builtinIndex).toBeGreaterThanOrEqual(0); + expect(userIndex).toBeLessThan(builtinIndex); + }); + + it("excludes bundled skills when enableBuiltinSkills is false", async () => { + writeSkill(bundledDir, "builtin-skill"); + settingsManager.setEnableBuiltinSkills(false); + + const packageManager = new DefaultPackageManager({ + cwd, + agentDir, + settingsManager, + bundledSkillsDir: bundledDir, + }); + const result = await packageManager.resolve(); + + expect(result.skills.some((r) => r.metadata.source === "builtin")).toBe(false); + }); + + it("disables individual bundled skills via settings override patterns", async () => { + writeSkill(bundledDir, "builtin-skill"); + writeSkill(bundledDir, "other-skill"); + settingsManager.setSkillPaths(["-builtin-skill/SKILL.md"]); + + const packageManager = new DefaultPackageManager({ + cwd, + agentDir, + settingsManager, + bundledSkillsDir: bundledDir, + }); + const result = await packageManager.resolve(); + + const disabled = result.skills.find((r) => r.path === join(bundledDir, "builtin-skill", "SKILL.md")); + const enabled = result.skills.find((r) => r.path === join(bundledDir, "other-skill", "SKILL.md")); + expect(disabled?.enabled).toBe(false); + expect(enabled?.enabled).toBe(true); + }); + }); + + describe("DefaultResourceLoader", () => { + it("loads bundled skills with builtin source info", async () => { + writeSkill(bundledDir, "builtin-skill"); + + const loader = new DefaultResourceLoader({ cwd, agentDir, bundledSkillsDir: bundledDir }); + await loader.reload(); + + const { skills } = loader.getSkills(); + const builtin = skills.find((s) => s.name === "builtin-skill"); + expect(builtin).toBeDefined(); + expect(builtin?.sourceInfo.source).toBe("builtin"); + }); + + it("prefers user skills over bundled skills with the same name", async () => { + writeSkill(bundledDir, "shared-skill", "Bundled variant"); + writeSkill(join(agentDir, "skills"), "shared-skill", "User variant"); + + const loader = new DefaultResourceLoader({ cwd, agentDir, bundledSkillsDir: bundledDir }); + await loader.reload(); + + const { skills } = loader.getSkills(); + const winner = skills.find((s) => s.name === "shared-skill"); + expect(winner?.description).toBe("User variant"); + }); + + it("excludes bundled skills with --no-skills", async () => { + writeSkill(bundledDir, "builtin-skill"); + + const loader = new DefaultResourceLoader({ cwd, agentDir, bundledSkillsDir: bundledDir, noSkills: true }); + await loader.reload(); + + expect(loader.getSkills().skills).toEqual([]); + }); + }); + + describe("shipped skill content", () => { + it("loads all bundled skills without diagnostics", () => { + const { skills, diagnostics } = loadSkillsFromDir({ dir: getBundledSkillsDir(), source: "builtin" }); + + expect(diagnostics).toEqual([]); + expect(skills.length).toBeGreaterThan(0); + expect(skills.map((s) => s.name)).toContain("prime-intellect"); + }); + }); +}); diff --git a/packages/coding-agent/test/package-manager.test.ts b/packages/coding-agent/test/package-manager.test.ts index e592def7e..a767677fc 100644 --- a/packages/coding-agent/test/package-manager.test.ts +++ b/packages/coding-agent/test/package-manager.test.ts @@ -63,6 +63,7 @@ describe("DefaultPackageManager", () => { cwd: tempDir, agentDir, settingsManager, + bundledSkillsDir: null, }); });