agentikit 0.0.8 → 0.0.12

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 (112) hide show
  1. package/README.md +135 -117
  2. package/dist/index.d.ts +13 -3
  3. package/dist/index.js +7 -1
  4. package/dist/src/asset-spec.d.ts +2 -0
  5. package/dist/src/asset-spec.js +22 -3
  6. package/dist/src/asset-type-handler.d.ts +27 -0
  7. package/dist/src/asset-type-handler.js +33 -0
  8. package/dist/src/cli.js +335 -100
  9. package/dist/src/common.d.ts +6 -1
  10. package/dist/src/common.js +18 -4
  11. package/dist/src/config-cli.d.ts +9 -0
  12. package/dist/src/config-cli.js +473 -0
  13. package/dist/src/config.d.ts +25 -6
  14. package/dist/src/config.js +188 -28
  15. package/dist/src/db.d.ts +46 -0
  16. package/dist/src/db.js +299 -0
  17. package/dist/src/embedder.js +12 -7
  18. package/dist/src/github.d.ts +4 -0
  19. package/dist/src/github.js +19 -0
  20. package/dist/src/handlers/agent-handler.d.ts +2 -0
  21. package/dist/src/handlers/agent-handler.js +26 -0
  22. package/dist/src/handlers/command-handler.d.ts +2 -0
  23. package/dist/src/handlers/command-handler.js +23 -0
  24. package/dist/src/handlers/index.d.ts +6 -0
  25. package/dist/src/handlers/index.js +23 -0
  26. package/dist/src/handlers/knowledge-handler.d.ts +2 -0
  27. package/dist/src/handlers/knowledge-handler.js +56 -0
  28. package/dist/src/handlers/markdown-helpers.d.ts +7 -0
  29. package/dist/src/handlers/markdown-helpers.js +15 -0
  30. package/dist/src/handlers/script-handler.d.ts +2 -0
  31. package/dist/src/handlers/script-handler.js +78 -0
  32. package/dist/src/handlers/skill-handler.d.ts +2 -0
  33. package/dist/src/handlers/skill-handler.js +30 -0
  34. package/dist/src/handlers/tool-handler.d.ts +2 -0
  35. package/dist/src/handlers/tool-handler.js +58 -0
  36. package/dist/src/indexer.d.ts +1 -23
  37. package/dist/src/indexer.js +162 -155
  38. package/dist/src/init.d.ts +2 -2
  39. package/dist/src/init.js +21 -9
  40. package/dist/src/llm.js +4 -3
  41. package/dist/src/metadata.d.ts +1 -1
  42. package/dist/src/metadata.js +22 -64
  43. package/dist/src/origin-resolve.d.ts +19 -0
  44. package/dist/src/origin-resolve.js +53 -0
  45. package/dist/src/registry-install.d.ts +11 -0
  46. package/dist/src/registry-install.js +315 -0
  47. package/dist/src/registry-resolve.d.ts +3 -0
  48. package/dist/src/registry-resolve.js +299 -0
  49. package/dist/src/registry-search.d.ts +27 -0
  50. package/dist/src/registry-search.js +263 -0
  51. package/dist/src/registry-types.d.ts +62 -0
  52. package/dist/src/registry-types.js +1 -0
  53. package/dist/src/stash-add.d.ts +4 -0
  54. package/dist/src/stash-add.js +59 -0
  55. package/dist/src/stash-clone.d.ts +22 -0
  56. package/dist/src/stash-clone.js +83 -0
  57. package/dist/src/stash-ref.d.ts +27 -3
  58. package/dist/src/stash-ref.js +63 -24
  59. package/dist/src/stash-registry.d.ts +18 -0
  60. package/dist/src/stash-registry.js +221 -0
  61. package/dist/src/stash-resolve.js +3 -0
  62. package/dist/src/stash-search.d.ts +3 -1
  63. package/dist/src/stash-search.js +357 -138
  64. package/dist/src/stash-show.d.ts +1 -1
  65. package/dist/src/stash-show.js +28 -89
  66. package/dist/src/stash-source.d.ts +24 -0
  67. package/dist/src/stash-source.js +81 -0
  68. package/dist/src/stash-types.d.ts +175 -1
  69. package/dist/src/stash.d.ts +9 -1
  70. package/dist/src/stash.js +5 -0
  71. package/dist/src/tool-runner.d.ts +1 -1
  72. package/dist/src/tool-runner.js +18 -5
  73. package/package.json +7 -2
  74. package/src/asset-spec.ts +20 -4
  75. package/src/asset-type-handler.ts +77 -0
  76. package/src/cli.ts +354 -103
  77. package/src/common.ts +23 -5
  78. package/src/config-cli.ts +499 -0
  79. package/src/config.ts +218 -37
  80. package/src/db.ts +411 -0
  81. package/src/embedder.ts +22 -11
  82. package/src/github.ts +21 -0
  83. package/src/handlers/agent-handler.ts +32 -0
  84. package/src/handlers/command-handler.ts +29 -0
  85. package/src/handlers/index.ts +25 -0
  86. package/src/handlers/knowledge-handler.ts +62 -0
  87. package/src/handlers/markdown-helpers.ts +19 -0
  88. package/src/handlers/script-handler.ts +92 -0
  89. package/src/handlers/skill-handler.ts +37 -0
  90. package/src/handlers/tool-handler.ts +71 -0
  91. package/src/indexer.ts +208 -187
  92. package/src/init.ts +17 -9
  93. package/src/llm.ts +4 -3
  94. package/src/metadata.ts +21 -65
  95. package/src/origin-resolve.ts +67 -0
  96. package/src/registry-install.ts +361 -0
  97. package/src/registry-resolve.ts +341 -0
  98. package/src/registry-search.ts +335 -0
  99. package/src/registry-types.ts +72 -0
  100. package/src/stash-add.ts +63 -0
  101. package/src/stash-clone.ts +127 -0
  102. package/src/stash-ref.ts +84 -26
  103. package/src/stash-registry.ts +259 -0
  104. package/src/stash-resolve.ts +3 -0
  105. package/src/stash-search.ts +425 -155
  106. package/src/stash-show.ts +33 -82
  107. package/src/stash-source.ts +103 -0
  108. package/src/stash-types.ts +186 -1
  109. package/src/stash.ts +23 -0
  110. package/src/tool-runner.ts +18 -5
  111. package/dist/src/similarity.d.ts +0 -34
  112. package/src/similarity.ts +0 -271
@@ -0,0 +1,77 @@
1
+ import type { StashEntry } from "./metadata"
2
+ import type { LocalSearchHit, ShowResponse, KnowledgeView } from "./stash-types"
3
+
4
+ // ── Interface ────────────────────────────────────────────────────────────────
5
+
6
+ export interface ShowInput {
7
+ name: string
8
+ path: string
9
+ content: string
10
+ view?: KnowledgeView
11
+ stashDirs?: string[]
12
+ }
13
+
14
+ export interface AssetTypeHandler {
15
+ /** The type name, e.g. "tool", "script" */
16
+ readonly typeName: string
17
+ /** Directory inside the stash root, e.g. "tools", "scripts" */
18
+ readonly stashDir: string
19
+
20
+ // -- File system spec --
21
+ isRelevantFile(fileName: string): boolean
22
+ toCanonicalName(typeRoot: string, filePath: string): string | undefined
23
+ toAssetPath(typeRoot: string, name: string): string
24
+
25
+ // -- Show behavior --
26
+ buildShowResponse(input: ShowInput): ShowResponse
27
+
28
+ // -- Search enrichment --
29
+ enrichSearchHit?(hit: LocalSearchHit, stashDir: string): void
30
+
31
+ // -- Usage guide --
32
+ readonly defaultUsageGuide: string[]
33
+
34
+ // -- Metadata generation hooks --
35
+ extractTypeMetadata?(entry: StashEntry, file: string, ext: string): void
36
+ }
37
+
38
+ // ── Registry ─────────────────────────────────────────────────────────────────
39
+
40
+ const handlers = new Map<string, AssetTypeHandler>()
41
+
42
+ let handlersInitialized = false
43
+
44
+ function ensureHandlersRegistered(): void {
45
+ if (handlersInitialized) return
46
+ handlersInitialized = true
47
+ // Import handler registrations
48
+ require("./handlers/index")
49
+ }
50
+
51
+ export function registerAssetType(handler: AssetTypeHandler): void {
52
+ handlers.set(handler.typeName, handler)
53
+ }
54
+
55
+ export function getHandler(type: string): AssetTypeHandler {
56
+ ensureHandlersRegistered()
57
+ const handler = handlers.get(type)
58
+ if (!handler) {
59
+ throw new Error(`Unknown asset type: "${type}"`)
60
+ }
61
+ return handler
62
+ }
63
+
64
+ export function tryGetHandler(type: string): AssetTypeHandler | undefined {
65
+ ensureHandlersRegistered()
66
+ return handlers.get(type)
67
+ }
68
+
69
+ export function getAllHandlers(): AssetTypeHandler[] {
70
+ ensureHandlersRegistered()
71
+ return Array.from(handlers.values())
72
+ }
73
+
74
+ export function getRegisteredTypeNames(): string[] {
75
+ ensureHandlersRegistered()
76
+ return Array.from(handlers.keys())
77
+ }
package/src/cli.ts CHANGED
@@ -1,16 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  import { defineCommand, runMain } from "citty"
3
- import { agentikitSearch, agentikitShow, type KnowledgeView } from "./stash"
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"
4
14
  import { agentikitInit } from "./init"
5
15
  import { agentikitIndex } from "./indexer"
6
- import { loadConfig, updateConfig, type AgentikitConfig } from "./config"
7
- import { resolveStashDir } from "./common"
16
+ import { agentikitClone } from "./stash-clone"
17
+
18
+ import { resolveStashSources } from "./stash-source"
19
+ import { loadConfig, saveConfig } from "./config"
20
+ import {
21
+ getConfigValue,
22
+ listConfig,
23
+ listProviders,
24
+ parseConfigValue,
25
+ setConfigValue,
26
+ unsetConfigValue,
27
+ useProvider,
28
+ } from "./config-cli"
8
29
 
9
30
  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))
31
+ meta: { name: "init", description: "Initialize Agent-i-Kit's working stash directory and set AKM_STASH_DIR" },
32
+ async run() {
33
+ await runWithJsonErrors(async () => {
34
+ const result = await agentikitInit()
35
+ console.log(JSON.stringify(result, null, 2))
36
+ })
14
37
  },
15
38
  })
16
39
 
@@ -20,8 +43,10 @@ const indexCommand = defineCommand({
20
43
  full: { type: "boolean", description: "Force full reindex", default: false },
21
44
  },
22
45
  async run({ args }) {
23
- const result = await agentikitIndex({ full: args.full })
24
- console.log(JSON.stringify(result, null, 2))
46
+ await runWithJsonErrors(async () => {
47
+ const result = await agentikitIndex({ full: args.full })
48
+ console.log(JSON.stringify(result, null, 2))
49
+ })
25
50
  },
26
51
  })
27
52
 
@@ -29,13 +54,82 @@ const searchCommand = defineCommand({
29
54
  meta: { name: "search", description: "Search the stash" },
30
55
  args: {
31
56
  query: { type: "positional", description: "Search query", required: false, default: "" },
32
- type: { type: "string", description: "Asset type filter (tool|skill|command|agent|knowledge|any)" },
57
+ type: { type: "string", description: "Asset type filter (tool|skill|command|agent|knowledge|script|any)" },
33
58
  limit: { type: "string", description: "Maximum number of results" },
59
+ usage: { type: "string", description: "Usage metadata mode (none|both|item|guide)", default: "both" },
60
+ source: { type: "string", description: "Search source (local|registry|both)", default: "local" },
61
+ },
62
+ async run({ args }) {
63
+ await runWithJsonErrors(async () => {
64
+ const type = args.type as "tool" | "skill" | "command" | "agent" | "knowledge" | "script" | "any" | undefined
65
+ const limit = args.limit ? parseInt(args.limit, 10) : undefined
66
+ const usage = parseSearchUsageMode(args.usage)
67
+ const source = parseSearchSource(args.source)
68
+ console.log(JSON.stringify(await agentikitSearch({ query: args.query, type, limit, usage, source }), null, 2))
69
+ })
70
+ },
71
+ })
72
+
73
+ const addCommand = defineCommand({
74
+ meta: { name: "add", description: "Install a registry package or local git directory into the stash" },
75
+ args: {
76
+ ref: {
77
+ type: "positional",
78
+ description: "Registry ref (npm package, owner/repo, github URL, or local git directory)",
79
+ required: true,
80
+ },
81
+ },
82
+ async run({ args }) {
83
+ await runWithJsonErrors(async () => {
84
+ console.log(JSON.stringify(await agentikitAdd({ ref: args.ref }), null, 2))
85
+ })
86
+ },
87
+ })
88
+
89
+ const listCommand = defineCommand({
90
+ meta: { name: "list", description: "List installed registry packages from config" },
91
+ async run() {
92
+ await runWithJsonErrors(async () => {
93
+ console.log(JSON.stringify(await agentikitList(), null, 2))
94
+ })
95
+ },
96
+ })
97
+
98
+ const removeCommand = defineCommand({
99
+ meta: { name: "remove", description: "Remove an installed registry package by id or ref" },
100
+ args: {
101
+ target: { type: "positional", description: "Installed target (id or ref)", required: true },
34
102
  },
35
103
  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))
104
+ await runWithJsonErrors(async () => {
105
+ console.log(JSON.stringify(await agentikitRemove({ target: args.target }), null, 2))
106
+ })
107
+ },
108
+ })
109
+
110
+ const updateCommand = defineCommand({
111
+ meta: { name: "update", description: "Update one or all installed registry packages" },
112
+ args: {
113
+ target: { type: "positional", description: "Installed target (id or ref)", required: false },
114
+ all: { type: "boolean", description: "Update all installed entries", default: false },
115
+ },
116
+ async run({ args }) {
117
+ await runWithJsonErrors(async () => {
118
+ console.log(JSON.stringify(await agentikitUpdate({ target: args.target, all: args.all }), null, 2))
119
+ })
120
+ },
121
+ })
122
+
123
+ const reinstallCommand = defineCommand({
124
+ meta: { name: "reinstall", description: "Reinstall one or all installed registry packages" },
125
+ args: {
126
+ target: { type: "positional", description: "Installed target (id or ref)", required: false },
127
+ all: { type: "boolean", description: "Reinstall all installed entries", default: false },
128
+ },
129
+ async run({ args }) {
130
+ await runWithJsonErrors(async () => {
131
+ console.log(JSON.stringify(await agentikitReinstall({ target: args.target, all: args.all }), null, 2))
132
+ })
39
133
  },
40
134
  })
41
135
 
@@ -48,129 +142,286 @@ const showCommand = defineCommand({
48
142
  start: { type: "string", description: "Start line (for --view lines)" },
49
143
  end: { type: "string", description: "End line (for --view lines)" },
50
144
  },
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)
145
+ async run({ args }) {
146
+ await runWithJsonErrors(async () => {
147
+ let view: KnowledgeView | undefined
148
+ if (args.view) {
149
+ switch (args.view) {
150
+ case "section":
151
+ view = { mode: "section", heading: args.heading ?? "" }
152
+ break
153
+ case "lines":
154
+ view = {
155
+ mode: "lines",
156
+ start: Number(args.start ?? "1"),
157
+ end: args.end ? parseInt(args.end, 10) : Number.MAX_SAFE_INTEGER,
158
+ }
159
+ break
160
+ case "toc":
161
+ case "frontmatter":
162
+ case "full":
163
+ view = { mode: args.view }
164
+ break
165
+ default:
166
+ throw new Error(`Unknown view mode: ${args.view}. Expected one of: full|toc|frontmatter|section|lines`)
167
+ }
73
168
  }
74
- }
75
- console.log(JSON.stringify(agentikitShow({ ref: args.ref, view }), null, 2))
169
+ console.log(JSON.stringify(await agentikitShow({ ref: args.ref, view }), null, 2))
170
+ })
76
171
  },
77
172
  })
78
173
 
79
174
  const configCommand = defineCommand({
80
- meta: { name: "config", description: "Show or update configuration" },
175
+ meta: { name: "config", description: "Show configuration, get/set keys, and manage embedding/LLM providers" },
81
176
  args: {
82
- set: { type: "string", description: "Update a config key (key=value format)" },
177
+ list: { type: "boolean", description: "List current configuration with effective defaults", default: false },
178
+ get: { type: "string", description: "Get a configuration value by key" },
179
+ unset: { type: "string", description: "Unset an optional configuration key or whole embedding/llm section" },
180
+ set: { type: "string", description: "Back-compat alias for updating a key (key=value format)" },
181
+ },
182
+ subCommands: {
183
+ list: defineCommand({
184
+ meta: { name: "list", description: "List current configuration with effective embedding/LLM settings" },
185
+ run() {
186
+ return runWithJsonErrors(() => {
187
+ console.log(JSON.stringify(listConfig(loadConfig()), null, 2))
188
+ })
189
+ },
190
+ }),
191
+ get: defineCommand({
192
+ meta: { name: "get", description: "Get a configuration value by key" },
193
+ args: {
194
+ key: { type: "positional", required: true, description: "Config key (for example: embedding.provider)" },
195
+ },
196
+ run({ args }) {
197
+ return runWithJsonErrors(() => {
198
+ console.log(JSON.stringify(getConfigValue(loadConfig(), args.key), null, 2))
199
+ })
200
+ },
201
+ }),
202
+ set: defineCommand({
203
+ meta: { name: "set", description: "Set a configuration value by key" },
204
+ args: {
205
+ key: { type: "positional", required: true, description: "Config key (for example: llm.temperature)" },
206
+ value: { type: "positional", required: true, description: "Config value" },
207
+ },
208
+ run({ args }) {
209
+ return runWithJsonErrors(() => {
210
+ const updated = setConfigValue(loadConfig(), args.key, args.value)
211
+ saveConfig(updated)
212
+ console.log(JSON.stringify(listConfig(updated), null, 2))
213
+ })
214
+ },
215
+ }),
216
+ unset: defineCommand({
217
+ meta: { name: "unset", description: "Unset an optional configuration key or whole embedding/llm section" },
218
+ args: {
219
+ key: { type: "positional", required: true, description: "Config key to unset" },
220
+ },
221
+ run({ args }) {
222
+ return runWithJsonErrors(() => {
223
+ const updated = unsetConfigValue(loadConfig(), args.key)
224
+ saveConfig(updated)
225
+ console.log(JSON.stringify(listConfig(updated), null, 2))
226
+ })
227
+ },
228
+ }),
229
+ providers: defineCommand({
230
+ meta: { name: "providers", description: "List available embedding or LLM providers" },
231
+ args: {
232
+ scope: { type: "positional", required: true, description: "Provider scope: embedding or llm" },
233
+ },
234
+ run({ args }) {
235
+ return runWithJsonErrors(() => {
236
+ const scope = parseProviderScope(args.scope)
237
+ console.log(JSON.stringify(listProviders(scope, loadConfig()), null, 2))
238
+ })
239
+ },
240
+ }),
241
+ use: defineCommand({
242
+ meta: { name: "use", description: "Switch the default embedding or LLM provider" },
243
+ args: {
244
+ scope: { type: "positional", required: true, description: "Provider scope: embedding or llm" },
245
+ provider: { type: "positional", required: true, description: "Provider name" },
246
+ },
247
+ run({ args }) {
248
+ return runWithJsonErrors(() => {
249
+ const scope = parseProviderScope(args.scope)
250
+ const updated = useProvider(loadConfig(), scope, args.provider)
251
+ saveConfig(updated)
252
+ console.log(JSON.stringify(listConfig(updated), null, 2))
253
+ })
254
+ },
255
+ }),
83
256
  },
84
257
  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)
258
+ return runWithJsonErrors(() => {
259
+ if (hasConfigSubcommand(args)) return
260
+ if (args.list) {
261
+ console.log(JSON.stringify(listConfig(loadConfig()), null, 2))
262
+ return
263
+ }
264
+ if (args.get) {
265
+ console.log(JSON.stringify(getConfigValue(loadConfig(), args.get), null, 2))
266
+ return
267
+ }
268
+ if (args.unset) {
269
+ const updated = unsetConfigValue(loadConfig(), args.unset)
270
+ saveConfig(updated)
271
+ console.log(JSON.stringify(listConfig(updated), null, 2))
272
+ return
273
+ }
274
+ if (args.set) {
275
+ const eqIndex = args.set.indexOf("=")
276
+ if (eqIndex === -1) {
277
+ throw new Error("--set expects key=value format")
278
+ }
279
+ const key = args.set.slice(0, eqIndex)
280
+ const value = args.set.slice(eqIndex + 1)
281
+ const partial = parseConfigValue(key, value)
282
+ const config = { ...loadConfig(), ...partial }
283
+ saveConfig(config)
284
+ console.log(JSON.stringify(listConfig(config), null, 2))
285
+ } else {
286
+ console.log(JSON.stringify(listConfig(loadConfig()), null, 2))
92
287
  }
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
- }
288
+ })
289
+ },
290
+ })
291
+
292
+ const cloneCommand = defineCommand({
293
+ meta: { name: "clone", description: "Clone an asset from any stash source into the working stash" },
294
+ args: {
295
+ ref: { type: "positional", description: "Asset ref (e.g. @installed:pkg/tool:script.sh)", required: true },
296
+ name: { type: "string", description: "New name for the cloned asset" },
297
+ force: { type: "boolean", description: "Overwrite if asset already exists in working stash", default: false },
298
+ },
299
+ async run({ args }) {
300
+ await runWithJsonErrors(async () => {
301
+ const result = await agentikitClone({
302
+ sourceRef: args.ref,
303
+ newName: args.name,
304
+ force: args.force,
305
+ })
306
+ console.log(JSON.stringify(result, null, 2))
307
+ })
308
+ },
309
+ })
310
+
311
+
312
+ const sourcesCommand = defineCommand({
313
+ meta: { name: "sources", description: "List all stash sources with their kind, path, and status" },
314
+ run() {
315
+ return runWithJsonErrors(() => {
316
+ const sources = resolveStashSources()
317
+ console.log(JSON.stringify({ sources }, null, 2))
318
+ })
102
319
  },
103
320
  })
104
321
 
105
322
  const main = defineCommand({
106
323
  meta: {
107
324
  name: "akm",
108
- description: "CLI tool to search, open, and run extension assets from an agentikit stash directory.",
325
+ description: "CLI tool to search, open, and manage assets from Agent-i-Kit stash.",
109
326
  },
110
327
  subCommands: {
111
328
  init: initCommand,
112
329
  index: indexCommand,
330
+ add: addCommand,
331
+ list: listCommand,
332
+ remove: removeCommand,
333
+ update: updateCommand,
334
+ reinstall: reinstallCommand,
113
335
  search: searchCommand,
114
336
  show: showCommand,
337
+ clone: cloneCommand,
338
+ sources: sourcesCommand,
115
339
  config: configCommand,
116
340
  },
117
341
  })
118
342
 
343
+ const SEARCH_USAGE_MODES: SearchUsageMode[] = ["none", "both", "item", "guide"]
344
+ const SEARCH_SOURCES: SearchSource[] = ["local", "registry", "both"]
345
+ const CONFIG_SUBCOMMAND_SET = new Set(["list", "get", "set", "unset", "providers", "use"])
346
+
347
+ // Note: citty reads process.argv directly, so we must normalize it in-place.
348
+ // This is done once at startup before runMain.
349
+ normalizeConfigArgv(process.argv)
119
350
  runMain(main)
120
351
 
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
352
+ function parseSearchUsageMode(value: string): SearchUsageMode {
353
+ if ((SEARCH_USAGE_MODES as string[]).includes(value)) return value as SearchUsageMode
354
+ throw new Error(`Invalid value for --usage: ${value}. Expected one of: ${SEARCH_USAGE_MODES.join("|")}`)
355
+ }
356
+
357
+ function parseSearchSource(value: string): SearchSource {
358
+ if ((SEARCH_SOURCES as string[]).includes(value)) return value as SearchSource
359
+ throw new Error(`Invalid value for --source: ${value}. Expected one of: ${SEARCH_SOURCES.join("|")}`)
360
+ }
361
+
362
+ async function runWithJsonErrors(fn: (() => void) | (() => Promise<void>)): Promise<void> {
129
363
  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
- )
364
+ await fn()
365
+ } catch (error: unknown) {
366
+ const message = error instanceof Error ? error.message : String(error)
367
+ const hint = buildHint(message)
368
+ console.error(JSON.stringify({ ok: false, error: message, hint }, null, 2))
369
+ process.exit(1)
136
370
  }
137
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
138
- throw new Error(`Invalid value for ${key}: expected a JSON object`)
371
+ }
372
+
373
+ function buildHint(message: string): string | undefined {
374
+ if (message.includes("AKM_STASH_DIR")) return "Run `akm init` or set AKM_STASH_DIR to a valid directory."
375
+ if (message.includes("Either <target> or --all is required")) return "Use `akm update --all` or pass a target like `akm update npm:@scope/pkg`."
376
+ if (message.includes("Specify either <target> or --all")) return "Use only one: a positional target or `--all`."
377
+ 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."
378
+ if (message.includes("Invalid value for --source")) return "Pick one of: local, registry, both."
379
+ if (message.includes("Invalid value for --usage")) return "Pick one of: none, both, item, guide."
380
+ if (message.includes("expected JSON object with endpoint and model")) {
381
+ return "Quote JSON values in your shell, for example: akm config set embedding '{\"endpoint\":\"http://localhost:11434/v1/embeddings\",\"model\":\"nomic-embed-text\"}'."
139
382
  }
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`)
383
+ return undefined
384
+ }
385
+
386
+ function parseProviderScope(value: string): "embedding" | "llm" {
387
+ if (value === "embedding" || value === "llm") return value
388
+ throw new Error(`Invalid provider scope: ${value}. Expected one of: embedding|llm`)
389
+ }
390
+
391
+ function hasConfigSubcommand(args: Record<string, unknown>): boolean {
392
+ const command = Array.isArray(args._) ? args._[0] : undefined
393
+ return typeof command === "string" && CONFIG_SUBCOMMAND_SET.has(command)
394
+ }
395
+
396
+ /**
397
+ * Mutate argv before citty parses it so git-style config forms like
398
+ * `akm config llm.maxTokens 512` and `akm config --get llm.maxTokens`
399
+ * are normalized into the existing config subcommands.
400
+ */
401
+ function normalizeConfigArgv(argv: string[]): void {
402
+ const [, , command, argAfterCommand, argAfterKey, ...rest] = argv
403
+ if (command !== "config") return
404
+ if (!argAfterCommand) return
405
+ if (argAfterCommand === "--list") {
406
+ argv.splice(3, argv.length - 3, "list")
407
+ return
143
408
  }
144
- const result: { endpoint: string; model: string; apiKey?: string } = {
145
- endpoint: obj.endpoint,
146
- model: obj.model,
409
+ if (argAfterCommand === "--get" && argAfterKey) {
410
+ argv.splice(3, argv.length - 3, "get", argAfterKey, ...rest)
411
+ return
147
412
  }
148
- if (typeof obj.apiKey === "string" && obj.apiKey) {
149
- result.apiKey = obj.apiKey
413
+ if (argAfterCommand === "--unset" && argAfterKey) {
414
+ argv.splice(3, argv.length - 3, "unset", argAfterKey, ...rest)
415
+ return
150
416
  }
151
- return result
152
- }
417
+ if (argAfterCommand.startsWith("-")) return
418
+ if (CONFIG_SUBCOMMAND_SET.has(argAfterCommand)) return
153
419
 
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}`)
420
+ // A single arg after `config` behaves like `git config <key>` and reads the value.
421
+ if (argAfterKey === undefined) {
422
+ argv.splice(3, argv.length - 3, "get", argAfterCommand)
423
+ return
175
424
  }
425
+
426
+ argv.splice(3, argv.length - 3, "set", argAfterCommand, argAfterKey, ...rest)
176
427
  }
package/src/common.ts CHANGED
@@ -4,7 +4,7 @@ import { TYPE_DIRS } from "./asset-spec"
4
4
 
5
5
  // ── Types ───────────────────────────────────────────────────────────────────
6
6
 
7
- export type AgentikitAssetType = "tool" | "skill" | "command" | "agent" | "knowledge"
7
+ export type AgentikitAssetType = "tool" | "skill" | "command" | "agent" | "knowledge" | "script"
8
8
 
9
9
  // ── Constants ───────────────────────────────────────────────────────────────
10
10
 
@@ -20,19 +20,19 @@ export function isAssetType(type: string): type is AgentikitAssetType {
20
20
  // ── Utilities ───────────────────────────────────────────────────────────────
21
21
 
22
22
  export function resolveStashDir(): string {
23
- const raw = process.env.AGENTIKIT_STASH_DIR?.trim()
23
+ const raw = process.env.AKM_STASH_DIR?.trim()
24
24
  if (!raw) {
25
- throw new Error("AGENTIKIT_STASH_DIR is not set. Set it to your Agentikit stash path.")
25
+ throw new Error("AKM_STASH_DIR is not set. Set it to your Agentikit stash path.")
26
26
  }
27
27
  const stashDir = path.resolve(raw)
28
28
  let stat: fs.Stats
29
29
  try {
30
30
  stat = fs.statSync(stashDir)
31
31
  } catch {
32
- throw new Error(`Unable to read AGENTIKIT_STASH_DIR at "${stashDir}".`)
32
+ throw new Error(`Unable to read AKM_STASH_DIR at "${stashDir}".`)
33
33
  }
34
34
  if (!stat.isDirectory()) {
35
- throw new Error(`AGENTIKIT_STASH_DIR must point to a directory: "${stashDir}".`)
35
+ throw new Error(`AKM_STASH_DIR must point to a directory: "${stashDir}".`)
36
36
  }
37
37
  return stashDir
38
38
  }
@@ -56,3 +56,21 @@ export function isWithin(candidate: string, root: string): boolean {
56
56
  function normalizeFsPathForComparison(value: string): string {
57
57
  return process.platform === "win32" ? value.toLowerCase() : value
58
58
  }
59
+
60
+ /**
61
+ * Fetch with an AbortController timeout.
62
+ * Defaults to 30 seconds if no timeout is specified.
63
+ */
64
+ export async function fetchWithTimeout(
65
+ url: string,
66
+ opts?: RequestInit,
67
+ timeoutMs = 30_000,
68
+ ): Promise<Response> {
69
+ const controller = new AbortController()
70
+ const timer = setTimeout(() => controller.abort(), timeoutMs)
71
+ try {
72
+ return await fetch(url, { ...opts, signal: controller.signal })
73
+ } finally {
74
+ clearTimeout(timer)
75
+ }
76
+ }