Skip to content
Closed
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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Host support is data-first.

- Put paths and aliases in `spec/hosts.json` whenever possible.
- Do not add host-specific branching unless the generic resolver cannot express the host.
- `projectSkillsDirs` and `userSkillsDirs` are ordered; the first path is the canonical install target.
- `projectSkillsDirs` and `userSkillsDirs` are ordered; reuse the first existing compatible path, or fall back to the first canonical path.
- Project paths must be relative. User paths must start with `~/`.
- A host may be project-only or user-only.
- Multiple selected hosts may resolve to the same target directory; copy once and report all matching hosts.
Expand Down
15 changes: 10 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ GO_FILES := $(shell find $(GO_DIR) $(GO_COBRA_DIR) $(EXAMPLE_GO_DIR) -name '*.go

# ── Quality ──────────────────────────────────────────────────────────────────

.PHONY: check test test-ts test-go test-go-cobra test-rust test-python fmt fmt-ts fmt-go fmt-rust fmt-python
.PHONY: check test test-ts test-go test-go-cobra test-go-release test-rust test-python fmt fmt-ts fmt-go fmt-rust fmt-python

check: ## Full parity gate
node scripts/check.mjs
Expand All @@ -31,10 +31,13 @@ test-ts: ## Run TypeScript tests
pnpm --dir $(TS_DIR) test

test-go: ## Run Go SDK tests
cd $(GO_DIR) && go test ./...
cd $(GO_DIR) && GOWORK=off go test ./...

test-go-cobra: ## Run Go Cobra adapter tests
cd $(GO_COBRA_DIR) && go test ./...
sh scripts/check-go-cobra.sh

test-go-release: ## Verify packaged Go modules from an external consumer
sh scripts/check-go-release.sh

test-rust: ## Run Rust SDK tests
cargo test --manifest-path $(RUST_DIR)/Cargo.toml
Expand Down Expand Up @@ -62,11 +65,13 @@ fmt-python: ## Lint and format Python code

.PHONY: generate generate-check

generate: ## Refresh generated host constants
generate: ## Refresh generated host constants and Go test fixtures
node scripts/sync-hosts.mjs
node scripts/sync-go-testdata.mjs

generate-check: ## Verify generated host constants
generate-check: ## Verify generated host constants and Go test fixtures
node scripts/sync-hosts.mjs --check
node scripts/sync-go-testdata.mjs --check

# ── Examples ─────────────────────────────────────────────────────────────────

Expand Down
4 changes: 2 additions & 2 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,8 @@ In TTY mode, zero detected hosts prompts from all supported hosts. One detected

Selector semantics:

- `scope: "user"` installs into the first `userSkillsDirs` path for each host.
- `scope: "project"` installs into the first `projectSkillsDirs` path for each host.
- `scope: "user"` reuses the first existing `userSkillsDirs` path, or creates the first canonical path when none exist.
- `scope: "project"` reuses the first existing `projectSkillsDirs` path, or creates the first canonical path when none exist.
- `agents: "auto"` uses host detection.
- `agents: "*"` selects every host adapter.
- explicit agents select canonical host ids or aliases.
Expand Down
10 changes: 10 additions & 0 deletions docs/RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ Do not tag the release branch. Do not publish packages by hand during the normal

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 Go modules share the same release version. No workspace or local replacement
is committed. Source checks copy the modules to a temporary directory and add a
one-off replacement there so the Cobra adapter can test unreleased core APIs.
Release checks disable workspace resolution and verify both module archives from
a temporary consumer:

```bash
make test-go-release
```

## First npm Release

npm trusted publishing is configured in the npm package settings. For the first package version, the package settings may not exist yet.
Expand Down
4 changes: 3 additions & 1 deletion docs/host-adapter-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Each host entry describes where a local Agent Skill can be installed and how the

`projectSkillsDirs` and `userSkillsDirs` are ordered.

The first path is the canonical install target for that host. Later paths are compatible discovery roots that the host also scans. SDKs should install to the first path unless a caller explicitly requests another supported path.
The first path is the canonical install target for that host. Later paths are compatible roots that the host also scans. SDKs reuse the first supported path that already exists, preserving an established installation location. If none exist, SDKs create the first canonical path.

Project paths must be relative paths. User paths must be home-relative paths beginning with `~/`.
All adapter paths use `/` separators and non-empty segments; `..`, backslashes, colons, and NUL bytes are invalid.
Expand All @@ -27,6 +27,8 @@ Aliases are for ecosystem compatibility only. SDK result objects should return t

Detection should check path existence. Entries may be home-relative paths such as `~/.codex` or project-relative paths such as `.replit`.

SDKs check every non-generic detection path for a host. Every `detect` entry must be evidence that the specific host is present; a compatibility root that merely belongs to another host belongs in `projectSkillsDirs` or `userSkillsDirs`, not in `detect`. Shared roots and files such as `~/.agents` and `package.json` do not identify a specific host by themselves and must not cause that host to be auto-selected.

Detection must not run host binaries, start editors, mutate configuration, or require network access.

Explicit host selection should still resolve install targets even when detection paths are absent.
Expand Down
2 changes: 0 additions & 2 deletions go-cobra/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.6 // indirect
)

replace github.com/lathe-cli/kitup/go => ../go
2 changes: 2 additions & 0 deletions go-cobra/go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/lathe-cli/kitup/go v0.1.3 h1:7eEW8mDr5MbXFaTwr2dlnH8ebg+3Kmhj2/j4d9YfQNQ=
github.com/lathe-cli/kitup/go v0.1.3/go.mod h1:dZgJDmFRKjaFBZyaP1qlzOB9IEafnf1/ai4KX4hMA4c=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
Expand Down
17 changes: 13 additions & 4 deletions go-cobra/skill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@ import (
"path/filepath"
"strings"
"testing"
"testing/fstest"

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

func testBundle() kitup.SkillBundle {
return kitup.FSBundle(fstest.MapFS{
"basic/SKILL.md": {
Data: []byte("---\nname: basic\ndescription: Basic skill.\n---\n"),
},
}, "basic")
}

func TestSkillCommandInstallsWithCoreFlags(t *testing.T) {
home := t.TempDir()
var out bytes.Buffer
cmd := NewSkillCommand(Options{
AppID: "example-cli",
Bundle: kitup.DirectoryBundle(filepath.Join("..", "testdata", "skills", "basic")),
Bundle: testBundle(),
Home: home,
Out: &out,
})
Expand All @@ -36,7 +45,7 @@ func TestInstallCommandPromptsForScopeBeforeInstall(t *testing.T) {
var out bytes.Buffer
cmd := NewSkillCommand(Options{
AppID: "example-cli",
Bundle: kitup.DirectoryBundle(filepath.Join("..", "testdata", "skills", "basic")),
Bundle: testBundle(),
Home: home,
CWD: workspace,
StdinTTY: true,
Expand Down Expand Up @@ -71,7 +80,7 @@ func TestInstallCommandForceOverwritesUnmanaged(t *testing.T) {
var out bytes.Buffer
cmd := NewSkillCommand(Options{
AppID: "example-cli",
Bundle: kitup.DirectoryBundle(filepath.Join("..", "testdata", "skills", "basic")),
Bundle: testBundle(),
Home: home,
Out: &out,
})
Expand All @@ -89,7 +98,7 @@ func TestInstallCommandForceOverwritesUnmanaged(t *testing.T) {
func TestInstallCommandReturnsCoreFlagError(t *testing.T) {
cmd := NewInstallCommand(Options{
AppID: "example-cli",
Bundle: kitup.DirectoryBundle(filepath.Join("..", "testdata", "skills", "basic")),
Bundle: testBundle(),
Home: t.TempDir(),
})
cmd.SetArgs([]string{"--scope", "bad"})
Expand Down
Loading
Loading