@outsidestudio/agent-kit 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,16 +3,18 @@
3
3
  One command to set up a developer's AI coding tools: skills, MCP servers and our self-hosted model provider, for **OpenCode** and **OMP (oh-my-pi)**.
4
4
 
5
5
  ```bash
6
- bunx @outsidestudio/agent-kit product-backend
6
+ bunx @outsidestudio/agent-kit add product-backend
7
7
  ```
8
8
 
9
9
  Need more than one profile (e.g. full-stack)? Pass them together; their items are merged:
10
10
 
11
11
  ```bash
12
- bunx @outsidestudio/agent-kit product-backend web-frontend
12
+ bunx @outsidestudio/agent-kit add product-backend web-frontend
13
13
  ```
14
14
 
15
- Run them in one command. Running them separately does not stack: each install replaces what the previous one set up.
15
+ Installs **stack**: every run is additive, so installing one profile never removes another profile's entries — run them in any order, as often as you like. `agent-kit remove <target>` takes something out again.
16
+
17
+ **Auto-update.** bunx caches its copy of the kit. From **0.2.3** a cached copy checks npm's `latest` on startup and transparently reruns the new version with your arguments, so bare commands stay current (`AGENT_KIT_NO_UPDATE_CHECK=1` to opt out). Copies older than 0.2.3 cannot self-update: run `bunx @outsidestudio/agent-kit@latest update` once; the bare form updates itself from then on.
16
18
 
17
19
  That command:
18
20
 
@@ -45,6 +47,7 @@ Options: `--tool opencode,omp`, `-y/--yes` (no prompts), `--no-shell` (don't edi
45
47
  profiles/ one file per department; "extends" + "include" registry ids
46
48
  registry/ one file per item; id = path under registry/ without .json
47
49
  outside/ our own items (registry/outside/llm.json → id "outside/llm")
50
+ anthropic/ Anthropic's skills (registry/anthropic/document-skills.json)
48
51
  skills/<group>/ our custom skills (SKILL.md folders)
49
52
  src/adapters/ one adapter per tool (opencode.ts, omp.ts)
50
53
  ```
@@ -55,21 +58,25 @@ src/adapters/ one adapter per tool (opencode.ts, omp.ts)
55
58
  {
56
59
  "description": "Product backend developers",
57
60
  "extends": ["shared"],
58
- "include": ["context-mode", "outside/backend-skills"]
61
+ "include": ["outside/product-backend-skills", "security-audit"]
59
62
  }
60
63
  ```
61
64
 
62
- `shared` is the base every department extends: the LLM provider, company skills, Superpowers and Context7.
65
+ `shared` is the base every department extends: the LLM provider, company skills, Superpowers, Context7, context-mode and Anthropic's document skills (docx, pdf, pptx, xlsx).
63
66
 
64
67
  ### Registry item kinds
65
68
 
66
- **`skills`**: a folder of skills, from this repo or any git repo (default branch, latest commit).
69
+ **`skills`**: a folder of skills, from this repo or any git repo (default branch, latest commit). `only` picks specific skill folders from the source; `requires` lists executables the skills expect on PATH (doctor warns when one is missing).
67
70
 
68
71
  ```json
69
- { "kind": "skills", "source": { "type": "local", "path": "skills/backend" } }
72
+ { "kind": "skills", "source": { "type": "local", "path": "skills/product-backend" } }
70
73
  { "kind": "skills", "source": { "type": "git", "repo": "https://github.com/org/repo.git", "path": "skills" }, "only": ["skill-a"] }
74
+ { "kind": "skills", "source": { "type": "git", "repo": "https://github.com/anthropics/skills.git", "path": "skills" },
75
+ "only": ["docx", "pdf", "pptx", "xlsx"], "requires": ["python3"] }
71
76
  ```
72
77
 
78
+ `anthropic/document-skills` uses the last shape: each run clones Anthropic's public skills repo and copies only the four document skills into the tools' skills dirs. Nothing from that repo is bundled in the npm package — worth knowing because those four carry Anthropic's source-available license (the repo's other skills are Apache 2.0).
79
+
73
80
  **`mcp`**: a local (stdio) or remote (HTTP) MCP server. Write secrets as `${VAR}`; each adapter converts them to the tool's syntax (`{env:VAR}` for OpenCode, `${VAR}` for OMP).
74
81
 
75
82
  ```json
@@ -87,16 +94,16 @@ src/adapters/ one adapter per tool (opencode.ts, omp.ts)
87
94
  "fallback": { "type": "git", "repo": "https://github.com/obra/superpowers.git", "path": "skills" } }
88
95
  ```
89
96
 
90
- **`provider`**: an OpenAI-compatible model endpoint (our vast.ai models). The provider id in each tool is the registry id with `/` replaced by `-`, so the model id `nvidia/Qwen2.8-Flash-Next-NVFP4` in `outside/llm` appears as `outside-llm/nvidia/Qwen2.8-Flash-Next-NVFP4`.
97
+ **`provider`**: an OpenAI-compatible model endpoint (our vast.ai models). The provider id in each tool is the registry id with `/` replaced by `-`, so the model id `nvidia/Qwen3.8-Flash-Next-NVFP4` in `outside/llm` appears as `outside-llm/nvidia/Qwen3.8-Flash-Next-NVFP4`.
91
98
 
92
99
  ```json
93
100
  { "kind": "provider", "name": "Outside LLM",
94
101
  "baseURL": "${OUTSIDE_LLM_BASE_URL}", "apiKey": "${OUTSIDE_LLM_API_KEY}",
95
- "models": { "nvidia/Qwen2.8-Flash-Next-NVFP4": {
96
- "name": "Qwen2.8 Flash Next (NVFP4)", "contextWindow": 131072, "maxTokens": 32768,
102
+ "models": { "nvidia/Qwen3.8-Flash-Next-NVFP4": {
103
+ "name": "Qwen3.8 Flash Next (NVFP4)", "contextWindow": 131072, "maxTokens": 32768,
97
104
  "reasoning": true, "input": ["text", "image"],
98
105
  "thinking": { "mode": "effort", "efforts": ["low", "medium", "high"], "defaultLevel": "medium" } } },
99
- "defaultModel": "nvidia/Qwen2.8-Flash-Next-NVFP4" }
106
+ "defaultModel": "nvidia/Qwen3.8-Flash-Next-NVFP4" }
100
107
  ```
101
108
 
102
109
  Model metadata is endpoint-specific — the same model id behind a different gateway has different limits — so each provider item carries the numbers for the server it points at. The kit maps `contextWindow`/`maxTokens` to OMP's `contextWindow`/`maxTokens` and OpenCode's `limit.context`/`limit.output`, `input` to OpenCode `modalities.input`, and passes `thinking` through to OMP.
@@ -112,30 +119,16 @@ Model metadata is endpoint-specific — the same model id behind a different gat
112
119
 
113
120
  `OPENCODE_CONFIG_DIR` and `OMP_PROFILE` are respected. The first time a file is changed, a backup is saved next to it as `*.agent-kit.bak`.
114
121
 
115
- **What the kit owns.** It records what it installed in `~/.config/outside-agent-kit/state.json`. On `update` or a profile switch it replaces only those entries. Your own MCP servers, plugins and skills are never touched; if one has the same name as a kit item, yours wins and the kit warns.
122
+ **What the kit owns.** It records what it installed in `~/.config/outside-agent-kit/state.json`. A plain install only ever adds; `remove` and `update` rebuild configs from the saved set, deleting only those recorded entries. Your own MCP servers, plugins and skills are never touched; if one has the same name as a kit item, yours wins and the kit warns.
116
123
 
117
124
  **JSONC.** If `opencode.json` has comments, they are removed when the kit rewrites it (the backup keeps them).
118
125
 
126
+ **Caveats.** OMP also reads other tools' configs (`opencode.json`, `.claude`, …): same-named MCP servers dedupe (OMP's own `mcp.json` wins), but a skill installed in several places may appear twice. OpenCode caches git plugins — if an update doesn't show up, clear OpenCode's plugin cache and restart it.
127
+
119
128
  ## Adding things
120
129
 
121
- - **Custom skill**: `agent-kit new skill backend/rate-limiting`, write the SKILL.md, open a PR. Everyone gets it on their next `update`.
130
+ - **Custom skill**: `agent-kit new skill product-backend/rate-limiting`, write the SKILL.md, open a PR. Everyone gets it on their next `update`.
122
131
  - **New department**: add `profiles/<dept>.json` and `skills/<dept>/` (`new skill <dept>/<name>` creates the registry entry for you).
123
132
  - **Third-party item**: add a `registry/<id>.json` (put your own under `registry/outside/`), then add the id to profiles.
124
133
  - **New tool** (Codex, Cursor…): write `src/adapters/<tool>.ts` implementing `Adapter` and list it in `src/adapters/index.ts`.
125
-
126
- ## Publishing
127
-
128
- Pick one:
129
-
130
- - **Private npm registry** (GitHub Packages, Verdaccio): `npm publish`, then devs run `bunx @outsidestudio/agent-kit <profile>`. Point the `@outsidestudio` scope at your registry in `~/.npmrc` or `bunfig.toml`.
131
- - **Straight from GitHub**, no registry: `bunx github:madebyoutside/agent-kit <profile>`.
132
-
133
- With "always latest", `bunx` may reuse a cached copy of the kit itself. Use `bunx @outsidestudio/agent-kit@latest update` to be sure you have the newest registry.
134
-
135
- ## Things to verify for your setup
136
-
137
- - **Base URL.** Put a stable gateway (LiteLLM, Cloudflare Tunnel) in front of vast.ai so `OUTSIDE_LLM_BASE_URL` doesn't change when instances are recreated.
138
- - **Model list.** Replace `qwen3-coder` in `registry/outside/llm.json` with the model ids your server actually serves (`curl $OUTSIDE_LLM_BASE_URL/models`).
139
- - **OMP plugins.** Superpowers has no `omp` entry yet, so OMP gets its skills via the fallback. Add `"omp": { "plugin": "..." }` if `omp plugin install` supports it.
140
- - **OpenCode plugin cache.** OpenCode caches git plugins. If an update doesn't show up, clear OpenCode's plugin cache and restart it.
141
- - **Duplicates in OMP.** OMP also reads other tools' configs (`opencode.json`, `.claude`, …). Same-named MCP servers dedupe (OMP's own `mcp.json` wins), but a skill installed in several places may appear twice.
134
+ - **Release**: bump `version` in `package.json`, commit, `npm publish --access public`, push. `latest` propagates in ~1-2 min; machines on 0.2.3+ pick it up on their next bare `bunx` run.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@outsidestudio/agent-kit",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "One-command setup of skills, MCP servers and model providers for OpenCode and OMP",
5
5
  "type": "module",
6
6
  "bin": {
package/profiles/pm.json CHANGED
@@ -1 +1,9 @@
1
- {"description":"Product managers","extends":["shared"],"include":["outside/pm-skills"]}
1
+ {
2
+ "description": "Product managers",
3
+ "extends": [
4
+ "shared"
5
+ ],
6
+ "include": [
7
+ "outside/pm-skills"
8
+ ]
9
+ }
@@ -1 +1,10 @@
1
- {"description":"Product backend developers","extends":["shared"],"include":["context-mode","outside/backend-skills"]}
1
+ {
2
+ "description": "Product backend developers",
3
+ "extends": [
4
+ "shared"
5
+ ],
6
+ "include": [
7
+ "outside/product-backend-skills",
8
+ "security-audit"
9
+ ]
10
+ }
@@ -1 +1,11 @@
1
- {"description":"Base setup every profile extends","include":["outside/llm","outside/shared-skills","superpowers","context7"]}
1
+ {
2
+ "description": "Base setup every profile extends",
3
+ "include": [
4
+ "outside/llm",
5
+ "outside/shared-skills",
6
+ "superpowers",
7
+ "context7",
8
+ "context-mode",
9
+ "anthropic/document-skills"
10
+ ]
11
+ }
@@ -1 +1,9 @@
1
- {"description":"Web / frontend developers","extends":["shared"],"include":["context-mode","outside/web-skills"]}
1
+ {
2
+ "description": "Web / frontend developers",
3
+ "extends": [
4
+ "shared"
5
+ ],
6
+ "include": [
7
+ "outside/web-frontend-skills"
8
+ ]
9
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "kind": "skills",
3
+ "description": "Anthropic's document skills: docx, pdf, pptx, xlsx (cloned from Anthropic's repo at install time; source-available license, never bundled in the package)",
4
+ "source": { "type": "git", "repo": "https://github.com/anthropics/skills.git", "path": "skills" },
5
+ "only": ["docx", "pdf", "pptx", "xlsx"],
6
+ "requires": ["python3"]
7
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "kind": "skills",
3
+ "description": "Product backend team custom skills (lives in this repo)",
4
+ "source": { "type": "local", "path": "skills/product-backend" }
5
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "kind": "skills",
3
- "description": "Web team custom skills (lives in this repo)",
3
+ "description": "Web / frontend team custom skills (lives in this repo)",
4
4
  "source": { "type": "local", "path": "skills/web-frontend" }
5
5
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "kind": "skills",
3
+ "description": "Cloudflare's security-audit skill: security guidance, focused reviews and full multi-agent codebase audits",
4
+ "source": { "type": "git", "repo": "https://github.com/cloudflare/security-audit-skill.git", "path": "skills" },
5
+ "only": ["security-audit"],
6
+ "requires": ["node"]
7
+ }
package/src/cli.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env bun
2
+ import { join } from "node:path";
2
3
  import { doctor } from "./commands/doctor";
3
4
  import { install, remove, secrets, update } from "./commands/install";
4
5
  import { list } from "./commands/list";
5
6
  import { newMcp, newSkill } from "./commands/new";
6
7
  import { color, log } from "./log";
8
+ import { KIT_ROOT } from "./paths";
7
9
 
8
10
  const HELP = `${color.bold("agent-kit")}: set up skills, MCP servers and models for OpenCode and OMP
9
11
 
@@ -24,9 +26,9 @@ ${color.bold("Options")}
24
26
  --no-shell Don't add the secrets loader to ~/.zshrc / ~/.bashrc
25
27
 
26
28
  ${color.bold("Examples")}
27
- bunx @outsidestudio/agent-kit add product-backend # bare form works too: bunx @outsidestudio/agent-kit product-backend
28
- bunx @outsidestudio/agent-kit product-backend web-frontend
29
- bunx @outsidestudio/agent-kit outside/llm # just the model provider, on top of what's installed (see: agent-kit list)
29
+ bunx @outsidestudio/agent-kit add product-backend # also: install, or just the bare profile name
30
+ bunx @outsidestudio/agent-kit add product-backend web-frontend
31
+ bunx @outsidestudio/agent-kit add outside/llm # just the model provider, on top of what's installed (see: agent-kit list)
30
32
  bunx @outsidestudio/agent-kit remove web-frontend # drop web skills, keep backend + shared
31
33
  bunx @outsidestudio/agent-kit update
32
34
  `;
@@ -47,8 +49,42 @@ function parse(argv: string[]) {
47
49
  return { args, flags };
48
50
  }
49
51
 
52
+
53
+ /**
54
+ * bunx can silently reuse its cached copy, so an installed copy may lag the
55
+ * published one. A cached copy (only: its path lives in bun's install cache)
56
+ * compares itself against the registry's `latest` and re-execs the new
57
+ * version with the same argv. Dev/repo/global copies never self-update.
58
+ */
59
+ async function selfUpdate(argv: string[]) {
60
+ if (process.env.AGENT_KIT_NO_UPDATE_CHECK) return;
61
+ if (!import.meta.dir.includes(".bun/install/cache")) return;
62
+ try {
63
+ const res = await fetch("https://registry.npmjs.org/@outsidestudio%2Fagent-kit", {
64
+ headers: { Accept: "application/vnd.npm.install-v1+json" },
65
+ signal: AbortSignal.timeout(2500),
66
+ });
67
+ if (!res.ok) return;
68
+ const latest = ((await res.json()) as { "dist-tags"?: { latest?: string } })["dist-tags"]?.latest;
69
+ const here = ((await Bun.file(join(KIT_ROOT, "package.json")).json()) as { version: string }).version;
70
+ if (!latest || latest === here) return;
71
+ log.info(`agent-kit ${here} is stale, rerunning with ${latest}…`);
72
+ const proc = Bun.spawn(["bunx", `@outsidestudio/agent-kit@${latest}`, ...argv], {
73
+ stdin: "inherit",
74
+ stdout: "inherit",
75
+ stderr: "inherit",
76
+ env: { ...process.env, AGENT_KIT_NO_UPDATE_CHECK: "1" },
77
+ });
78
+ process.exit(await proc.exited);
79
+ } catch {
80
+ // offline, slow registry, or no bunx on PATH: run the copy we have
81
+ }
82
+ }
83
+
50
84
  async function main() {
51
- const { args, flags } = parse(Bun.argv.slice(2));
85
+ const argv = Bun.argv.slice(2);
86
+ const { args, flags } = parse(argv);
87
+ await selfUpdate(argv);
52
88
  const [cmd, ...rest] = args;
53
89
  const opts = {
54
90
  tools: typeof flags.tool === "string" ? flags.tool.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
@@ -6,7 +6,7 @@ import { paths } from "../paths";
6
6
  import { loadItem, resolveProfiles, secretsOf } from "../registry";
7
7
  import { readEnvFile } from "../secrets";
8
8
  import { readState } from "../state";
9
- import type { McpItem, ProviderItem } from "../types";
9
+ import type { McpItem, ProviderItem, SkillsItem } from "../types";
10
10
 
11
11
  const expand = (v: string) => v.replace(/\$\{([A-Z0-9_]+)\}/g, (_, k) => process.env[k] ?? "");
12
12
 
@@ -55,6 +55,16 @@ export async function doctor(): Promise<number> {
55
55
  const ids = [...new Set([...(await resolveProfiles(state.profiles)), ...(state.items ?? [])])];
56
56
  const items = await Promise.all(ids.map(loadItem));
57
57
 
58
+ const skillDeps = items.filter((i): i is SkillsItem & { id: string } => i.kind === "skills" && !!i.requires?.length);
59
+ if (skillDeps.length) {
60
+ log.head("Skill dependencies");
61
+ for (const item of skillDeps) {
62
+ const missing = item.requires!.filter((b) => !Bun.which(b));
63
+ if (missing.length) log.warn(`${item.id}: ${missing.join(", ")} not on PATH; these skills may not work fully`);
64
+ else log.ok(`${item.id}: needs ${item.requires!.join(", ")} — found`);
65
+ }
66
+ }
67
+
58
68
  log.head("Secrets");
59
69
  const names = [...new Set(items.flatMap(secretsOf))];
60
70
  if (!names.length) log.ok("none needed");
@@ -17,7 +17,7 @@ export async function newSkill(spec: string) {
17
17
  const root = kitRepo();
18
18
  const [group, name] = spec.includes("/") ? spec.split("/") : ["shared", spec];
19
19
  if (!validName(group) || !validName(name)) {
20
- throw new Error("Use lowercase letters, digits and dashes: agent-kit new skill backend/my-skill");
20
+ throw new Error("Use lowercase letters, digits and dashes: agent-kit new skill product-backend/my-skill");
21
21
  }
22
22
  const dir = join(root, "skills", group, name);
23
23
  if (existsSync(dir)) throw new Error(`${dir} already exists`);
package/src/types.ts CHANGED
@@ -8,6 +8,8 @@ export interface SkillsItem {
8
8
  source: SkillSource;
9
9
  /** Optional allowlist of skill folder names from the source. */
10
10
  only?: string[];
11
+ /** Executables that should be on PATH for these skills to work fully (warned about by doctor). */
12
+ requires?: string[];
11
13
  }
12
14
 
13
15
  export interface McpItem {
@@ -1,5 +0,0 @@
1
- {
2
- "kind": "skills",
3
- "description": "Backend team custom skills (lives in this repo)",
4
- "source": { "type": "local", "path": "skills/backend" }
5
- }