@outsidestudio/agent-kit 0.2.1 → 0.2.3
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 +22 -15
- package/package.json +1 -1
- package/profiles/product-backend.json +1 -1
- package/profiles/shared.json +1 -1
- package/profiles/web-frontend.json +1 -1
- package/registry/anthropic/document-skills.json +7 -0
- package/registry/outside/product-backend-skills.json +5 -0
- package/registry/outside/{web-skills.json → web-frontend-skills.json} +1 -1
- package/src/cli.ts +49 -7
- package/src/commands/doctor.ts +11 -1
- package/src/commands/install.ts +39 -6
- package/src/commands/new.ts +1 -1
- package/src/types.ts +6 -2
- package/registry/outside/backend-skills.json +0 -5
- /package/skills/{backend → product-backend}/outside-db-migrations/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
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
|
-
|
|
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
16
|
|
|
17
17
|
That command:
|
|
18
18
|
|
|
@@ -20,15 +20,16 @@ That command:
|
|
|
20
20
|
2. Asks once for any secrets the profile needs (API keys, model endpoint) and stores them in `~/.config/outside-agent-kit/env` (chmod 600), loaded from `~/.zshrc` / `~/.bashrc`.
|
|
21
21
|
3. Fetches the **latest** version of every skill and plugin. Nothing is pinned.
|
|
22
22
|
4. Writes each tool's config, keeping anything the developer added themselves.
|
|
23
|
-
5.
|
|
23
|
+
5. **Installs are additive**: `agent-kit <target>` writes the named profiles/items and never removes anything else you already have — so `agent-kit product-backend` after `agent-kit product-backend web-frontend` just refreshes backend and leaves the web skills in place. To drop something, use `agent-kit remove <target>`; to rebuild configs to exactly your saved set, use `agent-kit update`.
|
|
24
24
|
|
|
25
25
|
## Commands
|
|
26
26
|
|
|
27
27
|
| Command | What it does |
|
|
28
28
|
|---|---|
|
|
29
|
-
| `agent-kit <profile...>` |
|
|
30
|
-
| `agent-kit outside/llm` | Add/refresh a single registry item
|
|
31
|
-
| `agent-kit
|
|
29
|
+
| `agent-kit add <profile...>` | Add/refresh one or more profiles (additive — keeps the rest). Bare `agent-kit <profile...>` and `install` are shorthands |
|
|
30
|
+
| `agent-kit add outside/llm` | Add/refresh a single registry item on top of what you have (see `agent-kit list`) |
|
|
31
|
+
| `agent-kit remove <target...>` | Remove installed profiles/items; everything else is reconciled to stay |
|
|
32
|
+
| `agent-kit update` | Re-fetch your saved targets and reconcile configs to exactly them (prunes leftovers) |
|
|
32
33
|
| `agent-kit doctor` | Check tools, secrets, model endpoint, MCP servers, skills |
|
|
33
34
|
| `agent-kit secrets` | Enter missing secrets |
|
|
34
35
|
| `agent-kit list` | Show profiles and registry items |
|
|
@@ -44,6 +45,7 @@ Options: `--tool opencode,omp`, `-y/--yes` (no prompts), `--no-shell` (don't edi
|
|
|
44
45
|
profiles/ one file per department; "extends" + "include" registry ids
|
|
45
46
|
registry/ one file per item; id = path under registry/ without .json
|
|
46
47
|
outside/ our own items (registry/outside/llm.json → id "outside/llm")
|
|
48
|
+
anthropic/ Anthropic's skills (registry/anthropic/document-skills.json)
|
|
47
49
|
skills/<group>/ our custom skills (SKILL.md folders)
|
|
48
50
|
src/adapters/ one adapter per tool (opencode.ts, omp.ts)
|
|
49
51
|
```
|
|
@@ -54,21 +56,25 @@ src/adapters/ one adapter per tool (opencode.ts, omp.ts)
|
|
|
54
56
|
{
|
|
55
57
|
"description": "Product backend developers",
|
|
56
58
|
"extends": ["shared"],
|
|
57
|
-
"include": ["context-mode", "outside/backend-skills"]
|
|
59
|
+
"include": ["context-mode", "outside/product-backend-skills"]
|
|
58
60
|
}
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
`shared` is the base every department extends: the LLM provider, company skills, Superpowers and
|
|
63
|
+
`shared` is the base every department extends: the LLM provider, company skills, Superpowers, Context7 and Anthropic's document skills (docx, pdf, pptx, xlsx).
|
|
62
64
|
|
|
63
65
|
### Registry item kinds
|
|
64
66
|
|
|
65
|
-
**`skills`**: a folder of skills, from this repo or any git repo (default branch, latest commit).
|
|
67
|
+
**`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).
|
|
66
68
|
|
|
67
69
|
```json
|
|
68
|
-
{ "kind": "skills", "source": { "type": "local", "path": "skills/backend" } }
|
|
70
|
+
{ "kind": "skills", "source": { "type": "local", "path": "skills/product-backend" } }
|
|
69
71
|
{ "kind": "skills", "source": { "type": "git", "repo": "https://github.com/org/repo.git", "path": "skills" }, "only": ["skill-a"] }
|
|
72
|
+
{ "kind": "skills", "source": { "type": "git", "repo": "https://github.com/anthropics/skills.git", "path": "skills" },
|
|
73
|
+
"only": ["docx", "pdf", "pptx", "xlsx"], "requires": ["python3"] }
|
|
70
74
|
```
|
|
71
75
|
|
|
76
|
+
`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).
|
|
77
|
+
|
|
72
78
|
**`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).
|
|
73
79
|
|
|
74
80
|
```json
|
|
@@ -111,13 +117,13 @@ Model metadata is endpoint-specific — the same model id behind a different gat
|
|
|
111
117
|
|
|
112
118
|
`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`.
|
|
113
119
|
|
|
114
|
-
**What the kit owns.** It records what it installed in `~/.config/outside-agent-kit/state.json`.
|
|
120
|
+
**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.
|
|
115
121
|
|
|
116
122
|
**JSONC.** If `opencode.json` has comments, they are removed when the kit rewrites it (the backup keeps them).
|
|
117
123
|
|
|
118
124
|
## Adding things
|
|
119
125
|
|
|
120
|
-
- **Custom skill**: `agent-kit new skill backend/rate-limiting`, write the SKILL.md, open a PR. Everyone gets it on their next `update`.
|
|
126
|
+
- **Custom skill**: `agent-kit new skill product-backend/rate-limiting`, write the SKILL.md, open a PR. Everyone gets it on their next `update`.
|
|
121
127
|
- **New department**: add `profiles/<dept>.json` and `skills/<dept>/` (`new skill <dept>/<name>` creates the registry entry for you).
|
|
122
128
|
- **Third-party item**: add a `registry/<id>.json` (put your own under `registry/outside/`), then add the id to profiles.
|
|
123
129
|
- **New tool** (Codex, Cursor…): write `src/adapters/<tool>.ts` implementing `Adapter` and list it in `src/adapters/index.ts`.
|
|
@@ -126,15 +132,16 @@ Model metadata is endpoint-specific — the same model id behind a different gat
|
|
|
126
132
|
|
|
127
133
|
Pick one:
|
|
128
134
|
|
|
129
|
-
- **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`.
|
|
135
|
+
- **Private npm registry** (GitHub Packages, Verdaccio): `npm publish`, then devs run `bunx @outsidestudio/agent-kit add <profile>`. Point the `@outsidestudio` scope at your registry in `~/.npmrc` or `bunfig.toml`.
|
|
130
136
|
- **Straight from GitHub**, no registry: `bunx github:madebyoutside/agent-kit <profile>`.
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
**Auto-update.** bunx reuses its cached 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 — bare commands stay current, no `@latest` needed. Set `AGENT_KIT_NO_UPDATE_CHECK=1` to opt out. Copies older than 0.2.3 cannot self-update: if a new command errors (e.g. `Unknown target "add"`), run `bunx @outsidestudio/agent-kit@latest update` once; from then on the bare form updates itself.
|
|
133
139
|
|
|
134
140
|
## Things to verify for your setup
|
|
135
141
|
|
|
136
142
|
- **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.
|
|
137
143
|
- **Model list.** Replace `qwen3-coder` in `registry/outside/llm.json` with the model ids your server actually serves (`curl $OUTSIDE_LLM_BASE_URL/models`).
|
|
144
|
+
- **Document skills.** Anthropic's docx/pdf/pptx/xlsx skills shell out to `python3` (and LibreOffice/pandoc for some conversions). `agent-kit doctor` warns when `python3` is missing; install the rest only if you use those flows.
|
|
138
145
|
- **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.
|
|
139
146
|
- **OpenCode plugin cache.** OpenCode caches git plugins. If an update doesn't show up, clear OpenCode's plugin cache and restart it.
|
|
140
147
|
- **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.
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"description":"Product backend developers","extends":["shared"],"include":["context-mode","outside/backend-skills"]}
|
|
1
|
+
{"description":"Product backend developers","extends":["shared"],"include":["context-mode","outside/product-backend-skills"]}
|
package/profiles/shared.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"description":"Base setup every profile extends","include":["outside/llm","outside/shared-skills","superpowers","context7"]}
|
|
1
|
+
{"description":"Base setup every profile extends","include":["outside/llm","outside/shared-skills","superpowers","context7","anthropic/document-skills"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"description":"Web / frontend developers","extends":["shared"],"include":["context-mode","outside/web-skills"]}
|
|
1
|
+
{"description":"Web / frontend developers","extends":["shared"],"include":["context-mode","outside/web-frontend-skills"]}
|
|
@@ -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
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { doctor } from "./commands/doctor";
|
|
3
|
-
import { install, secrets, update } from "./commands/install";
|
|
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
|
|
|
10
12
|
${color.bold("Usage")}
|
|
11
|
-
agent-kit <target...>
|
|
12
|
-
agent-kit
|
|
13
|
+
agent-kit add <target...> Add/refresh profiles and/or single registry items; leaves everything else installed untouched (shorthand: agent-kit <target...>, also: agent-kit install)
|
|
14
|
+
agent-kit remove <target...> Remove installed profiles/items; the rest is kept and reconciled
|
|
15
|
+
agent-kit update Re-fetch the saved targets and reconcile configs to exactly them
|
|
13
16
|
agent-kit doctor Check tools, secrets, model endpoint and MCP servers
|
|
14
17
|
agent-kit secrets Enter any missing secrets
|
|
15
18
|
agent-kit list Show profiles and registry items
|
|
@@ -23,9 +26,10 @@ ${color.bold("Options")}
|
|
|
23
26
|
--no-shell Don't add the secrets loader to ~/.zshrc / ~/.bashrc
|
|
24
27
|
|
|
25
28
|
${color.bold("Examples")}
|
|
26
|
-
bunx @outsidestudio/agent-kit product-backend
|
|
27
|
-
bunx @outsidestudio/agent-kit product-backend web-frontend
|
|
28
|
-
bunx @outsidestudio/agent-kit outside/llm # just the model provider (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)
|
|
32
|
+
bunx @outsidestudio/agent-kit remove web-frontend # drop web skills, keep backend + shared
|
|
29
33
|
bunx @outsidestudio/agent-kit update
|
|
30
34
|
`;
|
|
31
35
|
|
|
@@ -45,8 +49,42 @@ function parse(argv: string[]) {
|
|
|
45
49
|
return { args, flags };
|
|
46
50
|
}
|
|
47
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
|
+
|
|
48
84
|
async function main() {
|
|
49
|
-
const
|
|
85
|
+
const argv = Bun.argv.slice(2);
|
|
86
|
+
const { args, flags } = parse(argv);
|
|
87
|
+
await selfUpdate(argv);
|
|
50
88
|
const [cmd, ...rest] = args;
|
|
51
89
|
const opts = {
|
|
52
90
|
tools: typeof flags.tool === "string" ? flags.tool.split(",").map((s) => s.trim()).filter(Boolean) : undefined,
|
|
@@ -57,9 +95,13 @@ async function main() {
|
|
|
57
95
|
if (!cmd || flags.help) return console.log(HELP);
|
|
58
96
|
|
|
59
97
|
switch (cmd) {
|
|
98
|
+
case "add":
|
|
60
99
|
case "install":
|
|
61
100
|
if (!rest.length) throw new Error("What to install? Run: agent-kit list");
|
|
62
101
|
return install(rest, opts);
|
|
102
|
+
case "remove":
|
|
103
|
+
if (!rest.length) throw new Error("What to remove? Run: agent-kit list");
|
|
104
|
+
return remove(rest, opts);
|
|
63
105
|
case "update":
|
|
64
106
|
return update(opts);
|
|
65
107
|
case "doctor":
|
package/src/commands/doctor.ts
CHANGED
|
@@ -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");
|
package/src/commands/install.ts
CHANGED
|
@@ -11,6 +11,8 @@ export interface InstallOptions {
|
|
|
11
11
|
tools?: string[];
|
|
12
12
|
yes?: boolean;
|
|
13
13
|
shell?: boolean;
|
|
14
|
+
/** Rebuild configs from exactly these targets, purging managed leftovers. */
|
|
15
|
+
reconcile?: boolean;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
function pickTools(requested?: string[]): string[] {
|
|
@@ -46,10 +48,14 @@ function mergeToolState(prev: ToolState | undefined, next: ToolState): ToolState
|
|
|
46
48
|
export async function install(targets: string[], opts: InstallOptions = {}) {
|
|
47
49
|
const tools = pickTools(opts.tools);
|
|
48
50
|
const { profiles, items: direct } = await resolveTargets(targets);
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
const additive =
|
|
52
|
-
log.head(
|
|
51
|
+
// Installs are additive: they add/refresh the named targets and never touch
|
|
52
|
+
// what else is installed. Only --reconcile runs (update, remove) purge.
|
|
53
|
+
const additive = opts.reconcile !== true;
|
|
54
|
+
log.head(
|
|
55
|
+
targets.length
|
|
56
|
+
? `Setting up ${targets.map((t) => `"${t}"`).join(" + ")} for ${tools.join(" + ")}`
|
|
57
|
+
: `Removing everything the kit manages for ${tools.join(" + ")}`,
|
|
58
|
+
);
|
|
53
59
|
|
|
54
60
|
const ids = [...new Set([...(await resolveProfiles(profiles)), ...direct])];
|
|
55
61
|
const items = await Promise.all(ids.map(loadItem));
|
|
@@ -101,7 +107,10 @@ export async function install(targets: string[], opts: InstallOptions = {}) {
|
|
|
101
107
|
managed: { ...(prev?.managed ?? {}) },
|
|
102
108
|
installed: additive ? { ...(prev?.installed ?? {}), ...plan.fetched } : plan.fetched,
|
|
103
109
|
};
|
|
104
|
-
if (additive && prev)
|
|
110
|
+
if (additive && prev)
|
|
111
|
+
log.step(
|
|
112
|
+
`Additive: keeping ${[...prev.profiles, ...prev.items].join(", ") || "nothing"}; refreshing ${targets.join(", ")}`,
|
|
113
|
+
);
|
|
105
114
|
for (const t of tools) {
|
|
106
115
|
const next = await adapters[t].apply(plan, prev?.managed[t]);
|
|
107
116
|
state.managed[t] = additive ? mergeToolState(prev?.managed[t], next) : next;
|
|
@@ -124,7 +133,31 @@ export async function install(targets: string[], opts: InstallOptions = {}) {
|
|
|
124
133
|
export async function update(opts: InstallOptions = {}) {
|
|
125
134
|
const prev = await readState();
|
|
126
135
|
if (!prev) throw new Error("Nothing installed yet. Run: agent-kit <profile>");
|
|
127
|
-
|
|
136
|
+
// Reconcile: the saved targets are the whole truth, so leftovers from
|
|
137
|
+
// changed profile definitions get purged.
|
|
138
|
+
await install([...prev.profiles, ...prev.items], {
|
|
139
|
+
...opts,
|
|
140
|
+
tools: opts.tools ?? prev.tools,
|
|
141
|
+
reconcile: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Drop targets from the saved install; everything else is reconciled to stay. */
|
|
146
|
+
export async function remove(targets: string[], opts: InstallOptions = {}) {
|
|
147
|
+
const prev = await readState();
|
|
148
|
+
if (!prev) throw new Error("Nothing installed yet. Run: agent-kit <profile>");
|
|
149
|
+
await resolveTargets(targets); // reject unknown names
|
|
150
|
+
const saved = [...prev.profiles, ...prev.items];
|
|
151
|
+
const gone = targets.filter((t) => saved.includes(t));
|
|
152
|
+
if (!gone.length)
|
|
153
|
+
throw new Error(
|
|
154
|
+
`None of ${targets.join(", ")} is installed. Installed: ${saved.join(", ") || "nothing"}. ` +
|
|
155
|
+
`Items pulled in by a profile can only be removed by removing that profile.`,
|
|
156
|
+
);
|
|
157
|
+
const rest = saved.filter((t) => !gone.includes(t));
|
|
158
|
+
log.head(`Removing ${gone.join(", ")}`);
|
|
159
|
+
if (rest.length) log.info(`Keeping: ${rest.join(", ")}`);
|
|
160
|
+
await install(rest, { ...opts, tools: opts.tools ?? prev.tools, reconcile: true });
|
|
128
161
|
}
|
|
129
162
|
|
|
130
163
|
/** Re-prompt for every secret the current install needs. */
|
package/src/commands/new.ts
CHANGED
|
@@ -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 {
|
|
@@ -95,8 +97,10 @@ export interface Plan {
|
|
|
95
97
|
/** What was fetched, for the install log. */
|
|
96
98
|
fetched: Record<string, string>;
|
|
97
99
|
/**
|
|
98
|
-
*
|
|
99
|
-
* keep
|
|
100
|
+
* Additive run (the default for installs): write exactly what this plan
|
|
101
|
+
* names and keep every other entry the kit managed before — no purge of
|
|
102
|
+
* skills/MCP/plugins/providers. Reconcile runs (update, remove) leave this
|
|
103
|
+
* false and rebuild the config from only this plan.
|
|
100
104
|
*/
|
|
101
105
|
additive?: boolean;
|
|
102
106
|
}
|
|
File without changes
|