Skip to content

Latest commit

 

History

History
489 lines (397 loc) · 17.4 KB

File metadata and controls

489 lines (397 loc) · 17.4 KB

API

kitup exposes the same installer concepts in TypeScript, Go, Rust, and Python.

The core flow is:

  1. resolve a local directory tree, embedded files, or public GitHub bundle
  2. resolve safe target agent selection for CLI workflows
  3. validate SKILL.md
  4. copy, update, skip, or report conflicts
  5. write .kitup.json ownership metadata
  6. return a structured report

TypeScript

Package: @kitup/sdk

import {
  detectHosts,
  directoryBundle,
  filesBundle,
  githubBundle,
  installBundledSkill,
  installFlagError,
  installWorkflowError,
  installUxText,
  moduleDirBundle,
  planBundledSkill,
  parseInstallFlags,
  classifyInstallWorkflowExit,
  resolveInstallSelection,
  readInstalledMetadata,
  runBundledSkillInstall,
  statusBundledSkill,
  uninstallBundledSkill,
  updateBundledSkill,
  validateSkillBundle,
  withBundleMetadata,
} from "@kitup/sdk";

Primitive install call:

const report = await installBundledSkill({
  appId: "mycli",
  skillBundle: directoryBundle("./skills/mycli"),
  scope: "user",
});

Embedded module-directory bundle call:

const bundle = await moduleDirBundle(import.meta.url, "./skills/mycli");

Public GitHub bundle call:

const report = await installBundledSkill({
  appId: "mycli",
  skillBundle: githubBundle({
    owner: "acme",
    repo: "mycli-skills",
    path: "skills/mycli",
    ref: "v1.2.3",
  }),
  scope: "user",
});

Implemented functions:

  • loadHostSpec(hostsFile?)
  • resolveHosts({ agents, hostsFile?, hosts? })
  • detectHosts({ home?, cwd?, hostsFile?, scope? })
  • resolveInstallSelection({ home?, cwd?, hostsFile?, scope, agents?, yes?, stdinTTY?, currentAgent? })
  • resolveInstallTargets({ home?, cwd?, hostsFile?, agents?, scope, skillName })
  • validateSkillBundle(bundle, cwd?)
  • computeBundleContentHash(bundle, cwd?)
  • directoryBundle(path)
  • filesBundle(files)
  • moduleDirBundle(importMetaUrl, relativePath)
  • githubBundle(options)
  • withBundleMetadata(bundle, metadata)
  • parseInstallFlags(flags)
  • agentSelectorFromFlags(values)
  • parseScopeFlag(value)
  • classifyInstallWorkflowExit(workflow)
  • installWorkflowError(workflow)
  • installFlagError(errors)
  • runBundledSkillInstall(options)
  • planBundledSkill(options)
  • installBundledSkill(options)
  • updateBundledSkill(options)
  • statusBundledSkill(options)
  • readInstalledMetadata(targetDir)
  • uninstallBundledSkill(options)
  • installUxText

Go

Module: github.com/lathe-cli/kitup/go

import kitup "github.com/lathe-cli/kitup/go"

Primitive install call:

report, err := kitup.InstallBundledSkill(kitup.InstallOptions{
	AppID:       "mycli",
	SkillBundle: kitup.DirectoryBundle("./skills/mycli"),
	Scope:       kitup.UserScope,
})

Public GitHub bundle call:

report, err := kitup.InstallBundledSkill(kitup.InstallOptions{
	AppID: "mycli",
	SkillBundle: kitup.GitHubBundle(kitup.GitHubBundleOptions{
		Owner: "acme",
		Repo:  "mycli-skills",
		Path:  "skills/mycli",
		Ref:   "v1.2.3",
	}),
	Scope: kitup.UserScope,
})

Implemented functions:

  • LoadHostSpec(hostsFile string)
  • ResolveHosts(agents, hosts)
  • DetectHosts(opts, scope)
  • ResolveInstallSelection(opts)
  • ResolveInstallTargets(opts, agents, scope, skillName)
  • ValidateSkillBundle(bundle)
  • ComputeBundleContentHash(bundle)
  • DirectoryBundle(path)
  • FSBundle(fsys, root)
  • FilesBundle(files)
  • GitHubBundle(opts)
  • WithBundleMetadata(bundle, meta)
  • ParseInstallFlags(flags)
  • AgentSelectorFromFlags(values)
  • ParseScopeFlag(value)
  • ClassifyInstallWorkflowExit(report)
  • InstallWorkflowError(report)
  • InstallFlagError(errors)
  • RunBundledSkillInstall(opts)
  • PlanBundledSkill(opts)
  • InstallBundledSkill(opts)
  • UpdateBundledSkill(opts)
  • StatusBundledSkill(opts)
  • ReadInstalledMetadata(targetDir)
  • UninstallBundledSkill(opts)
  • InstallUX

Optional Cobra adapter module: github.com/lathe-cli/kitup/go-cobra

  • NewSkillCommand(opts)
  • NewInstallCommand(opts)
  • NewStatusCommand(opts)
  • NewUninstallCommand(opts)

Set Options.SkillName when mounting the lifecycle commands. Both commands accept --scope and repeatable --agent; status also accepts --json, while uninstall accepts --json and --yes. A non-TTY uninstall without --yes fails before mutation. In TTY mode, uninstall renders the owned targets and confirms before calling the core API. JSON output keeps prompts off stdout.

Rust

Crate: kitup

Primitive install call:

let report = kitup::install_bundled_skill(&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,
    force: false,
})?;

Public GitHub bundle call:

let report = kitup::install_bundled_skill(&kitup::InstallOptions {
    base: kitup::BaseOptions::default(),
    app_id: "mycli".to_string(),
    skill_bundle: kitup::github_bundle(kitup::GitHubBundleOptions {
        owner: "acme".to_string(),
        repo: "mycli-skills".to_string(),
        path: "skills/mycli".to_string(),
        ref_name: "v1.2.3".to_string(),
    }),
    scope: kitup::Scope::User,
    agents: kitup::AgentSelector::Auto,
    force: false,
})?;

Embedded include_dir bundle call:

static SKILL: include_dir::Dir<'_> =
    include_dir::include_dir!("$CARGO_MANIFEST_DIR/skills/mycli");

let bundle = kitup::include_dir_bundle(&SKILL);

Requires the kitup crate feature include-dir and a direct include_dir dependency for the macro.

Implemented functions:

  • load_host_spec(hosts_file)
  • resolve_hosts(agents, hosts)
  • detect_hosts(options, scope)
  • resolve_install_selection(options)
  • resolve_install_targets(options, agents, scope, skill_name)
  • validate_skill_bundle(bundle)
  • compute_bundle_content_hash(bundle)
  • directory_bundle(path)
  • files_bundle(files)
  • include_dir_bundle(dir) with the include-dir feature
  • github_bundle(options)
  • with_bundle_metadata(bundle, metadata)
  • parse_install_flags(flags)
  • agent_selector_from_flags(values, errors)
  • parse_scope_flag(value, errors)
  • classify_install_workflow_exit(report)
  • install_workflow_error(report)
  • install_flag_error(errors)
  • run_bundled_skill_install(options)
  • run_bundled_skill_install_with_io(options, input, output)
  • plan_bundled_skill(options)
  • install_bundled_skill(options)
  • update_bundled_skill(options)
  • status_bundled_skill(options)
  • read_installed_metadata(target_dir)
  • uninstall_bundled_skill(options)
  • INSTALL_UX

Python

Package: kitup-sdk

from kitup import (
    BaseOptions,
    InstallOptions,
    InstallWorkflowOptions,
    classify_install_workflow_exit,
    compute_bundle_content_hash,
    detect_hosts,
    directory_bundle,
    files_bundle,
    github_bundle,
    install_bundled_skill,
    install_flag_error,
    install_workflow_error,
    load_host_spec,
    parse_install_flags,
    parse_scope_flag,
    plan_bundled_skill,
    resolve_hosts,
    resolve_install_selection,
    resolve_install_targets,
    resources_bundle,
    read_installed_metadata,
    run_bundled_skill_install,
    status_bundled_skill,
    uninstall_bundled_skill,
    update_bundled_skill,
    validate_skill_bundle,
    with_bundle_metadata,
)

Primitive install call:

from kitup import BaseOptions, InstallOptions, directory_bundle, install_bundled_skill

report = install_bundled_skill(
    InstallOptions(
        base=BaseOptions(),
        app_id="mycli",
        skill_bundle=directory_bundle("./skills/mycli"),
        scope="user",
    )
)

Embedded package-data bundle call:

from importlib.resources import files
from kitup import resources_bundle

bundle = resources_bundle(files("mycli.skills") / "mycli")

Workflow call:

from kitup import (
    BaseOptions,
    InstallOptions,
    InstallWorkflowOptions,
    directory_bundle,
    run_bundled_skill_install,
)

workflow = run_bundled_skill_install(
    InstallWorkflowOptions(
        install=InstallOptions(
            base=BaseOptions(),
            app_id="mycli",
            skill_bundle=directory_bundle("./skills/mycli"),
            scope="user",
        ),
        stdin_tty=True,
        prompt_scope=True,
    )
)

Implemented functions:

  • load_host_spec(hosts_file=None)
  • resolve_hosts(agents, hosts)
  • detect_hosts(options, scope=None)
  • resolve_install_selection(options)
  • resolve_install_targets(options, agents, scope, skill_name)
  • validate_skill_bundle(bundle, cwd=None)
  • compute_bundle_content_hash(bundle, cwd=None)
  • directory_bundle(path)
  • files_bundle(files)
  • resources_bundle(root)
  • github_bundle(options)
  • with_bundle_metadata(bundle, metadata)
  • parse_install_flags(flags)
  • agent_selector_from_flags(values, errors)
  • parse_scope_flag(value, errors)
  • classify_install_workflow_exit(report)
  • install_workflow_error(report)
  • install_flag_error(errors)
  • run_bundled_skill_install(options)
  • run_bundled_skill_install_with_io(options, input, output)
  • plan_bundled_skill(options)
  • install_bundled_skill(options)
  • update_bundled_skill(options)
  • status_bundled_skill(options)
  • read_installed_metadata(target_dir)
  • uninstall_bundled_skill(options)
  • INSTALL_UX

Options

Install options use the same concepts across languages:

  • appId / AppID / app_id: non-empty owner id written to .kitup.json
  • skillBundle / SkillBundle / skill_bundle: local directory, embedded files, or public GitHub bundle
  • scope: user or project
  • agents: "auto", "*", or explicit host ids
  • force: overwrite unmanaged or different-owner target directories instead of reporting conflicts
  • home, cwd, hostsFile: optional test and embedding overrides

Bundle file paths must use root-relative POSIX paths. SDKs reject empty paths, absolute paths, .., duplicate files, and backslash paths. SDKs exclude .kitup.json, .git, .DS_Store, swap files, and editor backups before validation, hashing, and copy.

The first non-local bundle constructor is GitHub only:

  • TypeScript: githubBundle({ owner, repo, path, ref })
  • Go: GitHubBundle(GitHubBundleOptions{Owner, Repo, Path, Ref})
  • Rust: github_bundle(GitHubBundleOptions { owner, repo, path, ref_name })

GitHub bundle resolution downloads only files under the configured directory path, requires SKILL.md at that bundle root, records the requested ref and resolved commit, and writes GitHub provenance into .kitup.json. It does not search GitHub, install dependencies, execute scripts, handle private auth, or install whole repositories by default.

Installed metadata and lifecycle status

Bundled and embedded inputs can attach optional build identity without changing the skill content hash:

  • sourceId / SourceID / source_id: stable caller-defined bundle identity
  • cliVersion / CLIVersion / cli_version: embedding CLI release version
  • cliRevision / CLIRevision / cli_revision: embedding CLI source or build revision
  • provenance: string-to-string build provenance

Use withBundleMetadata / WithBundleMetadata / with_bundle_metadata with the language's BundledSkillMetadata type. Explicit metadata changes refresh .kitup.json even when skill bytes are unchanged. The existing schema stays at schemaVersion: 1; all new fields are optional, so metadata written by older kitup versions remains readable. The existing version field remains the source version or GitHub ref and is not overloaded with the embedding CLI version.

InstalledMetadata is the normalized public type returned by the reader API and included in each installed status entry. A missing target returns no metadata. An existing directory with missing or malformed ownership metadata returns an error from the reader and an unmanaged conflict from status.

Status is local and offline. StatusReport contains installed, missing, conflicts, and errors, using the same host selection and compatible-path rules as install and uninstall. It does not resolve or fetch a GitHub bundle.

Core uninstall revalidates appId and skillName after atomically moving the target to a same-parent quarantine path. A changed, malformed, or mismatched target is restored and reported as a conflict instead of being deleted. There is no implicit uninstall force mode.

The embedding CLI owns command names and framework attachment. kitup owns standard install flag semantics, selector mapping, user-facing workflow text, summary rendering, confirmation, dry-run planning, workflow exit classification, and execution. For user-facing commands, call runBundledSkillInstall / RunBundledSkillInstall / run_bundled_skill_install with values from the shared flag parsing helpers.

Workflow-only options:

  • yes: skip prompts and accept policy-selected targets
  • dryRun: render and return the plan without writing
  • stdinTTY: whether interactive prompts are allowed
  • currentAgent: host id detected by an embedding agent runtime
  • defaultScope: scope used by Enter at the scope prompt and by yes when scope was not explicit
  • scopeSet: whether the user explicitly provided --scope
  • promptScope: whether missing explicit scope should trigger workflow scope selection
  • input / In and output / Out: prompt and rendering streams

Standard install flags:

  • --scope: user or project
  • --agent: repeatable target agent id; comma-separated values are accepted; * means all hosts and must be the only agent value
  • --dry-run: render the plan without writing
  • --yes / -y: skip prompts and accept policy-selected targets
  • --force: overwrite unmanaged or different-owner target directories

Flag parsing returns a normalized scope, scopeSet, normalized agent selector, yes, dryRun, force, and structured flag errors. An empty agent list maps to auto; * maps to all hosts; explicit ids are deduplicated in input order.

For CLI workflows, pass promptScope: true. If scopeSet is false, TTY mode prompts for scope before agent selection and planning. Enter uses defaultScope or user. Non-TTY without explicit scope and without yes returns scope-selection-required. yes with no explicit scope uses defaultScope or user.

Workflow exit helpers classify reports into:

  • ok
  • canceled
  • selection-error
  • conflict
  • error

Recommended CLI behavior:

mycli skill install
mycli skill install --dry-run
mycli skill install --scope user --agent codex
mycli skill install --scope project --agent codex --agent claude-code
mycli skill install --scope user --agent codex --force

Do not expose plan, update, or upgrade as separate commands. planBundledSkill / updateBundledSkill are the same install path: --dry-run plans, and another install updates kitup-owned copies. Optional status and uninstall commands call the primitive APIs; only the Go Cobra adapter ships those commands.

The lower-level selection resolver remains available for custom shells. It returns one of:

  • install: proceed to plan and confirmation with selectedHostIds
  • select-agents: ask the user to choose from candidateHostIds
  • error: do not write

Non-TTY without explicit agents and without yes returns agent-selection-required. yes with zero detected hosts returns no-detected-hosts; it does not mean all hosts.

In TTY mode, zero detected hosts prompts from all supported hosts. One detected host is auto-selected but still goes through summary confirmation. Multiple detected hosts prompt from the detected host candidates without defaulting to all; an empty selection cancels instead of writing.

Selector semantics:

  • scope: "user" keeps a valid kitup-owned target for the same skill on its configured path; normal ownership checks still decide whether the requesting app may mutate it. Otherwise, it reuses the first userSkillsDirs path that exists as a directory or falls back to the first configured path.
  • scope: "project" keeps a valid kitup-owned target for the same skill on its configured path; normal ownership checks still decide whether the requesting app may mutate it. Otherwise, it reuses the first projectSkillsDirs path that exists as a directory or falls back to the first configured path.
  • agents: "auto" detects a host when any of its non-generic detection paths exists; shared roots such as .agents do not identify a host by themselves.
  • agents: "*" selects every host adapter.
  • explicit agents select canonical host ids or aliases.
  • hosts without a path for the selected scope return an unsupported-scope error.
  • uninstall removes every valid kitup-owned copy for the requested appId and skill across the selected hosts' configured paths; unmanaged and other-owner directories are never removed.

Reports

Install reports include:

  • installed
  • updated
  • skipped
  • conflicts
  • errors

Uninstall reports include:

  • removed
  • skipped
  • conflicts
  • errors

TypeScript returns typed report objects. Go exposes InstallReport, UninstallReport, TargetResult, TargetStatus, and ReportError. Rust exposes InstallReport, UninstallReport, TargetResult, TargetStatus, and ReportError.

The serialized JSON report shape is the same across TypeScript, Go, Rust, and Python. installed, updated, and removed contain target results. skipped and conflicts contain target results plus reason.

Conflict is the safe default. A target directory without matching .kitup.json ownership metadata is reported as a conflict, not overwritten unless force / --force is explicit.