akm-cli 0.0.18 → 0.0.20

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 CHANGED
@@ -10,167 +10,97 @@ A package manager for AI agent capabilities -- scripts, skills, commands,
10
10
  agents, and knowledge -- that works with any AI coding assistant that can run
11
11
  shell commands.
12
12
 
13
- `akm` organizes reusable scripts, prompts, and agent configs into a searchable
14
- **stash**, shares them as installable **kits** via **registries**, and gives
15
- any model a way to discover and use them. No plugins required -- just CLI
16
- output any tool-calling model can read.
13
+ ## Install
17
14
 
18
- ## Requirements
15
+ ```sh
16
+ # Standalone binary (no runtime dependencies)
17
+ curl -fsSL https://raw.githubusercontent.com/itlackey/agentikit/main/install.sh | bash
19
18
 
20
- `akm` requires [Bun](https://bun.sh) v1.0+ as its runtime. It uses Bun-specific
21
- APIs (`bun:sqlite`) that are **not available in Node.js**.
19
+ # Or via Bun
20
+ bun install -g akm-cli
21
+ ```
22
22
 
23
- > **Don't want Bun?** Use the [standalone binary](#standalone-binary) instead -- it
24
- > bundles everything and has no runtime dependencies.
23
+ Upgrade in place with `akm upgrade`.
25
24
 
26
25
  ## Quick Start
27
26
 
28
27
  ```sh
29
- # Install (requires Bun v1.0+)
30
- bun install -g akm-cli
31
-
32
- # Initialize your stash
33
- akm init
34
-
35
- # Add a kit from GitHub
36
- akm add github:owner/repo
37
-
38
- # Clone an asset to a specific directory
39
- akm clone script:deploy.sh --dest ./project/.claude
40
-
41
- # Search for assets
42
- akm search "deploy"
43
-
44
- # Show an asset
45
- akm show script:deploy.sh
28
+ akm init # Initialize your stash
29
+ akm add github:owner/repo # Add a kit from GitHub
30
+ akm search "deploy" # Find assets
31
+ akm show script:deploy.sh # View details and run command
46
32
  ```
47
33
 
48
- ## Works Any AI Agent
49
-
50
- `akm` is platform agnostic. Any model that can execute shell commands can search
51
- your stash and use what it finds. The workflow is three commands:
52
-
53
- 1. `akm search "what you need"` -- find relevant assets (returns JSON by default)
54
- 2. `akm show <ref>` -- get the details (run command, instructions, prompt, etc.)
55
- 3. Use the asset -- execute the `run` command, follow the skill instructions, fill in the template
34
+ ## Features
56
35
 
57
- ### Drop-in prompt snippet
36
+ ### Works with Any AI Agent
58
37
 
59
- Add this to your `AGENTS.md`, `CLAUDE.md`, `.github/copilot-instructions.md`, system prompt, or any instruction
60
- file to give your agent access to your stash without any additional setup:
38
+ Any model that can run shell commands can use `akm`. Add this to your
39
+ `AGENTS.md`, `CLAUDE.md`, or system prompt:
61
40
 
62
41
  ~~~markdown
63
42
  ## Resources & Capabilities
64
43
 
65
- You have access to a searchable library of scripts, skills, commands, agents, and knowledge documents via the `akm` CLI. Use it to find and use capabilities before writing something from scratch. Always search the stash first when you need a capability.
66
-
67
- Use `akm -h` for more information about searching and using assets.
44
+ You have access to a searchable library of scripts, skills, commands, agents,
45
+ and knowledge documents via the `akm` CLI. Use `akm -h` for details.
68
46
  ~~~
69
47
 
70
- That's it. No plugin, no SDK, no integration code. The model reads the JSON
71
- output from `akm` and acts on it. If you would like more detailed instructions, check out the example [AGENTS.md](docs/AGENTS.md)
72
-
73
- ### Platform plugins (optional)
48
+ No plugins, SDKs, or integration code required. Platform-specific plugins
49
+ (e.g., [OpenCode](https://github.com/itlackey/akm-plugins?tab=readme-ov-file#opencode))
50
+ are available for tighter integration but purely optional.
74
51
 
75
- For tighter integration, plugins are available for some platforms. These add
76
- native tool bindings so the agent doesn't need to shell out, but they're
77
- purely optional -- the CLI works everywhere.
52
+ ### Clone Assets Anywhere
78
53
 
79
- **OpenCode** -- Add the [OpenCode plugin](https://github.com/itlackey/akm-plugins?tab=readme-ov-file#opencode) to your `opencode.json`:
80
-
81
- ```json
82
- {
83
- "plugin": ["akm-opencode"]
84
- }
85
- ```
86
-
87
- **Claude Code** -- Add the prompt snippet above to your `CLAUDE.md` or
88
- project instructions. Claude Code can run `akm` commands directly.
89
-
90
- **Everything else** -- If your agent can run shell commands, it can use `akm`.
91
- Add the prompt snippet above or in [AGENTS.md](docs/AGENTS.md) to whatever instruction/rules mechanism your platform uses.
92
-
93
- ## The Stash
94
-
95
- Your stash is the local library where assets live. It combines three sources
96
- in priority order:
97
-
98
- 1. **Primary stash** -- Your personal assets (`AKM_STASH_DIR`), created by `akm init`
99
- 2. **Search paths** -- Additional directories (team shares, project dirs, etc.)
100
- 3. **Installed kits** -- Kits from npm, GitHub, or git via `akm add` (cache-managed)
101
-
102
- The first match wins, so local assets always override installed ones. Use
103
- `akm clone` to fork an installed asset into your stash for editing.
104
-
105
- ## Registries
106
-
107
- Registries are indexes of available kits. akm ships with the official
108
- [akm-registry](https://github.com/itlackey/akm-registry) pre-configured.
54
+ `akm clone` copies any asset from your stash or a remote source into a
55
+ target directory for local editing:
109
56
 
110
57
  ```sh
111
- # Search the official registry
112
- akm registry search "code review"
113
-
114
- # Add a third-party registry
115
- akm registry add https://example.com/registry/index.json --name team
116
-
117
- # List configured registries
118
- akm registry list
58
+ akm clone script:deploy.sh # Clone to your stash
59
+ akm clone script:deploy.sh --dest ./project/.claude # Clone to a specific directory
60
+ akm clone script:deploy.sh --name my-deploy.sh # Clone with a new name
61
+ akm clone "npm:@scope/pkg//script:deploy.sh" --force # Clone from a remote package
119
62
  ```
120
63
 
121
- See the [Registry docs](docs/registry.md) for hosting your own registry,
122
- the v2 index format with asset-level metadata, and more.
64
+ Key behaviors:
65
+ - Type subdirectories are appended automatically (e.g., `--dest ./project/.claude` becomes `./project/.claude/scripts/deploy.sh`)
66
+ - Skills clone as entire directories; scripts/commands clone as single files
67
+ - Remote packages are fetched on-demand without registering as installed kits
68
+ - `--force` overwrites existing assets
123
69
 
124
- ## Searching and Showing Assets
70
+ ### skills.sh Integration
125
71
 
126
- Search returns brief JSON by default. Use `--detail normal` or `--detail full`
127
- when you want origin, tags, or explainability metadata:
72
+ `akm` includes [skills.sh](https://skills.sh) as a built-in registry. Community
73
+ skills from skills.sh are searchable out of the box alongside the official
74
+ registry -- no setup required:
128
75
 
129
76
  ```sh
130
- akm search "docker" --type script
77
+ akm search "code review" # Searches skills.sh and official registry
78
+ akm registry search "code review" # Search registries directly
131
79
  ```
132
80
 
133
- ```json
134
- {
135
- "hits": [
136
- {
137
- "name": "docker-build",
138
- "type": "script",
139
- "description": "Build and push Docker images",
140
- "action": "akm show script:docker-build.sh -> execute the run command"
141
- }
142
- ]
143
- }
144
- ```
81
+ Results include install counts and link back to skills.sh for details. The
82
+ provider caches queries for 15 minutes with a 24-hour stale fallback.
145
83
 
146
- Show returns everything the agent needs to act:
84
+ ### Registries and Private Registry Support
147
85
 
148
- ```sh
149
- akm show script:docker-build.sh
150
- ```
151
-
152
- ```json
153
- {
154
- "type": "script",
155
- "name": "docker-build.sh",
156
- "origin": null,
157
- "action": "Execute the run command below",
158
- "run": "bash /path/to/scripts/docker-build.sh",
159
- "setup": "bun install",
160
- "cwd": "/path/to/scripts"
161
- }
162
- ```
163
-
164
- For knowledge assets, navigate without loading the entire document:
86
+ Registries are indexes of available kits. The official
87
+ [akm-registry](https://github.com/itlackey/akm-registry) is pre-configured.
165
88
 
166
89
  ```sh
167
- akm show knowledge:api-guide toc
168
- akm show knowledge:api-guide section "Authentication"
90
+ akm registry search "code review" # Search registries
91
+ akm registry add https://example.com/registry/index.json --name team # Add a registry
92
+ akm registry list # List configured registries
169
93
  ```
170
94
 
171
- ## Installing and Sharing Kits
95
+ Private access is supported through:
96
+ - **GitHub tokens** -- Set `GITHUB_TOKEN` to access private GitHub repos when installing kits
97
+ - **Provider options** -- Each registry entry supports an `options` field for provider-specific configuration (tokens, custom headers)
98
+ - **Pluggable providers** -- Custom registry providers can implement their own authentication
99
+
100
+ See the [Registry docs](docs/registry.md) for hosting your own registry and
101
+ the v2 index format.
172
102
 
173
- Install kits from npm, GitHub, any git host, or local directories:
103
+ ### Install Kits from Anywhere
174
104
 
175
105
  ```sh
176
106
  akm add @scope/my-kit # npm
@@ -179,75 +109,27 @@ akm add git+https://gitlab.com/org/kit # Any git repo
179
109
  akm add ./path/to/local/kit # Local directory
180
110
  ```
181
111
 
182
- Manage installed kits:
183
-
184
- ```sh
185
- akm list # Show installed kits with status
186
- akm update --all # Update all (reports version changes)
187
- akm remove owner/repo # Remove and reindex
188
- akm clone script:deploy.sh # Fork an asset into your stash for editing
189
- ```
112
+ Manage kits with `akm list`, `akm update --all`, and `akm remove`.
190
113
 
191
- ### Publishing your own kit
114
+ ### Publish Your Own Kit
192
115
 
193
- 1. Organize your assets (directory conventions are optional)
194
- 2. Add `"akm"` to `keywords` in `package.json` or add the `akm` topic to your GitHub repo
116
+ 1. Organize your assets into a directory
117
+ 2. Add `"akm"` to `keywords` in `package.json` or the `akm` topic to your GitHub repo
195
118
  3. Optionally add `akm.include` in `package.json` to control what gets installed
196
119
  4. Publish to npm or push to GitHub
197
120
 
198
121
  See the [Kit Maker's Guide](docs/kit-makers.md) for a full walkthrough.
199
122
 
200
- ## Installation
201
-
202
- `akm` requires [Bun](https://bun.sh) v1.0+ as its runtime. It uses Bun-specific
203
- APIs (`bun:sqlite`) that are not available in Node.js.
204
-
205
- ```sh
206
- # Install Bun if you don't have it
207
- curl -fsSL https://bun.sh/install | bash
208
-
209
- # Install akm
210
- bun install -g akm-cli
211
- ```
212
-
213
- ### Standalone binary
214
-
215
- The standalone binary bundles everything and has **no runtime dependencies** --
216
- no Bun, no Node.js.
217
-
218
- ```sh
219
- # macOS / Linux
220
- curl -fsSL https://raw.githubusercontent.com/itlackey/agentikit/main/install.sh | bash
221
-
222
- # Windows (PowerShell)
223
- irm https://raw.githubusercontent.com/itlackey/agentikit/main/install.ps1 -OutFile install.ps1; ./install.ps1
224
- ```
225
-
226
- The shell installer verifies the binary against release checksums.
227
-
228
- Upgrade the binary in place:
229
-
230
- ```sh
231
- akm upgrade # Download and replace the running binary
232
- akm upgrade --check # Check for updates without installing
233
- ```
234
-
235
123
  ## Documentation
236
124
 
237
125
  | Doc | Description |
238
126
  | --- | --- |
239
127
  | [Getting Started](docs/getting-started.md) | Quick setup guide |
240
- | [CLI Reference](docs/cli.md) | All `akm` commands and flags |
241
- | [Configuration](docs/configuration.md) | Providers, settings, and Ollama setup |
242
- | [Concepts](docs/concepts.md) | Asset types, classification, stash sources, metadata |
243
- | [Kit Maker's Guide](docs/kit-makers.md) | Build and share a kit on GitHub, npm, or a network share |
244
- | [Registry](docs/registry.md) | Registries, search, and managing kits |
245
-
246
- ## Status
247
-
248
- `akm` is approaching v1.0. The core CLI, stash model, and registry are generally stable
249
- and in daily use. Feedback, issues, and PRs welcome -- especially around
250
- real-world usage patterns and integrations with different AI coding assistants.
128
+ | [CLI Reference](docs/cli.md) | All commands and flags |
129
+ | [Configuration](docs/configuration.md) | Settings, providers, and Ollama setup |
130
+ | [Concepts](docs/concepts.md) | Asset types, classification, stash model |
131
+ | [Kit Maker's Guide](docs/kit-makers.md) | Build and share kits |
132
+ | [Registry](docs/registry.md) | Registries, search, and the v2 index format |
251
133
 
252
134
  ## License
253
135
 
package/dist/common.js CHANGED
@@ -147,6 +147,12 @@ export async function fetchWithTimeout(url, opts, timeoutMs = 30_000) {
147
147
  try {
148
148
  return await fetch(url, { ...opts, signal: controller.signal });
149
149
  }
150
+ catch (err) {
151
+ if (err instanceof DOMException && err.name === "AbortError") {
152
+ throw new Error(`Request timed out after ${timeoutMs}ms: ${url}`);
153
+ }
154
+ throw err;
155
+ }
150
156
  finally {
151
157
  clearTimeout(timer);
152
158
  }
package/dist/config.js CHANGED
@@ -5,7 +5,10 @@ import { getConfigDir as _getConfigDir, getConfigPath as _getConfigPath } from "
5
5
  export const DEFAULT_CONFIG = {
6
6
  semanticSearch: true,
7
7
  searchPaths: [],
8
- registries: [{ url: "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json", name: "official" }],
8
+ registries: [
9
+ { url: "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json", name: "official" },
10
+ { url: "https://skills.sh", name: "skills.sh", provider: "skills-sh" },
11
+ ],
9
12
  output: {
10
13
  format: "json",
11
14
  detail: "brief",
package/dist/db.js CHANGED
@@ -180,6 +180,15 @@ export function upsertEntry(db, entryKey, dirPath, filePath, stashDir, entry, se
180
180
  }
181
181
  export function deleteEntriesByDir(db, dirPath) {
182
182
  const ids = db.prepare("SELECT id FROM entries WHERE dir_path = ?").all(dirPath);
183
+ deleteRelatedRows(db, ids);
184
+ db.prepare("DELETE FROM entries WHERE dir_path = ?").run(dirPath);
185
+ }
186
+ export function deleteEntriesByStashDir(db, stashDir) {
187
+ const ids = db.prepare("SELECT id FROM entries WHERE stash_dir = ?").all(stashDir);
188
+ deleteRelatedRows(db, ids);
189
+ db.prepare("DELETE FROM entries WHERE stash_dir = ?").run(stashDir);
190
+ }
191
+ function deleteRelatedRows(db, ids) {
183
192
  for (const { id } of ids) {
184
193
  try {
185
194
  db.prepare("DELETE FROM embeddings WHERE id = ?").run(id);
@@ -196,7 +205,6 @@ export function deleteEntriesByDir(db, dirPath) {
196
205
  }
197
206
  }
198
207
  }
199
- db.prepare("DELETE FROM entries WHERE dir_path = ?").run(dirPath);
200
208
  }
201
209
  export function rebuildFts(db) {
202
210
  db.exec("DELETE FROM entries_fts");
package/dist/indexer.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { resolveStashDir } from "./common";
4
- import { closeDatabase, DB_VERSION, deleteEntriesByDir, getEntriesByDir, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, setMeta, upsertEmbedding, upsertEntry, warnIfVecMissing, } from "./db";
4
+ import { closeDatabase, DB_VERSION, deleteEntriesByDir, deleteEntriesByStashDir, getEntriesByDir, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, setMeta, upsertEmbedding, upsertEntry, warnIfVecMissing, } from "./db";
5
5
  import { generateMetadataFlat, loadStashFile } from "./metadata";
6
6
  import { getDbPath } from "./paths";
7
7
  import { walkStashFlat } from "./walker";
@@ -45,6 +45,20 @@ export async function agentikitIndex(options) {
45
45
  db.exec("DELETE FROM entries_fts");
46
46
  db.exec("DELETE FROM entries");
47
47
  }
48
+ else {
49
+ // Incremental: purge entries from stash dirs that have been removed
50
+ // (e.g. after `akm remove`) so orphaned entries don't linger.
51
+ const prevStashDirsJson = getMeta(db, "stashDirs");
52
+ if (prevStashDirsJson) {
53
+ const prevStashDirs = JSON.parse(prevStashDirsJson);
54
+ const currentSet = new Set(allStashDirs);
55
+ for (const dir of prevStashDirs) {
56
+ if (!currentSet.has(dir)) {
57
+ deleteEntriesByStashDir(db, dir);
58
+ }
59
+ }
60
+ }
61
+ }
48
62
  const tWalkStart = Date.now();
49
63
  // Walk stash dirs and index entries
50
64
  const { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm } = indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs);
@@ -75,6 +75,7 @@ class SkillsShProvider {
75
75
  id: `skills-sh:${entry.id}`,
76
76
  title: entry.name,
77
77
  ref: entry.source,
78
+ installRef: `github:${entry.source}`,
78
79
  homepage: `${baseUrl}/${entry.id}`,
79
80
  score,
80
81
  metadata: {
@@ -219,6 +219,7 @@ function toSearchHit(kit, score, registryName) {
219
219
  title: kit.name,
220
220
  description: kit.description,
221
221
  ref: kit.ref,
222
+ installRef: buildInstallRef(kit.source, kit.ref),
222
223
  homepage: kit.homepage,
223
224
  score: Math.round(score * 1000) / 1000,
224
225
  metadata,
@@ -260,13 +261,7 @@ function scoreAssets(kits, query, limit) {
260
261
  for (const { kit, registryName } of kits) {
261
262
  if (!kit.assets || kit.assets.length === 0)
262
263
  continue;
263
- const installRef = kit.source === "npm"
264
- ? `npm:${kit.ref}`
265
- : kit.source === "git"
266
- ? `git+${kit.ref}`
267
- : kit.source === "local"
268
- ? kit.ref
269
- : `github:${kit.ref}`;
264
+ const installRef = buildInstallRef(kit.source, kit.ref);
270
265
  for (const asset of kit.assets) {
271
266
  const score = scoreAsset(asset, tokens);
272
267
  if (score > 0) {
@@ -335,6 +330,18 @@ function asStringArray(value) {
335
330
  const filtered = value.filter((v) => typeof v === "string");
336
331
  return filtered.length > 0 ? filtered : undefined;
337
332
  }
333
+ function buildInstallRef(source, ref) {
334
+ switch (source) {
335
+ case "npm":
336
+ return `npm:${ref}`;
337
+ case "git":
338
+ return `git+${ref}`;
339
+ case "local":
340
+ return ref;
341
+ default:
342
+ return `github:${ref}`;
343
+ }
344
+ }
338
345
  function toErrorMessage(error) {
339
346
  return error instanceof Error ? error.message : String(error);
340
347
  }
@@ -9,6 +9,12 @@ export function parseRegistryRef(rawRef) {
9
9
  const ref = rawRef.trim();
10
10
  if (!ref)
11
11
  throw new Error("Registry ref is required.");
12
+ // Detect registry search result IDs (e.g. "skills-sh:org/skills/name")
13
+ // that are not installable refs. Known installable prefixes are handled below.
14
+ const registryIdHint = detectRegistrySearchId(ref);
15
+ if (registryIdHint) {
16
+ throw new Error(registryIdHint);
17
+ }
12
18
  if (ref.startsWith("npm:")) {
13
19
  return parseNpmRef(ref.slice(4), ref);
14
20
  }
@@ -33,6 +39,37 @@ export function parseRegistryRef(rawRef) {
33
39
  }
34
40
  return parseGithubShorthand(ref, ref);
35
41
  }
42
+ /**
43
+ * Known prefixes that `parseRegistryRef` handles as installable sources.
44
+ * Anything with a colon that doesn't start with one of these is likely a
45
+ * registry search result ID (e.g. `skills-sh:org/skills/name`).
46
+ */
47
+ const KNOWN_PREFIXES = ["npm:", "github:", "git+", "file:", "http://", "https://"];
48
+ function detectRegistrySearchId(ref) {
49
+ const colonIdx = ref.indexOf(":");
50
+ if (colonIdx < 1)
51
+ return undefined;
52
+ // Skip known installable prefixes
53
+ for (const prefix of KNOWN_PREFIXES) {
54
+ if (ref.startsWith(prefix))
55
+ return undefined;
56
+ }
57
+ const prefix = ref.slice(0, colonIdx);
58
+ // Registry IDs use lowercase-with-hyphens prefixes (e.g. skills-sh, static-index)
59
+ if (!/^[a-z][a-z0-9-]*$/.test(prefix))
60
+ return undefined;
61
+ const rest = ref.slice(colonIdx + 1);
62
+ return [
63
+ `"${ref}" looks like a registry search result ID, not an installable ref.`,
64
+ `The "${prefix}:" prefix is a registry identifier and cannot be passed to \`akm add\`.`,
65
+ "",
66
+ "Use the installRef or ref field from the search result instead. For example:",
67
+ ` akm registry search "${rest}" --format json`,
68
+ "Then install using the installRef value from the result:",
69
+ " akm add github:owner/repo",
70
+ " akm add npm:package-name",
71
+ ].join("\n");
72
+ }
36
73
  export async function resolveRegistryArtifact(parsed) {
37
74
  if (parsed.source === "npm") {
38
75
  return resolveNpmArtifact(parsed);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "description": "CLI tool to search, open, and run extension assets from an akm stash directory.",
6
6
  "keywords": [