akm-cli 0.0.16 → 0.0.17
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 +58 -107
- package/dist/asset-spec.js +9 -9
- package/dist/cli.js +140 -118
- package/dist/common.js +0 -7
- package/dist/config-cli.js +38 -0
- package/dist/config.js +40 -26
- package/dist/file-context.js +3 -28
- package/dist/indexer.js +0 -30
- package/dist/llm.js +1 -1
- package/dist/matchers.js +4 -15
- package/dist/metadata.js +3 -3
- package/dist/registry-install.js +5 -5
- package/dist/registry-search.js +140 -30
- package/dist/renderers.js +1 -1
- package/dist/stash-add.js +2 -2
- package/dist/stash-ref.js +7 -9
- package/dist/stash-registry.js +6 -6
- package/dist/stash-resolve.js +7 -44
- package/dist/stash-search.js +8 -12
- package/dist/stash-show.js +1 -2
- package/dist/stash-source.js +6 -7
- package/dist/walker.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { ConfigError, NotFoundError, UsageError } from "./errors";
|
|
|
9
9
|
import { agentikitIndex } from "./indexer";
|
|
10
10
|
import { agentikitInit } from "./init";
|
|
11
11
|
import { getCacheDir, getDbPath, getDefaultStashDir } from "./paths";
|
|
12
|
+
import { searchRegistry } from "./registry-search";
|
|
12
13
|
import { checkForUpdate, performUpgrade } from "./self-update";
|
|
13
14
|
import { agentikitAdd } from "./stash-add";
|
|
14
15
|
import { agentikitClone } from "./stash-clone";
|
|
@@ -101,6 +102,8 @@ function shapeForCommand(command, result, detail) {
|
|
|
101
102
|
switch (command) {
|
|
102
103
|
case "search":
|
|
103
104
|
return shapeSearchOutput(result, detail);
|
|
105
|
+
case "registry-search":
|
|
106
|
+
return shapeRegistrySearchOutput(result, detail);
|
|
104
107
|
case "show":
|
|
105
108
|
return shapeShowOutput(result, detail);
|
|
106
109
|
default:
|
|
@@ -127,6 +130,22 @@ function shapeSearchOutput(result, detail) {
|
|
|
127
130
|
...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
|
|
128
131
|
};
|
|
129
132
|
}
|
|
133
|
+
function shapeRegistrySearchOutput(result, detail) {
|
|
134
|
+
const hits = Array.isArray(result.hits) ? result.hits : [];
|
|
135
|
+
const assetHits = Array.isArray(result.assetHits) ? result.assetHits : [];
|
|
136
|
+
// Shape kit hits as registry type
|
|
137
|
+
const shapedKitHits = hits.map((hit) => shapeSearchHit({ ...hit, type: "registry" }, detail));
|
|
138
|
+
const shapedAssetHits = assetHits.map((hit) => shapeSearchHit(hit, detail));
|
|
139
|
+
const shaped = {
|
|
140
|
+
hits: shapedKitHits,
|
|
141
|
+
...(shapedAssetHits.length > 0 ? { assetHits: shapedAssetHits } : {}),
|
|
142
|
+
...(Array.isArray(result.warnings) && result.warnings.length > 0 ? { warnings: result.warnings } : {}),
|
|
143
|
+
};
|
|
144
|
+
if (detail === "full") {
|
|
145
|
+
shaped.query = result.query;
|
|
146
|
+
}
|
|
147
|
+
return shaped;
|
|
148
|
+
}
|
|
130
149
|
function shapeSearchHit(hit, detail) {
|
|
131
150
|
// Keep local and registry hit models separate internally so search and
|
|
132
151
|
// ranking logic can carry source-specific metadata. Normalize the external
|
|
@@ -139,6 +158,15 @@ function shapeSearchHit(hit, detail) {
|
|
|
139
158
|
return pickFields(hit, ["type", "name", "id", "description", "tags", "action", "curated"]);
|
|
140
159
|
return hit;
|
|
141
160
|
}
|
|
161
|
+
if (hit.type === "registry-asset") {
|
|
162
|
+
const brief = withTruncatedDescription(pickFields(hit, ["type", "assetType", "assetName", "description", "kit", "action"]));
|
|
163
|
+
if (detail === "brief")
|
|
164
|
+
return brief;
|
|
165
|
+
if (detail === "normal") {
|
|
166
|
+
return pickFields(hit, ["type", "assetType", "assetName", "description", "kit", "registryName", "action"]);
|
|
167
|
+
}
|
|
168
|
+
return hit;
|
|
169
|
+
}
|
|
142
170
|
const brief = withTruncatedDescription(pickFields(hit, ["type", "name", "description", "action"]));
|
|
143
171
|
if (detail === "brief")
|
|
144
172
|
return brief;
|
|
@@ -405,7 +433,7 @@ const searchCommand = defineCommand({
|
|
|
405
433
|
query: { type: "positional", description: "Search query (omit to list all assets)", required: false, default: "" },
|
|
406
434
|
type: {
|
|
407
435
|
type: "string",
|
|
408
|
-
description: "Asset type filter (skill|command|agent|knowledge|script|any).
|
|
436
|
+
description: "Asset type filter (skill|command|agent|knowledge|script|any).",
|
|
409
437
|
},
|
|
410
438
|
limit: { type: "string", description: "Maximum number of results" },
|
|
411
439
|
source: { type: "string", description: "Search source (local|registry|both)", default: "local" },
|
|
@@ -439,7 +467,7 @@ const addCommand = defineCommand({
|
|
|
439
467
|
},
|
|
440
468
|
});
|
|
441
469
|
const listCommand = defineCommand({
|
|
442
|
-
meta: { name: "list", description: "List installed
|
|
470
|
+
meta: { name: "list", description: "List installed kits" },
|
|
443
471
|
async run() {
|
|
444
472
|
await runWithJsonErrors(async () => {
|
|
445
473
|
const result = await agentikitList();
|
|
@@ -448,7 +476,7 @@ const listCommand = defineCommand({
|
|
|
448
476
|
},
|
|
449
477
|
});
|
|
450
478
|
const removeCommand = defineCommand({
|
|
451
|
-
meta: { name: "remove", description: "Remove an installed
|
|
479
|
+
meta: { name: "remove", description: "Remove an installed kit by id or ref" },
|
|
452
480
|
args: {
|
|
453
481
|
target: { type: "positional", description: "Installed target (id or ref)", required: true },
|
|
454
482
|
},
|
|
@@ -460,7 +488,7 @@ const removeCommand = defineCommand({
|
|
|
460
488
|
},
|
|
461
489
|
});
|
|
462
490
|
const updateCommand = defineCommand({
|
|
463
|
-
meta: { name: "update", description: "Update one or all installed
|
|
491
|
+
meta: { name: "update", description: "Update one or all installed kits" },
|
|
464
492
|
args: {
|
|
465
493
|
target: { type: "positional", description: "Installed target (id or ref)", required: false },
|
|
466
494
|
all: { type: "boolean", description: "Update all installed entries", default: false },
|
|
@@ -500,35 +528,33 @@ const showCommand = defineCommand({
|
|
|
500
528
|
ref: { type: "positional", description: "Asset ref (type:name)", required: true },
|
|
501
529
|
format: { type: "string", description: "Output format (json|text|yaml)" },
|
|
502
530
|
detail: { type: "string", description: "Detail level (brief|normal|full)" },
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
start: { type: "string", description: "Start line (for lines view)" },
|
|
508
|
-
end: { type: "string", description: "End line (for lines view)" },
|
|
531
|
+
akmView: { type: "string", description: "Internal positional knowledge view mode parser" },
|
|
532
|
+
akmHeading: { type: "string", description: "Internal positional section heading parser" },
|
|
533
|
+
akmStart: { type: "string", description: "Internal positional start-line parser" },
|
|
534
|
+
akmEnd: { type: "string", description: "Internal positional end-line parser" },
|
|
509
535
|
},
|
|
510
536
|
async run({ args }) {
|
|
511
537
|
await runWithJsonErrors(async () => {
|
|
512
538
|
let view;
|
|
513
|
-
if (args.
|
|
514
|
-
switch (args.
|
|
539
|
+
if (args.akmView) {
|
|
540
|
+
switch (args.akmView) {
|
|
515
541
|
case "section":
|
|
516
|
-
view = { mode: "section", heading: args.
|
|
542
|
+
view = { mode: "section", heading: args.akmHeading ?? "" };
|
|
517
543
|
break;
|
|
518
544
|
case "lines":
|
|
519
545
|
view = {
|
|
520
546
|
mode: "lines",
|
|
521
|
-
start: Number(args.
|
|
522
|
-
end: args.
|
|
547
|
+
start: Number(args.akmStart ?? "1"),
|
|
548
|
+
end: args.akmEnd ? parseInt(args.akmEnd, 10) : Number.MAX_SAFE_INTEGER,
|
|
523
549
|
};
|
|
524
550
|
break;
|
|
525
551
|
case "toc":
|
|
526
552
|
case "frontmatter":
|
|
527
553
|
case "full":
|
|
528
|
-
view = { mode: args.
|
|
554
|
+
view = { mode: args.akmView };
|
|
529
555
|
break;
|
|
530
556
|
default:
|
|
531
|
-
throw new UsageError(`Unknown view mode: ${args.
|
|
557
|
+
throw new UsageError(`Unknown view mode: ${args.akmView}. Expected one of: full|toc|frontmatter|section|lines`);
|
|
532
558
|
}
|
|
533
559
|
}
|
|
534
560
|
const result = await agentikitShow({ ref: args.ref, view });
|
|
@@ -540,9 +566,6 @@ const configCommand = defineCommand({
|
|
|
540
566
|
meta: { name: "config", description: "Show and manage configuration" },
|
|
541
567
|
args: {
|
|
542
568
|
list: { type: "boolean", description: "List current configuration", default: false },
|
|
543
|
-
get: { type: "string", description: "Get a configuration value by key" },
|
|
544
|
-
unset: { type: "string", description: "Unset an optional configuration key or whole embedding/llm section" },
|
|
545
|
-
set: { type: "string", description: "Back-compat alias for updating a key (key=value format)" },
|
|
546
569
|
},
|
|
547
570
|
subCommands: {
|
|
548
571
|
path: defineCommand({
|
|
@@ -631,30 +654,7 @@ const configCommand = defineCommand({
|
|
|
631
654
|
output("config", listConfig(loadConfig()));
|
|
632
655
|
return;
|
|
633
656
|
}
|
|
634
|
-
|
|
635
|
-
output("config", getConfigValue(loadConfig(), args.get));
|
|
636
|
-
return;
|
|
637
|
-
}
|
|
638
|
-
if (args.unset) {
|
|
639
|
-
const updated = unsetConfigValue(loadConfig(), args.unset);
|
|
640
|
-
saveConfig(updated);
|
|
641
|
-
output("config", listConfig(updated));
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
if (args.set) {
|
|
645
|
-
const eqIndex = args.set.indexOf("=");
|
|
646
|
-
if (eqIndex === -1) {
|
|
647
|
-
throw new UsageError("--set expects key=value format");
|
|
648
|
-
}
|
|
649
|
-
const key = args.set.slice(0, eqIndex);
|
|
650
|
-
const value = args.set.slice(eqIndex + 1);
|
|
651
|
-
const config = setConfigValue(loadConfig(), key, value);
|
|
652
|
-
saveConfig(config);
|
|
653
|
-
output("config", listConfig(config));
|
|
654
|
-
}
|
|
655
|
-
else {
|
|
656
|
-
output("config", listConfig(loadConfig()));
|
|
657
|
-
}
|
|
657
|
+
output("config", listConfig(loadConfig()));
|
|
658
658
|
});
|
|
659
659
|
},
|
|
660
660
|
});
|
|
@@ -664,7 +664,7 @@ const cloneCommand = defineCommand({
|
|
|
664
664
|
description: "Clone an asset from any stash source into the working stash or a custom destination",
|
|
665
665
|
},
|
|
666
666
|
args: {
|
|
667
|
-
ref: { type: "positional", description: "Asset ref (e.g.
|
|
667
|
+
ref: { type: "positional", description: "Asset ref (e.g. npm:@scope/pkg//script:deploy.sh)", required: true },
|
|
668
668
|
name: { type: "string", description: "New name for the cloned asset" },
|
|
669
669
|
force: { type: "boolean", description: "Overwrite if asset already exists in working stash", default: false },
|
|
670
670
|
dest: { type: "string", description: "Destination directory (default: working stash)" },
|
|
@@ -681,12 +681,91 @@ const cloneCommand = defineCommand({
|
|
|
681
681
|
});
|
|
682
682
|
},
|
|
683
683
|
});
|
|
684
|
+
const registryCommand = defineCommand({
|
|
685
|
+
meta: { name: "registry", description: "Manage kit registries" },
|
|
686
|
+
subCommands: {
|
|
687
|
+
list: defineCommand({
|
|
688
|
+
meta: { name: "list", description: "List configured registries" },
|
|
689
|
+
run() {
|
|
690
|
+
return runWithJsonErrors(() => {
|
|
691
|
+
const config = loadConfig();
|
|
692
|
+
const registries = config.registries ?? [];
|
|
693
|
+
output("registry-list", { registries });
|
|
694
|
+
});
|
|
695
|
+
},
|
|
696
|
+
}),
|
|
697
|
+
add: defineCommand({
|
|
698
|
+
meta: { name: "add", description: "Add a registry by URL" },
|
|
699
|
+
args: {
|
|
700
|
+
url: { type: "positional", description: "Registry index URL", required: true },
|
|
701
|
+
name: { type: "string", description: "Human-friendly name for the registry" },
|
|
702
|
+
},
|
|
703
|
+
run({ args }) {
|
|
704
|
+
return runWithJsonErrors(() => {
|
|
705
|
+
if (!args.url.startsWith("http")) {
|
|
706
|
+
throw new UsageError("Registry URL must start with http:// or https://");
|
|
707
|
+
}
|
|
708
|
+
const config = loadConfig();
|
|
709
|
+
const registries = [...(config.registries ?? [])];
|
|
710
|
+
// Deduplicate by URL
|
|
711
|
+
if (registries.some((r) => r.url === args.url)) {
|
|
712
|
+
output("registry-add", { registries, added: false, message: "Registry URL already configured" });
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
const entry = { url: args.url };
|
|
716
|
+
if (args.name)
|
|
717
|
+
entry.name = args.name;
|
|
718
|
+
registries.push(entry);
|
|
719
|
+
saveConfig({ ...config, registries });
|
|
720
|
+
output("registry-add", { registries, added: true });
|
|
721
|
+
});
|
|
722
|
+
},
|
|
723
|
+
}),
|
|
724
|
+
remove: defineCommand({
|
|
725
|
+
meta: { name: "remove", description: "Remove a registry by URL or name" },
|
|
726
|
+
args: {
|
|
727
|
+
target: { type: "positional", description: "Registry URL or name to remove", required: true },
|
|
728
|
+
},
|
|
729
|
+
run({ args }) {
|
|
730
|
+
return runWithJsonErrors(() => {
|
|
731
|
+
const config = loadConfig();
|
|
732
|
+
const registries = [...(config.registries ?? [])];
|
|
733
|
+
const idx = registries.findIndex((r) => r.url === args.target || r.name === args.target);
|
|
734
|
+
if (idx === -1) {
|
|
735
|
+
output("registry-remove", { registries, removed: false, message: "No matching registry found" });
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const removed = registries.splice(idx, 1)[0];
|
|
739
|
+
saveConfig({ ...config, registries });
|
|
740
|
+
output("registry-remove", { registries, removed: true, entry: removed });
|
|
741
|
+
});
|
|
742
|
+
},
|
|
743
|
+
}),
|
|
744
|
+
search: defineCommand({
|
|
745
|
+
meta: { name: "search", description: "Search enabled registries for kits" },
|
|
746
|
+
args: {
|
|
747
|
+
query: { type: "positional", description: "Search query", required: true },
|
|
748
|
+
limit: { type: "string", description: "Maximum number of results" },
|
|
749
|
+
assets: { type: "boolean", description: "Include asset-level search results", default: false },
|
|
750
|
+
},
|
|
751
|
+
async run({ args }) {
|
|
752
|
+
await runWithJsonErrors(async () => {
|
|
753
|
+
const limit = args.limit ? parseInt(args.limit, 10) : undefined;
|
|
754
|
+
const result = await searchRegistry(args.query, { limit, includeAssets: args.assets });
|
|
755
|
+
output("registry-search", result);
|
|
756
|
+
});
|
|
757
|
+
},
|
|
758
|
+
}),
|
|
759
|
+
},
|
|
760
|
+
});
|
|
684
761
|
const sourcesCommand = defineCommand({
|
|
685
762
|
meta: { name: "sources", description: "List all stash search paths and their status" },
|
|
686
763
|
run() {
|
|
687
764
|
return runWithJsonErrors(() => {
|
|
765
|
+
const config = loadConfig();
|
|
688
766
|
const sources = resolveStashSources();
|
|
689
|
-
|
|
767
|
+
const registries = config.registries ?? [];
|
|
768
|
+
output("sources", { sources, registries });
|
|
690
769
|
});
|
|
691
770
|
},
|
|
692
771
|
});
|
|
@@ -713,6 +792,7 @@ const main = defineCommand({
|
|
|
713
792
|
show: showCommand,
|
|
714
793
|
clone: cloneCommand,
|
|
715
794
|
sources: sourcesCommand,
|
|
795
|
+
registry: registryCommand,
|
|
716
796
|
config: configCommand,
|
|
717
797
|
},
|
|
718
798
|
});
|
|
@@ -721,7 +801,7 @@ const CONFIG_SUBCOMMAND_SET = new Set(["path", "list", "get", "set", "unset"]);
|
|
|
721
801
|
const SHOW_VIEW_MODES = new Set(["toc", "frontmatter", "full", "section", "lines"]);
|
|
722
802
|
// citty reads process.argv directly and does not accept a custom argv array,
|
|
723
803
|
// so we must replace process.argv with the normalized version before runMain.
|
|
724
|
-
process.argv = normalizeShowArgv(
|
|
804
|
+
process.argv = normalizeShowArgv(process.argv);
|
|
725
805
|
runMain(main);
|
|
726
806
|
function parseSearchSource(value) {
|
|
727
807
|
if (SEARCH_SOURCES.includes(value))
|
|
@@ -764,7 +844,7 @@ function buildHint(message) {
|
|
|
764
844
|
return "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg`.";
|
|
765
845
|
if (message.includes("Specify either <target> or --all"))
|
|
766
846
|
return "Use only one: a positional target or `--all`.";
|
|
767
|
-
if (message.includes("No installed
|
|
847
|
+
if (message.includes("No installed kit matched target"))
|
|
768
848
|
return "Run `akm list` to view installed ids/refs, then retry with one of those values.";
|
|
769
849
|
if (message.includes("remote package fetched but asset not found"))
|
|
770
850
|
return "The remote package was fetched but doesn't contain the requested asset. Check the asset name and type.";
|
|
@@ -783,83 +863,25 @@ function hasConfigSubcommand(args) {
|
|
|
783
863
|
const command = Array.isArray(args._) ? args._[0] : undefined;
|
|
784
864
|
return typeof command === "string" && CONFIG_SUBCOMMAND_SET.has(command);
|
|
785
865
|
}
|
|
786
|
-
/**
|
|
787
|
-
* Normalize argv before citty parses it so git-style config forms like
|
|
788
|
-
* `akm config llm.maxTokens 512` and `akm config --get llm.maxTokens`
|
|
789
|
-
* are normalized into the existing config subcommands.
|
|
790
|
-
*
|
|
791
|
-
* Returns a new array; the input is never modified.
|
|
792
|
-
*/
|
|
793
|
-
function normalizeConfigArgv(argv) {
|
|
794
|
-
// Global flags should not be treated as config subcommand arguments.
|
|
795
|
-
// We strip them from the analysis portion, normalize, then re-append them.
|
|
796
|
-
const globalFlags = [];
|
|
797
|
-
const configArgs = [];
|
|
798
|
-
for (let i = 3; i < argv.length; i++) {
|
|
799
|
-
const arg = argv[i];
|
|
800
|
-
if (arg === "--quiet" || arg === "-q") {
|
|
801
|
-
globalFlags.push(arg);
|
|
802
|
-
continue;
|
|
803
|
-
}
|
|
804
|
-
if (arg.startsWith("--format=") || arg.startsWith("--detail=")) {
|
|
805
|
-
globalFlags.push(arg);
|
|
806
|
-
continue;
|
|
807
|
-
}
|
|
808
|
-
if (arg === "--format" || arg === "--detail") {
|
|
809
|
-
globalFlags.push(arg);
|
|
810
|
-
if (argv[i + 1] !== undefined) {
|
|
811
|
-
globalFlags.push(argv[i + 1]);
|
|
812
|
-
i++;
|
|
813
|
-
}
|
|
814
|
-
continue;
|
|
815
|
-
}
|
|
816
|
-
configArgs.push(arg);
|
|
817
|
-
}
|
|
818
|
-
const [command, argAfterCommand, argAfterKey, ...rest] = [argv[2], ...configArgs];
|
|
819
|
-
if (command !== "config")
|
|
820
|
-
return argv;
|
|
821
|
-
if (!argAfterCommand)
|
|
822
|
-
return argv;
|
|
823
|
-
const prefix = argv.slice(0, 3);
|
|
824
|
-
const buildResult = (...newArgs) => [...prefix, ...newArgs, ...globalFlags];
|
|
825
|
-
if (argAfterCommand === "--list") {
|
|
826
|
-
return buildResult("list");
|
|
827
|
-
}
|
|
828
|
-
if (argAfterCommand === "--get" && argAfterKey) {
|
|
829
|
-
return buildResult("get", argAfterKey, ...rest);
|
|
830
|
-
}
|
|
831
|
-
if (argAfterCommand === "--unset" && argAfterKey) {
|
|
832
|
-
return buildResult("unset", argAfterKey, ...rest);
|
|
833
|
-
}
|
|
834
|
-
if (argAfterCommand.startsWith("-"))
|
|
835
|
-
return argv;
|
|
836
|
-
if (CONFIG_SUBCOMMAND_SET.has(argAfterCommand))
|
|
837
|
-
return argv;
|
|
838
|
-
// A single arg after `config` behaves like `git config <key>` and reads the value.
|
|
839
|
-
if (argAfterKey === undefined) {
|
|
840
|
-
return buildResult("get", argAfterCommand);
|
|
841
|
-
}
|
|
842
|
-
return buildResult("set", argAfterCommand, argAfterKey, ...rest);
|
|
843
|
-
}
|
|
844
866
|
/**
|
|
845
867
|
* Normalize argv so positional view-mode arguments after the asset ref
|
|
846
|
-
* are rewritten into
|
|
868
|
+
* are rewritten into internal flags that citty can parse.
|
|
847
869
|
*
|
|
848
870
|
* Converts:
|
|
849
|
-
* akm show knowledge:guide.md toc → akm show knowledge:guide.md --
|
|
850
|
-
* akm show knowledge:guide.md section Auth
|
|
851
|
-
* akm show knowledge:guide.md lines 1 50
|
|
871
|
+
* akm show knowledge:guide.md toc → akm show knowledge:guide.md --akmView toc
|
|
872
|
+
* akm show knowledge:guide.md section Auth → akm show knowledge:guide.md --akmView section --akmHeading Auth
|
|
873
|
+
* akm show knowledge:guide.md lines 1 50 → akm show knowledge:guide.md --akmView lines --akmStart 1 --akmEnd 50
|
|
852
874
|
*
|
|
853
|
-
*
|
|
875
|
+
* Legacy `--view` is intentionally unsupported.
|
|
854
876
|
* Returns a new array; the input is never modified.
|
|
855
877
|
*/
|
|
856
878
|
function normalizeShowArgv(argv) {
|
|
857
879
|
// argv[0]=bun argv[1]=script argv[2]=subcommand argv[3]=ref argv[4..]=rest
|
|
858
880
|
if (argv[2] !== "show")
|
|
859
881
|
return argv;
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
882
|
+
if (argv.includes("--view") || argv.includes("--heading") || argv.includes("--start") || argv.includes("--end")) {
|
|
883
|
+
throw new UsageError('Legacy show flags are no longer supported. Use positional syntax like `akm show knowledge:guide toc` or `akm show knowledge:guide section "Auth"`.');
|
|
884
|
+
}
|
|
863
885
|
// Separate global flags from positional/show-specific args
|
|
864
886
|
const prefix = argv.slice(0, 3); // [bun, script, show]
|
|
865
887
|
const rest = argv.slice(3);
|
|
@@ -891,21 +913,21 @@ function normalizeShowArgv(argv) {
|
|
|
891
913
|
if (!ref || !viewMode || !SHOW_VIEW_MODES.has(viewMode)) {
|
|
892
914
|
return argv;
|
|
893
915
|
}
|
|
894
|
-
const result = [...prefix, ref, "--
|
|
916
|
+
const result = [...prefix, ref, "--akmView", viewMode];
|
|
895
917
|
if (viewMode === "section") {
|
|
896
918
|
// Next arg is the heading name; pass empty string when missing so the
|
|
897
919
|
// show handler can produce a clear "section not found" error.
|
|
898
920
|
const heading = showArgs[2] ?? "";
|
|
899
|
-
result.push("--
|
|
921
|
+
result.push("--akmHeading", heading);
|
|
900
922
|
}
|
|
901
923
|
else if (viewMode === "lines") {
|
|
902
924
|
// Next two args are start and end
|
|
903
925
|
const start = showArgs[2];
|
|
904
926
|
const end = showArgs[3];
|
|
905
927
|
if (start)
|
|
906
|
-
result.push("--
|
|
928
|
+
result.push("--akmStart", start);
|
|
907
929
|
if (end)
|
|
908
|
-
result.push("--
|
|
930
|
+
result.push("--akmEnd", end);
|
|
909
931
|
}
|
|
910
932
|
result.push(...globalFlags);
|
|
911
933
|
return result;
|
package/dist/common.js
CHANGED
|
@@ -3,13 +3,6 @@ import path from "node:path";
|
|
|
3
3
|
import { TYPE_DIRS } from "./asset-spec";
|
|
4
4
|
import { ConfigError } from "./errors";
|
|
5
5
|
import { getConfigPath, getDefaultStashDir } from "./paths";
|
|
6
|
-
/**
|
|
7
|
-
* Normalize an asset type for output purposes.
|
|
8
|
-
* "tool" is a transparent alias for "script" -- all output should use "script".
|
|
9
|
-
*/
|
|
10
|
-
export function normalizeAssetType(type) {
|
|
11
|
-
return type === "tool" ? "script" : type;
|
|
12
|
-
}
|
|
13
6
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
14
7
|
export const IS_WINDOWS = process.platform === "win32";
|
|
15
8
|
// ── Validators ──────────────────────────────────────────────────────────────
|
package/dist/config-cli.js
CHANGED
|
@@ -23,6 +23,8 @@ export function parseConfigValue(key, value) {
|
|
|
23
23
|
return { embedding: parseEmbeddingConnectionValue(value) };
|
|
24
24
|
case "llm":
|
|
25
25
|
return { llm: parseLlmConnectionValue(value) };
|
|
26
|
+
case "registries":
|
|
27
|
+
return { registries: parseRegistriesValue(value) };
|
|
26
28
|
case "output.format":
|
|
27
29
|
return { output: { format: parseOutputFormat(value) } };
|
|
28
30
|
case "output.detail":
|
|
@@ -43,6 +45,8 @@ export function getConfigValue(config, key) {
|
|
|
43
45
|
return config.embedding ?? null;
|
|
44
46
|
case "llm":
|
|
45
47
|
return config.llm ?? null;
|
|
48
|
+
case "registries":
|
|
49
|
+
return config.registries ?? DEFAULT_CONFIG.registries ?? [];
|
|
46
50
|
case "output.format":
|
|
47
51
|
return config.output?.format ?? null;
|
|
48
52
|
case "output.detail":
|
|
@@ -58,6 +62,7 @@ export function setConfigValue(config, key, rawValue) {
|
|
|
58
62
|
case "searchPaths":
|
|
59
63
|
case "embedding":
|
|
60
64
|
case "llm":
|
|
65
|
+
case "registries":
|
|
61
66
|
case "output.format":
|
|
62
67
|
case "output.detail":
|
|
63
68
|
return mergeConfigValue(config, parseConfigValue(key, rawValue));
|
|
@@ -73,6 +78,8 @@ export function unsetConfigValue(config, key) {
|
|
|
73
78
|
return { ...config, embedding: undefined };
|
|
74
79
|
case "llm":
|
|
75
80
|
return { ...config, llm: undefined };
|
|
81
|
+
case "registries":
|
|
82
|
+
return { ...config, registries: undefined };
|
|
76
83
|
case "output.format":
|
|
77
84
|
return { ...config, output: mergeOutputConfig(config.output, { format: undefined }) };
|
|
78
85
|
case "output.detail":
|
|
@@ -89,6 +96,7 @@ export function listConfig(config) {
|
|
|
89
96
|
stashDir: config.stashDir ?? null,
|
|
90
97
|
embedding: config.embedding ?? null,
|
|
91
98
|
llm: config.llm ?? null,
|
|
99
|
+
registries: config.registries ?? DEFAULT_CONFIG.registries ?? [],
|
|
92
100
|
};
|
|
93
101
|
}
|
|
94
102
|
function mergeConfigValue(config, partial) {
|
|
@@ -115,6 +123,36 @@ function parseOutputDetail(value) {
|
|
|
115
123
|
return value;
|
|
116
124
|
throw new UsageError(`Invalid value for output.detail: expected one of brief|normal|full`);
|
|
117
125
|
}
|
|
126
|
+
function parseRegistriesValue(value) {
|
|
127
|
+
if (value === "null" || value === "")
|
|
128
|
+
return undefined;
|
|
129
|
+
let parsed;
|
|
130
|
+
try {
|
|
131
|
+
parsed = JSON.parse(value);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
throw new UsageError(`Invalid value for registries: expected JSON array of {url, name?, enabled?} objects` +
|
|
135
|
+
` (e.g. '[{"url":"https://example.com/index.json","name":"my-registry"}]')`);
|
|
136
|
+
}
|
|
137
|
+
if (!Array.isArray(parsed)) {
|
|
138
|
+
throw new UsageError(`Invalid value for registries: expected a JSON array`);
|
|
139
|
+
}
|
|
140
|
+
return parsed.map((entry, i) => {
|
|
141
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
142
|
+
throw new UsageError(`Invalid value for registries[${i}]: expected an object with a "url" field`);
|
|
143
|
+
}
|
|
144
|
+
const obj = entry;
|
|
145
|
+
if (typeof obj.url !== "string" || !obj.url) {
|
|
146
|
+
throw new UsageError(`Invalid value for registries[${i}]: "url" is required`);
|
|
147
|
+
}
|
|
148
|
+
const result = { url: obj.url };
|
|
149
|
+
if (typeof obj.name === "string" && obj.name)
|
|
150
|
+
result.name = obj.name;
|
|
151
|
+
if (typeof obj.enabled === "boolean")
|
|
152
|
+
result.enabled = obj.enabled;
|
|
153
|
+
return result;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
118
156
|
function parseEmbeddingConnectionValue(value) {
|
|
119
157
|
if (value === "null" || value === "")
|
|
120
158
|
return undefined;
|
package/dist/config.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getConfigDir as _getConfigDir, getConfigPath as _getConfigPath } from "
|
|
|
5
5
|
export const DEFAULT_CONFIG = {
|
|
6
6
|
semanticSearch: true,
|
|
7
7
|
searchPaths: [],
|
|
8
|
+
registries: [{ url: "https://raw.githubusercontent.com/itlackey/akm-registry/main/index.json", name: "official" }],
|
|
8
9
|
output: {
|
|
9
10
|
format: "json",
|
|
10
11
|
detail: "brief",
|
|
@@ -113,27 +114,18 @@ function pickKnownKeys(raw) {
|
|
|
113
114
|
if (Array.isArray(raw.searchPaths)) {
|
|
114
115
|
config.searchPaths = raw.searchPaths.filter((d) => typeof d === "string");
|
|
115
116
|
}
|
|
116
|
-
// Backward compat: merge legacy mountedStashDirs into searchPaths
|
|
117
|
-
if (Array.isArray(raw.mountedStashDirs)) {
|
|
118
|
-
const legacy = raw.mountedStashDirs.filter((d) => typeof d === "string");
|
|
119
|
-
const existing = new Set(config.searchPaths);
|
|
120
|
-
for (const d of legacy) {
|
|
121
|
-
if (!existing.has(d))
|
|
122
|
-
config.searchPaths.push(d);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
117
|
const embedding = parseEmbeddingConfig(raw.embedding);
|
|
126
118
|
if (embedding)
|
|
127
119
|
config.embedding = embedding;
|
|
128
120
|
const llm = parseLlmConfig(raw.llm);
|
|
129
121
|
if (llm)
|
|
130
122
|
config.llm = llm;
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
133
|
-
config.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
123
|
+
const installed = parseInstalledEntries(raw.installed);
|
|
124
|
+
if (installed)
|
|
125
|
+
config.installed = installed;
|
|
126
|
+
const registries = parseRegistriesConfig(raw.registries);
|
|
127
|
+
if (registries)
|
|
128
|
+
config.registries = registries;
|
|
137
129
|
const output = parseOutputConfig(raw.output);
|
|
138
130
|
if (output)
|
|
139
131
|
config.output = output;
|
|
@@ -273,23 +265,20 @@ function parseLlmConfig(value) {
|
|
|
273
265
|
}
|
|
274
266
|
return result;
|
|
275
267
|
}
|
|
276
|
-
function
|
|
277
|
-
if (
|
|
268
|
+
function parseInstalledEntries(value) {
|
|
269
|
+
if (!Array.isArray(value))
|
|
278
270
|
return undefined;
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
return undefined;
|
|
282
|
-
const installed = obj.installed
|
|
283
|
-
.map((entry) => parseRegistryInstalledEntry(entry))
|
|
271
|
+
const entries = value
|
|
272
|
+
.map((entry) => parseInstalledKitEntry(entry))
|
|
284
273
|
.filter((entry) => entry !== undefined);
|
|
285
|
-
return
|
|
274
|
+
return entries.length > 0 ? entries : undefined;
|
|
286
275
|
}
|
|
287
|
-
function
|
|
276
|
+
function parseInstalledKitEntry(value) {
|
|
288
277
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
289
278
|
return undefined;
|
|
290
279
|
const obj = value;
|
|
291
280
|
const id = asNonEmptyString(obj.id);
|
|
292
|
-
const source =
|
|
281
|
+
const source = asKitSource(obj.source);
|
|
293
282
|
const ref = asNonEmptyString(obj.ref);
|
|
294
283
|
const artifactUrl = asNonEmptyString(obj.artifactUrl);
|
|
295
284
|
const stashRoot = asNonEmptyString(obj.stashRoot);
|
|
@@ -317,8 +306,33 @@ function parseRegistryInstalledEntry(value) {
|
|
|
317
306
|
function asNonEmptyString(value) {
|
|
318
307
|
return typeof value === "string" && value ? value : undefined;
|
|
319
308
|
}
|
|
320
|
-
function
|
|
309
|
+
function asKitSource(value) {
|
|
321
310
|
if (value === "npm" || value === "github" || value === "git" || value === "local")
|
|
322
311
|
return value;
|
|
323
312
|
return undefined;
|
|
324
313
|
}
|
|
314
|
+
function parseRegistriesConfig(value) {
|
|
315
|
+
if (!Array.isArray(value))
|
|
316
|
+
return undefined;
|
|
317
|
+
const entries = value
|
|
318
|
+
.map((entry) => parseRegistryConfigEntry(entry))
|
|
319
|
+
.filter((entry) => entry !== undefined);
|
|
320
|
+
// Return the array even if empty — an explicit empty array means "no registries"
|
|
321
|
+
// which overrides the default. Only return undefined if the field was not an array.
|
|
322
|
+
return entries;
|
|
323
|
+
}
|
|
324
|
+
function parseRegistryConfigEntry(value) {
|
|
325
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
326
|
+
return undefined;
|
|
327
|
+
const obj = value;
|
|
328
|
+
const url = asNonEmptyString(obj.url);
|
|
329
|
+
if (!url || !url.startsWith("http"))
|
|
330
|
+
return undefined;
|
|
331
|
+
const entry = { url };
|
|
332
|
+
const name = asNonEmptyString(obj.name);
|
|
333
|
+
if (name)
|
|
334
|
+
entry.name = name;
|
|
335
|
+
if (typeof obj.enabled === "boolean")
|
|
336
|
+
entry.enabled = obj.enabled;
|
|
337
|
+
return entry;
|
|
338
|
+
}
|