agentikit 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +215 -76
  2. package/dist/index.d.ts +17 -3
  3. package/dist/index.js +10 -2
  4. package/dist/src/asset-spec.d.ts +14 -0
  5. package/dist/src/asset-spec.js +46 -0
  6. package/dist/src/cli.js +268 -57
  7. package/dist/src/common.d.ts +8 -0
  8. package/dist/src/common.js +46 -0
  9. package/dist/src/config.d.ts +37 -0
  10. package/dist/src/config.js +124 -0
  11. package/dist/src/embedder.d.ts +10 -0
  12. package/dist/src/embedder.js +87 -0
  13. package/dist/src/frontmatter.d.ts +30 -0
  14. package/dist/src/frontmatter.js +86 -0
  15. package/dist/src/indexer.d.ts +20 -2
  16. package/dist/src/indexer.js +212 -80
  17. package/dist/src/init.d.ts +19 -0
  18. package/dist/src/init.js +87 -0
  19. package/dist/src/llm.d.ts +15 -0
  20. package/dist/src/llm.js +91 -0
  21. package/dist/src/markdown.d.ts +18 -0
  22. package/dist/src/markdown.js +77 -0
  23. package/dist/src/metadata.d.ts +11 -2
  24. package/dist/src/metadata.js +161 -29
  25. package/dist/src/registry-install.d.ts +11 -0
  26. package/dist/src/registry-install.js +208 -0
  27. package/dist/src/registry-resolve.d.ts +3 -0
  28. package/dist/src/registry-resolve.js +231 -0
  29. package/dist/src/registry-search.d.ts +5 -0
  30. package/dist/src/registry-search.js +129 -0
  31. package/dist/src/registry-types.d.ts +55 -0
  32. package/dist/src/registry-types.js +1 -0
  33. package/dist/src/ripgrep-install.d.ts +12 -0
  34. package/dist/src/ripgrep-install.js +169 -0
  35. package/dist/src/ripgrep-resolve.d.ts +13 -0
  36. package/dist/src/ripgrep-resolve.js +68 -0
  37. package/dist/src/ripgrep.d.ts +3 -36
  38. package/dist/src/ripgrep.js +2 -262
  39. package/dist/src/similarity.d.ts +1 -2
  40. package/dist/src/similarity.js +11 -0
  41. package/dist/src/stash-add.d.ts +4 -0
  42. package/dist/src/stash-add.js +59 -0
  43. package/dist/src/stash-ref.d.ts +7 -0
  44. package/dist/src/stash-ref.js +33 -0
  45. package/dist/src/stash-registry.d.ts +18 -0
  46. package/dist/src/stash-registry.js +221 -0
  47. package/dist/src/stash-resolve.d.ts +2 -0
  48. package/dist/src/stash-resolve.js +45 -0
  49. package/dist/src/stash-search.d.ts +8 -0
  50. package/dist/src/stash-search.js +484 -0
  51. package/dist/src/stash-show.d.ts +5 -0
  52. package/dist/src/stash-show.js +114 -0
  53. package/dist/src/stash-types.d.ts +217 -0
  54. package/dist/src/stash-types.js +1 -0
  55. package/dist/src/stash.d.ts +10 -63
  56. package/dist/src/stash.js +6 -633
  57. package/dist/src/tool-runner.d.ts +35 -0
  58. package/dist/src/tool-runner.js +100 -0
  59. package/dist/src/walker.d.ts +19 -0
  60. package/dist/src/walker.js +47 -0
  61. package/package.json +8 -14
  62. package/src/asset-spec.ts +69 -0
  63. package/src/cli.ts +282 -46
  64. package/src/common.ts +58 -0
  65. package/src/config.ts +183 -0
  66. package/src/embedder.ts +117 -0
  67. package/src/frontmatter.ts +95 -0
  68. package/src/indexer.ts +244 -84
  69. package/src/init.ts +106 -0
  70. package/src/llm.ts +124 -0
  71. package/src/markdown.ts +106 -0
  72. package/src/metadata.ts +171 -27
  73. package/src/registry-install.ts +245 -0
  74. package/src/registry-resolve.ts +272 -0
  75. package/src/registry-search.ts +145 -0
  76. package/src/registry-types.ts +64 -0
  77. package/src/ripgrep-install.ts +200 -0
  78. package/src/ripgrep-resolve.ts +72 -0
  79. package/src/ripgrep.ts +3 -315
  80. package/src/similarity.ts +13 -1
  81. package/src/stash-add.ts +66 -0
  82. package/src/stash-ref.ts +41 -0
  83. package/src/stash-registry.ts +259 -0
  84. package/src/stash-resolve.ts +47 -0
  85. package/src/stash-search.ts +595 -0
  86. package/src/stash-show.ts +112 -0
  87. package/src/stash-types.ts +221 -0
  88. package/src/stash.ts +31 -760
  89. package/src/tool-runner.ts +129 -0
  90. package/src/walker.ts +53 -0
  91. package/.claude-plugin/plugin.json +0 -21
  92. package/commands/open.md +0 -11
  93. package/commands/run.md +0 -11
  94. package/commands/search.md +0 -11
  95. package/dist/src/plugin.d.ts +0 -2
  96. package/dist/src/plugin.js +0 -55
  97. package/skills/stash/SKILL.md +0 -73
  98. package/src/plugin.ts +0 -56
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Tool execution utilities.
3
+ *
4
+ * Handles building run commands and executing tool scripts for all supported
5
+ * kinds (bash, bun, powershell, cmd).
6
+ */
7
+ import fs from "node:fs";
8
+ import path from "node:path";
9
+ import { IS_WINDOWS, isWithin } from "./common";
10
+ // ── Public API ──────────────────────────────────────────────────────────────
11
+ /**
12
+ * Build execution metadata for a tool file based on its extension.
13
+ *
14
+ * For `.ts` / `.js` files, looks up the nearest `package.json` so that
15
+ * `bun install` can be run in the correct working directory when the
16
+ * `AGENTIKIT_BUN_INSTALL` env flag is set.
17
+ */
18
+ export function buildToolInfo(stashDir, filePath) {
19
+ const ext = path.extname(filePath).toLowerCase();
20
+ if (ext === ".sh") {
21
+ return {
22
+ runCmd: `bash ${shellQuote(filePath)}`,
23
+ kind: "bash",
24
+ execute: { command: "bash", args: [filePath] },
25
+ };
26
+ }
27
+ if (ext === ".ps1") {
28
+ return {
29
+ runCmd: `powershell -ExecutionPolicy Bypass -File ${shellQuote(filePath)}`,
30
+ kind: "powershell",
31
+ execute: { command: "powershell", args: ["-ExecutionPolicy", "Bypass", "-File", filePath] },
32
+ };
33
+ }
34
+ if (ext === ".cmd" || ext === ".bat") {
35
+ return {
36
+ runCmd: `cmd /c ${shellQuote(filePath)}`,
37
+ kind: "cmd",
38
+ execute: { command: "cmd", args: ["/c", filePath] },
39
+ };
40
+ }
41
+ if (ext !== ".ts" && ext !== ".js") {
42
+ throw new Error(`Unsupported tool extension: ${ext}`);
43
+ }
44
+ const toolsRoot = path.resolve(path.join(stashDir, "tools"));
45
+ const pkgDir = findNearestPackageDir(path.dirname(filePath), toolsRoot);
46
+ if (!pkgDir) {
47
+ return {
48
+ runCmd: `bun ${shellQuote(filePath)}`,
49
+ kind: "bun",
50
+ execute: { command: "bun", args: [filePath] },
51
+ };
52
+ }
53
+ const installFlag = process.env.AGENTIKIT_BUN_INSTALL;
54
+ const shouldInstall = installFlag === "1" || installFlag === "true" || installFlag === "yes";
55
+ const quotedPkgDir = shellQuote(pkgDir);
56
+ const quotedFilePath = shellQuote(filePath);
57
+ const cdCmd = IS_WINDOWS ? `cd /d ${quotedPkgDir}` : `cd ${quotedPkgDir}`;
58
+ const chain = IS_WINDOWS ? " & " : " && ";
59
+ return {
60
+ runCmd: shouldInstall
61
+ ? `${cdCmd}${chain}bun install${chain}bun ${quotedFilePath}`
62
+ : `${cdCmd}${chain}bun ${quotedFilePath}`,
63
+ kind: "bun",
64
+ install: shouldInstall ? { command: "bun", args: ["install"], cwd: pkgDir } : undefined,
65
+ execute: { command: "bun", args: [filePath], cwd: pkgDir },
66
+ };
67
+ }
68
+ /**
69
+ * Shell-quote a path for inclusion in a human-readable `runCmd` string.
70
+ */
71
+ export function shellQuote(input) {
72
+ if (/[\r\n\t\0]/.test(input)) {
73
+ throw new Error("Unsupported control characters in stash path.");
74
+ }
75
+ if (IS_WINDOWS) {
76
+ return `"${input.replace(/"/g, '""')}"`;
77
+ }
78
+ const escaped = input
79
+ .replace(/\\/g, "\\\\")
80
+ .replace(/"/g, '\\"')
81
+ .replace(/\$/g, "\\$")
82
+ .replace(/`/g, "\\`");
83
+ return `"${escaped}"`;
84
+ }
85
+ /**
86
+ * Walk up from `startDir` toward `toolsRoot` looking for the nearest `package.json`.
87
+ */
88
+ export function findNearestPackageDir(startDir, toolsRoot) {
89
+ let current = path.resolve(startDir);
90
+ const root = path.resolve(toolsRoot);
91
+ while (isWithin(current, root)) {
92
+ if (fs.existsSync(path.join(current, "package.json"))) {
93
+ return current;
94
+ }
95
+ if (current === root)
96
+ return undefined;
97
+ current = path.dirname(current);
98
+ }
99
+ return undefined;
100
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared filesystem walker for agentikit stash directories.
3
+ *
4
+ * Provides a single implementation used by both the search fallback
5
+ * (stash.ts) and the indexer (indexer.ts) to walk type-specific asset
6
+ * directories and group files by parent directory.
7
+ */
8
+ import { type AgentikitAssetType } from "./common";
9
+ export interface DirectoryGroup {
10
+ dirPath: string;
11
+ files: string[];
12
+ }
13
+ /**
14
+ * Walk a type root directory and return files grouped by their parent directory.
15
+ *
16
+ * Only files relevant to the given `assetType` are included (e.g. `.md` for
17
+ * commands, script extensions for tools, `SKILL.md` for skills).
18
+ */
19
+ export declare function walkStash(typeRoot: string, assetType: AgentikitAssetType): DirectoryGroup[];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Shared filesystem walker for agentikit stash directories.
3
+ *
4
+ * Provides a single implementation used by both the search fallback
5
+ * (stash.ts) and the indexer (indexer.ts) to walk type-specific asset
6
+ * directories and group files by parent directory.
7
+ */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { isRelevantAssetFile } from "./asset-spec";
11
+ /**
12
+ * Walk a type root directory and return files grouped by their parent directory.
13
+ *
14
+ * Only files relevant to the given `assetType` are included (e.g. `.md` for
15
+ * commands, script extensions for tools, `SKILL.md` for skills).
16
+ */
17
+ export function walkStash(typeRoot, assetType) {
18
+ if (!fs.existsSync(typeRoot))
19
+ return [];
20
+ const groups = new Map();
21
+ const stack = [typeRoot];
22
+ while (stack.length > 0) {
23
+ const current = stack.pop();
24
+ if (!current)
25
+ continue;
26
+ const entries = fs.readdirSync(current, { withFileTypes: true });
27
+ for (const entry of entries) {
28
+ if (entry.name === ".stash.json")
29
+ continue;
30
+ const fullPath = path.join(current, entry.name);
31
+ if (entry.isDirectory()) {
32
+ stack.push(fullPath);
33
+ }
34
+ else if (entry.isFile() && isRelevantAssetFile(assetType, entry.name)) {
35
+ const parentDir = path.dirname(fullPath);
36
+ const existing = groups.get(parentDir);
37
+ if (existing) {
38
+ existing.push(fullPath);
39
+ }
40
+ else {
41
+ groups.set(parentDir, [fullPath]);
42
+ }
43
+ }
44
+ }
45
+ }
46
+ return Array.from(groups, ([dirPath, files]) => ({ dirPath, files }));
47
+ }
package/package.json CHANGED
@@ -1,20 +1,14 @@
1
1
  {
2
2
  "name": "agentikit",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
- "description": "Search, open, and run extension assets from a stash directory. Works as both an OpenCode plugin and a Claude Code plugin.",
5
+ "description": "CLI tool to search, open, and run extension assets from an agentikit stash directory.",
6
6
  "keywords": [
7
- "opencode",
8
- "opencode-ai",
9
- "opencode-plugin",
10
- "opencode-extensions",
11
- "claude-code",
12
- "claude-code-plugin",
13
7
  "agentikit",
14
8
  "ai-agent",
15
9
  "agent-framework",
16
10
  "developer-tools",
17
- "plugin",
11
+ "cli",
18
12
  "tools",
19
13
  "skills",
20
14
  "commands"
@@ -31,13 +25,10 @@
31
25
  "files": [
32
26
  "dist",
33
27
  "src",
34
- ".claude-plugin",
35
- "skills",
36
- "commands",
37
28
  "README.md"
38
29
  ],
39
30
  "bin": {
40
- "agentikit": "dist/src/cli.js"
31
+ "akm": "dist/src/cli.js"
41
32
  },
42
33
  "main": "./dist/index.js",
43
34
  "types": "./dist/index.d.ts",
@@ -60,7 +51,10 @@
60
51
  "@types/node": "^24.0.0",
61
52
  "typescript": "^5.9.3"
62
53
  },
54
+ "optionalDependencies": {
55
+ "@xenova/transformers": "^2.17.0"
56
+ },
63
57
  "dependencies": {
64
- "@opencode-ai/plugin": "^1.2.20"
58
+ "citty": "^0.2.1"
65
59
  }
66
60
  }
@@ -0,0 +1,69 @@
1
+ import path from "node:path"
2
+ import type { AgentikitAssetType } from "./common"
3
+
4
+ export const SCRIPT_EXTENSIONS = new Set([".sh", ".ts", ".js", ".ps1", ".cmd", ".bat"])
5
+
6
+ export interface AssetSpec {
7
+ stashDir: string
8
+ isRelevantFile: (fileName: string) => boolean
9
+ toCanonicalName: (typeRoot: string, filePath: string) => string | undefined
10
+ toAssetPath: (typeRoot: string, name: string) => string
11
+ }
12
+
13
+ const markdownSpec: Omit<AssetSpec, "stashDir"> = {
14
+ isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
15
+ toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
16
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
17
+ }
18
+
19
+ export const ASSET_SPECS: Record<AgentikitAssetType, AssetSpec> = {
20
+ tool: {
21
+ stashDir: "tools",
22
+ isRelevantFile: (fileName) => SCRIPT_EXTENSIONS.has(path.extname(fileName).toLowerCase()),
23
+ toCanonicalName: (typeRoot, filePath) => toPosix(path.relative(typeRoot, filePath)),
24
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name),
25
+ },
26
+ skill: {
27
+ stashDir: "skills",
28
+ isRelevantFile: (fileName) => fileName === "SKILL.md",
29
+ toCanonicalName: (typeRoot, filePath) => {
30
+ const relDir = toPosix(path.dirname(path.relative(typeRoot, filePath)))
31
+ if (!relDir || relDir === ".") return undefined
32
+ return relDir
33
+ },
34
+ toAssetPath: (typeRoot, name) => path.join(typeRoot, name, "SKILL.md"),
35
+ },
36
+ command: { stashDir: "commands", ...markdownSpec },
37
+ agent: { stashDir: "agents", ...markdownSpec },
38
+ knowledge: { stashDir: "knowledge", ...markdownSpec },
39
+ }
40
+
41
+ export const ASSET_TYPES = Object.keys(ASSET_SPECS) as AgentikitAssetType[]
42
+
43
+ export const TYPE_DIRS: Record<AgentikitAssetType, string> = ASSET_TYPES.reduce(
44
+ (acc, type) => {
45
+ acc[type] = ASSET_SPECS[type].stashDir
46
+ return acc
47
+ },
48
+ {} as Record<AgentikitAssetType, string>,
49
+ )
50
+
51
+ export function isRelevantAssetFile(assetType: AgentikitAssetType, fileName: string): boolean {
52
+ return ASSET_SPECS[assetType].isRelevantFile(fileName)
53
+ }
54
+
55
+ export function deriveCanonicalAssetName(
56
+ assetType: AgentikitAssetType,
57
+ typeRoot: string,
58
+ filePath: string,
59
+ ): string | undefined {
60
+ return ASSET_SPECS[assetType].toCanonicalName(typeRoot, filePath)
61
+ }
62
+
63
+ export function resolveAssetPathFromName(assetType: AgentikitAssetType, typeRoot: string, name: string): string {
64
+ return ASSET_SPECS[assetType].toAssetPath(typeRoot, name)
65
+ }
66
+
67
+ function toPosix(input: string): string {
68
+ return input.replace(/\\/g, "/")
69
+ }
package/src/cli.ts CHANGED
@@ -1,60 +1,296 @@
1
1
  #!/usr/bin/env node
2
- import { agentikitSearch, agentikitOpen, agentikitRun, agentikitInit } from "./stash"
2
+ import { defineCommand, runMain } from "citty"
3
+ import {
4
+ agentikitAdd,
5
+ agentikitList,
6
+ agentikitReinstall,
7
+ agentikitRemove,
8
+ agentikitSearch,
9
+ agentikitShow,
10
+ agentikitUpdate,
11
+ type KnowledgeView,
12
+ } from "./stash"
13
+ import type { SearchSource, SearchUsageMode } from "./stash-types"
14
+ import { agentikitInit } from "./init"
3
15
  import { agentikitIndex } from "./indexer"
16
+ import { loadConfig, updateConfig, type AgentikitConfig } from "./config"
17
+ import { resolveStashDir } from "./common"
4
18
 
5
- const args = process.argv.slice(2)
6
- const command = args[0]
19
+ const initCommand = defineCommand({
20
+ meta: { name: "init", description: "Initialize agentikit stash directory and set AGENTIKIT_STASH_DIR" },
21
+ run() {
22
+ return runWithJsonErrors(() => {
23
+ const result = agentikitInit()
24
+ console.log(JSON.stringify(result, null, 2))
25
+ })
26
+ },
27
+ })
7
28
 
8
- function flag(name: string): string | undefined {
9
- const idx = args.indexOf(name)
10
- return idx >= 0 && idx + 1 < args.length ? args[idx + 1] : undefined
29
+ const indexCommand = defineCommand({
30
+ meta: { name: "index", description: "Build search index (incremental by default; --full forces full reindex)" },
31
+ args: {
32
+ full: { type: "boolean", description: "Force full reindex", default: false },
33
+ },
34
+ async run({ args }) {
35
+ await runWithJsonErrors(async () => {
36
+ const result = await agentikitIndex({ full: args.full })
37
+ console.log(JSON.stringify(result, null, 2))
38
+ })
39
+ },
40
+ })
41
+
42
+ const searchCommand = defineCommand({
43
+ meta: { name: "search", description: "Search the stash" },
44
+ args: {
45
+ query: { type: "positional", description: "Search query", required: false, default: "" },
46
+ type: { type: "string", description: "Asset type filter (tool|skill|command|agent|knowledge|any)" },
47
+ limit: { type: "string", description: "Maximum number of results" },
48
+ usage: { type: "string", description: "Usage metadata mode (none|both|item|guide)", default: "both" },
49
+ source: { type: "string", description: "Search source (local|registry|both)", default: "local" },
50
+ },
51
+ async run({ args }) {
52
+ await runWithJsonErrors(async () => {
53
+ const type = args.type as "tool" | "skill" | "command" | "agent" | "knowledge" | "any" | undefined
54
+ const limit = args.limit ? parseInt(args.limit, 10) : undefined
55
+ const usage = parseSearchUsageMode(args.usage)
56
+ const source = parseSearchSource(args.source)
57
+ console.log(JSON.stringify(await agentikitSearch({ query: args.query, type, limit, usage, source }), null, 2))
58
+ })
59
+ },
60
+ })
61
+
62
+ const addCommand = defineCommand({
63
+ meta: { name: "add", description: "Install a registry package into the stash" },
64
+ args: {
65
+ ref: { type: "positional", description: "Registry ref (npm package, owner/repo, or github URL)", required: true },
66
+ },
67
+ async run({ args }) {
68
+ await runWithJsonErrors(async () => {
69
+ console.log(JSON.stringify(await agentikitAdd({ ref: args.ref }), null, 2))
70
+ })
71
+ },
72
+ })
73
+
74
+ const listCommand = defineCommand({
75
+ meta: { name: "list", description: "List installed registry packages from config" },
76
+ async run() {
77
+ await runWithJsonErrors(async () => {
78
+ console.log(JSON.stringify(await agentikitList(), null, 2))
79
+ })
80
+ },
81
+ })
82
+
83
+ const removeCommand = defineCommand({
84
+ meta: { name: "remove", description: "Remove an installed registry package by id or ref" },
85
+ args: {
86
+ target: { type: "positional", description: "Installed target (id or ref)", required: true },
87
+ },
88
+ async run({ args }) {
89
+ await runWithJsonErrors(async () => {
90
+ console.log(JSON.stringify(await agentikitRemove({ target: args.target }), null, 2))
91
+ })
92
+ },
93
+ })
94
+
95
+ const updateCommand = defineCommand({
96
+ meta: { name: "update", description: "Update one or all installed registry packages" },
97
+ args: {
98
+ target: { type: "positional", description: "Installed target (id or ref)", required: false },
99
+ all: { type: "boolean", description: "Update all installed entries", default: false },
100
+ },
101
+ async run({ args }) {
102
+ await runWithJsonErrors(async () => {
103
+ console.log(JSON.stringify(await agentikitUpdate({ target: args.target, all: args.all }), null, 2))
104
+ })
105
+ },
106
+ })
107
+
108
+ const reinstallCommand = defineCommand({
109
+ meta: { name: "reinstall", description: "Reinstall one or all installed registry packages" },
110
+ args: {
111
+ target: { type: "positional", description: "Installed target (id or ref)", required: false },
112
+ all: { type: "boolean", description: "Reinstall all installed entries", default: false },
113
+ },
114
+ async run({ args }) {
115
+ await runWithJsonErrors(async () => {
116
+ console.log(JSON.stringify(await agentikitReinstall({ target: args.target, all: args.all }), null, 2))
117
+ })
118
+ },
119
+ })
120
+
121
+ const showCommand = defineCommand({
122
+ meta: { name: "show", description: "Show a stash asset by ref (e.g. agent:bunjs-typescript-coder.md)" },
123
+ args: {
124
+ ref: { type: "positional", description: "Asset ref (type:name)", required: true },
125
+ view: { type: "string", description: "Knowledge view mode (full|toc|frontmatter|section|lines)" },
126
+ heading: { type: "string", description: "Section heading (for --view section)" },
127
+ start: { type: "string", description: "Start line (for --view lines)" },
128
+ end: { type: "string", description: "End line (for --view lines)" },
129
+ },
130
+ run({ args }) {
131
+ return runWithJsonErrors(() => {
132
+ let view: KnowledgeView | undefined
133
+ if (args.view) {
134
+ switch (args.view) {
135
+ case "section":
136
+ view = { mode: "section", heading: args.heading ?? "" }
137
+ break
138
+ case "lines":
139
+ view = {
140
+ mode: "lines",
141
+ start: Number(args.start ?? "1"),
142
+ end: args.end ? parseInt(args.end, 10) : Number.MAX_SAFE_INTEGER,
143
+ }
144
+ break
145
+ case "toc":
146
+ case "frontmatter":
147
+ case "full":
148
+ view = { mode: args.view }
149
+ break
150
+ default:
151
+ throw new Error(`Unknown view mode: ${args.view}. Expected one of: full|toc|frontmatter|section|lines`)
152
+ }
153
+ }
154
+ console.log(JSON.stringify(agentikitShow({ ref: args.ref, view }), null, 2))
155
+ })
156
+ },
157
+ })
158
+
159
+ const configCommand = defineCommand({
160
+ meta: { name: "config", description: "Show or update configuration" },
161
+ args: {
162
+ set: { type: "string", description: "Update a config key (key=value format)" },
163
+ },
164
+ run({ args }) {
165
+ return runWithJsonErrors(() => {
166
+ const stashDir = resolveStashDir()
167
+
168
+ if (args.set) {
169
+ const eqIndex = args.set.indexOf("=")
170
+ if (eqIndex === -1) {
171
+ throw new Error("--set expects key=value format")
172
+ }
173
+ const key = args.set.slice(0, eqIndex)
174
+ const value = args.set.slice(eqIndex + 1)
175
+ const partial = parseConfigValue(key, value)
176
+ const config = updateConfig(partial, stashDir)
177
+ console.log(JSON.stringify(config, null, 2))
178
+ } else {
179
+ const config = loadConfig(stashDir)
180
+ console.log(JSON.stringify(config, null, 2))
181
+ }
182
+ })
183
+ },
184
+ })
185
+
186
+ const main = defineCommand({
187
+ meta: {
188
+ name: "akm",
189
+ description: "CLI tool to search, open, and run extension assets from an agentikit stash directory.",
190
+ },
191
+ subCommands: {
192
+ init: initCommand,
193
+ index: indexCommand,
194
+ add: addCommand,
195
+ list: listCommand,
196
+ remove: removeCommand,
197
+ update: updateCommand,
198
+ reinstall: reinstallCommand,
199
+ search: searchCommand,
200
+ show: showCommand,
201
+ config: configCommand,
202
+ },
203
+ })
204
+
205
+ runMain(main)
206
+
207
+ const SEARCH_USAGE_MODES: SearchUsageMode[] = ["none", "both", "item", "guide"]
208
+ const SEARCH_SOURCES: SearchSource[] = ["local", "registry", "both"]
209
+
210
+ function parseSearchUsageMode(value: string): SearchUsageMode {
211
+ if ((SEARCH_USAGE_MODES as string[]).includes(value)) return value as SearchUsageMode
212
+ throw new Error(`Invalid value for --usage: ${value}. Expected one of: ${SEARCH_USAGE_MODES.join("|")}`)
11
213
  }
12
214
 
13
- function usage(): never {
14
- console.error("Usage: agentikit <init|search|open|run> [options]")
15
- console.error("")
16
- console.error("Commands:")
17
- console.error(" init Initialize agentikit stash directory and set AGENTIKIT_STASH_DIR")
18
- console.error(" index Build search index with metadata generation")
19
- console.error(" search [query] Search the stash (--type tool|skill|command|agent|any) (--limit N)")
20
- console.error(" open <type:name> Open a stash asset by ref")
21
- console.error(" run <type:name> Run a tool by ref")
22
- process.exit(1)
215
+ function parseSearchSource(value: string): SearchSource {
216
+ if ((SEARCH_SOURCES as string[]).includes(value)) return value as SearchSource
217
+ throw new Error(`Invalid value for --source: ${value}. Expected one of: ${SEARCH_SOURCES.join("|")}`)
23
218
  }
24
219
 
25
- switch (command) {
26
- case "init": {
27
- const result = agentikitInit()
28
- console.log(JSON.stringify(result, null, 2))
29
- break
220
+ function parseConnectionValue(
221
+ key: string,
222
+ value: string,
223
+ exampleEndpoint: string,
224
+ exampleModel: string,
225
+ ): { endpoint: string; model: string; apiKey?: string } | undefined {
226
+ if (value === "null" || value === "") return undefined
227
+ let parsed: unknown
228
+ try {
229
+ parsed = JSON.parse(value)
230
+ } catch {
231
+ throw new Error(
232
+ `Invalid value for ${key}: expected JSON object with endpoint and model`
233
+ + ` (e.g. '{"endpoint":"${exampleEndpoint}","model":"${exampleModel}"}')`,
234
+ )
235
+ }
236
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
237
+ throw new Error(`Invalid value for ${key}: expected a JSON object`)
30
238
  }
31
- case "index": {
32
- const result = agentikitIndex()
33
- console.log(JSON.stringify(result, null, 2))
34
- break
239
+ const obj = parsed as Record<string, unknown>
240
+ if (typeof obj.endpoint !== "string" || !obj.endpoint || typeof obj.model !== "string" || !obj.model) {
241
+ throw new Error(`Invalid value for ${key}: "endpoint" and "model" are required string fields`)
35
242
  }
36
- case "search": {
37
- const query = args.find((a, i) => i > 0 && !a.startsWith("--") && args[i - 1] !== "--type" && args[i - 1] !== "--limit") ?? ""
38
- const type = flag("--type") as "tool" | "skill" | "command" | "agent" | "any" | undefined
39
- const limitStr = flag("--limit")
40
- const limit = limitStr ? parseInt(limitStr, 10) : undefined
41
- console.log(JSON.stringify(agentikitSearch({ query, type, limit }), null, 2))
42
- break
243
+ const result: { endpoint: string; model: string; apiKey?: string } = {
244
+ endpoint: obj.endpoint,
245
+ model: obj.model,
43
246
  }
44
- case "open": {
45
- const ref = args[1]
46
- if (!ref) { console.error("Error: missing ref argument\n"); usage() }
47
- console.log(JSON.stringify(agentikitOpen({ ref }), null, 2))
48
- break
247
+ if (typeof obj.apiKey === "string" && obj.apiKey) {
248
+ result.apiKey = obj.apiKey
249
+ }
250
+ return result
251
+ }
252
+
253
+ function parseConfigValue(key: string, value: string): Partial<AgentikitConfig> {
254
+ switch (key) {
255
+ case "semanticSearch":
256
+ if (value !== "true" && value !== "false") {
257
+ throw new Error(`Invalid value for semanticSearch: expected "true" or "false"`)
258
+ }
259
+ return { semanticSearch: value === "true" }
260
+ case "additionalStashDirs":
261
+ try {
262
+ const parsed = JSON.parse(value)
263
+ if (!Array.isArray(parsed)) throw new Error("expected JSON array")
264
+ return { additionalStashDirs: parsed.filter((d: unknown): d is string => typeof d === "string") }
265
+ } catch {
266
+ throw new Error(`Invalid value for additionalStashDirs: expected JSON array (e.g. '["/path/a","/path/b"]')`)
267
+ }
268
+ case "embedding":
269
+ return { embedding: parseConnectionValue("embedding", value, "http://localhost:11434/v1/embeddings", "nomic-embed-text") }
270
+ case "llm":
271
+ return { llm: parseConnectionValue("llm", value, "http://localhost:11434/v1/chat/completions", "llama3.2") }
272
+ default:
273
+ throw new Error(`Unknown config key: ${key}`)
49
274
  }
50
- case "run": {
51
- const ref = args[1]
52
- if (!ref) { console.error("Error: missing ref argument\n"); usage() }
53
- const result = agentikitRun({ ref })
54
- console.log(JSON.stringify(result, null, 2))
55
- process.exit(result.exitCode)
56
- break
275
+ }
276
+
277
+ async function runWithJsonErrors(fn: (() => void) | (() => Promise<void>)): Promise<void> {
278
+ try {
279
+ await fn()
280
+ } catch (error: unknown) {
281
+ const message = error instanceof Error ? error.message : String(error)
282
+ const hint = buildHint(message)
283
+ console.error(JSON.stringify({ ok: false, error: message, hint }, null, 2))
284
+ process.exit(1)
57
285
  }
58
- default:
59
- usage()
286
+ }
287
+
288
+ function buildHint(message: string): string | undefined {
289
+ if (message.includes("AGENTIKIT_STASH_DIR")) return "Run `akm init` or set AGENTIKIT_STASH_DIR to a valid directory."
290
+ if (message.includes("Either <target> or --all is required")) return "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg`."
291
+ if (message.includes("Specify either <target> or --all")) return "Use only one: a positional target or `--all`."
292
+ if (message.includes("No installed registry entry matched target")) return "Run `akm list` to view installed ids/refs, then retry with one of those values."
293
+ if (message.includes("Invalid value for --source")) return "Pick one of: local, registry, both."
294
+ if (message.includes("Invalid value for --usage")) return "Pick one of: none, both, item, guide."
295
+ return undefined
60
296
  }