agentikit 0.0.7 → 0.0.8

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 (80) hide show
  1. package/README.md +113 -77
  2. package/dist/index.d.ts +13 -3
  3. package/dist/index.js +7 -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 +154 -52
  7. package/dist/src/common.d.ts +8 -0
  8. package/dist/src/common.js +46 -0
  9. package/dist/src/config.d.ts +31 -0
  10. package/dist/src/config.js +74 -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 +10 -2
  24. package/dist/src/metadata.js +146 -30
  25. package/dist/src/ripgrep-install.d.ts +12 -0
  26. package/dist/src/ripgrep-install.js +169 -0
  27. package/dist/src/ripgrep-resolve.d.ts +13 -0
  28. package/dist/src/ripgrep-resolve.js +68 -0
  29. package/dist/src/ripgrep.d.ts +3 -36
  30. package/dist/src/ripgrep.js +2 -262
  31. package/dist/src/similarity.d.ts +1 -2
  32. package/dist/src/similarity.js +11 -0
  33. package/dist/src/stash-ref.d.ts +7 -0
  34. package/dist/src/stash-ref.js +33 -0
  35. package/dist/src/stash-resolve.d.ts +2 -0
  36. package/dist/src/stash-resolve.js +45 -0
  37. package/dist/src/stash-search.d.ts +6 -0
  38. package/dist/src/stash-search.js +269 -0
  39. package/dist/src/stash-show.d.ts +5 -0
  40. package/dist/src/stash-show.js +107 -0
  41. package/dist/src/stash-types.d.ts +53 -0
  42. package/dist/src/stash-types.js +1 -0
  43. package/dist/src/stash.d.ts +8 -63
  44. package/dist/src/stash.js +4 -633
  45. package/dist/src/tool-runner.d.ts +35 -0
  46. package/dist/src/tool-runner.js +100 -0
  47. package/dist/src/walker.d.ts +19 -0
  48. package/dist/src/walker.js +47 -0
  49. package/package.json +8 -14
  50. package/src/asset-spec.ts +69 -0
  51. package/src/cli.ts +164 -48
  52. package/src/common.ts +58 -0
  53. package/src/config.ts +124 -0
  54. package/src/embedder.ts +117 -0
  55. package/src/frontmatter.ts +95 -0
  56. package/src/indexer.ts +244 -84
  57. package/src/init.ts +106 -0
  58. package/src/llm.ts +124 -0
  59. package/src/markdown.ts +106 -0
  60. package/src/metadata.ts +157 -29
  61. package/src/ripgrep-install.ts +200 -0
  62. package/src/ripgrep-resolve.ts +72 -0
  63. package/src/ripgrep.ts +3 -315
  64. package/src/similarity.ts +13 -1
  65. package/src/stash-ref.ts +41 -0
  66. package/src/stash-resolve.ts +47 -0
  67. package/src/stash-search.ts +343 -0
  68. package/src/stash-show.ts +104 -0
  69. package/src/stash-types.ts +46 -0
  70. package/src/stash.ts +16 -760
  71. package/src/tool-runner.ts +129 -0
  72. package/src/walker.ts +53 -0
  73. package/.claude-plugin/plugin.json +0 -21
  74. package/commands/open.md +0 -11
  75. package/commands/run.md +0 -11
  76. package/commands/search.md +0 -11
  77. package/dist/src/plugin.d.ts +0 -2
  78. package/dist/src/plugin.js +0 -55
  79. package/skills/stash/SKILL.md +0 -73
  80. 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.8",
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,176 @@
1
1
  #!/usr/bin/env node
2
- import { agentikitSearch, agentikitOpen, agentikitRun, agentikitInit } from "./stash"
2
+ import { defineCommand, runMain } from "citty"
3
+ import { agentikitSearch, agentikitShow, type KnowledgeView } from "./stash"
4
+ import { agentikitInit } from "./init"
3
5
  import { agentikitIndex } from "./indexer"
6
+ import { loadConfig, updateConfig, type AgentikitConfig } from "./config"
7
+ import { resolveStashDir } from "./common"
4
8
 
5
- const args = process.argv.slice(2)
6
- const command = args[0]
9
+ const initCommand = defineCommand({
10
+ meta: { name: "init", description: "Initialize agentikit stash directory and set AGENTIKIT_STASH_DIR" },
11
+ run() {
12
+ const result = agentikitInit()
13
+ console.log(JSON.stringify(result, null, 2))
14
+ },
15
+ })
7
16
 
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
11
- }
17
+ const indexCommand = defineCommand({
18
+ meta: { name: "index", description: "Build search index (incremental by default; --full forces full reindex)" },
19
+ args: {
20
+ full: { type: "boolean", description: "Force full reindex", default: false },
21
+ },
22
+ async run({ args }) {
23
+ const result = await agentikitIndex({ full: args.full })
24
+ console.log(JSON.stringify(result, null, 2))
25
+ },
26
+ })
12
27
 
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)
23
- }
28
+ const searchCommand = defineCommand({
29
+ meta: { name: "search", description: "Search the stash" },
30
+ args: {
31
+ query: { type: "positional", description: "Search query", required: false, default: "" },
32
+ type: { type: "string", description: "Asset type filter (tool|skill|command|agent|knowledge|any)" },
33
+ limit: { type: "string", description: "Maximum number of results" },
34
+ },
35
+ async run({ args }) {
36
+ const type = args.type as "tool" | "skill" | "command" | "agent" | "knowledge" | "any" | undefined
37
+ const limit = args.limit ? parseInt(args.limit, 10) : undefined
38
+ console.log(JSON.stringify(await agentikitSearch({ query: args.query, type, limit }), null, 2))
39
+ },
40
+ })
24
41
 
25
- switch (command) {
26
- case "init": {
27
- const result = agentikitInit()
28
- console.log(JSON.stringify(result, null, 2))
29
- break
42
+ const showCommand = defineCommand({
43
+ meta: { name: "show", description: "Show a stash asset by ref (e.g. agent:bunjs-typescript-coder.md)" },
44
+ args: {
45
+ ref: { type: "positional", description: "Asset ref (type:name)", required: true },
46
+ view: { type: "string", description: "Knowledge view mode (full|toc|frontmatter|section|lines)" },
47
+ heading: { type: "string", description: "Section heading (for --view section)" },
48
+ start: { type: "string", description: "Start line (for --view lines)" },
49
+ end: { type: "string", description: "End line (for --view lines)" },
50
+ },
51
+ run({ args }) {
52
+ let view: KnowledgeView | undefined
53
+ if (args.view) {
54
+ switch (args.view) {
55
+ case "section":
56
+ view = { mode: "section", heading: args.heading ?? "" }
57
+ break
58
+ case "lines":
59
+ view = {
60
+ mode: "lines",
61
+ start: Number(args.start ?? "1"),
62
+ end: args.end ? parseInt(args.end, 10) : Number.MAX_SAFE_INTEGER,
63
+ }
64
+ break
65
+ case "toc":
66
+ case "frontmatter":
67
+ case "full":
68
+ view = { mode: args.view }
69
+ break
70
+ default:
71
+ console.error(`Unknown view mode: ${args.view}`)
72
+ process.exit(1)
73
+ }
74
+ }
75
+ console.log(JSON.stringify(agentikitShow({ ref: args.ref, view }), null, 2))
76
+ },
77
+ })
78
+
79
+ const configCommand = defineCommand({
80
+ meta: { name: "config", description: "Show or update configuration" },
81
+ args: {
82
+ set: { type: "string", description: "Update a config key (key=value format)" },
83
+ },
84
+ run({ args }) {
85
+ const stashDir = resolveStashDir()
86
+
87
+ if (args.set) {
88
+ const eqIndex = args.set.indexOf("=")
89
+ if (eqIndex === -1) {
90
+ console.error("Error: --set expects key=value format")
91
+ process.exit(1)
92
+ }
93
+ const key = args.set.slice(0, eqIndex)
94
+ const value = args.set.slice(eqIndex + 1)
95
+ const partial = parseConfigValue(key, value)
96
+ const config = updateConfig(partial, stashDir)
97
+ console.log(JSON.stringify(config, null, 2))
98
+ } else {
99
+ const config = loadConfig(stashDir)
100
+ console.log(JSON.stringify(config, null, 2))
101
+ }
102
+ },
103
+ })
104
+
105
+ const main = defineCommand({
106
+ meta: {
107
+ name: "akm",
108
+ description: "CLI tool to search, open, and run extension assets from an agentikit stash directory.",
109
+ },
110
+ subCommands: {
111
+ init: initCommand,
112
+ index: indexCommand,
113
+ search: searchCommand,
114
+ show: showCommand,
115
+ config: configCommand,
116
+ },
117
+ })
118
+
119
+ runMain(main)
120
+
121
+ function parseConnectionValue(
122
+ key: string,
123
+ value: string,
124
+ exampleEndpoint: string,
125
+ exampleModel: string,
126
+ ): { endpoint: string; model: string; apiKey?: string } | undefined {
127
+ if (value === "null" || value === "") return undefined
128
+ let parsed: unknown
129
+ try {
130
+ parsed = JSON.parse(value)
131
+ } catch {
132
+ throw new Error(
133
+ `Invalid value for ${key}: expected JSON object with endpoint and model`
134
+ + ` (e.g. '{"endpoint":"${exampleEndpoint}","model":"${exampleModel}"}')`,
135
+ )
30
136
  }
31
- case "index": {
32
- const result = agentikitIndex()
33
- console.log(JSON.stringify(result, null, 2))
34
- break
137
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
138
+ throw new Error(`Invalid value for ${key}: expected a JSON object`)
35
139
  }
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
140
+ const obj = parsed as Record<string, unknown>
141
+ if (typeof obj.endpoint !== "string" || !obj.endpoint || typeof obj.model !== "string" || !obj.model) {
142
+ throw new Error(`Invalid value for ${key}: "endpoint" and "model" are required string fields`)
43
143
  }
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
144
+ const result: { endpoint: string; model: string; apiKey?: string } = {
145
+ endpoint: obj.endpoint,
146
+ model: obj.model,
49
147
  }
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
148
+ if (typeof obj.apiKey === "string" && obj.apiKey) {
149
+ result.apiKey = obj.apiKey
150
+ }
151
+ return result
152
+ }
153
+
154
+ function parseConfigValue(key: string, value: string): Partial<AgentikitConfig> {
155
+ switch (key) {
156
+ case "semanticSearch":
157
+ if (value !== "true" && value !== "false") {
158
+ throw new Error(`Invalid value for semanticSearch: expected "true" or "false"`)
159
+ }
160
+ return { semanticSearch: value === "true" }
161
+ case "additionalStashDirs":
162
+ try {
163
+ const parsed = JSON.parse(value)
164
+ if (!Array.isArray(parsed)) throw new Error("expected JSON array")
165
+ return { additionalStashDirs: parsed.filter((d: unknown): d is string => typeof d === "string") }
166
+ } catch {
167
+ throw new Error(`Invalid value for additionalStashDirs: expected JSON array (e.g. '["/path/a","/path/b"]')`)
168
+ }
169
+ case "embedding":
170
+ return { embedding: parseConnectionValue("embedding", value, "http://localhost:11434/v1/embeddings", "nomic-embed-text") }
171
+ case "llm":
172
+ return { llm: parseConnectionValue("llm", value, "http://localhost:11434/v1/chat/completions", "llama3.2") }
173
+ default:
174
+ throw new Error(`Unknown config key: ${key}`)
57
175
  }
58
- default:
59
- usage()
60
176
  }
package/src/common.ts ADDED
@@ -0,0 +1,58 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import { TYPE_DIRS } from "./asset-spec"
4
+
5
+ // ── Types ───────────────────────────────────────────────────────────────────
6
+
7
+ export type AgentikitAssetType = "tool" | "skill" | "command" | "agent" | "knowledge"
8
+
9
+ // ── Constants ───────────────────────────────────────────────────────────────
10
+
11
+ export const IS_WINDOWS = process.platform === "win32"
12
+ export { SCRIPT_EXTENSIONS, TYPE_DIRS } from "./asset-spec"
13
+
14
+ // ── Validators ──────────────────────────────────────────────────────────────
15
+
16
+ export function isAssetType(type: string): type is AgentikitAssetType {
17
+ return type in TYPE_DIRS
18
+ }
19
+
20
+ // ── Utilities ───────────────────────────────────────────────────────────────
21
+
22
+ export function resolveStashDir(): string {
23
+ const raw = process.env.AGENTIKIT_STASH_DIR?.trim()
24
+ if (!raw) {
25
+ throw new Error("AGENTIKIT_STASH_DIR is not set. Set it to your Agentikit stash path.")
26
+ }
27
+ const stashDir = path.resolve(raw)
28
+ let stat: fs.Stats
29
+ try {
30
+ stat = fs.statSync(stashDir)
31
+ } catch {
32
+ throw new Error(`Unable to read AGENTIKIT_STASH_DIR at "${stashDir}".`)
33
+ }
34
+ if (!stat.isDirectory()) {
35
+ throw new Error(`AGENTIKIT_STASH_DIR must point to a directory: "${stashDir}".`)
36
+ }
37
+ return stashDir
38
+ }
39
+
40
+ export function toPosix(input: string): string {
41
+ return input.replace(/\\/g, "/")
42
+ }
43
+
44
+ export function hasErrnoCode(error: unknown, code: string): boolean {
45
+ if (typeof error !== "object" || error === null || !("code" in error)) return false
46
+ return (error as Record<string, unknown>).code === code
47
+ }
48
+
49
+ export function isWithin(candidate: string, root: string): boolean {
50
+ const normalizedRoot = normalizeFsPathForComparison(path.resolve(root))
51
+ const normalizedCandidate = normalizeFsPathForComparison(path.resolve(candidate))
52
+ const rel = path.relative(normalizedRoot, normalizedCandidate)
53
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel))
54
+ }
55
+
56
+ function normalizeFsPathForComparison(value: string): string {
57
+ return process.platform === "win32" ? value.toLowerCase() : value
58
+ }