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.
- package/README.md +14 -7
- package/dist/asset-spec.js +11 -2
- package/dist/asset-type-handler.js +4 -3
- package/dist/cli.js +100 -62
- package/dist/common.js +6 -6
- package/dist/config-cli.js +23 -25
- package/dist/config.js +3 -1
- package/dist/db.js +28 -30
- package/dist/errors.js +28 -0
- package/dist/file-context.js +36 -6
- package/dist/frontmatter.js +1 -1
- package/dist/github.js +1 -3
- package/dist/handlers/agent-handler.js +6 -13
- package/dist/handlers/command-handler.js +8 -13
- package/dist/handlers/handler-bridge.js +51 -0
- package/dist/handlers/index.js +12 -10
- package/dist/handlers/knowledge-handler.js +7 -31
- package/dist/handlers/script-handler.js +9 -45
- package/dist/handlers/skill-handler.js +5 -6
- package/dist/handlers/tool-handler.js +8 -24
- package/dist/indexer.js +21 -21
- package/dist/init.js +3 -3
- package/dist/llm.js +6 -11
- package/dist/lockfile.js +9 -4
- package/dist/matchers.js +11 -5
- package/dist/metadata.js +25 -16
- package/dist/paths.js +5 -4
- package/dist/registry-install.js +9 -5
- package/dist/registry-resolve.js +12 -8
- package/dist/registry-search.js +5 -5
- package/dist/renderers.js +24 -14
- package/dist/ripgrep-install.js +3 -22
- package/dist/ripgrep.js +1 -1
- package/dist/self-update.js +15 -9
- package/dist/stash-add.js +4 -3
- package/dist/stash-clone.js +4 -4
- package/dist/stash-ref.js +10 -9
- package/dist/stash-registry.js +6 -5
- package/dist/stash-resolve.js +10 -9
- package/dist/stash-search.js +27 -24
- package/dist/stash-show.js +8 -7
- package/dist/stash-source.js +10 -11
- package/dist/submit.js +26 -21
- package/dist/tool-runner.js +1 -5
- package/dist/warn.js +20 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Agent-i-Kit
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/agentikit)
|
|
4
|
+
[](https://github.com/itlackey/agentikit/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
3
7
|
A package manager for AI agent capabilities — tools, skills, commands, agents,
|
|
4
8
|
knowledge, and scripts — that works with any AI coding assistant that can run
|
|
5
9
|
shell commands.
|
|
@@ -9,6 +13,11 @@ organize them into a searchable **stash**, share them as installable **kits**,
|
|
|
9
13
|
and give any model a way to discover and use them through `akm` (Agent Kit
|
|
10
14
|
Manager). No plugins required — just CLI output any tool-calling model can read.
|
|
11
15
|
|
|
16
|
+
## Requirements
|
|
17
|
+
|
|
18
|
+
Agent-i-Kit requires [Bun](https://bun.sh) v1.0+ as its runtime. It uses
|
|
19
|
+
Bun-specific APIs (`bun:sqlite`) that are **not available in Node.js**.
|
|
20
|
+
|
|
12
21
|
## Quick Start
|
|
13
22
|
|
|
14
23
|
```sh
|
|
@@ -46,10 +55,9 @@ Add this to your `AGENTS.md`, `CLAUDE.md`, system prompt, or any instruction
|
|
|
46
55
|
file to give your agent access to your stash without any additional setup:
|
|
47
56
|
|
|
48
57
|
~~~markdown
|
|
49
|
-
## Agent-i-Kit
|
|
50
58
|
|
|
51
59
|
You have access to a searchable library of tools, skills, commands, agents,
|
|
52
|
-
and knowledge documents via `akm
|
|
60
|
+
and knowledge documents via `akm`. Use it to find and
|
|
53
61
|
use capabilities before writing something from scratch.
|
|
54
62
|
|
|
55
63
|
**Finding assets:**
|
|
@@ -277,11 +285,10 @@ akm upgrade --check # Check for updates without installing
|
|
|
277
285
|
|
|
278
286
|
## Status
|
|
279
287
|
|
|
280
|
-
Agent-i-Kit is
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
AI coding assistants.
|
|
288
|
+
Agent-i-Kit is approaching v1.0. The core CLI, stash model, and registry are
|
|
289
|
+
stable and in daily use. Feedback, issues, and PRs welcome — especially around
|
|
290
|
+
real-world usage patterns and integrations with different AI coding assistants.
|
|
284
291
|
|
|
285
292
|
## License
|
|
286
293
|
|
|
287
|
-
[
|
|
294
|
+
[MPL-2.0](LICENSE)
|
package/dist/asset-spec.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { toPosix } from "./common";
|
|
3
2
|
import { tryGetHandler } from "./asset-type-handler";
|
|
3
|
+
import { toPosix } from "./common";
|
|
4
4
|
export const SCRIPT_EXTENSIONS = new Set([".sh", ".ts", ".js", ".ps1", ".cmd", ".bat"]);
|
|
5
5
|
const markdownSpec = {
|
|
6
6
|
isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
|
|
@@ -10,7 +10,16 @@ const markdownSpec = {
|
|
|
10
10
|
/** Extended set of script extensions for the script asset type */
|
|
11
11
|
export const SCRIPT_EXTENSIONS_BROAD = new Set([
|
|
12
12
|
...SCRIPT_EXTENSIONS,
|
|
13
|
-
".py",
|
|
13
|
+
".py",
|
|
14
|
+
".rb",
|
|
15
|
+
".go",
|
|
16
|
+
".pl",
|
|
17
|
+
".php",
|
|
18
|
+
".lua",
|
|
19
|
+
".r",
|
|
20
|
+
".swift",
|
|
21
|
+
".kt",
|
|
22
|
+
".kts",
|
|
14
23
|
]);
|
|
15
24
|
export const ASSET_SPECS = {
|
|
16
25
|
tool: {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { UsageError } from "./errors";
|
|
2
|
+
import { registerBuiltinHandlers } from "./handlers/index";
|
|
1
3
|
// ── Registry ─────────────────────────────────────────────────────────────────
|
|
2
4
|
const handlers = new Map();
|
|
3
5
|
let handlersInitialized = false;
|
|
@@ -5,8 +7,7 @@ function ensureHandlersRegistered() {
|
|
|
5
7
|
if (handlersInitialized)
|
|
6
8
|
return;
|
|
7
9
|
handlersInitialized = true;
|
|
8
|
-
|
|
9
|
-
require("./handlers/index");
|
|
10
|
+
registerBuiltinHandlers();
|
|
10
11
|
}
|
|
11
12
|
export function registerAssetType(handler) {
|
|
12
13
|
handlers.set(handler.typeName, handler);
|
|
@@ -15,7 +16,7 @@ export function getHandler(type) {
|
|
|
15
16
|
ensureHandlersRegistered();
|
|
16
17
|
const handler = handlers.get(type);
|
|
17
18
|
if (!handler) {
|
|
18
|
-
throw new
|
|
19
|
+
throw new UsageError(`Unknown asset type: "${type}"`);
|
|
19
20
|
}
|
|
20
21
|
return handler;
|
|
21
22
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2,27 +2,56 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
|
+
import { resolveStashDir } from "./common";
|
|
6
|
+
import { getConfigPath, loadConfig, saveConfig } from "./config";
|
|
7
|
+
import { getConfigValue, listConfig, listProviders, parseConfigValue, setConfigValue, unsetConfigValue, useProvider, } from "./config-cli";
|
|
8
|
+
import { ConfigError, NotFoundError, UsageError } from "./errors";
|
|
9
|
+
import { agentikitIndex } from "./indexer";
|
|
10
|
+
import { agentikitInit } from "./init";
|
|
11
|
+
import { getCacheDir, getDbPath, getDefaultStashDir } from "./paths";
|
|
12
|
+
import { checkForUpdate, performUpgrade } from "./self-update";
|
|
5
13
|
import { agentikitAdd } from "./stash-add";
|
|
14
|
+
import { agentikitClone } from "./stash-clone";
|
|
6
15
|
import { agentikitList, agentikitRemove, agentikitUpdate } from "./stash-registry";
|
|
7
16
|
import { agentikitSearch } from "./stash-search";
|
|
8
17
|
import { agentikitShow } from "./stash-show";
|
|
9
|
-
import { checkForUpdate, performUpgrade } from "./self-update";
|
|
10
|
-
import { agentikitInit } from "./init";
|
|
11
|
-
import { agentikitIndex } from "./indexer";
|
|
12
|
-
import { agentikitClone } from "./stash-clone";
|
|
13
|
-
import { agentikitSubmit } from "./submit";
|
|
14
18
|
import { resolveStashSources } from "./stash-source";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
import { agentikitSubmit } from "./submit";
|
|
20
|
+
import { setQuiet, warn } from "./warn";
|
|
21
|
+
// Version: prefer compile-time define, then package.json, then fallback
|
|
22
|
+
const pkgVersion = (() => {
|
|
23
|
+
// Injected at compile time via `bun build --define`
|
|
24
|
+
if (typeof AKM_VERSION !== "undefined")
|
|
25
|
+
return AKM_VERSION;
|
|
26
|
+
try {
|
|
27
|
+
const pkgPath = path.resolve(import.meta.dir ?? __dirname, "../package.json");
|
|
28
|
+
if (fs.existsSync(pkgPath)) {
|
|
29
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
30
|
+
if (typeof pkg.version === "string")
|
|
31
|
+
return pkg.version;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// swallow — running as compiled binary without package.json
|
|
36
|
+
}
|
|
37
|
+
return "0.0.0-dev";
|
|
38
|
+
})();
|
|
22
39
|
/** Check whether --json flag is present in argv */
|
|
23
40
|
function isJsonMode() {
|
|
24
41
|
return process.argv.includes("--json");
|
|
25
42
|
}
|
|
43
|
+
function hasBunYAML(b) {
|
|
44
|
+
// biome-ignore lint/suspicious/noExplicitAny: type guard for runtime feature detection
|
|
45
|
+
return typeof b.YAML?.stringify === "function";
|
|
46
|
+
}
|
|
47
|
+
/** Try Bun.YAML.stringify; fall back to JSON if the API is unavailable */
|
|
48
|
+
function yamlStringify(obj) {
|
|
49
|
+
if (hasBunYAML(Bun)) {
|
|
50
|
+
return Bun.YAML.stringify(obj);
|
|
51
|
+
}
|
|
52
|
+
warn("YAML output not available, using JSON");
|
|
53
|
+
return JSON.stringify(obj, null, 2);
|
|
54
|
+
}
|
|
26
55
|
/** Output result: JSON if --json flag set, otherwise YAML (default) */
|
|
27
56
|
function output(command, result) {
|
|
28
57
|
if (isJsonMode()) {
|
|
@@ -35,7 +64,7 @@ function output(command, result) {
|
|
|
35
64
|
console.log(plain);
|
|
36
65
|
return;
|
|
37
66
|
}
|
|
38
|
-
console.log(
|
|
67
|
+
console.log(yamlStringify(result));
|
|
39
68
|
}
|
|
40
69
|
/**
|
|
41
70
|
* Return a plain-text string for commands that are better as short messages,
|
|
@@ -117,7 +146,7 @@ function formatPlain(command, result) {
|
|
|
117
146
|
const lines = [
|
|
118
147
|
`Dry run: prepared registry entry ${entry?.name ?? entry?.id ?? "unknown"}`,
|
|
119
148
|
"",
|
|
120
|
-
|
|
149
|
+
yamlStringify(entry),
|
|
121
150
|
];
|
|
122
151
|
if (commands.length > 0) {
|
|
123
152
|
lines.push("", "Would run:");
|
|
@@ -139,7 +168,10 @@ function formatPlain(command, result) {
|
|
|
139
168
|
}
|
|
140
169
|
}
|
|
141
170
|
const initCommand = defineCommand({
|
|
142
|
-
meta: {
|
|
171
|
+
meta: {
|
|
172
|
+
name: "init",
|
|
173
|
+
description: "Initialize Agent-i-Kit's working stash directory and persist stashDir in config",
|
|
174
|
+
},
|
|
143
175
|
args: {
|
|
144
176
|
dir: { type: "string", description: "Custom stash directory path (default: ~/agentikit)" },
|
|
145
177
|
},
|
|
@@ -281,7 +313,7 @@ const showCommand = defineCommand({
|
|
|
281
313
|
view = { mode: args.view };
|
|
282
314
|
break;
|
|
283
315
|
default:
|
|
284
|
-
throw new
|
|
316
|
+
throw new UsageError(`Unknown view mode: ${args.view}. Expected one of: full|toc|frontmatter|section|lines`);
|
|
285
317
|
}
|
|
286
318
|
}
|
|
287
319
|
const result = await agentikitShow({ ref: args.ref, view });
|
|
@@ -424,7 +456,7 @@ const configCommand = defineCommand({
|
|
|
424
456
|
if (args.set) {
|
|
425
457
|
const eqIndex = args.set.indexOf("=");
|
|
426
458
|
if (eqIndex === -1) {
|
|
427
|
-
throw new
|
|
459
|
+
throw new UsageError("--set expects key=value format");
|
|
428
460
|
}
|
|
429
461
|
const key = args.set.slice(0, eqIndex);
|
|
430
462
|
const value = args.set.slice(eqIndex + 1);
|
|
@@ -440,7 +472,10 @@ const configCommand = defineCommand({
|
|
|
440
472
|
},
|
|
441
473
|
});
|
|
442
474
|
const cloneCommand = defineCommand({
|
|
443
|
-
meta: {
|
|
475
|
+
meta: {
|
|
476
|
+
name: "clone",
|
|
477
|
+
description: "Clone an asset from any stash source into the working stash or a custom destination",
|
|
478
|
+
},
|
|
444
479
|
args: {
|
|
445
480
|
ref: { type: "positional", description: "Asset ref (e.g. @installed:pkg/tool:script.sh)", required: true },
|
|
446
481
|
name: { type: "string", description: "New name for the cloned asset" },
|
|
@@ -462,7 +497,11 @@ const cloneCommand = defineCommand({
|
|
|
462
497
|
const submitCommand = defineCommand({
|
|
463
498
|
meta: { name: "submit", description: "Submit a kit to agentikit-registry by opening a pull request" },
|
|
464
499
|
args: {
|
|
465
|
-
ref: {
|
|
500
|
+
ref: {
|
|
501
|
+
type: "positional",
|
|
502
|
+
description: "Public ref to submit (npm package, owner/repo, or local kit directory)",
|
|
503
|
+
required: false,
|
|
504
|
+
},
|
|
466
505
|
name: { type: "string", description: "Display name for the registry entry" },
|
|
467
506
|
description: { type: "string", description: "Short description for the registry entry" },
|
|
468
507
|
tags: { type: "string", description: "Comma-separated tags" },
|
|
@@ -470,8 +509,16 @@ const submitCommand = defineCommand({
|
|
|
470
509
|
author: { type: "string", description: "Author name" },
|
|
471
510
|
license: { type: "string", description: "License identifier" },
|
|
472
511
|
homepage: { type: "string", description: "Homepage URL" },
|
|
473
|
-
"dry-run": {
|
|
474
|
-
|
|
512
|
+
"dry-run": {
|
|
513
|
+
type: "boolean",
|
|
514
|
+
description: "Preview the entry and gh commands without creating a pull request",
|
|
515
|
+
default: false,
|
|
516
|
+
},
|
|
517
|
+
"cleanup-fork": {
|
|
518
|
+
type: "boolean",
|
|
519
|
+
description: "Show the fork cleanup command after the pull request is created (run it after the PR is merged)",
|
|
520
|
+
default: false,
|
|
521
|
+
},
|
|
475
522
|
},
|
|
476
523
|
async run({ args }) {
|
|
477
524
|
await runWithJsonErrors(async () => {
|
|
@@ -509,6 +556,7 @@ const main = defineCommand({
|
|
|
509
556
|
},
|
|
510
557
|
args: {
|
|
511
558
|
json: { type: "boolean", description: "Output in JSON format", default: false },
|
|
559
|
+
quiet: { type: "boolean", alias: "q", description: "Suppress stderr warnings", default: false },
|
|
512
560
|
},
|
|
513
561
|
subCommands: {
|
|
514
562
|
init: initCommand,
|
|
@@ -531,48 +579,43 @@ const SEARCH_SOURCES = ["local", "registry", "both"];
|
|
|
531
579
|
const CONFIG_SUBCOMMAND_SET = new Set(["path", "list", "get", "set", "unset", "providers", "use"]);
|
|
532
580
|
// citty reads process.argv directly and does not accept a custom argv array,
|
|
533
581
|
// so we must replace process.argv with the normalized version before runMain.
|
|
534
|
-
|
|
535
|
-
normalizeConfigArgv(normalizedArgv);
|
|
536
|
-
process.argv = normalizedArgv;
|
|
582
|
+
process.argv = normalizeConfigArgv(process.argv);
|
|
537
583
|
runMain(main);
|
|
538
584
|
function parseSearchUsageMode(value) {
|
|
539
585
|
if (SEARCH_USAGE_MODES.includes(value))
|
|
540
586
|
return value;
|
|
541
|
-
throw new
|
|
587
|
+
throw new UsageError(`Invalid value for --usage: ${value}. Expected one of: ${SEARCH_USAGE_MODES.join("|")}`);
|
|
542
588
|
}
|
|
543
589
|
function parseSearchSource(value) {
|
|
544
590
|
if (SEARCH_SOURCES.includes(value))
|
|
545
591
|
return value;
|
|
546
|
-
throw new
|
|
592
|
+
throw new UsageError(`Invalid value for --source: ${value}. Expected one of: ${SEARCH_SOURCES.join("|")}`);
|
|
547
593
|
}
|
|
548
594
|
// ── Exit codes ──────────────────────────────────────────────────────────────
|
|
549
595
|
const EXIT_GENERAL = 1;
|
|
550
596
|
const EXIT_USAGE = 2;
|
|
551
597
|
const EXIT_CONFIG = 78;
|
|
552
|
-
function classifyExitCode(
|
|
553
|
-
|
|
554
|
-
if (message.includes("required") ||
|
|
555
|
-
message.includes("Invalid value for") ||
|
|
556
|
-
message.includes("Expected one of") ||
|
|
557
|
-
message.includes("expected JSON object")) {
|
|
598
|
+
function classifyExitCode(error) {
|
|
599
|
+
if (error instanceof UsageError)
|
|
558
600
|
return EXIT_USAGE;
|
|
559
|
-
|
|
560
|
-
// Configuration errors
|
|
561
|
-
if (message.includes("No stash directory found") ||
|
|
562
|
-
message.includes("Unable to determine") ||
|
|
563
|
-
message.includes("config")) {
|
|
601
|
+
if (error instanceof ConfigError)
|
|
564
602
|
return EXIT_CONFIG;
|
|
565
|
-
|
|
603
|
+
if (error instanceof NotFoundError)
|
|
604
|
+
return EXIT_GENERAL;
|
|
566
605
|
return EXIT_GENERAL;
|
|
567
606
|
}
|
|
568
607
|
async function runWithJsonErrors(fn) {
|
|
569
608
|
try {
|
|
609
|
+
// Apply --quiet flag early so warnings inside the command are suppressed
|
|
610
|
+
if (process.argv.includes("--quiet") || process.argv.includes("-q")) {
|
|
611
|
+
setQuiet(true);
|
|
612
|
+
}
|
|
570
613
|
await fn();
|
|
571
614
|
}
|
|
572
615
|
catch (error) {
|
|
573
616
|
const message = error instanceof Error ? error.message : String(error);
|
|
574
617
|
const hint = buildHint(message);
|
|
575
|
-
const exitCode = classifyExitCode(
|
|
618
|
+
const exitCode = classifyExitCode(error);
|
|
576
619
|
console.error(JSON.stringify({ ok: false, error: message, hint }, null, 2));
|
|
577
620
|
process.exit(exitCode);
|
|
578
621
|
}
|
|
@@ -600,11 +643,12 @@ function buildHint(message) {
|
|
|
600
643
|
return "Check that the npm package is published or the GitHub repository is public, then retry.";
|
|
601
644
|
if (message.includes("already exists in agentikit-registry"))
|
|
602
645
|
return "Update the existing registry entry instead of creating a duplicate, or choose a different public ref.";
|
|
603
|
-
if (message.includes("Unable to infer a public npm or GitHub ref") ||
|
|
646
|
+
if (message.includes("Unable to infer a public npm or GitHub ref") ||
|
|
647
|
+
message.includes("Unable to infer a publicly accessible npm package or GitHub repository")) {
|
|
604
648
|
return "Run `akm submit <package-or-owner/repo>` explicitly, or add name/repository metadata to package.json.";
|
|
605
649
|
}
|
|
606
650
|
if (message.includes("expected JSON object with endpoint and model")) {
|
|
607
|
-
return
|
|
651
|
+
return 'Quote JSON values in your shell, for example: akm config set embedding \'{"endpoint":"http://localhost:11434/v1/embeddings","model":"nomic-embed-text"}\'.';
|
|
608
652
|
}
|
|
609
653
|
return undefined;
|
|
610
654
|
}
|
|
@@ -618,7 +662,7 @@ function buildGhInstallHint() {
|
|
|
618
662
|
function parseProviderScope(value) {
|
|
619
663
|
if (value === "embedding" || value === "llm")
|
|
620
664
|
return value;
|
|
621
|
-
throw new
|
|
665
|
+
throw new UsageError(`Invalid provider scope: ${value}. Expected one of: embedding|llm`);
|
|
622
666
|
}
|
|
623
667
|
function hasConfigSubcommand(args) {
|
|
624
668
|
const command = Array.isArray(args._) ? args._[0] : undefined;
|
|
@@ -629,43 +673,37 @@ function hasConfigSubcommand(args) {
|
|
|
629
673
|
* `akm config llm.maxTokens 512` and `akm config --get llm.maxTokens`
|
|
630
674
|
* are normalized into the existing config subcommands.
|
|
631
675
|
*
|
|
632
|
-
*
|
|
633
|
-
* with the normalized result (safer than in-place splice).
|
|
676
|
+
* Returns a new array; the input is never modified.
|
|
634
677
|
*/
|
|
635
678
|
function normalizeConfigArgv(argv) {
|
|
636
|
-
// Global flags (like --json) should not be treated as config subcommand arguments.
|
|
679
|
+
// Global flags (like --json, --quiet) should not be treated as config subcommand arguments.
|
|
637
680
|
// We strip them from the analysis portion, normalize, then re-append them.
|
|
638
|
-
const GLOBAL_FLAGS = new Set(["--json"]);
|
|
681
|
+
const GLOBAL_FLAGS = new Set(["--json", "--quiet", "-q"]);
|
|
639
682
|
const globalFlags = argv.slice(3).filter((a) => GLOBAL_FLAGS.has(a));
|
|
640
683
|
const configArgs = argv.slice(3).filter((a) => !GLOBAL_FLAGS.has(a));
|
|
641
684
|
const [command, argAfterCommand, argAfterKey, ...rest] = [argv[2], ...configArgs];
|
|
642
685
|
if (command !== "config")
|
|
643
|
-
return;
|
|
686
|
+
return argv;
|
|
644
687
|
if (!argAfterCommand)
|
|
645
|
-
return;
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
};
|
|
688
|
+
return argv;
|
|
689
|
+
const prefix = argv.slice(0, 3);
|
|
690
|
+
const buildResult = (...newArgs) => [...prefix, ...newArgs, ...globalFlags];
|
|
649
691
|
if (argAfterCommand === "--list") {
|
|
650
|
-
|
|
651
|
-
return;
|
|
692
|
+
return buildResult("list");
|
|
652
693
|
}
|
|
653
694
|
if (argAfterCommand === "--get" && argAfterKey) {
|
|
654
|
-
|
|
655
|
-
return;
|
|
695
|
+
return buildResult("get", argAfterKey, ...rest);
|
|
656
696
|
}
|
|
657
697
|
if (argAfterCommand === "--unset" && argAfterKey) {
|
|
658
|
-
|
|
659
|
-
return;
|
|
698
|
+
return buildResult("unset", argAfterKey, ...rest);
|
|
660
699
|
}
|
|
661
700
|
if (argAfterCommand.startsWith("-"))
|
|
662
|
-
return;
|
|
701
|
+
return argv;
|
|
663
702
|
if (CONFIG_SUBCOMMAND_SET.has(argAfterCommand))
|
|
664
|
-
return;
|
|
703
|
+
return argv;
|
|
665
704
|
// A single arg after `config` behaves like `git config <key>` and reads the value.
|
|
666
705
|
if (argAfterKey === undefined) {
|
|
667
|
-
|
|
668
|
-
return;
|
|
706
|
+
return buildResult("get", argAfterCommand);
|
|
669
707
|
}
|
|
670
|
-
|
|
708
|
+
return buildResult("set", argAfterCommand, argAfterKey, ...rest);
|
|
671
709
|
}
|
package/dist/common.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { TYPE_DIRS } from "./asset-spec";
|
|
4
|
+
import { ConfigError } from "./errors";
|
|
4
5
|
import { getConfigPath, getDefaultStashDir } from "./paths";
|
|
5
6
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
6
7
|
export const IS_WINDOWS = process.platform === "win32";
|
|
7
|
-
export { SCRIPT_EXTENSIONS, TYPE_DIRS } from "./asset-spec";
|
|
8
8
|
// ── Validators ──────────────────────────────────────────────────────────────
|
|
9
9
|
export function isAssetType(type) {
|
|
10
10
|
return type in TYPE_DIRS;
|
|
@@ -36,7 +36,7 @@ export function resolveStashDir(options) {
|
|
|
36
36
|
if (isValidDirectory(defaultDir)) {
|
|
37
37
|
return defaultDir;
|
|
38
38
|
}
|
|
39
|
-
throw new
|
|
39
|
+
throw new ConfigError(`No stash directory found. Run "akm init" to create one at ${defaultDir}, ` +
|
|
40
40
|
`or set stashDir in ${getConfigPath()}.`);
|
|
41
41
|
}
|
|
42
42
|
function validateStashDir(raw) {
|
|
@@ -46,10 +46,10 @@ function validateStashDir(raw) {
|
|
|
46
46
|
stat = fs.statSync(stashDir);
|
|
47
47
|
}
|
|
48
48
|
catch {
|
|
49
|
-
throw new
|
|
49
|
+
throw new ConfigError(`Unable to read stash directory at "${stashDir}".`);
|
|
50
50
|
}
|
|
51
51
|
if (!stat.isDirectory()) {
|
|
52
|
-
throw new
|
|
52
|
+
throw new ConfigError(`Stash path must point to a directory: "${stashDir}".`);
|
|
53
53
|
}
|
|
54
54
|
return stashDir;
|
|
55
55
|
}
|
|
@@ -165,7 +165,7 @@ export async function fetchWithRetry(url, init, options) {
|
|
|
165
165
|
const response = await fetchWithTimeout(url, init, timeout);
|
|
166
166
|
if (attempt < maxRetries && shouldRetry(response.status)) {
|
|
167
167
|
const retryAfter = parseRetryAfter(response);
|
|
168
|
-
const delay = retryAfter ?? baseDelay *
|
|
168
|
+
const delay = retryAfter ?? baseDelay * 2 ** attempt * (0.5 + Math.random() * 0.5);
|
|
169
169
|
await new Promise((r) => setTimeout(r, delay));
|
|
170
170
|
continue;
|
|
171
171
|
}
|
|
@@ -174,7 +174,7 @@ export async function fetchWithRetry(url, init, options) {
|
|
|
174
174
|
catch (err) {
|
|
175
175
|
if (attempt >= maxRetries)
|
|
176
176
|
throw err;
|
|
177
|
-
const delay = baseDelay *
|
|
177
|
+
const delay = baseDelay * 2 ** attempt * (0.5 + Math.random() * 0.5);
|
|
178
178
|
await new Promise((r) => setTimeout(r, delay));
|
|
179
179
|
}
|
|
180
180
|
}
|
package/dist/config-cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_CONFIG, } from "./config";
|
|
2
2
|
import { EMBEDDING_DIM } from "./db";
|
|
3
|
+
import { ConfigError, UsageError } from "./errors";
|
|
3
4
|
const LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
|
|
4
5
|
const DEFAULT_LLM_TEMPERATURE = 0.3;
|
|
5
6
|
const DEFAULT_LLM_MAX_TOKENS = 512;
|
|
@@ -63,25 +64,25 @@ export function parseConfigValue(key, value) {
|
|
|
63
64
|
return { stashDir: requireNonEmptyString(value, key) };
|
|
64
65
|
case "semanticSearch":
|
|
65
66
|
if (value !== "true" && value !== "false") {
|
|
66
|
-
throw new
|
|
67
|
+
throw new UsageError(`Invalid value for semanticSearch: expected "true" or "false"`);
|
|
67
68
|
}
|
|
68
69
|
return { semanticSearch: value === "true" };
|
|
69
70
|
case "searchPaths":
|
|
70
71
|
try {
|
|
71
72
|
const parsed = JSON.parse(value);
|
|
72
73
|
if (!Array.isArray(parsed))
|
|
73
|
-
throw new
|
|
74
|
+
throw new UsageError("expected JSON array");
|
|
74
75
|
return { searchPaths: parsed.filter((d) => typeof d === "string") };
|
|
75
76
|
}
|
|
76
77
|
catch {
|
|
77
|
-
throw new
|
|
78
|
+
throw new UsageError(`Invalid value for searchPaths: expected JSON array (e.g. '["/path/a","/path/b"]')`);
|
|
78
79
|
}
|
|
79
80
|
case "embedding":
|
|
80
81
|
return { embedding: parseEmbeddingConnectionValue(value) };
|
|
81
82
|
case "llm":
|
|
82
83
|
return { llm: parseLlmConnectionValue(value) };
|
|
83
84
|
default:
|
|
84
|
-
throw new
|
|
85
|
+
throw new UsageError(`Unknown config key: ${key}`);
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
export function getConfigValue(config, key) {
|
|
@@ -119,7 +120,7 @@ export function getConfigValue(config, key) {
|
|
|
119
120
|
case "llm.apiKey":
|
|
120
121
|
return maskSecret(getLlmDisplayConfig(config).apiKey) ?? null;
|
|
121
122
|
default:
|
|
122
|
-
throw new
|
|
123
|
+
throw new UsageError(`Unknown config key: ${key}`);
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
export function setConfigValue(config, key, rawValue) {
|
|
@@ -207,7 +208,7 @@ export function setConfigValue(config, key, rawValue) {
|
|
|
207
208
|
},
|
|
208
209
|
};
|
|
209
210
|
default:
|
|
210
|
-
throw new
|
|
211
|
+
throw new UsageError(`Unknown config key: ${key}`);
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
export function unsetConfigValue(config, key) {
|
|
@@ -247,7 +248,7 @@ export function unsetConfigValue(config, key) {
|
|
|
247
248
|
return config;
|
|
248
249
|
return { ...config, llm: omitKey(config.llm, "provider") };
|
|
249
250
|
default:
|
|
250
|
-
throw new
|
|
251
|
+
throw new UsageError(`Unknown or unsupported unset key: ${key}`);
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
export function listConfig(config) {
|
|
@@ -273,7 +274,7 @@ export function useProvider(config, scope, providerName) {
|
|
|
273
274
|
if (scope === "embedding") {
|
|
274
275
|
const preset = EMBEDDING_PROVIDER_PRESETS[providerName];
|
|
275
276
|
if (!preset) {
|
|
276
|
-
throw new
|
|
277
|
+
throw new UsageError(`Unknown embedding provider: ${providerName}`);
|
|
277
278
|
}
|
|
278
279
|
if (!preset.config) {
|
|
279
280
|
return { ...config, embedding: undefined };
|
|
@@ -282,7 +283,7 @@ export function useProvider(config, scope, providerName) {
|
|
|
282
283
|
}
|
|
283
284
|
const preset = LLM_PROVIDER_PRESETS[providerName];
|
|
284
285
|
if (!preset) {
|
|
285
|
-
throw new
|
|
286
|
+
throw new UsageError(`Unknown llm provider: ${providerName}`);
|
|
286
287
|
}
|
|
287
288
|
if (!preset.config) {
|
|
288
289
|
return { ...config, llm: undefined };
|
|
@@ -388,68 +389,65 @@ function parseJsonObject(value, key, example) {
|
|
|
388
389
|
parsed = JSON.parse(value);
|
|
389
390
|
}
|
|
390
391
|
catch {
|
|
391
|
-
throw new
|
|
392
|
-
|
|
392
|
+
throw new UsageError(`Invalid value for ${key}: expected JSON object with endpoint and model` +
|
|
393
|
+
` (e.g. '{"endpoint":"${example.endpoint}","model":"${example.model}"}')`);
|
|
393
394
|
}
|
|
394
395
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
395
|
-
throw new
|
|
396
|
+
throw new UsageError(`Invalid value for ${key}: expected a JSON object`);
|
|
396
397
|
}
|
|
397
398
|
return parsed;
|
|
398
399
|
}
|
|
399
400
|
function asRequiredString(value, key, field) {
|
|
400
401
|
if (typeof value !== "string" || !value) {
|
|
401
|
-
throw new
|
|
402
|
+
throw new UsageError(`Invalid value for ${key}: "${field}" is a required string field`);
|
|
402
403
|
}
|
|
403
404
|
return value;
|
|
404
405
|
}
|
|
405
406
|
function requireEmbeddingConfig(config) {
|
|
406
407
|
if (!config.embedding) {
|
|
407
|
-
throw new
|
|
408
|
+
throw new ConfigError("Embedding provider is using the built-in local default. Run `akm config use embedding <provider>` first.");
|
|
408
409
|
}
|
|
409
410
|
return config.embedding;
|
|
410
411
|
}
|
|
411
412
|
function requireLlmConfig(config) {
|
|
412
413
|
if (!config.llm) {
|
|
413
|
-
throw new
|
|
414
|
+
throw new ConfigError("LLM provider is disabled. Run `akm config use llm <provider>` first.");
|
|
414
415
|
}
|
|
415
416
|
return config.llm;
|
|
416
417
|
}
|
|
417
418
|
function requireNonEmptyString(value, key) {
|
|
418
419
|
if (!value) {
|
|
419
|
-
throw new
|
|
420
|
+
throw new UsageError(`Invalid value for ${key}: expected a non-empty string`);
|
|
420
421
|
}
|
|
421
422
|
return value;
|
|
422
423
|
}
|
|
423
424
|
function parseNumber(value, key) {
|
|
424
425
|
const parsed = Number(value);
|
|
425
426
|
if (!Number.isFinite(parsed)) {
|
|
426
|
-
throw new
|
|
427
|
+
throw new UsageError(`Invalid value for ${key}: expected a number`);
|
|
427
428
|
}
|
|
428
429
|
return parsed;
|
|
429
430
|
}
|
|
430
431
|
function parsePositiveInteger(value, key) {
|
|
431
432
|
const trimmed = value.trim();
|
|
432
433
|
if (!/^[1-9]\d*$/.test(trimmed)) {
|
|
433
|
-
throw new
|
|
434
|
+
throw new UsageError(`Invalid value for ${key}: expected a positive integer`);
|
|
434
435
|
}
|
|
435
436
|
const parsed = Number(trimmed);
|
|
436
437
|
if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed <= 0) {
|
|
437
|
-
throw new
|
|
438
|
+
throw new UsageError(`Invalid value for ${key}: expected a positive integer`);
|
|
438
439
|
}
|
|
439
440
|
return parsed;
|
|
440
441
|
}
|
|
441
442
|
function parseUnknownNumber(value, key) {
|
|
442
443
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
443
|
-
throw new
|
|
444
|
+
throw new UsageError(`Invalid value for ${key}: expected a number`);
|
|
444
445
|
}
|
|
445
446
|
return value;
|
|
446
447
|
}
|
|
447
448
|
function parseUnknownPositiveInteger(value, key) {
|
|
448
|
-
if (typeof value !== "number" ||
|
|
449
|
-
|
|
450
|
-
!Number.isInteger(value) ||
|
|
451
|
-
value <= 0) {
|
|
452
|
-
throw new Error(`Invalid value for ${key}: expected a positive integer`);
|
|
449
|
+
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
450
|
+
throw new UsageError(`Invalid value for ${key}: expected a positive integer`);
|
|
453
451
|
}
|
|
454
452
|
return value;
|
|
455
453
|
}
|