akm-cli 0.0.16 → 0.0.18

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.
@@ -1,14 +1,8 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fetchWithRetry } from "./common";
4
- import { getRegistryIndexCacheDir } from "./paths";
5
- // ── Constants ───────────────────────────────────────────────────────────────
6
- /** Default registry index URL. Override via config or AKM_REGISTRY_URL env var. */
7
- const DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json";
8
- /** Cache TTL in milliseconds (1 hour). */
9
- const CACHE_TTL_MS = 60 * 60 * 1000;
10
- /** Maximum age before cache is considered stale but still usable as fallback (7 days). */
11
- const CACHE_STALE_MS = 7 * 24 * 60 * 60 * 1000;
1
+ import { DEFAULT_CONFIG, loadConfig } from "./config";
2
+ import { resolveProviderFactory } from "./provider-registry";
3
+ // ── Eagerly import providers to trigger self-registration ───────────────────
4
+ import "./providers/static-index";
5
+ import "./providers/skills-sh";
12
6
  // ── Public API ──────────────────────────────────────────────────────────────
13
7
  export async function searchRegistry(query, options) {
14
8
  const trimmed = query.trim();
@@ -16,219 +10,78 @@ export async function searchRegistry(query, options) {
16
10
  return { query: "", hits: [], warnings: [] };
17
11
  }
18
12
  const limit = clampLimit(options?.limit);
19
- const urls = resolveRegistryUrls(options?.registryUrls);
13
+ // resolveRegistries() already filters by enabled; explicit registries are filtered here
14
+ const raw = options?.registries ?? resolveRegistries();
15
+ const entries = options?.registries ? raw.filter((r) => r.enabled !== false) : raw;
20
16
  const warnings = [];
21
- // Load index from all configured registries, merge kits
22
- const allKits = [];
23
- for (const url of urls) {
24
- try {
25
- const index = await loadIndex(url);
26
- if (index) {
27
- allKits.push(...index.kits);
28
- }
29
- }
30
- catch (err) {
31
- warnings.push(`Registry ${url}: ${toErrorMessage(err)}`);
32
- }
33
- }
34
- // Score and rank
35
- const hits = scoreKits(allKits, trimmed, limit);
36
- return { query: trimmed, hits, warnings };
37
- }
38
- // ── Index loading with cache ────────────────────────────────────────────────
39
- async function loadIndex(url) {
40
- const cachePath = indexCachePath(url);
41
- const cached = readCachedIndex(cachePath);
42
- // Fresh cache: return immediately
43
- if (cached && !isCacheExpired(cached.mtime)) {
44
- return cached.index;
45
- }
46
- // Try to fetch fresh index
47
- try {
48
- const response = await fetchWithRetry(url, undefined, { timeout: 10_000 });
49
- if (!response.ok) {
50
- throw new Error(`HTTP ${response.status}`);
51
- }
52
- const data = (await response.json());
53
- const index = parseRegistryIndex(data);
54
- if (index) {
55
- writeCachedIndex(cachePath, index);
56
- return index;
57
- }
58
- throw new Error("Invalid registry index format");
59
- }
60
- catch (err) {
61
- // Fetch failed — use stale cache if available
62
- if (cached && !isCacheStale(cached.mtime)) {
63
- return cached.index;
64
- }
65
- throw err;
66
- }
67
- }
68
- function readCachedIndex(cachePath) {
69
- try {
70
- const stat = fs.statSync(cachePath);
71
- const raw = JSON.parse(fs.readFileSync(cachePath, "utf8"));
72
- const index = parseRegistryIndex(raw);
73
- if (!index)
74
- return null;
75
- return { index, mtime: stat.mtimeMs };
76
- }
77
- catch {
78
- return null;
79
- }
80
- }
81
- function writeCachedIndex(cachePath, index) {
82
- try {
83
- const dir = path.dirname(cachePath);
84
- fs.mkdirSync(dir, { recursive: true });
85
- const tmpPath = `${cachePath}.tmp.${process.pid}`;
86
- fs.writeFileSync(tmpPath, JSON.stringify(index), "utf8");
87
- fs.renameSync(tmpPath, cachePath);
88
- }
89
- catch {
90
- // Best-effort caching — don't fail the search if we can't write
91
- }
92
- }
93
- function indexCachePath(url) {
94
- const indexDir = getRegistryIndexCacheDir();
95
- // Deterministic filename from URL
96
- const slug = url
97
- .replace(/[^a-zA-Z0-9]+/g, "-")
98
- .replace(/^-+|-+$/g, "")
99
- .slice(0, 120);
100
- return path.join(indexDir, `${slug}.json`);
101
- }
102
- function isCacheExpired(mtimeMs) {
103
- return Date.now() - mtimeMs > CACHE_TTL_MS;
104
- }
105
- function isCacheStale(mtimeMs) {
106
- return Date.now() - mtimeMs > CACHE_STALE_MS;
107
- }
108
- // ── Index parsing ───────────────────────────────────────────────────────────
109
- function parseRegistryIndex(data) {
110
- if (typeof data !== "object" || data === null || Array.isArray(data))
111
- return null;
112
- const obj = data;
113
- if (typeof obj.version !== "number" || obj.version !== 1)
114
- return null;
115
- if (typeof obj.updatedAt !== "string")
116
- return null;
117
- if (!Array.isArray(obj.kits))
118
- return null;
119
- const kits = obj.kits.flatMap((raw) => {
120
- const kit = parseKitEntry(raw);
121
- return kit ? [kit] : [];
122
- });
123
- return { version: 1, updatedAt: obj.updatedAt, kits };
124
- }
125
- function parseKitEntry(raw) {
126
- if (typeof raw !== "object" || raw === null || Array.isArray(raw))
127
- return null;
128
- const obj = raw;
129
- const id = asString(obj.id);
130
- const name = asString(obj.name);
131
- const ref = asString(obj.ref);
132
- const source = asSource(obj.source);
133
- if (!id || !name || !ref || !source)
134
- return null;
135
- return {
136
- id,
137
- name,
138
- ref,
139
- source,
140
- description: asString(obj.description),
141
- homepage: asString(obj.homepage),
142
- tags: asStringArray(obj.tags),
143
- assetTypes: asStringArray(obj.assetTypes),
144
- author: asString(obj.author),
145
- license: asString(obj.license),
146
- latestVersion: asString(obj.latestVersion),
147
- curated: obj.curated === true ? true : undefined,
148
- };
149
- }
150
- // ── Scoring ─────────────────────────────────────────────────────────────────
151
- function scoreKits(kits, query, limit) {
152
- const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
153
- const scored = [];
154
- for (const kit of kits) {
155
- const score = scoreKit(kit, tokens);
156
- if (score > 0) {
157
- scored.push({ kit, score });
17
+ // Resolve and search all providers concurrently
18
+ const results = await Promise.allSettled(entries.map((entry) => {
19
+ const provider = createProvider(entry, warnings);
20
+ if (!provider)
21
+ return Promise.resolve(null);
22
+ return provider.search({ query: trimmed, limit, includeAssets: options?.includeAssets });
23
+ }));
24
+ // Merge results grouped by provider
25
+ const allHits = [];
26
+ const allAssetHits = [];
27
+ for (const result of results) {
28
+ if (result.status === "rejected") {
29
+ warnings.push(toErrorMessage(result.reason));
30
+ continue;
158
31
  }
32
+ const value = result.value;
33
+ if (!value)
34
+ continue;
35
+ allHits.push(...value.hits);
36
+ if (value.assetHits)
37
+ allAssetHits.push(...value.assetHits);
38
+ if (value.warnings)
39
+ warnings.push(...value.warnings);
159
40
  }
160
- scored.sort((a, b) => b.score - a.score);
161
- return scored.slice(0, limit).map(({ kit, score }) => toSearchHit(kit, score));
162
- }
163
- function scoreKit(kit, tokens) {
164
- let score = 0;
165
- const nameLower = kit.name.toLowerCase();
166
- const descLower = (kit.description ?? "").toLowerCase();
167
- const tagsLower = (kit.tags ?? []).map((t) => t.toLowerCase());
168
- for (const token of tokens) {
169
- // Exact name match is strongest signal
170
- if (nameLower === token) {
171
- score += 1.0;
172
- }
173
- else if (nameLower.includes(token)) {
174
- score += 0.6;
175
- }
176
- // Tag matches are high-signal (curated keywords)
177
- if (tagsLower.some((tag) => tag === token)) {
178
- score += 0.5;
179
- }
180
- else if (tagsLower.some((tag) => tag.includes(token))) {
181
- score += 0.25;
182
- }
183
- // Description substring
184
- if (descLower.includes(token)) {
185
- score += 0.2;
186
- }
187
- // Author match
188
- if (kit.author?.toLowerCase().includes(token)) {
189
- score += 0.15;
190
- }
191
- }
192
- // Normalize by token count so multi-word queries don't inflate scores
193
- return tokens.length > 0 ? score / tokens.length : 0;
194
- }
195
- function toSearchHit(kit, score) {
196
- const metadata = {};
197
- if (kit.latestVersion)
198
- metadata.version = kit.latestVersion;
199
- if (kit.author)
200
- metadata.author = kit.author;
201
- if (kit.license)
202
- metadata.license = kit.license;
203
- if (kit.assetTypes?.length)
204
- metadata.assetTypes = kit.assetTypes.join(", ");
41
+ // Sort merged hits by score descending, apply limit
42
+ allHits.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
43
+ const limitedHits = allHits.slice(0, limit);
44
+ allAssetHits.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
45
+ const limitedAssetHits = allAssetHits.slice(0, limit);
205
46
  return {
206
- source: kit.source,
207
- id: kit.id,
208
- title: kit.name,
209
- description: kit.description,
210
- ref: kit.ref,
211
- homepage: kit.homepage,
212
- score: Math.round(score * 1000) / 1000,
213
- metadata,
214
- curated: kit.curated,
47
+ query: trimmed,
48
+ hits: limitedHits,
49
+ warnings,
50
+ assetHits: limitedAssetHits.length > 0 ? limitedAssetHits : undefined,
215
51
  };
216
52
  }
217
- // ── Registry URL resolution ─────────────────────────────────────────────────
218
- function resolveRegistryUrls(override) {
219
- if (override) {
220
- const urls = Array.isArray(override) ? override : [override];
221
- return urls.filter(Boolean);
222
- }
223
- // Allow env var override (comma-separated)
53
+ // ── Registry resolution ─────────────────────────────────────────────────────
54
+ /**
55
+ * Resolve the list of enabled registries.
56
+ *
57
+ * Priority:
58
+ * 1. AKM_REGISTRY_URL env var (CI override, comma-separated)
59
+ * 2. config.registries (filtered by enabled !== false)
60
+ * 3. Default registries from DEFAULT_CONFIG
61
+ */
62
+ export function resolveRegistries(configRegistries) {
63
+ // Allow env var override (comma-separated URLs) — CI escape hatch
224
64
  const envUrls = process.env.AKM_REGISTRY_URL?.trim();
225
65
  if (envUrls) {
226
66
  return envUrls
227
67
  .split(",")
228
68
  .map((u) => u.trim())
229
- .filter(Boolean);
69
+ .filter(Boolean)
70
+ .map((url) => ({ url }));
230
71
  }
231
- return [DEFAULT_REGISTRY_URL];
72
+ const registries = configRegistries ?? loadConfig().registries ?? DEFAULT_CONFIG.registries ?? [];
73
+ return registries.filter((r) => r.enabled !== false);
74
+ }
75
+ // ── Provider resolution ─────────────────────────────────────────────────────
76
+ function createProvider(entry, warnings) {
77
+ const providerType = entry.provider ?? "static-index";
78
+ const factory = resolveProviderFactory(providerType);
79
+ if (!factory) {
80
+ const label = entry.name ? `${entry.name} (${entry.url})` : entry.url;
81
+ warnings.push(`Registry ${label}: unknown provider type "${providerType}"`);
82
+ return null;
83
+ }
84
+ return factory(entry);
232
85
  }
233
86
  // ── Utilities ───────────────────────────────────────────────────────────────
234
87
  function clampLimit(limit) {
@@ -236,20 +89,6 @@ function clampLimit(limit) {
236
89
  return 20;
237
90
  return Math.min(100, Math.max(1, Math.trunc(limit)));
238
91
  }
239
- function asString(value) {
240
- return typeof value === "string" && value ? value : undefined;
241
- }
242
- function asSource(value) {
243
- if (value === "npm" || value === "github" || value === "git" || value === "local")
244
- return value;
245
- return undefined;
246
- }
247
- function asStringArray(value) {
248
- if (!Array.isArray(value))
249
- return undefined;
250
- const filtered = value.filter((v) => typeof v === "string");
251
- return filtered.length > 0 ? filtered : undefined;
252
- }
253
92
  function toErrorMessage(error) {
254
93
  return error instanceof Error ? error.message : String(error);
255
94
  }
package/dist/renderers.js CHANGED
@@ -112,7 +112,7 @@ export function detectExecHints(filePath) {
112
112
  }
113
113
  // ── Resolution ───────────────────────────────────────────────────────────────
114
114
  /**
115
- * Resolve execution hints for a script/tool asset.
115
+ * Resolve execution hints for a script asset.
116
116
  *
117
117
  * Resolution order (first non-empty value wins for each field):
118
118
  * 1. `.stash.json` fields (`run`/`setup`/`cwd`) take priority
package/dist/stash-add.js CHANGED
@@ -11,7 +11,7 @@ export async function agentikitAdd(input) {
11
11
  throw new UsageError("Install ref or local directory is required.");
12
12
  const stashDir = resolveStashDir();
13
13
  const installed = await installRegistryRef(ref);
14
- const replaced = loadConfig().registry?.installed.find((entry) => entry.id === installed.id);
14
+ const replaced = (loadConfig().installed ?? []).find((entry) => entry.id === installed.id);
15
15
  const config = upsertInstalledRegistryEntry({
16
16
  id: installed.id,
17
17
  source: installed.source,
@@ -59,7 +59,7 @@ export async function agentikitAdd(input) {
59
59
  },
60
60
  config: {
61
61
  searchPaths: config.searchPaths,
62
- installedRegistryCount: config.registry?.installed.length ?? 0,
62
+ installedKitCount: config.installed?.length ?? 0,
63
63
  },
64
64
  index: {
65
65
  mode: index.mode,
package/dist/stash-ref.js CHANGED
@@ -6,21 +6,19 @@ import { UsageError } from "./errors";
6
6
  * Build a ref string from components.
7
7
  *
8
8
  * Examples:
9
- * makeAssetRef("tool", "deploy.sh")
10
- * → "tool:deploy.sh"
11
- * makeAssetRef("tool", "deploy.sh", "npm:@scope/pkg")
12
- * → "npm:@scope/pkg//tool:deploy.sh"
9
+ * makeAssetRef("script", "deploy.sh")
10
+ * → "script:deploy.sh"
11
+ * makeAssetRef("script", "deploy.sh", "npm:@scope/pkg")
12
+ * → "npm:@scope/pkg//script:deploy.sh"
13
13
  * makeAssetRef("skill", "code-review", "local")
14
14
  * → "local//skill:code-review"
15
- * makeAssetRef("tool", "db/migrate/run.sh", "owner/repo")
16
- * → "owner/repo//tool:db/migrate/run.sh"
15
+ * makeAssetRef("script", "db/migrate/run.sh", "owner/repo")
16
+ * → "owner/repo//script:db/migrate/run.sh"
17
17
  */
18
18
  export function makeAssetRef(type, name, origin) {
19
19
  validateName(name);
20
20
  const normalized = normalizeName(name);
21
- // "tool" is a transparent alias for "script" -- normalize to "script" in refs
22
- const resolvedType = type === "tool" ? "script" : type;
23
- const asset = `${resolvedType}:${normalized}`;
21
+ const asset = `${type}:${normalized}`;
24
22
  if (!origin)
25
23
  return asset;
26
24
  return `${origin}//${asset}`;
@@ -9,7 +9,7 @@ import { parseRegistryRef } from "./registry-resolve";
9
9
  export async function agentikitList(input) {
10
10
  const stashDir = input?.stashDir ?? resolveStashDir();
11
11
  const config = loadConfig();
12
- const installed = config.registry?.installed ?? [];
12
+ const installed = config.installed ?? [];
13
13
  return {
14
14
  schemaVersion: 1,
15
15
  stashDir,
@@ -29,7 +29,7 @@ export async function agentikitRemove(input) {
29
29
  throw new UsageError("Target is required.");
30
30
  const stashDir = input.stashDir ?? resolveStashDir();
31
31
  const config = loadConfig();
32
- const installed = config.registry?.installed ?? [];
32
+ const installed = config.installed ?? [];
33
33
  const entry = resolveInstalledTarget(installed, target);
34
34
  const updatedConfig = removeInstalledRegistryEntry(entry.id);
35
35
  removeLockEntry(entry.id);
@@ -52,7 +52,7 @@ export async function agentikitRemove(input) {
52
52
  },
53
53
  config: {
54
54
  searchPaths: updatedConfig.searchPaths,
55
- installedRegistryCount: updatedConfig.registry?.installed.length ?? 0,
55
+ installedKitCount: updatedConfig.installed?.length ?? 0,
56
56
  },
57
57
  index: {
58
58
  mode: index.mode,
@@ -67,7 +67,7 @@ export async function agentikitUpdate(input) {
67
67
  const target = input?.target?.trim();
68
68
  const all = input?.all === true;
69
69
  const force = input?.force === true;
70
- const installedEntries = loadConfig().registry?.installed ?? [];
70
+ const installedEntries = loadConfig().installed ?? [];
71
71
  const selectedEntries = selectTargets(installedEntries, target, all);
72
72
  const processed = [];
73
73
  for (const entry of selectedEntries) {
@@ -116,7 +116,7 @@ export async function agentikitUpdate(input) {
116
116
  processed,
117
117
  config: {
118
118
  searchPaths: config.searchPaths,
119
- installedRegistryCount: config.registry?.installed.length ?? 0,
119
+ installedKitCount: config.installed?.length ?? 0,
120
120
  },
121
121
  index: {
122
122
  mode: index.mode,
@@ -156,7 +156,7 @@ function resolveInstalledTarget(installed, target) {
156
156
  if (byParsedId)
157
157
  return byParsedId;
158
158
  }
159
- throw new NotFoundError(`No installed registry entry matched target: ${target}`);
159
+ throw new NotFoundError(`No installed kit matched target: ${target}`);
160
160
  }
161
161
  function toInstalledEntry(status) {
162
162
  return {
@@ -1,47 +1,12 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { isRelevantAssetFile, resolveAssetPathFromName, TYPE_DIRS } from "./asset-spec";
4
- import { hasErrnoCode, isWithin, normalizeAssetType } from "./common";
4
+ import { hasErrnoCode, isWithin } from "./common";
5
5
  import { NotFoundError, UsageError } from "./errors";
6
6
  /**
7
7
  * Resolve an asset path from a stash directory, type, and name.
8
- *
9
- * When `type` is "script" or "tool" (which is a transparent alias for "script"),
10
- * resolution tries both the primary type directory and the alias directory:
11
- * - script → tries scripts/ then tools/
12
- * - tool → tries tools/ then scripts/
13
- * This ensures that `script:deploy.sh` can find files in either `scripts/` or `tools/`.
14
8
  */
15
9
  export function resolveAssetPath(stashDir, type, name) {
16
- // For script/tool, try the primary directory first, then the alias directory.
17
- if (type === "script" || type === "tool") {
18
- const primaryDir = TYPE_DIRS[type];
19
- const aliasDir = type === "script" ? "tools" : "scripts";
20
- const dirsToTry = [primaryDir, aliasDir];
21
- let primaryError;
22
- let lastError;
23
- for (let i = 0; i < dirsToTry.length; i++) {
24
- try {
25
- return resolveInTypeDir(stashDir, dirsToTry[i], type, name);
26
- }
27
- catch (err) {
28
- const error = err instanceof Error ? err : new Error(String(err));
29
- if (i === 0)
30
- primaryError = error;
31
- lastError = error;
32
- // Only fall through on NotFoundError -- rethrow security/usage errors immediately
33
- if (err instanceof UsageError)
34
- throw err;
35
- }
36
- }
37
- // Prefer the primary directory's error when it's about extension validation
38
- // (i.e., the file was found but had the wrong extension) over a generic
39
- // "not found" from the alias directory.
40
- const errorToThrow = primaryError?.message.includes("supported script extension")
41
- ? primaryError
42
- : (lastError ?? new NotFoundError(`Stash asset not found for ref: ${normalizeAssetType(type)}:${name}`));
43
- throw errorToThrow;
44
- }
45
10
  return resolveInTypeDir(stashDir, TYPE_DIRS[type], type, name);
46
11
  }
47
12
  /**
@@ -56,26 +21,24 @@ function resolveInTypeDir(stashDir, typeDir, type, name) {
56
21
  throw new UsageError("Ref resolves outside the stash root.");
57
22
  }
58
23
  if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isFile()) {
59
- throw new NotFoundError(`Stash asset not found for ref: ${normalizeAssetType(type)}:${name}`);
24
+ throw new NotFoundError(`Stash asset not found for ref: ${type}:${name}`);
60
25
  }
61
26
  const realTarget = fs.realpathSync(resolvedTarget);
62
27
  if (!isWithin(realTarget, resolvedRoot)) {
63
28
  throw new UsageError("Ref resolves outside the stash root.");
64
29
  }
65
- // Use "script" for relevance check since tool is an alias for script
66
- const relevanceType = type === "tool" ? "script" : type;
67
- if (!isRelevantAssetFile(relevanceType, path.basename(resolvedTarget))) {
68
- if (type === "tool" || type === "script") {
30
+ if (!isRelevantAssetFile(type, path.basename(resolvedTarget))) {
31
+ if (type === "script") {
69
32
  throw new NotFoundError("Script ref must resolve to a file with a supported script extension. Refer to the akm documentation for the complete list of supported script extensions.");
70
33
  }
71
- throw new NotFoundError(`Stash asset not found for ref: ${normalizeAssetType(type)}:${name}`);
34
+ throw new NotFoundError(`Stash asset not found for ref: ${type}:${name}`);
72
35
  }
73
36
  return realTarget;
74
37
  }
75
38
  function resolveAndValidateTypeRoot(root, type, name) {
76
39
  const rootStat = readTypeRootStat(root, type, name);
77
40
  if (!rootStat.isDirectory()) {
78
- throw new NotFoundError(`Stash type root is not a directory for ref: ${normalizeAssetType(type)}:${name}`);
41
+ throw new NotFoundError(`Stash type root is not a directory for ref: ${type}:${name}`);
79
42
  }
80
43
  return fs.realpathSync(root);
81
44
  }
@@ -85,7 +48,7 @@ function readTypeRootStat(root, type, name) {
85
48
  }
86
49
  catch (error) {
87
50
  if (hasErrnoCode(error, "ENOENT")) {
88
- throw new NotFoundError(`Stash type root not found for ref: ${normalizeAssetType(type)}:${name}`);
51
+ throw new NotFoundError(`Stash type root not found for ref: ${type}:${name}`);
89
52
  }
90
53
  throw error;
91
54
  }
@@ -1,7 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { deriveCanonicalAssetName, TYPE_DIRS } from "./asset-spec";
4
- import { normalizeAssetType } from "./common";
5
4
  import { loadConfig } from "./config";
6
5
  import { closeDatabase, getAllEntries, getEntryById, getEntryCount, getMeta, openDatabase, searchFts, searchVec, } from "./db";
7
6
  import { UsageError } from "./errors";
@@ -45,7 +44,7 @@ export async function agentikitSearch(input) {
45
44
  sources,
46
45
  config,
47
46
  });
48
- const registryResult = source === "local" ? undefined : await searchRegistry(query, { limit, registryUrls: config.registryUrls });
47
+ const registryResult = source === "local" ? undefined : await searchRegistry(query, { limit, registries: config.registries });
49
48
  if (source === "local") {
50
49
  return {
51
50
  schemaVersion: 1,
@@ -67,6 +66,7 @@ export async function agentikitSearch(input) {
67
66
  action: `akm add ${installRef} -> then search again`,
68
67
  score: hit.score,
69
68
  curated: hit.curated,
69
+ registryName: hit.registryName,
70
70
  };
71
71
  });
72
72
  if (source === "registry") {
@@ -315,7 +315,7 @@ function buildDbHit(input) {
315
315
  const source = findSourceForPath(input.path, input.sources);
316
316
  const editable = isEditable(input.path, input.config);
317
317
  const hit = {
318
- type: normalizeAssetType(input.entry.type),
318
+ type: input.entry.type,
319
319
  name: input.entry.name,
320
320
  path: input.path,
321
321
  ref: makeAssetRef(input.entry.type, refName, source?.registryId),
@@ -325,7 +325,7 @@ function buildDbHit(input) {
325
325
  description: input.entry.description,
326
326
  tags: input.entry.tags,
327
327
  size: deriveSize(input.entry.fileSize),
328
- action: buildLocalAction(normalizeAssetType(input.entry.type), makeAssetRef(input.entry.type, refName, source?.registryId)),
328
+ action: buildLocalAction(input.entry.type, makeAssetRef(input.entry.type, refName, source?.registryId)),
329
329
  score,
330
330
  whyMatched,
331
331
  };
@@ -364,7 +364,7 @@ function assetToSearchHit(asset, stashDir, sources, config) {
364
364
  const fileSize = readFileSize(asset.path);
365
365
  const size = deriveSize(fileSize);
366
366
  const hit = {
367
- type: normalizeAssetType(asset.entry.type),
367
+ type: asset.entry.type,
368
368
  name: asset.entry.name,
369
369
  path: asset.path,
370
370
  ref,
@@ -376,7 +376,7 @@ function assetToSearchHit(asset, stashDir, sources, config) {
376
376
  description: asset.entry.description,
377
377
  tags: asset.entry.tags,
378
378
  ...(size ? { size } : {}),
379
- action: buildLocalAction(normalizeAssetType(asset.entry.type), ref),
379
+ action: buildLocalAction(asset.entry.type, ref),
380
380
  };
381
381
  const renderer = rendererForType(asset.entry.type);
382
382
  if (renderer?.enrichSearchHit) {
@@ -404,7 +404,6 @@ function mergeSearchHits(localHits, registryHits, limit) {
404
404
  }
405
405
  /** Map asset types to their primary renderer names. */
406
406
  const TYPE_TO_RENDERER = {
407
- tool: "script-source",
408
407
  script: "script-source",
409
408
  skill: "skill-md",
410
409
  command: "command-md",
@@ -426,8 +425,6 @@ function buildLocalAction(type, ref) {
426
425
  return `akm show ${ref} -> dispatch with full prompt`;
427
426
  case "knowledge":
428
427
  return `akm show ${ref} -> read reference material`;
429
- case "tool":
430
- return `akm show ${ref} -> execute the run command`;
431
428
  }
432
429
  }
433
430
  function deriveSize(bytes) {
@@ -449,7 +446,7 @@ function readFileSize(filePath) {
449
446
  }
450
447
  function indexAssets(stashDir, type) {
451
448
  const assets = [];
452
- const filterType = type === "any" ? undefined : normalizeAssetType(type);
449
+ const filterType = type === "any" ? undefined : type;
453
450
  const fileContexts = walkStashFlat(stashDir);
454
451
  const dirGroups = new Map();
455
452
  for (const ctx of fileContexts) {
@@ -478,8 +475,7 @@ function indexAssets(stashDir, type) {
478
475
  stash = generated;
479
476
  }
480
477
  for (const entry of stash.entries) {
481
- const normalizedType = normalizeAssetType(entry.type);
482
- if (filterType && normalizedType !== filterType)
478
+ if (filterType && entry.type !== filterType)
483
479
  continue;
484
480
  const entryPath = entry.filename ? path.join(dirPath, entry.filename) : files[0] || dirPath;
485
481
  assets.push({ entry, path: entryPath });
@@ -1,4 +1,3 @@
1
- import { normalizeAssetType } from "./common";
2
1
  import { loadConfig } from "./config";
3
2
  import { NotFoundError, UsageError } from "./errors";
4
3
  import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "./file-context";
@@ -8,7 +7,7 @@ import { resolveAssetPath } from "./stash-resolve";
8
7
  import { buildEditHint, findSourceForPath, isEditable, resolveStashSources } from "./stash-source";
9
8
  export async function agentikitShow(input) {
10
9
  const parsed = parseAssetRef(input.ref);
11
- const displayType = normalizeAssetType(parsed.type);
10
+ const displayType = parsed.type;
12
11
  const config = loadConfig();
13
12
  const allSources = resolveStashSources();
14
13
  const searchSources = resolveSourcesForOrigin(parsed.origin, allSources);