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 +4 -4
- package/dist/asset-spec.js +1 -1
- package/dist/cli.js +105 -44
- package/dist/completions.js +137 -0
- package/dist/config-cli.js +0 -15
- package/dist/config.js +13 -6
- package/dist/github.js +22 -3
- package/dist/indexer.js +2 -2
- package/dist/init.js +1 -1
- package/dist/installed-kits.js +10 -10
- package/dist/kit-include.js +3 -3
- package/dist/local-search.js +1 -1
- package/dist/lockfile.js +42 -3
- package/dist/registry-build-index.js +4 -11
- package/dist/registry-install.js +12 -12
- package/dist/registry-resolve.js +1 -1
- package/dist/registry-search.js +1 -1
- package/dist/{stash-source.js → search-source.js} +4 -8
- package/dist/stash-add.js +26 -7
- package/dist/stash-clone.js +28 -2
- package/dist/stash-providers/filesystem.js +1 -1
- package/dist/stash-providers/openviking.js +1 -1
- package/dist/stash-search.js +3 -3
- package/dist/stash-show.js +8 -5
- package/dist/stash-source-manage.js +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent Kit Manager
|
|
2
2
|
|
|
3
|
-
> Agent
|
|
3
|
+
> **akm** — Agent Kit Manager
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/akm-cli)
|
|
6
6
|
[](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
|
|
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) |
|
|
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
|
|
package/dist/asset-spec.js
CHANGED
|
@@ -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
|
|
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 {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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 {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
|
849
|
+
meta: { name: "list", description: "List all stashes in search order" },
|
|
825
850
|
run() {
|
|
826
851
|
return runWithJsonErrors(() => {
|
|
827
|
-
output(`${outputPrefix}`,
|
|
852
|
+
output(`${outputPrefix}`, listStashes());
|
|
828
853
|
});
|
|
829
854
|
},
|
|
830
855
|
}),
|
|
831
856
|
add: defineCommand({
|
|
832
|
-
meta: { name: "add", description: "
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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: "
|
|
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
|
|
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
|
|
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 #
|
|
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
|
|
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
|
|
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 #
|
|
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
|
|
1267
|
+
akm init # Initialize working stash
|
|
1210
1268
|
akm index # Rebuild search index
|
|
1211
1269
|
akm index --full # Full reindex
|
|
1212
|
-
akm
|
|
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
|
+
}
|
package/dist/config-cli.js
CHANGED
|
@@ -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
|
-
|
|
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(`[
|
|
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(`[
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|