akm-cli 0.0.23 → 0.1.1

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
@@ -1,6 +1,6 @@
1
1
  # Agent Kit Manager
2
2
 
3
- > Agent-i-Kit
3
+ > **akm** — Agent Kit Manager
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/akm-cli)](https://www.npmjs.com/package/akm-cli)
6
6
  [![CI](https://github.com/itlackey/agentikit/actions/workflows/ci.yml/badge.svg)](https://github.com/itlackey/agentikit/actions/workflows/ci.yml)
@@ -25,7 +25,7 @@ Upgrade in place with `akm upgrade`.
25
25
  ## Quick Start
26
26
 
27
27
  ```sh
28
- akm init # Initialize your stash
28
+ akm init # Initialize your working stash
29
29
  akm add github:owner/repo # Add a kit from GitHub
30
30
  akm search "deploy" # Find assets
31
31
  akm show script:deploy.sh # View details and run command
@@ -89,7 +89,7 @@ Registries are indexes of available kits. The official
89
89
  ```sh
90
90
  akm registry search "code review" # Search registries
91
91
  akm registry add https://example.com/registry/index.json --name team # Add a registry
92
- akm sources add http://host:1933 --provider openviking \
92
+ akm stash add http://host:1933 --provider openviking \
93
93
  --options '{"apiKey":"key"}' # Add an OpenViking stash source
94
94
  akm registry list # List configured registries
95
95
  akm show viking://resources/my-doc # Fetch remote content from OpenViking
@@ -130,7 +130,7 @@ See the [Kit Maker's Guide](docs/kit-makers.md) for a full walkthrough.
130
130
  | [Getting Started](docs/getting-started.md) | Quick setup guide |
131
131
  | [CLI Reference](docs/cli.md) | All commands and flags |
132
132
  | [Configuration](docs/configuration.md) | Settings, providers, and Ollama setup |
133
- | [Concepts](docs/concepts.md) | Asset types, classification, stash model |
133
+ | [Concepts](docs/concepts.md) | Stashes, kits, registries, asset types |
134
134
  | [Kit Maker's Guide](docs/kit-makers.md) | Build and share kits |
135
135
  | [Registry](docs/registry.md) | Registries, search, and the v2 index format |
136
136
 
@@ -79,7 +79,7 @@ export function _setAssetTypeHooks(rendererHook, actionBuilderHook) {
79
79
  _registerActionBuilder = actionBuilderHook;
80
80
  }
81
81
  /**
82
- * Register a custom asset type with the Agentikit asset system.
82
+ * Register a custom asset type with the akm asset system.
83
83
  *
84
84
  * ## Full extension registration API
85
85
  *
package/dist/cli.js CHANGED
@@ -3,21 +3,22 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { defineCommand, runMain } from "citty";
5
5
  import { resolveStashDir } from "./common";
6
+ import { generateBashCompletions, installBashCompletions } from "./completions";
6
7
  import { DEFAULT_CONFIG, getConfigPath, loadConfig, saveConfig } from "./config";
7
8
  import { getConfigValue, listConfig, setConfigValue, unsetConfigValue } from "./config-cli";
8
9
  import { ConfigError, NotFoundError, UsageError } from "./errors";
9
- import { agentikitIndex } from "./indexer";
10
- import { agentikitInit } from "./init";
11
- import { agentikitList, agentikitRemove, agentikitUpdate } from "./installed-kits";
10
+ import { akmIndex } from "./indexer";
11
+ import { akmInit } from "./init";
12
+ import { akmList, akmRemove, akmUpdate } from "./installed-kits";
12
13
  import { getCacheDir, getDbPath, getDefaultStashDir } from "./paths";
13
14
  import { buildRegistryIndex, writeRegistryIndex } from "./registry-build-index";
14
15
  import { searchRegistry } from "./registry-search";
15
16
  import { checkForUpdate, performUpgrade } from "./self-update";
16
- import { agentikitAdd } from "./stash-add";
17
- import { agentikitClone } from "./stash-clone";
18
- import { agentikitSearch, parseSearchSource } from "./stash-search";
19
- import { agentikitShowUnified } from "./stash-show";
20
- import { addStashSource, listStashSources, removeStashSource } from "./stash-source-manage";
17
+ import { akmAdd, akmKitAdd } from "./stash-add";
18
+ import { akmClone } from "./stash-clone";
19
+ import { akmSearch, parseSearchSource } from "./stash-search";
20
+ import { akmShowUnified } from "./stash-show";
21
+ import { addStash, listStashes, removeStash } from "./stash-source-manage";
21
22
  import { setQuiet, warn } from "./warn";
22
23
  // Version: prefer compile-time define, then package.json, then fallback
23
24
  const pkgVersion = (() => {
@@ -405,14 +406,14 @@ function formatSearchPlain(r, detail) {
405
406
  const initCommand = defineCommand({
406
407
  meta: {
407
408
  name: "init",
408
- description: "Initialize Agent-i-Kit's working stash directory and persist stashDir in config",
409
+ description: "Initialize akm's working stash directory and persist stashDir in config",
409
410
  },
410
411
  args: {
411
412
  dir: { type: "string", description: "Custom stash directory path (default: ~/akm)" },
412
413
  },
413
414
  async run({ args }) {
414
415
  await runWithJsonErrors(async () => {
415
- const result = await agentikitInit({ dir: args.dir });
416
+ const result = await akmInit({ dir: args.dir });
416
417
  output("init", result);
417
418
  });
418
419
  },
@@ -424,7 +425,7 @@ const indexCommand = defineCommand({
424
425
  },
425
426
  async run({ args }) {
426
427
  await runWithJsonErrors(async () => {
427
- const result = await agentikitIndex({ full: args.full });
428
+ const result = await akmIndex({ full: args.full });
428
429
  output("index", result);
429
430
  });
430
431
  },
@@ -451,7 +452,7 @@ const searchCommand = defineCommand({
451
452
  }
452
453
  const limit = limitRaw;
453
454
  const source = parseSearchSource(args.source);
454
- const result = await agentikitSearch({ query: args.query, type, limit, source });
455
+ const result = await akmSearch({ query: args.query, type, limit, source });
455
456
  output("search", result);
456
457
  });
457
458
  },
@@ -467,7 +468,7 @@ const addCommand = defineCommand({
467
468
  },
468
469
  async run({ args }) {
469
470
  await runWithJsonErrors(async () => {
470
- const result = await agentikitAdd({ ref: args.ref });
471
+ const result = await akmAdd({ ref: args.ref });
471
472
  output("add", result);
472
473
  });
473
474
  },
@@ -476,7 +477,7 @@ const listCommand = defineCommand({
476
477
  meta: { name: "list", description: "List installed kits" },
477
478
  async run() {
478
479
  await runWithJsonErrors(async () => {
479
- const result = await agentikitList();
480
+ const result = await akmList();
480
481
  output("list", result);
481
482
  });
482
483
  },
@@ -488,7 +489,7 @@ const removeCommand = defineCommand({
488
489
  },
489
490
  async run({ args }) {
490
491
  await runWithJsonErrors(async () => {
491
- const result = await agentikitRemove({ target: args.target });
492
+ const result = await akmRemove({ target: args.target });
492
493
  output("remove", result);
493
494
  });
494
495
  },
@@ -502,11 +503,36 @@ const updateCommand = defineCommand({
502
503
  },
503
504
  async run({ args }) {
504
505
  await runWithJsonErrors(async () => {
505
- const result = await agentikitUpdate({ target: args.target, all: args.all, force: args.force });
506
+ const result = await akmUpdate({ target: args.target, all: args.all, force: args.force });
506
507
  output("update", result);
507
508
  });
508
509
  },
509
510
  });
511
+ const kitAddCommand = defineCommand({
512
+ meta: { name: "add", description: "Install a kit from npm, GitHub, or any git host" },
513
+ args: {
514
+ ref: {
515
+ type: "positional",
516
+ description: "Registry ref (npm package, owner/repo, or git URL)",
517
+ required: true,
518
+ },
519
+ },
520
+ async run({ args }) {
521
+ await runWithJsonErrors(async () => {
522
+ const result = await akmKitAdd({ ref: args.ref });
523
+ output("add", result);
524
+ });
525
+ },
526
+ });
527
+ const kitCommand = defineCommand({
528
+ meta: { name: "kit", description: "Manage installed kits" },
529
+ subCommands: {
530
+ add: kitAddCommand,
531
+ list: listCommand,
532
+ remove: removeCommand,
533
+ update: updateCommand,
534
+ },
535
+ });
510
536
  const upgradeCommand = defineCommand({
511
537
  meta: { name: "upgrade", description: "Upgrade akm to the latest release" },
512
538
  args: {
@@ -568,7 +594,7 @@ const showCommand = defineCommand({
568
594
  throw new UsageError(`Unknown view mode: ${args.akmView}. Expected one of: full|toc|frontmatter|section|lines`);
569
595
  }
570
596
  }
571
- const result = await agentikitShowUnified({ ref: args.ref, view });
597
+ const result = await akmShowUnified({ ref: args.ref, view });
572
598
  output("show", result);
573
599
  });
574
600
  },
@@ -672,7 +698,7 @@ const configCommand = defineCommand({
672
698
  const cloneCommand = defineCommand({
673
699
  meta: {
674
700
  name: "clone",
675
- description: "Clone an asset from any stash source into the working stash or a custom destination",
701
+ description: "Clone an asset from any source into the working stash or a custom destination",
676
702
  },
677
703
  args: {
678
704
  ref: { type: "positional", description: "Asset ref (e.g. npm:@scope/pkg//script:deploy.sh)", required: true },
@@ -682,7 +708,7 @@ const cloneCommand = defineCommand({
682
708
  },
683
709
  async run({ args }) {
684
710
  await runWithJsonErrors(async () => {
685
- const result = await agentikitClone({
711
+ const result = await akmClone({
686
712
  sourceRef: args.ref,
687
713
  newName: args.name,
688
714
  force: args.force,
@@ -815,21 +841,20 @@ const registryCommand = defineCommand({
815
841
  },
816
842
  });
817
843
  /**
818
- * Shared subcommand definitions for stash source management.
819
- * Used by both `akm stash` (preferred) and `akm sources` (legacy alias).
844
+ * Subcommand definitions for managing additional stashes.
820
845
  */
821
846
  function buildSourceSubCommands(outputPrefix) {
822
847
  return {
823
848
  list: defineCommand({
824
- meta: { name: "list", description: "List all stash sources" },
849
+ meta: { name: "list", description: "List all stashes in search order" },
825
850
  run() {
826
851
  return runWithJsonErrors(() => {
827
- output(`${outputPrefix}`, listStashSources());
852
+ output(`${outputPrefix}`, listStashes());
828
853
  });
829
854
  },
830
855
  }),
831
856
  add: defineCommand({
832
- meta: { name: "add", description: "Add a stash source (filesystem path or remote URL)" },
857
+ meta: { name: "add", description: "Register an additional stash (filesystem path or remote URL)" },
833
858
  args: {
834
859
  target: { type: "positional", description: "Path or URL to add", required: true },
835
860
  name: { type: "string", description: "Human-friendly name for the source" },
@@ -856,7 +881,7 @@ function buildSourceSubCommands(outputPrefix) {
856
881
  throw new UsageError("--options must be valid JSON");
857
882
  }
858
883
  }
859
- const result = addStashSource({
884
+ const result = addStash({
860
885
  target: args.target,
861
886
  name: args.name,
862
887
  providerType: args.provider,
@@ -867,13 +892,13 @@ function buildSourceSubCommands(outputPrefix) {
867
892
  },
868
893
  }),
869
894
  remove: defineCommand({
870
- meta: { name: "remove", description: "Remove a stash source by URL, path, or name" },
895
+ meta: { name: "remove", description: "Remove an additional stash by URL, path, or name" },
871
896
  args: {
872
897
  target: { type: "positional", description: "Source URL, path, or name to remove", required: true },
873
898
  },
874
899
  run({ args }) {
875
900
  return runWithJsonErrors(() => {
876
- const result = removeStashSource(args.target);
901
+ const result = removeStash(args.target);
877
902
  output(`${outputPrefix}-remove`, result);
878
903
  });
879
904
  },
@@ -881,13 +906,9 @@ function buildSourceSubCommands(outputPrefix) {
881
906
  };
882
907
  }
883
908
  const stashCommand = defineCommand({
884
- meta: { name: "stash", description: "Manage stash sources (local paths and remote providers)" },
909
+ meta: { name: "stash", description: "Manage additional stashes (local directories and remote providers)" },
885
910
  subCommands: buildSourceSubCommands("stash"),
886
911
  });
887
- const sourcesCommand = defineCommand({
888
- meta: { name: "sources", description: "Manage stash sources (alias for 'akm stash')" },
889
- subCommands: buildSourceSubCommands("sources"),
890
- });
891
912
  const hintsCommand = defineCommand({
892
913
  meta: {
893
914
  name: "hints",
@@ -901,11 +922,43 @@ const hintsCommand = defineCommand({
901
922
  process.stdout.write(loadHints(detail));
902
923
  },
903
924
  });
925
+ const completionsCommand = defineCommand({
926
+ meta: {
927
+ name: "completions",
928
+ description: "Generate or install shell completion script",
929
+ },
930
+ args: {
931
+ install: {
932
+ type: "boolean",
933
+ description: "Install completions to the appropriate directory",
934
+ default: false,
935
+ },
936
+ shell: {
937
+ type: "string",
938
+ description: "Shell type (bash)",
939
+ default: "bash",
940
+ },
941
+ },
942
+ run({ args }) {
943
+ if (args.shell !== "bash") {
944
+ throw new UsageError(`Unsupported shell: ${args.shell}. Only bash is supported.`);
945
+ }
946
+ const script = generateBashCompletions(main);
947
+ if (args.install) {
948
+ const dest = installBashCompletions(script);
949
+ console.error(`Completions installed to ${dest}`);
950
+ console.error(`Restart your shell or run: source ${dest}`);
951
+ }
952
+ else {
953
+ process.stdout.write(script);
954
+ }
955
+ },
956
+ });
904
957
  const main = defineCommand({
905
958
  meta: {
906
959
  name: "akm",
907
960
  version: pkgVersion,
908
- description: "CLI tool to search, open, and manage assets from Agent-i-Kit stash.",
961
+ description: "Agent Kit Manager search, show, and manage assets from your stash.",
909
962
  },
910
963
  args: {
911
964
  format: { type: "string", description: "Output format (json|text|yaml)" },
@@ -919,15 +972,16 @@ const main = defineCommand({
919
972
  list: listCommand,
920
973
  remove: removeCommand,
921
974
  update: updateCommand,
975
+ kit: kitCommand,
922
976
  upgrade: upgradeCommand,
923
977
  search: searchCommand,
924
978
  show: showCommand,
925
979
  clone: cloneCommand,
926
980
  stash: stashCommand,
927
- sources: sourcesCommand,
928
981
  registry: registryCommand,
929
982
  config: configCommand,
930
983
  hints: hintsCommand,
984
+ completions: completionsCommand,
931
985
  },
932
986
  });
933
987
  const CONFIG_SUBCOMMAND_SET = new Set(["path", "list", "get", "set", "unset"]);
@@ -1079,14 +1133,14 @@ function loadHints(detail = "normal") {
1079
1133
  }
1080
1134
  const EMBEDDED_HINTS = `# akm CLI
1081
1135
 
1082
- You have access to a searchable library of scripts, skills, commands, agents, and knowledge documents via \`akm\`. Search the stash first before writing something from scratch.
1136
+ You have access to a searchable library of scripts, skills, commands, agents, and knowledge documents via \`akm\`. Search your stashes first before writing something from scratch.
1083
1137
 
1084
1138
  ## Quick Reference
1085
1139
 
1086
1140
  \`\`\`sh
1087
- akm search "<query>" # Search for assets
1141
+ akm search "<query>" # Search your stashes and installed kits
1088
1142
  akm search "<query>" --type skill # Filter by type
1089
- akm search "<query>" --source both # Search stash providers and registries for assets
1143
+ akm search "<query>" --source both # Also search registries for installable kits
1090
1144
  akm show <ref> # View asset details
1091
1145
  akm add <ref> # Install a kit (npm, GitHub, git, local)
1092
1146
  akm clone <ref> # Copy an asset to the working stash (optional --dest arg to clone to specific location)
@@ -1107,14 +1161,14 @@ Run \`akm -h\` for the full command reference.
1107
1161
  `;
1108
1162
  const EMBEDDED_HINTS_FULL = `# akm CLI — Full Reference
1109
1163
 
1110
- You have access to a searchable library of scripts, skills, commands, agents, and knowledge documents via \`akm\`. Search the stash first before writing something from scratch.
1164
+ You have access to a searchable library of scripts, skills, commands, agents, and knowledge documents via \`akm\`. Search your stashes first before writing something from scratch.
1111
1165
 
1112
1166
  ## Search
1113
1167
 
1114
1168
  \`\`\`sh
1115
- akm search "<query>" # Search local stash
1169
+ akm search "<query>" # Search your stashes and installed kits
1116
1170
  akm search "<query>" --type skill # Filter by asset type
1117
- akm search "<query>" --source both # Search all stash providers and registries
1171
+ akm search "<query>" --source both # Also search registries for installable kits
1118
1172
  akm search "<query>" --source registry # Search registries only
1119
1173
  akm search "<query>" --limit 10 # Limit results
1120
1174
  akm search "<query>" --detail full # Include scores, paths, timing
@@ -1155,10 +1209,14 @@ akm show viking://resources/my-doc # Show remote OpenViking content
1155
1209
  ## Install & Manage Kits
1156
1210
 
1157
1211
  \`\`\`sh
1158
- akm add <ref> # Install a kit
1212
+ akm add <ref> # Install a kit (smart router: local dirs become stash adds)
1159
1213
  akm add @scope/kit # From npm
1160
1214
  akm add owner/repo # From GitHub
1161
- akm add ./path/to/local/kit # From local directory
1215
+ akm add ./path/to/local/kit # From local directory (adds as stash)
1216
+ akm kit add <ref> # Install a kit (explicit)
1217
+ akm kit list # List installed kits
1218
+ akm kit remove <target> # Remove a kit
1219
+ akm kit update --all # Update all kits
1162
1220
  akm list # List installed kits
1163
1221
  akm remove <target> # Remove by id or ref
1164
1222
  akm update --all # Update all installed kits
@@ -1206,13 +1264,16 @@ akm config path --all # Show all config paths
1206
1264
  ## Other Commands
1207
1265
 
1208
1266
  \`\`\`sh
1209
- akm init # Initialize stash directory
1267
+ akm init # Initialize working stash
1210
1268
  akm index # Rebuild search index
1211
1269
  akm index --full # Full reindex
1212
- akm sources # List stash search paths
1270
+ akm stash # List all stashes
1271
+ akm kit # Kit management (add, list, remove, update)
1213
1272
  akm upgrade # Upgrade akm binary
1214
1273
  akm upgrade --check # Check for updates
1215
1274
  akm hints # Print this reference
1275
+ akm completions # Print bash completion script
1276
+ akm completions --install # Install completions
1216
1277
  \`\`\`
1217
1278
 
1218
1279
  ## Output Control
@@ -0,0 +1,137 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ // ── Known flag values ────────────────────────────────────────────────────────
5
+ const FLAG_VALUES = {
6
+ "--format": ["json", "text", "yaml"],
7
+ "--detail": ["brief", "normal", "full"],
8
+ "--type": ["skill", "command", "agent", "knowledge", "script", "memory", "any"],
9
+ "--source": ["stash", "registry", "both"],
10
+ "--shell": ["bash"],
11
+ };
12
+ function walkCommandTree(cmd, parentPath = "") {
13
+ const name = cmd.meta?.name ?? "";
14
+ const currentPath = parentPath ? `${parentPath} ${name}` : name;
15
+ const result = [];
16
+ const subcommands = Object.keys(cmd.subCommands ?? {});
17
+ const flags = [];
18
+ if (cmd.args) {
19
+ for (const [flagName, arg] of Object.entries(cmd.args)) {
20
+ if (arg.type === "positional")
21
+ continue;
22
+ flags.push(`--${flagName}`);
23
+ }
24
+ }
25
+ result.push({ path: currentPath, subcommands, flags });
26
+ if (cmd.subCommands) {
27
+ for (const sub of Object.values(cmd.subCommands)) {
28
+ result.push(...walkCommandTree(sub, currentPath));
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+ // ── Bash completion script generator ─────────────────────────────────────────
34
+ export function generateBashCompletions(cmd) {
35
+ const commands = walkCommandTree(cmd);
36
+ const rootName = cmd.meta?.name ?? "akm";
37
+ // Collect global flags from root command
38
+ const rootInfo = commands.find((c) => c.path === rootName);
39
+ const globalFlags = rootInfo?.flags ?? [];
40
+ // Build the case blocks for subcommand completion
41
+ const caseBlocks = [];
42
+ for (const info of commands) {
43
+ const allFlags = [...new Set([...info.flags, ...globalFlags])];
44
+ if (info.subcommands.length > 0 || allFlags.length > 0) {
45
+ const matchPath = info.path;
46
+ const subcmdStr = info.subcommands.join(" ");
47
+ const flagStr = allFlags.join(" ");
48
+ caseBlocks.push(` "${matchPath}")
49
+ if [[ "\${cur}" == -* ]]; then
50
+ COMPREPLY=( $(compgen -W "${flagStr}" -- "\${cur}") )
51
+ else
52
+ COMPREPLY=( $(compgen -W "${subcmdStr}" -- "\${cur}") )
53
+ fi
54
+ return 0
55
+ ;;`);
56
+ }
57
+ }
58
+ // Build flag-value completion cases
59
+ const valueCases = [];
60
+ for (const [flag, values] of Object.entries(FLAG_VALUES)) {
61
+ valueCases.push(` ${flag})
62
+ COMPREPLY=( $(compgen -W "${values.join(" ")}" -- "\${cur}") )
63
+ return 0
64
+ ;;`);
65
+ }
66
+ const script = `#!/bin/bash
67
+ # Bash completion for ${rootName}
68
+ # Generated by ${rootName} completions
69
+
70
+ _${rootName}() {
71
+ local cur prev words cword
72
+ if type _init_completion &>/dev/null; then
73
+ _init_completion || return
74
+ else
75
+ cur="\${COMP_WORDS[COMP_CWORD]}"
76
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
77
+ words=("\${COMP_WORDS[@]}")
78
+ cword=\${COMP_CWORD}
79
+ fi
80
+
81
+ # Complete flag values
82
+ case "\${prev}" in
83
+ ${valueCases.join("\n")}
84
+ esac
85
+
86
+ # Build the command path from COMP_WORDS
87
+ local cmd_path="${rootName}"
88
+ for (( i=1; i < cword; i++ )); do
89
+ case "\${words[i]}" in
90
+ -*) continue ;;
91
+ *) cmd_path="\${cmd_path} \${words[i]}" ;;
92
+ esac
93
+ done
94
+
95
+ # Complete based on current command path
96
+ case "\${cmd_path}" in
97
+ ${caseBlocks.join("\n")}
98
+ esac
99
+
100
+ return 0
101
+ }
102
+
103
+ complete -F _${rootName} ${rootName}
104
+ `;
105
+ return script;
106
+ }
107
+ // ── Install ──────────────────────────────────────────────────────────────────
108
+ export function installBashCompletions(script) {
109
+ const dest = resolveInstallPath();
110
+ const dir = path.dirname(dest);
111
+ fs.mkdirSync(dir, { recursive: true });
112
+ fs.writeFileSync(dest, script, "utf8");
113
+ return dest;
114
+ }
115
+ function resolveInstallPath() {
116
+ const xdgData = process.env.XDG_DATA_HOME?.trim();
117
+ if (xdgData) {
118
+ return path.join(xdgData, "bash-completion", "completions", "akm");
119
+ }
120
+ const home = os.homedir();
121
+ // Default XDG location
122
+ const defaultXdg = path.join(home, ".local", "share", "bash-completion", "completions", "akm");
123
+ const defaultXdgDir = path.dirname(defaultXdg);
124
+ if (isDir(defaultXdgDir) || isDir(path.dirname(defaultXdgDir))) {
125
+ return defaultXdg;
126
+ }
127
+ // Fallback
128
+ return path.join(home, ".bash_completion.d", "akm");
129
+ }
130
+ function isDir(p) {
131
+ try {
132
+ return fs.statSync(p).isDirectory();
133
+ }
134
+ catch {
135
+ return false;
136
+ }
137
+ }
@@ -9,16 +9,6 @@ export function parseConfigValue(key, value) {
9
9
  throw new UsageError(`Invalid value for semanticSearch: expected "true" or "false"`);
10
10
  }
11
11
  return { semanticSearch: value === "true" };
12
- case "searchPaths":
13
- try {
14
- const parsed = JSON.parse(value);
15
- if (!Array.isArray(parsed))
16
- throw new UsageError("expected JSON array");
17
- return { searchPaths: parsed.filter((d) => typeof d === "string") };
18
- }
19
- catch {
20
- throw new UsageError(`Invalid value for searchPaths: expected JSON array (e.g. '["/path/a","/path/b"]')`);
21
- }
22
12
  case "embedding":
23
13
  return { embedding: parseEmbeddingConnectionValue(value) };
24
14
  case "llm":
@@ -41,8 +31,6 @@ export function getConfigValue(config, key) {
41
31
  return config.stashDir ?? null;
42
32
  case "semanticSearch":
43
33
  return config.semanticSearch;
44
- case "searchPaths":
45
- return [...config.searchPaths];
46
34
  case "embedding":
47
35
  return config.embedding ?? null;
48
36
  case "llm":
@@ -63,7 +51,6 @@ export function setConfigValue(config, key, rawValue) {
63
51
  switch (key) {
64
52
  case "stashDir":
65
53
  case "semanticSearch":
66
- case "searchPaths":
67
54
  case "embedding":
68
55
  case "llm":
69
56
  case "registries":
@@ -108,8 +95,6 @@ export function listConfig(config) {
108
95
  result.embedding = config.embedding;
109
96
  if (config.llm)
110
97
  result.llm = config.llm;
111
- if (config.searchPaths?.length)
112
- result.searchPaths = config.searchPaths;
113
98
  return result;
114
99
  }
115
100
  function mergeConfigValue(config, partial) {
package/dist/config.js CHANGED
@@ -4,7 +4,6 @@ import { getConfigDir as _getConfigDir, getConfigPath as _getConfigPath } from "
4
4
  // ── Defaults ────────────────────────────────────────────────────────────────
5
5
  export const DEFAULT_CONFIG = {
6
6
  semanticSearch: true,
7
- searchPaths: [],
8
7
  registries: [
9
8
  { url: "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json", name: "official" },
10
9
  { url: "https://skills.sh", name: "skills.sh", provider: "skills-sh" },
@@ -93,8 +92,6 @@ function sanitizeConfigForWrite(config) {
93
92
  sanitized.llm = rest;
94
93
  }
95
94
  // Drop empty keys to keep config clean
96
- if (!config.searchPaths?.length)
97
- delete sanitized.searchPaths;
98
95
  return sanitized;
99
96
  }
100
97
  export function updateConfig(partial) {
@@ -125,8 +122,18 @@ function pickKnownKeys(raw) {
125
122
  if (typeof raw.semanticSearch === "boolean") {
126
123
  config.semanticSearch = raw.semanticSearch;
127
124
  }
125
+ // Migrate legacy searchPaths into stashes
128
126
  if (Array.isArray(raw.searchPaths)) {
129
- config.searchPaths = raw.searchPaths.filter((d) => typeof d === "string");
127
+ const legacyPaths = raw.searchPaths.filter((d) => typeof d === "string");
128
+ if (legacyPaths.length > 0) {
129
+ const existing = config.stashes ?? [];
130
+ const migrated = legacyPaths
131
+ .filter((p) => !existing.some((s) => s.type === "filesystem" && s.path === p))
132
+ .map((p) => ({ type: "filesystem", path: p }));
133
+ if (migrated.length > 0) {
134
+ config.stashes = [...existing, ...migrated];
135
+ }
136
+ }
130
137
  }
131
138
  const embedding = parseEmbeddingConfig(raw.embedding);
132
139
  if (embedding)
@@ -270,7 +277,7 @@ function parseEmbeddingConfig(value) {
270
277
  if (typeof obj.endpoint !== "string" || !obj.endpoint)
271
278
  return undefined;
272
279
  if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
273
- console.warn(`[agentikit] Ignoring embedding config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
280
+ console.warn(`[akm] Ignoring embedding config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
274
281
  return undefined;
275
282
  }
276
283
  if (typeof obj.model !== "string" || !obj.model)
@@ -303,7 +310,7 @@ function parseLlmConfig(value) {
303
310
  if (typeof obj.endpoint !== "string" || !obj.endpoint)
304
311
  return undefined;
305
312
  if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
306
- console.warn(`[agentikit] Ignoring llm config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
313
+ console.warn(`[akm] Ignoring llm config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
307
314
  return undefined;
308
315
  }
309
316
  if (typeof obj.model !== "string" || !obj.model)
package/dist/github.js CHANGED
@@ -1,12 +1,31 @@
1
1
  export const GITHUB_API_BASE = "https://api.github.com";
2
- export function githubHeaders() {
2
+ const GITHUB_TOKEN_DOMAINS = new Set(["api.github.com", "github.com", "uploads.github.com"]);
3
+ /**
4
+ * Build headers for GitHub API requests.
5
+ * When a `url` is provided, the Authorization header is only included if the
6
+ * URL points to a known GitHub domain, preventing token leakage on redirects
7
+ * to third-party hosts.
8
+ */
9
+ export function githubHeaders(url) {
3
10
  const token = process.env.GITHUB_TOKEN?.trim();
4
11
  const headers = {
5
12
  Accept: "application/vnd.github+json",
6
13
  "User-Agent": "akm-registry",
7
14
  };
8
- if (token)
9
- headers.Authorization = `Bearer ${token}`;
15
+ if (token) {
16
+ let includeToken = true;
17
+ if (url) {
18
+ try {
19
+ const hostname = new URL(url).hostname;
20
+ includeToken = GITHUB_TOKEN_DOMAINS.has(hostname);
21
+ }
22
+ catch {
23
+ includeToken = false;
24
+ }
25
+ }
26
+ if (includeToken)
27
+ headers.Authorization = `Bearer ${token}`;
28
+ }
10
29
  return headers;
11
30
  }
12
31
  export function asRecord(value) {