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 +64 -182
- package/dist/common.js +6 -0
- package/dist/config.js +4 -1
- package/dist/db.js +9 -1
- package/dist/indexer.js +15 -1
- package/dist/providers/skills-sh.js +1 -0
- package/dist/providers/static-index.js +14 -7
- package/dist/registry-resolve.js +37 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
15
|
+
```sh
|
|
16
|
+
# Standalone binary (no runtime dependencies)
|
|
17
|
+
curl -fsSL https://raw.githubusercontent.com/itlackey/agentikit/main/install.sh | bash
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
# Or via Bun
|
|
20
|
+
bun install -g akm-cli
|
|
21
|
+
```
|
|
22
22
|
|
|
23
|
-
|
|
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
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
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
|
-
##
|
|
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
|
-
###
|
|
36
|
+
### Works with Any AI Agent
|
|
58
37
|
|
|
59
|
-
|
|
60
|
-
|
|
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,
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
112
|
-
akm
|
|
113
|
-
|
|
114
|
-
#
|
|
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
|
-
|
|
122
|
-
|
|
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
|
-
|
|
70
|
+
### skills.sh Integration
|
|
125
71
|
|
|
126
|
-
|
|
127
|
-
|
|
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 "
|
|
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
|
-
|
|
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
|
-
|
|
84
|
+
### Registries and Private Registry Support
|
|
147
85
|
|
|
148
|
-
|
|
149
|
-
akm
|
|
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
|
|
168
|
-
akm
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
###
|
|
114
|
+
### Publish Your Own Kit
|
|
192
115
|
|
|
193
|
-
1. Organize your assets
|
|
194
|
-
2. Add `"akm"` to `keywords` in `package.json` or
|
|
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
|
|
241
|
-
| [Configuration](docs/configuration.md) |
|
|
242
|
-
| [Concepts](docs/concepts.md) | Asset types, classification, stash
|
|
243
|
-
| [Kit Maker's Guide](docs/kit-makers.md) | Build and share
|
|
244
|
-
| [Registry](docs/registry.md) | Registries, search, and
|
|
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: [
|
|
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);
|
|
@@ -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
|
|
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
|
}
|
package/dist/registry-resolve.js
CHANGED
|
@@ -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);
|