agentikit 0.0.14 → 0.0.15

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.
Files changed (46) hide show
  1. package/README.md +14 -7
  2. package/dist/asset-spec.js +11 -2
  3. package/dist/asset-type-handler.js +4 -3
  4. package/dist/cli.js +100 -62
  5. package/dist/common.js +6 -6
  6. package/dist/config-cli.js +23 -25
  7. package/dist/config.js +3 -1
  8. package/dist/db.js +28 -30
  9. package/dist/errors.js +28 -0
  10. package/dist/file-context.js +36 -6
  11. package/dist/frontmatter.js +1 -1
  12. package/dist/github.js +1 -3
  13. package/dist/handlers/agent-handler.js +6 -13
  14. package/dist/handlers/command-handler.js +8 -13
  15. package/dist/handlers/handler-bridge.js +51 -0
  16. package/dist/handlers/index.js +12 -10
  17. package/dist/handlers/knowledge-handler.js +7 -31
  18. package/dist/handlers/script-handler.js +9 -45
  19. package/dist/handlers/skill-handler.js +5 -6
  20. package/dist/handlers/tool-handler.js +8 -24
  21. package/dist/indexer.js +21 -21
  22. package/dist/init.js +3 -3
  23. package/dist/llm.js +6 -11
  24. package/dist/lockfile.js +9 -4
  25. package/dist/matchers.js +11 -5
  26. package/dist/metadata.js +25 -16
  27. package/dist/paths.js +5 -4
  28. package/dist/registry-install.js +9 -5
  29. package/dist/registry-resolve.js +12 -8
  30. package/dist/registry-search.js +5 -5
  31. package/dist/renderers.js +24 -14
  32. package/dist/ripgrep-install.js +3 -22
  33. package/dist/ripgrep.js +1 -1
  34. package/dist/self-update.js +15 -9
  35. package/dist/stash-add.js +4 -3
  36. package/dist/stash-clone.js +4 -4
  37. package/dist/stash-ref.js +10 -9
  38. package/dist/stash-registry.js +6 -5
  39. package/dist/stash-resolve.js +10 -9
  40. package/dist/stash-search.js +27 -24
  41. package/dist/stash-show.js +8 -7
  42. package/dist/stash-source.js +10 -11
  43. package/dist/submit.js +26 -21
  44. package/dist/tool-runner.js +1 -5
  45. package/dist/warn.js +20 -0
  46. package/package.json +7 -3
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { IS_WINDOWS, fetchWithRetry } from "./common";
4
+ import { fetchWithRetry, IS_WINDOWS } from "./common";
5
5
  import { githubHeaders } from "./github";
6
6
  const REPO = "itlackey/agentikit";
7
7
  export function detectInstallMethod() {
@@ -43,7 +43,7 @@ export async function checkForUpdate(currentVersion) {
43
43
  return {
44
44
  currentVersion,
45
45
  latestVersion,
46
- updateAvailable: latestVersion !== "" && latestVersion !== currentVersion,
46
+ updateAvailable: latestVersion !== "" && Bun.semver.order(currentVersion, latestVersion) < 0,
47
47
  installMethod,
48
48
  };
49
49
  }
@@ -101,9 +101,7 @@ export async function performUpgrade(check, opts) {
101
101
  if (expectedHash) {
102
102
  const actualHash = createHash("sha256").update(binaryData).digest("hex");
103
103
  if (actualHash !== expectedHash) {
104
- throw new Error(`Checksum mismatch for ${binaryName}.\n` +
105
- `Expected: ${expectedHash}\n` +
106
- `Got: ${actualHash}`);
104
+ throw new Error(`Checksum mismatch for ${binaryName}.\n` + `Expected: ${expectedHash}\n` + `Got: ${actualHash}`);
107
105
  }
108
106
  checksumVerified = true;
109
107
  }
@@ -162,7 +160,9 @@ export async function performUpgrade(check, opts) {
162
160
  try {
163
161
  fs.unlinkSync(tmpPath);
164
162
  }
165
- catch { /* ignore */ }
163
+ catch {
164
+ /* ignore */
165
+ }
166
166
  const code = err.code;
167
167
  if (code === "EACCES" || code === "EPERM") {
168
168
  throw new Error(`Permission denied writing to ${execDir}.\n` +
@@ -181,20 +181,26 @@ export async function performUpgrade(check, opts) {
181
181
  try {
182
182
  fs.unlinkSync(tmpPath);
183
183
  }
184
- catch { /* ignore */ }
184
+ catch {
185
+ /* ignore */
186
+ }
185
187
  try {
186
188
  if (fs.existsSync(bakPath) && !fs.existsSync(execPath)) {
187
189
  fs.renameSync(bakPath, execPath);
188
190
  }
189
191
  }
190
- catch { /* ignore */ }
192
+ catch {
193
+ /* ignore */
194
+ }
191
195
  throw err;
192
196
  }
193
197
  // Cleanup backup
194
198
  try {
195
199
  fs.unlinkSync(bakPath);
196
200
  }
197
- catch { /* ignore */ }
201
+ catch {
202
+ /* ignore */
203
+ }
198
204
  }
199
205
  return {
200
206
  currentVersion,
package/dist/stash-add.js CHANGED
@@ -1,13 +1,14 @@
1
- import { agentikitIndex } from "./indexer";
2
1
  import fs from "node:fs";
3
2
  import { resolveStashDir } from "./common";
4
3
  import { loadConfig } from "./config";
4
+ import { UsageError } from "./errors";
5
+ import { agentikitIndex } from "./indexer";
5
6
  import { upsertLockEntry } from "./lockfile";
6
- import { upsertInstalledRegistryEntry, installRegistryRef } from "./registry-install";
7
+ import { installRegistryRef, upsertInstalledRegistryEntry } from "./registry-install";
7
8
  export async function agentikitAdd(input) {
8
9
  const ref = input.ref.trim();
9
10
  if (!ref)
10
- throw new Error("Install ref or local directory is required.");
11
+ throw new UsageError("Install ref or local directory is required.");
11
12
  const stashDir = resolveStashDir();
12
13
  const installed = await installRegistryRef(ref);
13
14
  const replaced = loadConfig().registry?.installed.find((entry) => entry.id === installed.id);
@@ -1,11 +1,11 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { TYPE_DIRS } from "./asset-spec";
4
- import { parseAssetRef, makeAssetRef } from "./stash-ref";
5
- import { resolveSourcesForOrigin, isRemoteOrigin } from "./origin-resolve";
6
- import { resolveAssetPath } from "./stash-resolve";
7
- import { resolveStashSources, findSourceForPath, getPrimarySource } from "./stash-source";
4
+ import { isRemoteOrigin, resolveSourcesForOrigin } from "./origin-resolve";
8
5
  import { installRegistryRef } from "./registry-install";
6
+ import { makeAssetRef, parseAssetRef } from "./stash-ref";
7
+ import { resolveAssetPath } from "./stash-resolve";
8
+ import { findSourceForPath, getPrimarySource, resolveStashSources } from "./stash-source";
9
9
  export async function agentikitClone(options) {
10
10
  const parsed = parseAssetRef(options.sourceRef);
11
11
  // When --dest is provided, the working stash is optional
package/dist/stash-ref.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { isAssetType } from "./common";
3
+ import { UsageError } from "./errors";
3
4
  // ── Construction ────────────────────────────────────────────────────────────
4
5
  /**
5
6
  * Build a ref string from components.
@@ -29,7 +30,7 @@ export function makeAssetRef(type, name, origin) {
29
30
  export function parseAssetRef(ref) {
30
31
  const trimmed = ref.trim();
31
32
  if (!trimmed)
32
- throw new Error("Empty ref.");
33
+ throw new UsageError("Empty ref.");
33
34
  let origin;
34
35
  let body = trimmed;
35
36
  const boundary = trimmed.indexOf("//");
@@ -37,16 +38,16 @@ export function parseAssetRef(ref) {
37
38
  origin = trimmed.slice(0, boundary);
38
39
  body = trimmed.slice(boundary + 2);
39
40
  if (!origin)
40
- throw new Error("Empty origin in ref.");
41
+ throw new UsageError("Empty origin in ref.");
41
42
  }
42
43
  const colon = body.indexOf(":");
43
44
  if (colon <= 0) {
44
- throw new Error(`Invalid ref "${trimmed}". Expected [origin//]type:name`);
45
+ throw new UsageError(`Invalid ref "${trimmed}". Expected [origin//]type:name`);
45
46
  }
46
47
  const rawType = body.slice(0, colon);
47
48
  const rawName = body.slice(colon + 1);
48
49
  if (!isAssetType(rawType)) {
49
- throw new Error(`Invalid asset type: "${rawType}".`);
50
+ throw new UsageError(`Invalid asset type: "${rawType}".`);
50
51
  }
51
52
  validateName(rawName);
52
53
  const name = normalizeName(rawName);
@@ -55,16 +56,16 @@ export function parseAssetRef(ref) {
55
56
  // ── Validation ──────────────────────────────────────────────────────────────
56
57
  function validateName(name) {
57
58
  if (!name)
58
- throw new Error("Empty asset name.");
59
+ throw new UsageError("Empty asset name.");
59
60
  if (name.includes("\0"))
60
- throw new Error("Null byte in asset name.");
61
+ throw new UsageError("Null byte in asset name.");
61
62
  if (/^[A-Za-z]:/.test(name))
62
- throw new Error("Windows drive path in asset name.");
63
+ throw new UsageError("Windows drive path in asset name.");
63
64
  const normalized = path.posix.normalize(name.replace(/\\/g, "/"));
64
65
  if (path.posix.isAbsolute(normalized))
65
- throw new Error("Absolute path in asset name.");
66
+ throw new UsageError("Absolute path in asset name.");
66
67
  if (normalized === ".." || normalized.startsWith("../")) {
67
- throw new Error("Path traversal in asset name.");
68
+ throw new UsageError("Path traversal in asset name.");
68
69
  }
69
70
  }
70
71
  function normalizeName(name) {
@@ -1,9 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import { resolveStashDir } from "./common";
3
3
  import { loadConfig } from "./config";
4
+ import { NotFoundError, UsageError } from "./errors";
4
5
  import { agentikitIndex } from "./indexer";
5
6
  import { removeLockEntry, upsertLockEntry } from "./lockfile";
6
- import { installRegistryRef, removeInstalledRegistryEntry, upsertInstalledRegistryEntry, } from "./registry-install";
7
+ import { installRegistryRef, removeInstalledRegistryEntry, upsertInstalledRegistryEntry } from "./registry-install";
7
8
  import { parseRegistryRef } from "./registry-resolve";
8
9
  export async function agentikitList(input) {
9
10
  const stashDir = input?.stashDir ?? resolveStashDir();
@@ -24,7 +25,7 @@ export async function agentikitList(input) {
24
25
  export async function agentikitRemove(input) {
25
26
  const target = input.target.trim();
26
27
  if (!target)
27
- throw new Error("Target is required.");
28
+ throw new UsageError("Target is required.");
28
29
  const stashDir = input.stashDir ?? resolveStashDir();
29
30
  const config = loadConfig();
30
31
  const installed = config.registry?.installed ?? [];
@@ -120,12 +121,12 @@ export async function agentikitUpdate(input) {
120
121
  }
121
122
  function selectTargets(installed, target, all) {
122
123
  if (all && target) {
123
- throw new Error("Specify either <target> or --all, not both.");
124
+ throw new UsageError("Specify either <target> or --all, not both.");
124
125
  }
125
126
  if (all)
126
127
  return installed;
127
128
  if (!target) {
128
- throw new Error("Either <target> or --all is required.");
129
+ throw new UsageError("Either <target> or --all is required.");
129
130
  }
130
131
  return [resolveInstalledTarget(installed, target)];
131
132
  }
@@ -148,7 +149,7 @@ function resolveInstalledTarget(installed, target) {
148
149
  if (byParsedId)
149
150
  return byParsedId;
150
151
  }
151
- throw new Error(`No installed registry entry matched target: ${target}`);
152
+ throw new NotFoundError(`No installed registry entry matched target: ${target}`);
152
153
  }
153
154
  function toInstalledEntry(status) {
154
155
  return {
@@ -1,37 +1,38 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
+ import { isRelevantAssetFile, resolveAssetPathFromName, TYPE_DIRS } from "./asset-spec";
3
4
  import { hasErrnoCode, isWithin } from "./common";
4
- import { TYPE_DIRS, isRelevantAssetFile, resolveAssetPathFromName } from "./asset-spec";
5
+ import { NotFoundError, UsageError } from "./errors";
5
6
  export function resolveAssetPath(stashDir, type, name) {
6
7
  const root = path.join(stashDir, TYPE_DIRS[type]);
7
8
  const target = resolveAssetPathFromName(type, root, name);
8
9
  const resolvedRoot = resolveAndValidateTypeRoot(root, type, name);
9
10
  const resolvedTarget = path.resolve(target);
10
11
  if (!isWithin(resolvedTarget, resolvedRoot)) {
11
- throw new Error("Ref resolves outside the stash root.");
12
+ throw new UsageError("Ref resolves outside the stash root.");
12
13
  }
13
14
  if (!fs.existsSync(resolvedTarget) || !fs.statSync(resolvedTarget).isFile()) {
14
- throw new Error(`Stash asset not found for ref: ${type}:${name}`);
15
+ throw new NotFoundError(`Stash asset not found for ref: ${type}:${name}`);
15
16
  }
16
17
  const realTarget = fs.realpathSync(resolvedTarget);
17
18
  if (!isWithin(realTarget, resolvedRoot)) {
18
- throw new Error("Ref resolves outside the stash root.");
19
+ throw new UsageError("Ref resolves outside the stash root.");
19
20
  }
20
21
  if (!isRelevantAssetFile(type, path.basename(resolvedTarget))) {
21
22
  if (type === "tool") {
22
- throw new Error("Tool ref must resolve to a .sh, .ts, .js, .ps1, .cmd, or .bat file.");
23
+ throw new NotFoundError("Tool ref must resolve to a .sh, .ts, .js, .ps1, .cmd, or .bat file.");
23
24
  }
24
25
  if (type === "script") {
25
- throw new Error("Script ref must resolve to a file with a supported script extension. Refer to the Agentikit documentation for the complete list of supported script extensions.");
26
+ throw new NotFoundError("Script ref must resolve to a file with a supported script extension. Refer to the Agentikit documentation for the complete list of supported script extensions.");
26
27
  }
27
- throw new Error(`Stash asset not found for ref: ${type}:${name}`);
28
+ throw new NotFoundError(`Stash asset not found for ref: ${type}:${name}`);
28
29
  }
29
30
  return realTarget;
30
31
  }
31
32
  function resolveAndValidateTypeRoot(root, type, name) {
32
33
  const rootStat = readTypeRootStat(root, type, name);
33
34
  if (!rootStat.isDirectory()) {
34
- throw new Error(`Stash type root is not a directory for ref: ${type}:${name}`);
35
+ throw new NotFoundError(`Stash type root is not a directory for ref: ${type}:${name}`);
35
36
  }
36
37
  return fs.realpathSync(root);
37
38
  }
@@ -41,7 +42,7 @@ function readTypeRootStat(root, type, name) {
41
42
  }
42
43
  catch (error) {
43
44
  if (hasErrnoCode(error, "ENOENT")) {
44
- throw new Error(`Stash type root not found for ref: ${type}:${name}`);
45
+ throw new NotFoundError(`Stash type root not found for ref: ${type}:${name}`);
45
46
  }
46
47
  throw error;
47
48
  }
@@ -1,13 +1,16 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { ASSET_TYPES, TYPE_DIRS, deriveCanonicalAssetName } from "./asset-spec";
4
- import { walkStash } from "./walker";
5
- import { makeAssetRef } from "./stash-ref";
3
+ import { ASSET_TYPES, deriveCanonicalAssetName, TYPE_DIRS } from "./asset-spec";
4
+ import { tryGetHandler } from "./asset-type-handler";
6
5
  import { loadConfig } from "./config";
6
+ import { closeDatabase, getAllEntries, getEntryById, getEntryCount, getMeta, openDatabase, searchFts, searchVec, } from "./db";
7
+ import { UsageError } from "./errors";
8
+ import { getDbPath } from "./paths";
7
9
  import { searchRegistry } from "./registry-search";
8
- import { openDatabase, closeDatabase, getDbPath, getMeta, searchFts, searchVec, getAllEntries, getEntryCount, getEntryById, } from "./db";
9
- import { tryGetHandler } from "./asset-type-handler";
10
- import { resolveStashSources, findSourceForPath, isEditable, buildEditHint } from "./stash-source";
10
+ import { makeAssetRef } from "./stash-ref";
11
+ import { buildEditHint, findSourceForPath, isEditable, resolveStashSources } from "./stash-source";
12
+ import { walkStash } from "./walker";
13
+ import { warn } from "./warn";
11
14
  const DEFAULT_LIMIT = 20;
12
15
  export async function agentikitSearch(input) {
13
16
  const t0 = Date.now();
@@ -17,7 +20,8 @@ export async function agentikitSearch(input) {
17
20
  const limit = normalizeLimit(input.limit);
18
21
  const usageMode = parseSearchUsageMode(input.usage);
19
22
  const source = parseSearchSource(input.source);
20
- const sources = resolveStashSources();
23
+ const config = loadConfig();
24
+ const sources = resolveStashSources(undefined, config);
21
25
  if (sources.length === 0) {
22
26
  return {
23
27
  stashDir: "",
@@ -37,11 +41,9 @@ export async function agentikitSearch(input) {
37
41
  usageMode,
38
42
  stashDir,
39
43
  sources,
44
+ config,
40
45
  });
41
- const config = loadConfig();
42
- const registryResult = source === "local"
43
- ? undefined
44
- : await searchRegistry(query, { limit, registryUrls: config.registryUrls });
46
+ const registryResult = source === "local" ? undefined : await searchRegistry(query, { limit, registryUrls: config.registryUrls });
45
47
  if (source === "local") {
46
48
  return {
47
49
  stashDir,
@@ -83,21 +85,19 @@ export async function agentikitSearch(input) {
83
85
  };
84
86
  }
85
87
  const mergedHits = mergeSearchHits(localResult?.hits ?? [], registryHits, limit);
88
+ const warnings = [...(localResult?.warnings ?? []), ...(registryResult?.warnings ?? [])];
86
89
  return {
87
90
  stashDir,
88
91
  source,
89
92
  hits: mergedHits,
90
93
  usageGuide: localResult?.usageGuide,
91
94
  tip: mergedHits.length === 0 ? "No matching stash assets or registry entries were found." : undefined,
92
- warnings: [...(localResult?.warnings ?? []), ...(registryResult?.warnings ?? [])].length
93
- ? [...(localResult?.warnings ?? []), ...(registryResult?.warnings ?? [])]
94
- : undefined,
95
+ warnings: warnings.length ? warnings : undefined,
95
96
  timing: { totalMs: Date.now() - t0 },
96
97
  };
97
98
  }
98
99
  async function searchLocal(input) {
99
- const { query, searchType, limit, usageMode, stashDir, sources } = input;
100
- const config = loadConfig();
100
+ const { query, searchType, limit, usageMode, stashDir, sources, config } = input;
101
101
  const allStashDirs = sources.map((s) => s.path);
102
102
  // Try to open the database
103
103
  const dbPath = getDbPath();
@@ -113,7 +113,9 @@ async function searchLocal(input) {
113
113
  return {
114
114
  hits,
115
115
  usageGuide,
116
- tip: hits.length === 0 ? "No matching stash assets were found. Try running 'akm index' to rebuild." : undefined,
116
+ tip: hits.length === 0
117
+ ? "No matching stash assets were found. Try running 'akm index' to rebuild."
118
+ : undefined,
117
119
  embedMs,
118
120
  rankMs,
119
121
  };
@@ -125,12 +127,14 @@ async function searchLocal(input) {
125
127
  }
126
128
  }
127
129
  catch (error) {
128
- console.warn("Search index unavailable, falling back to substring search:", error instanceof Error ? error.message : String(error));
130
+ warn("Search index unavailable, falling back to substring search:", error instanceof Error ? error.message : String(error));
129
131
  }
130
132
  const hits = allStashDirs
131
133
  .flatMap((dir) => substringSearch(query, searchType, limit, dir, sources, config))
132
134
  .slice(0, limit);
133
- const usageGuide = shouldIncludeUsageGuide(usageMode) ? buildUsageGuide(hits.map((hit) => hit.type), searchType) : undefined;
135
+ const usageGuide = shouldIncludeUsageGuide(usageMode)
136
+ ? buildUsageGuide(hits.map((hit) => hit.type), searchType)
137
+ : undefined;
134
138
  return {
135
139
  hits,
136
140
  usageGuide,
@@ -297,7 +301,7 @@ async function tryVecScores(db, query, k, config) {
297
301
  return scores;
298
302
  }
299
303
  catch (error) {
300
- console.warn("Vector search failed, skipping:", error instanceof Error ? error.message : String(error));
304
+ warn("Vector search failed, skipping:", error instanceof Error ? error.message : String(error));
301
305
  return null;
302
306
  }
303
307
  }
@@ -314,8 +318,7 @@ function substringSearch(query, searchType, limit, stashDir, sources, config) {
314
318
  function buildDbHit(input) {
315
319
  const entryStashDir = findSourceForPath(input.path, input.sources)?.path ?? input.defaultStashDir;
316
320
  const typeRoot = path.join(entryStashDir, TYPE_DIRS[input.entry.type]);
317
- const openRefName = deriveCanonicalAssetName(input.entry.type, typeRoot, input.path)
318
- ?? input.entry.name;
321
+ const openRefName = deriveCanonicalAssetName(input.entry.type, typeRoot, input.path) ?? input.entry.name;
319
322
  const qualityBoost = input.entry.generated === true ? 0 : 0.05;
320
323
  const confidenceBoost = typeof input.entry.confidence === "number" ? Math.min(0.05, Math.max(0, input.entry.confidence) * 0.05) : 0;
321
324
  const score = Math.round(input.score * (1 + qualityBoost + confidenceBoost) * 1000) / 1000;
@@ -398,14 +401,14 @@ function parseSearchUsageMode(mode) {
398
401
  }
399
402
  if (typeof mode === "undefined")
400
403
  return "both";
401
- throw new Error(`Invalid usage mode: ${String(mode)}. Expected one of: none|both|item|guide`);
404
+ throw new UsageError(`Invalid usage mode: ${String(mode)}. Expected one of: none|both|item|guide`);
402
405
  }
403
406
  function parseSearchSource(source) {
404
407
  if (source === "local" || source === "registry" || source === "both")
405
408
  return source;
406
409
  if (typeof source === "undefined")
407
410
  return "local";
408
- throw new Error(`Invalid search source: ${String(source)}. Expected one of: local|registry|both`);
411
+ throw new UsageError(`Invalid search source: ${String(source)}. Expected one of: local|registry|both`);
409
412
  }
410
413
  function mergeSearchHits(localHits, registryHits, limit) {
411
414
  const all = [...localHits, ...registryHits];
@@ -1,11 +1,12 @@
1
1
  import fs from "node:fs";
2
- import { parseAssetRef } from "./stash-ref";
3
- import { resolveSourcesForOrigin } from "./origin-resolve";
4
- import { resolveAssetPath } from "./stash-resolve";
5
2
  import { getHandler } from "./asset-type-handler";
6
- import { resolveStashSources, findSourceForPath, isEditable, buildEditHint } from "./stash-source";
7
- import { buildFileContext, runMatchers, getRenderer, buildRenderContext } from "./file-context";
8
3
  import { loadConfig } from "./config";
4
+ import { NotFoundError } from "./errors";
5
+ import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "./file-context";
6
+ import { resolveSourcesForOrigin } from "./origin-resolve";
7
+ import { parseAssetRef } from "./stash-ref";
8
+ import { resolveAssetPath } from "./stash-resolve";
9
+ import { buildEditHint, findSourceForPath, isEditable, resolveStashSources } from "./stash-source";
9
10
  export async function agentikitShow(input) {
10
11
  const parsed = parseAssetRef(input.ref);
11
12
  const config = loadConfig();
@@ -25,11 +26,11 @@ export async function agentikitShow(input) {
25
26
  }
26
27
  if (!assetPath && parsed.origin && searchSources.length === 0) {
27
28
  const installCmd = `akm add ${parsed.origin}`;
28
- throw new Error(`Stash asset not found for ref: ${parsed.type}:${parsed.name}. ` +
29
+ throw new NotFoundError(`Stash asset not found for ref: ${parsed.type}:${parsed.name}. ` +
29
30
  `Kit "${parsed.origin}" is not installed. Run: ${installCmd}`);
30
31
  }
31
32
  if (!assetPath) {
32
- throw lastError ?? new Error(`Stash asset not found for ref: ${parsed.type}:${parsed.name}`);
33
+ throw lastError ?? new NotFoundError(`Stash asset not found for ref: ${parsed.type}:${parsed.name}`);
33
34
  }
34
35
  const source = findSourceForPath(assetPath, allSources);
35
36
  const sourceStashDir = source?.path ?? allStashDirs[0];
@@ -2,6 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { resolveStashDir } from "./common";
4
4
  import { loadConfig } from "./config";
5
+ import { warn } from "./warn";
5
6
  // ── Resolution ──────────────────────────────────────────────────────────────
6
7
  /**
7
8
  * Build the ordered list of stash sources (search paths):
@@ -12,15 +13,13 @@ import { loadConfig } from "./config";
12
13
  * The first entry is always the primary stash. Additional entries come
13
14
  * from `searchPaths` config and `registry.installed` entries.
14
15
  */
15
- export function resolveStashSources(overrideStashDir) {
16
+ export function resolveStashSources(overrideStashDir, existingConfig) {
16
17
  const stashDir = overrideStashDir ?? resolveStashDir();
17
- const config = loadConfig();
18
- const sources = [
19
- { path: stashDir },
20
- ];
18
+ const config = existingConfig ?? loadConfig();
19
+ const sources = [{ path: stashDir }];
21
20
  for (const dir of config.searchPaths) {
22
21
  if (isSuspiciousStashRoot(dir)) {
23
- console.warn(`Warning: stash root "${dir}" appears to be a system directory. This may be unintentional.`);
22
+ warn(`Warning: stash root "${dir}" appears to be a system directory. This may be unintentional.`);
24
23
  }
25
24
  if (isValidDirectory(dir)) {
26
25
  sources.push({ path: dir });
@@ -28,7 +27,7 @@ export function resolveStashSources(overrideStashDir) {
28
27
  }
29
28
  for (const entry of config.registry?.installed ?? []) {
30
29
  if (isSuspiciousStashRoot(entry.stashRoot)) {
31
- console.warn(`Warning: stash root "${entry.stashRoot}" appears to be a system directory. This may be unintentional.`);
30
+ warn(`Warning: stash root "${entry.stashRoot}" appears to be a system directory. This may be unintentional.`);
32
31
  }
33
32
  if (isValidDirectory(entry.stashRoot)) {
34
33
  sources.push({
@@ -103,15 +102,15 @@ export function buildEditHint(filePath, assetType, assetName, origin) {
103
102
  return `This asset is managed by akm and may be overwritten on update. To edit, run: akm clone ${ref}`;
104
103
  }
105
104
  // ── Validation ──────────────────────────────────────────────────────────────
106
- const SUSPICIOUS_ROOTS = new Set(['/', '/etc', '/bin', '/sbin', '/usr', '/var', '/tmp', '/dev', '/proc', '/sys']);
105
+ const SUSPICIOUS_ROOTS = new Set(["/", "/etc", "/bin", "/sbin", "/usr", "/var", "/tmp", "/dev", "/proc", "/sys"]);
107
106
  function isSuspiciousStashRoot(dir) {
108
107
  const resolved = path.resolve(dir);
109
- const normalized = process.platform === 'win32' ? resolved.toLowerCase() : resolved;
108
+ const normalized = process.platform === "win32" ? resolved.toLowerCase() : resolved;
110
109
  if (SUSPICIOUS_ROOTS.has(normalized))
111
110
  return true;
112
- if (process.platform === 'win32') {
111
+ if (process.platform === "win32") {
113
112
  // Check for Windows system directories
114
- const winDir = (process.env.SystemRoot || 'C:\\Windows').toLowerCase();
113
+ const winDir = (process.env.SystemRoot || "C:\\Windows").toLowerCase();
115
114
  if (normalized === winDir || normalized.startsWith(winDir + path.sep))
116
115
  return true;
117
116
  }
package/dist/submit.js CHANGED
@@ -4,7 +4,7 @@ import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { fetchWithRetry, isAssetType } from "./common";
7
- import { GITHUB_API_BASE, asRecord, asString, githubHeaders } from "./github";
7
+ import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "./github";
8
8
  import { parseRegistryRef } from "./registry-resolve";
9
9
  const REGISTRY_OWNER = "itlackey";
10
10
  const REGISTRY_REPO = "agentikit-registry";
@@ -66,7 +66,9 @@ export async function agentikitSubmit(options = {}) {
66
66
  let forkCreated = false;
67
67
  try {
68
68
  progress("Forking and cloning agentikit-registry");
69
- runCommand(runtime, "gh", ["repo", "fork", `${REGISTRY_OWNER}/${REGISTRY_REPO}`, "--clone", "--remote"], { cwd: tempRoot });
69
+ runCommand(runtime, "gh", ["repo", "fork", `${REGISTRY_OWNER}/${REGISTRY_REPO}`, "--clone", "--remote"], {
70
+ cwd: tempRoot,
71
+ });
70
72
  forkCreated = true;
71
73
  progress(`Creating branch ${branchName}`);
72
74
  runCommand(runtime, "git", ["checkout", "-b", branchName], { cwd: cloneDir });
@@ -112,20 +114,13 @@ export async function agentikitSubmit(options = {}) {
112
114
  export async function buildSubmitEntry(options) {
113
115
  const packageJson = options.packageJson;
114
116
  const interactive = options.interactive === true;
115
- const defaultName = options.name?.trim()
116
- || packageJson?.name
117
- || inferNameFromParsedRef(options.parsed);
118
- const defaultDescription = options.description?.trim()
119
- || packageJson?.description;
117
+ const defaultName = options.name?.trim() || packageJson?.name || inferNameFromParsedRef(options.parsed);
118
+ const defaultDescription = options.description?.trim() || packageJson?.description;
120
119
  const defaultTags = normalizeTags(options.tags ?? packageJson?.keywords ?? []);
121
120
  const defaultAssetTypes = normalizeAssetTypes(options.assetTypes ?? packageJson?.agentikitAssetTypes ?? []);
122
- const defaultAuthor = options.author?.trim()
123
- || packageJson?.author;
124
- const defaultLicense = options.license?.trim()
125
- || packageJson?.license;
126
- const defaultHomepage = options.homepage?.trim()
127
- || packageJson?.homepage
128
- || inferHomepage(options.parsed);
121
+ const defaultAuthor = options.author?.trim() || packageJson?.author;
122
+ const defaultLicense = options.license?.trim() || packageJson?.license;
123
+ const defaultHomepage = options.homepage?.trim() || packageJson?.homepage || inferHomepage(options.parsed);
129
124
  const rl = interactive ? createInterface({ input: process.stdin, output: process.stdout }) : undefined;
130
125
  try {
131
126
  const promptedName = await promptWithDefault("Name", defaultName, rl);
@@ -209,8 +204,7 @@ async function inferSubmitTargetFromDir(dir) {
209
204
  if (packageJson.name) {
210
205
  candidates.push(parseSubmitRef(packageJson.name));
211
206
  }
212
- const githubRef = extractGithubRepoRef(packageJson.repositoryUrl)
213
- ?? extractGithubRepoRef(packageJson.homepage);
207
+ const githubRef = extractGithubRepoRef(packageJson.repositoryUrl) ?? extractGithubRepoRef(packageJson.homepage);
214
208
  if (githubRef) {
215
209
  candidates.push(parseSubmitRef(githubRef));
216
210
  }
@@ -277,7 +271,9 @@ function inferNameFromParsedRef(parsed) {
277
271
  function canonicalSubmitRef(parsed) {
278
272
  if (parsed.source === "npm")
279
273
  return parsed.packageName;
280
- return parsed.requestedRef ? `${parsed.owner}/${parsed.repo}#${parsed.requestedRef}` : `${parsed.owner}/${parsed.repo}`;
274
+ return parsed.requestedRef
275
+ ? `${parsed.owner}/${parsed.repo}#${parsed.requestedRef}`
276
+ : `${parsed.owner}/${parsed.repo}`;
281
277
  }
282
278
  function inferHomepage(parsed) {
283
279
  if (parsed.source === "npm") {
@@ -394,7 +390,7 @@ async function fetchRegistryManualEntries() {
394
390
  if (!entriesResponse.ok) {
395
391
  throw new Error(`Failed to load ${MANUAL_ENTRIES_FILE} from ${REGISTRY_OWNER}/${REGISTRY_REPO} (${entriesResponse.status}).`);
396
392
  }
397
- const parsed = await entriesResponse.json();
393
+ const parsed = (await entriesResponse.json());
398
394
  if (!Array.isArray(parsed)) {
399
395
  throw new Error(`${MANUAL_ENTRIES_FILE} is not a JSON array.`);
400
396
  }
@@ -424,7 +420,10 @@ function readManualEntriesFile(filePath) {
424
420
  }
425
421
  function createPullRequest(options) {
426
422
  const output = runCommand(options.runtime, "gh", buildPullRequestArgs(options.entry, options.username, options.branchName, options.pullRequestBody), { cwd: options.cloneDir });
427
- const url = output.stdout.trim().split(/\r?\n/).find((line) => /^https:\/\/github\.com\//.test(line));
423
+ const url = output.stdout
424
+ .trim()
425
+ .split(/\r?\n/)
426
+ .find((line) => /^https:\/\/github\.com\//.test(line));
428
427
  if (!url) {
429
428
  throw new Error("gh pr create did not return a pull request URL.");
430
429
  }
@@ -460,7 +459,10 @@ function buildPlannedCommands(options) {
460
459
  formatCommand(["git", "add", MANUAL_ENTRIES_FILE]),
461
460
  formatCommand(["git", "commit", "-m", `feat: add ${options.entry.name} to registry`]),
462
461
  formatCommand(["git", "push", "origin", options.branchName]),
463
- formatCommand(["gh", ...buildPullRequestArgs(options.entry, options.username, options.branchName, options.pullRequestBody)]),
462
+ formatCommand([
463
+ "gh",
464
+ ...buildPullRequestArgs(options.entry, options.username, options.branchName, options.pullRequestBody),
465
+ ]),
464
466
  ];
465
467
  if (options.cleanupFork) {
466
468
  commands.push(`# After PR is merged: ${formatCommand(["gh", "repo", "delete", `${options.username}/${REGISTRY_REPO}`, "--yes"])}`);
@@ -512,7 +514,10 @@ function ensureGhAvailable(runtime) {
512
514
  }
513
515
  }
514
516
  function ensureGhAuthenticated(runtime) {
515
- const result = spawnSync(runtime.ghBin, ["auth", "status", "--hostname", "github.com"], { encoding: "utf8", timeout: 10_000 });
517
+ const result = spawnSync(runtime.ghBin, ["auth", "status", "--hostname", "github.com"], {
518
+ encoding: "utf8",
519
+ timeout: 10_000,
520
+ });
516
521
  if (result.status !== 0) {
517
522
  throw new Error("gh CLI is not authenticated for github.com.");
518
523
  }
@@ -88,11 +88,7 @@ export function shellQuote(input) {
88
88
  .replace(/"/g, '""');
89
89
  return `"${escaped}"`;
90
90
  }
91
- const escaped = input
92
- .replace(/\\/g, "\\\\")
93
- .replace(/"/g, '\\"')
94
- .replace(/\$/g, "\\$")
95
- .replace(/`/g, "\\`");
91
+ const escaped = input.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
96
92
  return `"${escaped}"`;
97
93
  }
98
94
  /**
package/dist/warn.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Module-level quiet flag for suppressing stderr warnings.
3
+ * Controlled by the CLI --quiet / -q flag.
4
+ */
5
+ let quiet = false;
6
+ export function setQuiet(value) {
7
+ quiet = value;
8
+ }
9
+ export function isQuiet() {
10
+ return quiet;
11
+ }
12
+ /**
13
+ * Emit a warning to stderr unless --quiet is active.
14
+ * Drop-in replacement for console.warn() across the codebase.
15
+ */
16
+ export function warn(...args) {
17
+ if (!quiet) {
18
+ console.warn(...args);
19
+ }
20
+ }