Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
The v0.1 product is deliberately small:

- validate a local skill directory
- validate an embedded bundled skill directory tree
- detect local agent hosts
- resolve safe CLI install selection before writes
- run the safe CLI install workflow for bundled skills
- resolve user and project skill directories
- copy bundled skill files
- write `.kitup.json` ownership metadata
Expand All @@ -22,12 +25,12 @@ Do not turn kitup into a marketplace, registry client, package manager, MCP serv

One bundled skill, one SDK call, many agent hosts.

The embedding CLI owns the skill. `kitup` owns the boring installer layer: host path lookup, detection, validation, copy/update/uninstall semantics, metadata, conflicts, and reports.
The embedding CLI owns the skill and command shell. `kitup` owns the boring installer layer: host path lookup, detection, selection prompts, summary confirmation, validation, copy/update/uninstall semantics, metadata, conflicts, and reports.

Keep the developer experience boring. A CLI author should only need to provide:

- `appId`
- `skillDir`
- `skillBundle`
- `scope`
- `agents`

Expand All @@ -52,15 +55,15 @@ When docs and executable checks disagree, fix the source of truth or surface the

Before non-trivial work, classify the request as one of:

- v0.1 bundled-skill installer behavior
- v0.1 bundled-skill source, selection, and installer behavior
- host adapter data correction
- fixture or parity improvement
- documentation of existing behavior
- post-v0.1 scope

Only the first four are normal work. For post-v0.1 scope, do not implement product surface until the boundary is explicitly changed in docs and golden cases.

Every new installer behavior needs a golden case. A behavior that cannot be expressed in `testdata/cases` is not ready to become SDK behavior.
Every new source, selection, or installer behavior needs a golden case. A behavior that cannot be expressed in `testdata/cases` is not ready to become SDK behavior.

Do not add speculative abstractions for future registries, package managers, remote sources, marketplace metadata, auth, or dependency resolution. Build the shallowest implementation that satisfies the existing case matrix.

Expand Down Expand Up @@ -98,6 +101,8 @@ Content hashes must be deterministic across TypeScript, Go, and Rust. Hash bundl

Reports are API contracts. Return structured `installed`, `updated`, `skipped`, `conflicts`, and `errors` data instead of relying on logs.

Bundled skill sources are directory trees. `SKILL.md` must live at the bundle root, but references, scripts, assets, and other regular files are part of the same source and must be validated, hashed, and copied as a tree.

## Multi-Language Parity

TypeScript, Go, and Rust should be native SDKs that consume the same shared spec and fixtures.
Expand Down
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
Keep changes inside the v0.1 boundary:

- validate local skill directories
- validate embedded skill directory trees
- detect agent hosts
- resolve safe CLI install selection
- run the safe CLI install workflow
- resolve user and project skill directories
- copy, update, and uninstall kitup-owned installs
- preserve `.kitup.json` ownership safety
Expand Down Expand Up @@ -70,7 +73,7 @@ Host support is data-first.

## SDK Behavior Changes

Every installer behavior needs a golden case in `testdata/cases`.
Every source, selection, or installer behavior needs a golden case in `testdata/cases`.

Before opening a pull request:

Expand Down
129 changes: 109 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

Shared installer SDK for bundled Agent Skills.

CLI authors ship a skill with their tool. `kitup` detects local coding-agent hosts, validates `SKILL.md`, copies the bundled skill into the right host directories, and writes ownership metadata so updates stay safe.
CLI authors ship a skill with their tool. `kitup` models that bundled skill as a directory tree, resolves safe agent targets, validates `SKILL.md`, copies the full tree into the right host directories, and writes ownership metadata so updates stay safe.

```text
mycli skill install
-> kitup SDK
-> local agent hosts
-> installed SKILL.md + .kitup.json
-> installed skill tree + .kitup.json
```

## What it does

- detect installed agent hosts
- resolve user and project skill directories
- resolve safe CLI install selection before writing
- validate bundled skills
- install from a local directory or embedded bundle tree
- copy, update, and uninstall kitup-owned installs
- refuse unsafe overwrite conflicts
- return structured install reports
Expand All @@ -35,7 +37,7 @@ mycli/
skills/mycli/SKILL.md
```

Your CLI owns the command name, flags, prompts, and bundled skill location. `kitup` owns host detection, target paths, copy/update semantics, metadata, and conflicts.
Your CLI owns the command name and framework shell. `kitup` owns the standard install flags, agent selector mapping, host detection, safe selection policy, summary text, confirmation, workflow exit classification, target paths, bundle validation, copy/update semantics, metadata, and conflicts.

### TypeScript

Expand All @@ -45,15 +47,53 @@ Install:
npm install @kitup/sdk
```

Use:
Use the workflow API for user-facing install commands:

```ts
import { installBundledSkill } from "@kitup/sdk";
import {
directoryBundle,
installFlagError,
installWorkflowError,
parseInstallFlags,
runBundledSkillInstall,
} from "@kitup/sdk";

const flags = parseInstallFlags({
scope: "user",
agents: ["codex"],
yes: false,
dryRun: false,
});
const flagError = installFlagError(flags.errors);
if (flagError) throw flagError;

const report = await installBundledSkill({
const result = await runBundledSkillInstall({
appId: "mycli",
skillDir: "./skills/mycli",
skillBundle: directoryBundle("./skills/mycli"),
scope: flags.scope,
scopeSet: flags.scopeSet,
promptScope: true,
agents: flags.agents,
yes: flags.yes,
dryRun: flags.dryRun,
});
const workflowError = installWorkflowError(result);
if (workflowError) throw workflowError;
```

For embedded bundles, pass the whole skill tree:

```ts
import { filesBundle, runBundledSkillInstall } from "@kitup/sdk";

await runBundledSkillInstall({
appId: "mycli",
skillBundle: filesBundle([
{ path: "SKILL.md", contents: skillMd },
{ path: "references/guide.md", contents: guide },
]),
scope: "user",
agents: ["codex"],
});
```

Expand All @@ -65,18 +105,55 @@ Install:
go get github.com/samzong/kitup/go
```

Use:
Use the workflow API for user-facing install commands:

```go
import kitup "github.com/samzong/kitup/go"
import (
"os"

kitup "github.com/samzong/kitup/go"
)

result, err := kitup.RunBundledSkillInstall(kitup.InstallWorkflowOptions{
InstallOptions: kitup.InstallOptions{
AppID: "mycli",
SkillBundle: kitup.DirectoryBundle("./skills/mycli"),
Scope: kitup.UserScope,
Agents: kitup.AutoAgents(),
},
Yes: false,
Out: os.Stdout,
})
```

For `go:embed`, pass the embedded directory tree:

report, err := kitup.InstallBundledSkill(kitup.InstallOptions{
AppID: "mycli",
SkillDir: "./skills/mycli",
Scope: kitup.UserScope,
```go
result, err := kitup.RunBundledSkillInstall(kitup.InstallWorkflowOptions{
InstallOptions: kitup.InstallOptions{
AppID: "mycli",
SkillBundle: kitup.FSBundle(embeddedSkills, "skills/mycli"),
Scope: kitup.UserScope,
Agents: kitup.ExplicitAgents("codex"),
},
Yes: false,
})
```

For Cobra CLIs, use the optional adapter. The adapter does not own installer behavior; it only wires Cobra flags to the core workflow.

```go
import (
kitup "github.com/samzong/kitup/go"
kitupcobra "github.com/samzong/kitup/go-cobra"
)

root.AddCommand(kitupcobra.NewSkillCommand(kitupcobra.Options{
AppID: "mycli",
Bundle: kitup.FSBundle(embeddedSkills, "skills/mycli"),
}))
```

### Rust

Install:
Expand All @@ -87,17 +164,29 @@ cargo add kitup

Use:

Set `stdin_tty` from your CLI's terminal detection.

```rust
let report = kitup::install_bundled_skill(&kitup::InstallOptions {
base: kitup::BaseOptions::default(),
app_id: "mycli".to_string(),
skill_dir: "./skills/mycli".into(),
scope: kitup::Scope::User,
agents: kitup::AgentSelector::Auto,
let result = kitup::run_bundled_skill_install(&kitup::InstallWorkflowOptions {
install: kitup::InstallOptions {
base: kitup::BaseOptions::default(),
app_id: "mycli".to_string(),
skill_bundle: kitup::directory_bundle("./skills/mycli"),
scope: kitup::Scope::User,
agents: kitup::AgentSelector::Auto,
},
yes: false,
dry_run: false,
stdin_tty: stdin_tty,
current_agent: None,
})?;
```

The report contains `installed`, `updated`, `skipped`, `conflicts`, and `errors`.
The workflow result contains the selected agents, dry-run plan, final install report, and cancellation state. The final install report contains `installed`, `updated`, `skipped`, `conflicts`, and `errors`.

For lower-level integrations, `InstallBundledSkill` / `installBundledSkill` / `install_bundled_skill` remain primitive copy APIs. Do not wire a user-facing CLI directly to `agents: "auto"` unless you intentionally want primitive auto-detect behavior. Use the shared flag parsing, selector mapping, UX strings, and workflow exit helpers for CLI commands.

For user-facing install commands, missing `--scope` is interactive state, not `user`. Pass `scopeSet` and enable `promptScope` so TTY workflows ask for scope before agent selection. `--yes` uses the configured default scope.

## Docs

Expand Down
Loading
Loading