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
37 changes: 25 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ jobs:
- name: Build Python package
if: steps.published.outputs.pypi != 'true'
run: uv build --directory python
- name: Publish Go core module tag
run: |
version="${GITHUB_REF_NAME#v}"
go_tag="go/v${version}"
if git rev-parse -q --verify "refs/tags/${go_tag}" >/dev/null; then
test "$(git rev-list -n 1 "${go_tag}")" = "$GITHUB_SHA"
else
git tag "$go_tag" "$GITHUB_SHA"
git push origin "refs/tags/${go_tag}"
fi
- name: Verify Go Cobra against published core
run: node scripts/check-go-modules.mjs --published-core
env:
GOPROXY: direct
GONOSUMDB: github.com/lathe-cli/kitup/go
- name: Publish Go Cobra module tag
run: |
version="${GITHUB_REF_NAME#v}"
go_tag="go-cobra/v${version}"
if git rev-parse -q --verify "refs/tags/${go_tag}" >/dev/null; then
test "$(git rev-list -n 1 "${go_tag}")" = "$GITHUB_SHA"
else
git tag "$go_tag" "$GITHUB_SHA"
git push origin "refs/tags/${go_tag}"
fi
- name: Publish npm package
if: steps.published.outputs.npm != 'true'
run: |
Expand All @@ -94,18 +119,6 @@ jobs:
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist
- name: Publish Go module tags
run: |
version="${GITHUB_REF_NAME#v}"
for module in go go-cobra; do
go_tag="${module}/v${version}"
if git rev-parse -q --verify "refs/tags/${go_tag}" >/dev/null; then
test "$(git rev-list -n 1 "${go_tag}")" = "$GITHUB_SHA"
else
git tag "$go_tag" "$GITHUB_SHA"
git push origin "refs/tags/${go_tag}"
fi
done
- name: Create GitHub release
run: |
gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 || gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" --generate-notes
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ mycli skill install
- validate bundled skills
- install from a local directory, embedded bundle tree, or public GitHub bundle directory
- copy, update, and uninstall kitup-owned installs
- inspect installed ownership and CLI build metadata without parsing files directly
- refuse unsafe overwrite conflicts
- return structured install reports
- return structured install, status, and uninstall reports

## What it is not

Expand Down Expand Up @@ -91,6 +92,8 @@ const githubSkillBundle = githubBundle({
});
```

Attach CLI build identity to a bundled or embedded skill with `withBundleMetadata`. The SDK records it in the backward-compatible `.kitup.json` document and exposes it through `readInstalledMetadata` and `statusBundledSkill`.

### Go

Install:
Expand Down Expand Up @@ -143,11 +146,14 @@ import (
)

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

The adapter mounts `skill install`, `skill status`, and `skill uninstall`. Status and uninstall accept `--json`; non-interactive uninstall requires `--yes`, while interactive uninstall confirms before removing any target.

### Rust

Install:
Expand Down Expand Up @@ -225,7 +231,7 @@ from kitup import resources_bundle
bundle = resources_bundle(files("mycli.skills") / "mycli")
```

For non-interactive or embedding scenarios, call `install_bundled_skill`, `plan_bundled_skill`, `update_bundled_skill`, or `uninstall_bundled_skill` directly.
For non-interactive or embedding scenarios, call `install_bundled_skill`, `plan_bundled_skill`, `update_bundled_skill`, `status_bundled_skill`, `read_installed_metadata`, or `uninstall_bundled_skill` directly.

## Docs

Expand Down
39 changes: 39 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ import {
parseInstallFlags,
classifyInstallWorkflowExit,
resolveInstallSelection,
readInstalledMetadata,
runBundledSkillInstall,
statusBundledSkill,
uninstallBundledSkill,
updateBundledSkill,
validateSkillBundle,
withBundleMetadata,
} from "@kitup/sdk";
```

Expand Down Expand Up @@ -81,6 +84,7 @@ Implemented functions:
- `filesBundle(files)`
- `moduleDirBundle(importMetaUrl, relativePath)`
- `githubBundle(options)`
- `withBundleMetadata(bundle, metadata)`
- `parseInstallFlags(flags)`
- `agentSelectorFromFlags(values)`
- `parseScopeFlag(value)`
Expand All @@ -91,6 +95,8 @@ Implemented functions:
- `planBundledSkill(options)`
- `installBundledSkill(options)`
- `updateBundledSkill(options)`
- `statusBundledSkill(options)`
- `readInstalledMetadata(targetDir)`
- `uninstallBundledSkill(options)`
- `installUxText`

Expand Down Expand Up @@ -140,6 +146,7 @@ Implemented functions:
- `FSBundle(fsys, root)`
- `FilesBundle(files)`
- `GitHubBundle(opts)`
- `WithBundleMetadata(bundle, meta)`
- `ParseInstallFlags(flags)`
- `AgentSelectorFromFlags(values)`
- `ParseScopeFlag(value)`
Expand All @@ -150,13 +157,19 @@ Implemented functions:
- `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

Expand Down Expand Up @@ -217,6 +230,7 @@ Implemented functions:
- `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)`
Expand All @@ -228,6 +242,8 @@ Implemented functions:
- `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`

Expand Down Expand Up @@ -257,10 +273,13 @@ from kitup import (
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,
)
```

Expand Down Expand Up @@ -326,6 +345,7 @@ Implemented functions:
- `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)`
Expand All @@ -337,6 +357,8 @@ Implemented functions:
- `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`

Expand All @@ -361,6 +383,23 @@ The first non-local bundle constructor is GitHub only:

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:
Expand Down
3 changes: 2 additions & 1 deletion docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ git push origin vX.Y.Z

Do not tag the release branch. Do not publish packages by hand during the normal flow.

The release workflow publishes npm, PyPI, and crates.io packages, creates the `go/vX.Y.Z` and `go-cobra/vX.Y.Z` tags, creates the GitHub Release, and runs the public install smoke check.
The release workflow creates `go/vX.Y.Z`, verifies the Cobra adapter against that published core version, creates `go-cobra/vX.Y.Z`, publishes npm, PyPI, and crates.io packages, creates the GitHub Release, and runs the public install smoke check. Both Go modules keep the same release version; only their publication steps are ordered by dependency.

## First npm Release

Expand All @@ -76,6 +76,7 @@ The release workflow is resumable:
- If PyPI already has the version, Python build and publish are skipped.
- If crates.io already has the version, crate publish is skipped.
- If `go/vX.Y.Z` or `go-cobra/vX.Y.Z` already exists, the workflow verifies that it points at the release commit.
- If Cobra verification fails after the core tag is published, rerunning the workflow reuses the verified core tag and does not create the Cobra tag until its declared core dependency passes.

Do not delete and recreate a release tag after any registry has accepted the version unless the tag points at the wrong commit and the recovery plan is explicit.

Expand Down
10 changes: 5 additions & 5 deletions docs/architecture.mmd
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ flowchart TB

subgraph SDK["SDK (ts / go / rust / python)"]
direction TB
WORKFLOW["Install Workflow\nRunBundledSkillInstall"]:::execution
WORKFLOW["Install Workflow and Cobra Adapter\ninstall · status · uninstall"]:::execution
BUNDLE["Bundle Resolver\nlocal · embedded · GitHub"]:::execution
VALIDATE["Validator\nSKILL.md frontmatter"]:::execution
HOST["Host Resolver\nids · aliases · detection · targets"]:::execution
INSTALL["Installer\nplan · conflict policy · copy · update · uninstall"]:::execution
REPORT["Reports\nInstallReport · UninstallReport"]:::execution
INSTALL["Lifecycle Core\nplan · status · copy · update · safe uninstall"]:::execution
REPORT["Reports\nInstallReport · StatusReport · UninstallReport"]:::execution
end

HOSTSPEC["Host Spec\nspec/hosts.json"]:::contract
Expand All @@ -21,7 +21,7 @@ flowchart TB
TARGETS["Agent Host\nDirectory State"]:::state
METADATA[".kitup.json"]:::state

AUTHOR -->|"provides flags"| WORKFLOW
AUTHOR -->|"provides flags and optional build metadata"| WORKFLOW
WORKFLOW --> BUNDLE
WORKFLOW --> HOST
BUNDLE --> VALIDATE
Expand All @@ -31,7 +31,7 @@ flowchart TB
VALIDATE --> INSTALL
HOST --> INSTALL
INSTALL -->|"copies, updates, removes"| TARGETS
INSTALL -->|"writes .kitup.json"| METADATA
INSTALL -->|"reads and writes .kitup.json"| METADATA
INSTALL -->|"returns report"| REPORT

SCHEMAS -.-> HOSTSPEC
Expand Down
Loading
Loading