akm-cli 0.2.0 → 0.2.2

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/LICENSE CHANGED
@@ -2,7 +2,6 @@ Mozilla Public License Version 2.0
2
2
  ==================================
3
3
 
4
4
  1. Definitions
5
-
6
5
  --------------
7
6
 
8
7
  1.1. "Contributor"
@@ -84,8 +83,7 @@ Mozilla Public License Version 2.0
84
83
  fifty percent (50%) of the outstanding shares or beneficial
85
84
  ownership of such entity.
86
85
 
87
- 1. License Grants and Conditions
88
-
86
+ 2. License Grants and Conditions
89
87
  --------------------------------
90
88
 
91
89
  2.1. Grants
@@ -156,8 +154,7 @@ equivalents.
156
154
  Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
157
155
  in Section 2.1.
158
156
 
159
- 1. Responsibilities
160
-
157
+ 3. Responsibilities
161
158
  -------------------
162
159
 
163
160
  3.1. Distribution of Source Form
@@ -219,8 +216,7 @@ indemnity or liability terms You offer. You may include additional
219
216
  disclaimers of warranty and limitations of liability specific to any
220
217
  jurisdiction.
221
218
 
222
- 1. Inability to Comply Due to Statute or Regulation
223
-
219
+ 4. Inability to Comply Due to Statute or Regulation
224
220
  ---------------------------------------------------
225
221
 
226
222
  If it is impossible for You to comply with any of the terms of this
@@ -229,12 +225,12 @@ statute, judicial order, or regulation then You must: (a) comply with
229
225
  the terms of this License to the maximum extent possible; and (b)
230
226
  describe the limitations and the code they affect. Such description must
231
227
  be placed in a text file included with all distributions of the Covered
232
- Software under this License. Except to the extent prohibited by statute
233
- or regulation, such description must be sufficiently detailed for a
234
- recipient of ordinary skill to be able to understand it.
235
-
236
- 1. Termination
228
+ Software under the name "LEGAL", with additions for new restrictions
229
+ placed at the end of the file. Except to the extent prohibited by
230
+ statute or regulation, such description must be sufficiently detailed
231
+ for a recipient of ordinary skill to be able to understand it.
237
232
 
233
+ 5. Termination
238
234
  --------------
239
235
 
240
236
  5.1. The rights granted under this License will terminate automatically
@@ -264,53 +260,48 @@ have been validly granted by You or Your distributors under this License
264
260
  prior to termination shall survive termination.
265
261
 
266
262
  ************************************************************************
267
-
268
263
  * *
269
- * 1. Disclaimer of Warranty *
270
- * ------------------------- *
264
+ * 6. Disclaimer of Warranty *
265
+ * ------------------------- *
271
266
  * *
272
- * Covered Software is provided under this License on an "as is" *
273
- * basis, without warranty of any kind, either expressed, implied, or *
274
- * statutory, including, without limitation, warranties that the *
275
- * Covered Software is free of defects, merchantable, fit for a *
276
- * particular purpose or non-infringing. The entire risk as to the *
277
- * quality and performance of the Covered Software is with You. *
278
- * Should any Covered Software prove defective in any respect, You *
279
- * (not any Contributor) assume the cost of any necessary servicing, *
280
- * repair, or correction. This disclaimer of warranty constitutes an *
281
- * essential part of this License. No use of any Covered Software is *
282
- * authorized under this License except under this disclaimer. *
267
+ * Covered Software is provided under this License on an "as is" *
268
+ * basis, without warranty of any kind, either expressed, implied, or *
269
+ * statutory, including, without limitation, warranties that the *
270
+ * Covered Software is free of defects, merchantable, fit for a *
271
+ * particular purpose or non-infringing. The entire risk as to the *
272
+ * quality and performance of the Covered Software is with You. *
273
+ * Should any Covered Software prove defective in any respect, You *
274
+ * (not any Contributor) assume the cost of any necessary servicing, *
275
+ * repair, or correction. This disclaimer of warranty constitutes an *
276
+ * essential part of this License. No use of any Covered Software is *
277
+ * authorized under this License except under this disclaimer. *
283
278
  * *
284
-
285
279
  ************************************************************************
286
280
 
287
281
  ************************************************************************
288
-
289
282
  * *
290
- * 1. Limitation of Liability *
291
- * -------------------------- *
283
+ * 7. Limitation of Liability *
284
+ * -------------------------- *
292
285
  * *
293
- * Under no circumstances and under no legal theory, whether tort *
294
- * (including negligence), contract, or otherwise, shall any *
295
- * Contributor, or anyone who distributes Covered Software as *
296
- * permitted above, be liable to You for any direct, indirect, *
297
- * special, incidental, or consequential damages of any character *
298
- * including, without limitation, damages for lost profits, loss of *
299
- * goodwill, work stoppage, computer failure or malfunction, or any *
300
- * and all other commercial damages or losses, even if such party *
301
- * shall have been informed of the possibility of such damages. This *
302
- * limitation of liability shall not apply to liability for death or *
303
- * personal injury resulting from such party's negligence to the *
304
- * extent applicable law prohibits such limitation. Some *
305
- * jurisdictions do not allow the exclusion or limitation of *
306
- * incidental or consequential damages, so this exclusion and *
307
- * limitation may not apply to You. *
286
+ * Under no circumstances and under no legal theory, whether tort *
287
+ * (including negligence), contract, or otherwise, shall any *
288
+ * Contributor, or anyone who distributes Covered Software as *
289
+ * permitted above, be liable to You for any direct, indirect, *
290
+ * special, incidental, or consequential damages of any character *
291
+ * including, without limitation, damages for lost profits, loss of *
292
+ * goodwill, work stoppage, computer failure or malfunction, or any *
293
+ * and all other commercial damages or losses, even if such party *
294
+ * shall have been informed of the possibility of such damages. This *
295
+ * limitation of liability shall not apply to liability for death or *
296
+ * personal injury resulting from such party's negligence to the *
297
+ * extent applicable law prohibits such limitation. Some *
298
+ * jurisdictions do not allow the exclusion or limitation of *
299
+ * incidental or consequential damages, so this exclusion and *
300
+ * limitation may not apply to You. *
308
301
  * *
309
-
310
302
  ************************************************************************
311
303
 
312
- 1. Litigation
313
-
304
+ 8. Litigation
314
305
  -------------
315
306
 
316
307
  Any litigation relating to this License may be brought only in the
@@ -320,8 +311,7 @@ jurisdiction, without reference to its conflict-of-law provisions.
320
311
  Nothing in this Section shall prevent a party's ability to bring
321
312
  cross-claims or counter-claims.
322
313
 
323
- 1. Miscellaneous
324
-
314
+ 9. Miscellaneous
325
315
  ----------------
326
316
 
327
317
  This License represents the complete agreement concerning the subject
@@ -331,8 +321,7 @@ necessary to make it enforceable. Any law or regulation which provides
331
321
  that the language of a contract shall be construed against the drafter
332
322
  shall not be used to construe this License against a Contributor.
333
323
 
334
- 1. Versions of the License
335
-
324
+ 10. Versions of the License
336
325
  ---------------------------
337
326
 
338
327
  10.1. New Versions
@@ -369,7 +358,7 @@ Exhibit A - Source Code Form License Notice
369
358
 
370
359
  This Source Code Form is subject to the terms of the Mozilla Public
371
360
  License, v. 2.0. If a copy of the MPL was not distributed with this
372
- file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
361
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
373
362
 
374
363
  If it is not possible or desirable to put the notice in a particular
375
364
  file, then You may include the notice in a location (such as a LICENSE
package/README.md CHANGED
@@ -1,112 +1,53 @@
1
- # Agent Kit Manager
1
+ # akm -- Agent Kit Manager
2
2
 
3
- > **akm** Agent Kit Manager
3
+ > **akm** (Agent Kit Manager) -- A package manager for AI agent skills, commands, tools, and knowledge.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/akm-cli)](https://www.npmjs.com/package/akm-cli)
6
- [![CI](https://github.com/itlackey/agentikit/actions/workflows/ci.yml/badge.svg)](https://github.com/itlackey/agentikit/actions/workflows/ci.yml)
7
- [![license](https://img.shields.io/npm/l/akm-cli)](LICENSE)
6
+ [![npm downloads](https://img.shields.io/npm/dm/akm-cli)](https://www.npmjs.com/package/akm-cli)
7
+ [![license](https://img.shields.io/github/license/itlackey/akm)](https://github.com/itlackey/akm/blob/main/LICENSE)
8
8
 
9
- A package manager for AI agent capabilities -- scripts, skills, commands,
10
- agents, knowledge, and memories -- that works with any AI coding assistant that
11
- can run shell commands.
9
+ `akm` is a package manager for AI agent capabilities -- scripts, skills, commands,
10
+ agents, knowledge, and memories. It works with any AI coding assistant that can
11
+ run shell commands, including [Claude Code](https://claude.ai/code),
12
+ [OpenCode](https://opencode.ai), [Cursor](https://cursor.com), and more.
12
13
 
13
14
  ## Install
14
15
 
15
16
  ```sh
16
- # Standalone binary (no runtime dependencies)
17
- curl -fsSL https://raw.githubusercontent.com/itlackey/agentikit/main/install.sh | bash
18
-
19
- # Or via Bun
20
17
  bun install -g akm-cli
21
18
  ```
22
19
 
23
- Upgrade in place with `akm upgrade`.
20
+ Requires [Bun](https://bun.sh) runtime. Upgrade in place with `akm upgrade`.
24
21
 
25
22
  ## Quick Start
26
23
 
27
24
  ```sh
28
25
  akm setup # Guided setup: configure, initialize, and index
29
26
  akm add github:owner/repo # Add a kit from GitHub
30
- akm search "deploy" # Find assets
27
+ akm search "deploy" # Find assets across all sources
31
28
  akm show script:deploy.sh # View details and run command
32
29
  ```
33
30
 
34
- If you want to skip the wizard, `akm init --dir ~/custom-stash` initializes the
35
- working stash at a custom path.
31
+ ## Why akm?
36
32
 
37
- ## Features
33
+ - **Works with any AI agent** -- No plugins or SDKs required. Any model that can run shell commands can use `akm`.
34
+ - **One command to search everything** -- Local stash, registries, and community skills from [skills.sh](https://skills.sh) in a single query.
35
+ - **Install kits from anywhere** -- npm, GitHub, GitLab, local directories.
36
+ - **Semantic search** -- Optional local embeddings (via Ollama or HuggingFace) for finding assets by meaning, not just keywords.
37
+ - **Private registries** -- Host your own registry for team or enterprise use.
38
38
 
39
- ### Works with Any AI Agent
39
+ ## Agent Integration
40
40
 
41
- Any model that can run shell commands can use `akm`. Add this to your
42
- `AGENTS.md`, `CLAUDE.md`, or system prompt:
41
+ Add this to your `AGENTS.md`, `CLAUDE.md`, or system prompt:
43
42
 
44
- ~~~markdown
43
+ ```markdown
45
44
  ## Resources & Capabilities
46
45
 
47
46
  You have access to a searchable library of scripts, skills, commands, agents,
48
47
  knowledge, and memories via the `akm` CLI. Use `akm -h` for details.
49
- ~~~
50
-
51
- No plugins, SDKs, or integration code required. Platform-specific plugins
52
- (e.g., [OpenCode](https://github.com/itlackey/akm-plugins?tab=readme-ov-file#opencode))
53
- are available for tighter integration but purely optional.
54
-
55
- ### Clone Assets Anywhere
56
-
57
- `akm clone` copies any asset from your stash or a remote source into a
58
- target directory for local editing:
59
-
60
- ```sh
61
- akm clone script:deploy.sh # Clone to your stash
62
- akm clone script:deploy.sh --dest ./project/.claude # Clone to a specific directory
63
- akm clone script:deploy.sh --name my-deploy.sh # Clone with a new name
64
- akm clone "npm:@scope/pkg//script:deploy.sh" --force # Clone from a remote package
65
- ```
66
-
67
- Key behaviors:
68
- - Type subdirectories are appended automatically (e.g., `--dest ./project/.claude` becomes `./project/.claude/scripts/deploy.sh`)
69
- - Skills clone as entire directories; scripts/commands clone as single files
70
- - Remote packages are fetched on-demand without registering as installed kits
71
- - `--force` overwrites existing assets
72
-
73
- ### skills.sh Integration
74
-
75
- `akm` includes [skills.sh](https://skills.sh) as a built-in registry. Community
76
- skills from skills.sh are searchable out of the box alongside the official
77
- registry -- no setup required:
78
-
79
- ```sh
80
- akm search "code review" # Searches skills.sh and official registry
81
- akm registry search "code review" # Search registries directly
82
48
  ```
83
49
 
84
- Results include install counts and link back to skills.sh for details. The
85
- provider caches queries for 15 minutes with a 24-hour stale fallback.
86
-
87
- ### Registries and Private Registry Support
88
-
89
- Registries are indexes of available kits. The official
90
- [akm-registry](https://github.com/itlackey/akm-registry) is pre-configured.
91
-
92
- ```sh
93
- akm registry search "code review" # Search registries
94
- akm registry add https://example.com/registry/index.json --name team # Add a registry
95
- akm stash add http://host:1933 --provider openviking \
96
- --options '{"apiKey":"key"}' # Add an OpenViking stash source
97
- akm registry list # List configured registries
98
- akm show viking://resources/my-doc # Fetch remote content from OpenViking
99
- ```
100
-
101
- Private access is supported through:
102
- - **GitHub tokens** -- Set `GITHUB_TOKEN` to access private GitHub repos when installing kits
103
- - **Provider options** -- `--options` flag accepts JSON for provider-specific configuration (API keys, custom headers)
104
- - **Pluggable providers** -- Built-in registry providers include `static-index` and `skills-sh`; stash providers include `filesystem` and `openviking`; custom providers can implement their own authentication
105
-
106
- See the [Registry docs](docs/registry.md) for hosting your own registry and
107
- the v2 index format.
108
-
109
- ### Install Kits from Anywhere
50
+ ## Install Kits from Anywhere
110
51
 
111
52
  ```sh
112
53
  akm add @scope/my-kit # npm
@@ -117,26 +58,23 @@ akm add ./path/to/local/kit # Local directory
117
58
 
118
59
  Manage kits with `akm list`, `akm update --all`, and `akm remove`.
119
60
 
120
- ### Publish Your Own Kit
61
+ ## Publish Your Own Kit
121
62
 
122
63
  1. Organize your assets into a directory
123
- 2. Add `"akm"` to `keywords` in `package.json` or the `akm` topic to your GitHub repo
64
+ 2. Add `"akm"` to `keywords` in `package.json`
124
65
  3. Optionally add `akm.include` in `package.json` to control what gets installed
125
66
  4. Publish to npm or push to GitHub
126
67
 
127
- See the [Kit Maker's Guide](docs/kit-makers.md) for a full walkthrough.
128
-
129
68
  ## Documentation
130
69
 
131
- | Doc | Description |
132
- | --- | --- |
133
- | [Getting Started](docs/getting-started.md) | Quick setup guide |
134
- | [CLI Reference](docs/cli.md) | All commands and flags |
135
- | [Configuration](docs/configuration.md) | Settings, providers, and Ollama setup |
136
- | [Concepts](docs/concepts.md) | Stashes, kits, registries, asset types |
137
- | [Kit Maker's Guide](docs/kit-makers.md) | Build and share kits |
138
- | [Registry](docs/registry.md) | Registries, search, and the v2 index format |
70
+ Full docs, CLI reference, and guides are available on [GitHub](https://github.com/itlackey/akm):
71
+
72
+ - [Getting Started](https://github.com/itlackey/akm/blob/main/docs/getting-started.md)
73
+ - [CLI Reference](https://github.com/itlackey/akm/blob/main/docs/cli.md)
74
+ - [Configuration](https://github.com/itlackey/akm/blob/main/docs/configuration.md)
75
+ - [Kit Maker's Guide](https://github.com/itlackey/akm/blob/main/docs/kit-makers.md)
76
+ - [Registry](https://github.com/itlackey/akm/blob/main/docs/registry.md)
139
77
 
140
78
  ## License
141
79
 
142
- [MPL-2.0](LICENSE)
80
+ [MPL-2.0](https://github.com/itlackey/akm/blob/main/LICENSE)
package/dist/cli.js CHANGED
@@ -283,7 +283,13 @@ function formatPlain(command, result, detail) {
283
283
  return out;
284
284
  }
285
285
  case "index": {
286
- return `Indexed ${r.totalEntries ?? 0} entries from ${r.directoriesScanned ?? 0} directories (mode: ${r.mode ?? "unknown"})`;
286
+ const indexResult = result;
287
+ let out = `Indexed ${indexResult.totalEntries ?? 0} entries from ${indexResult.directoriesScanned ?? 0} directories (mode: ${indexResult.mode ?? "unknown"})`;
288
+ const verification = indexResult.verification;
289
+ if (verification?.ok === false && verification.message) {
290
+ out += `\nVerification: ${String(verification.message)}`;
291
+ }
292
+ return out;
287
293
  }
288
294
  case "show": {
289
295
  const lines = [];
@@ -481,10 +487,14 @@ const indexCommand = defineCommand({
481
487
  meta: { name: "index", description: "Build search index (incremental by default; --full forces full reindex)" },
482
488
  args: {
483
489
  full: { type: "boolean", description: "Force full reindex", default: false },
490
+ verbose: { type: "boolean", description: "Print indexing summary and phase progress to stderr", default: false },
484
491
  },
485
492
  async run({ args }) {
486
493
  await runWithJsonErrors(async () => {
487
- const result = await akmIndex({ full: args.full });
494
+ const result = await akmIndex({
495
+ full: args.full,
496
+ onProgress: args.verbose ? ({ message }) => console.error(`[index] ${message}`) : undefined,
497
+ });
488
498
  output("index", result);
489
499
  });
490
500
  },
package/dist/common.js CHANGED
@@ -5,6 +5,9 @@ import { ConfigError } from "./errors";
5
5
  import { getConfigPath, getDefaultStashDir } from "./paths";
6
6
  // ── Constants ───────────────────────────────────────────────────────────────
7
7
  export const IS_WINDOWS = process.platform === "win32";
8
+ export function isHttpUrl(value) {
9
+ return !!value && /^https?:\/\//.test(value);
10
+ }
8
11
  // ── Validators ──────────────────────────────────────────────────────────────
9
12
  export function isAssetType(type) {
10
13
  return Object.hasOwn(TYPE_DIRS, type);
package/dist/config.js CHANGED
@@ -460,7 +460,7 @@ function parseRegistryConfigEntry(value) {
460
460
  return undefined;
461
461
  const obj = value;
462
462
  const url = asNonEmptyString(obj.url);
463
- if (!url || !url.startsWith("http"))
463
+ if (!url?.startsWith("http"))
464
464
  return undefined;
465
465
  const entry = { url };
466
466
  const name = asNonEmptyString(obj.name);
package/dist/db.js CHANGED
@@ -46,7 +46,7 @@ function loadVecExtension(db) {
46
46
  export function isVecAvailable(db) {
47
47
  return vecStatus.get(db) ?? false;
48
48
  }
49
- const VEC_DOCS_URL = "https://github.com/itlackey/agentikit/blob/main/docs/configuration.md#sqlite-vec-extension";
49
+ const VEC_DOCS_URL = "https://github.com/itlackey/akm/blob/main/docs/configuration.md#sqlite-vec-extension";
50
50
  const VEC_FALLBACK_THRESHOLD = 10_000;
51
51
  // Per-database warning state: tracks which databases have already emitted the
52
52
  // vec-missing warning so we don't spam on every openDatabase() call.
@@ -535,6 +535,10 @@ export function getEntryCount(db) {
535
535
  const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get();
536
536
  return row.cnt;
537
537
  }
538
+ export function getEmbeddingCount(db) {
539
+ const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
540
+ return row.cnt;
541
+ }
538
542
  export function getEntryById(db, id) {
539
543
  const row = db.prepare("SELECT file_path, entry_json FROM entries WHERE id = ?").get(id);
540
544
  if (!row)
package/dist/embedder.js CHANGED
@@ -1,4 +1,4 @@
1
- import { fetchWithTimeout } from "./common";
1
+ import { fetchWithTimeout, isHttpUrl } from "./common";
2
2
  import { warn } from "./warn";
3
3
  // ── Default local model ─────────────────────────────────────────────────────
4
4
  /**
@@ -32,11 +32,11 @@ async function getLocalEmbedder(modelName) {
32
32
  localEmbedderPromise = (async () => {
33
33
  let pipeline;
34
34
  try {
35
- const mod = await import("@xenova/transformers");
35
+ const mod = await import("@huggingface/transformers");
36
36
  pipeline = mod.pipeline;
37
37
  }
38
38
  catch {
39
- throw new Error("Semantic search requires @xenova/transformers. Install it with: npm install @xenova/transformers");
39
+ throw new Error("Semantic search requires @huggingface/transformers. Install it with: npm install @huggingface/transformers");
40
40
  }
41
41
  const pipelineFn = pipeline;
42
42
  return pipelineFn("feature-extraction", resolvedModel);
@@ -103,7 +103,7 @@ async function embedRemote(text, config) {
103
103
  // ── Helpers ──────────────────────────────────────────────────────────────────
104
104
  /** Check whether an EmbeddingConnectionConfig has a valid remote endpoint. */
105
105
  function hasRemoteEndpoint(config) {
106
- return !!config.endpoint && (config.endpoint.startsWith("http://") || config.endpoint.startsWith("https://"));
106
+ return isHttpUrl(config.endpoint);
107
107
  }
108
108
  // ── LRU embedding cache ─────────────────────────────────────────────────────
109
109
  // Caches query embeddings to avoid redundant computation for repeated queries.
@@ -133,7 +133,7 @@ export function clearEmbeddingCache() {
133
133
  /**
134
134
  * Generate an embedding for the given text.
135
135
  * If embeddingConfig has a remote endpoint, uses the configured OpenAI-compatible endpoint.
136
- * Otherwise falls back to local @xenova/transformers using the model from
136
+ * Otherwise falls back to local @huggingface/transformers using the model from
137
137
  * `embeddingConfig.localModel` or `DEFAULT_LOCAL_MODEL`.
138
138
  *
139
139
  * Results are cached in an LRU cache (max ~100 entries) keyed by query text
@@ -244,21 +244,57 @@ export function cosineSimilarity(a, b) {
244
244
  return denom === 0 ? 0 : dot / denom;
245
245
  }
246
246
  // ── Availability check ──────────────────────────────────────────────────────
247
- export async function isEmbeddingAvailable(embeddingConfig) {
247
+ /**
248
+ * Check whether the `@huggingface/transformers` package can be imported.
249
+ * Returns `true` if it can, `false` otherwise.
250
+ */
251
+ export async function isTransformersAvailable() {
252
+ try {
253
+ await import("@huggingface/transformers");
254
+ return true;
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
260
+ /**
261
+ * Check whether embedding is available with a detailed reason on failure.
262
+ */
263
+ export async function checkEmbeddingAvailability(embeddingConfig) {
248
264
  if (embeddingConfig && hasRemoteEndpoint(embeddingConfig)) {
249
265
  try {
250
266
  await embedRemote("test", embeddingConfig);
251
- return true;
267
+ return { available: true };
252
268
  }
253
- catch {
254
- return false;
269
+ catch (err) {
270
+ return {
271
+ available: false,
272
+ reason: "remote-unreachable",
273
+ message: err instanceof Error ? err.message : String(err),
274
+ };
255
275
  }
256
276
  }
277
+ // Check if the package is importable before attempting the model download.
278
+ if (!(await isTransformersAvailable())) {
279
+ return {
280
+ available: false,
281
+ reason: "missing-package",
282
+ message: "@huggingface/transformers is not installed.",
283
+ };
284
+ }
257
285
  try {
258
286
  await getLocalEmbedder(embeddingConfig?.localModel);
259
- return true;
287
+ return { available: true };
260
288
  }
261
- catch {
262
- return false;
289
+ catch (err) {
290
+ return {
291
+ available: false,
292
+ reason: "model-download-failed",
293
+ message: err instanceof Error ? err.message : String(err),
294
+ };
263
295
  }
264
296
  }
297
+ export async function isEmbeddingAvailable(embeddingConfig) {
298
+ const result = await checkEmbeddingAvailability(embeddingConfig);
299
+ return result.available;
300
+ }
package/dist/indexer.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { resolveStashDir } from "./common";
4
- import { closeDatabase, deleteEntriesByDir, deleteEntriesByStashDir, getEntriesByDir, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, setMeta, upsertEmbedding, upsertEntry, upsertUtilityScore, warnIfVecMissing, } from "./db";
3
+ import { isHttpUrl, resolveStashDir } from "./common";
4
+ import { closeDatabase, deleteEntriesByDir, deleteEntriesByStashDir, getEmbeddingCount, getEntriesByDir, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, setMeta, upsertEmbedding, upsertEntry, upsertUtilityScore, warnIfVecMissing, } from "./db";
5
5
  import { generateMetadataFlat, loadStashFile } from "./metadata";
6
6
  import { getDbPath } from "./paths";
7
7
  import { buildSearchText } from "./search-fields";
@@ -11,6 +11,7 @@ import { warn } from "./warn";
11
11
  // ── Indexer ──────────────────────────────────────────────────────────────────
12
12
  export async function akmIndex(options) {
13
13
  const stashDir = options?.stashDir || resolveStashDir();
14
+ const onProgress = options?.onProgress ?? (() => { });
14
15
  // Load config and resolve all stash sources
15
16
  const { loadConfig } = await import("./config.js");
16
17
  const config = loadConfig();
@@ -30,6 +31,17 @@ export async function akmIndex(options) {
30
31
  const prevBuiltAt = getMeta(db, "builtAt");
31
32
  const isIncremental = !options?.full && prevStashDir === stashDir && !!prevBuiltAt;
32
33
  const builtAtMs = isIncremental && prevBuiltAt ? new Date(prevBuiltAt).getTime() : 0;
34
+ onProgress({
35
+ phase: "summary",
36
+ message: buildIndexSummaryMessage({
37
+ mode: isIncremental ? "incremental" : "full",
38
+ stashSources: allStashDirs.length,
39
+ semanticSearch: config.semanticSearch,
40
+ embeddingProvider: getEmbeddingProvider(config.embedding),
41
+ llmEnabled: !!config.llm,
42
+ vecAvailable: isVecAvailable(db),
43
+ }),
44
+ });
33
45
  if (options?.full || !isIncremental) {
34
46
  // The delete is now merged into the insert transaction inside
35
47
  // indexEntries() so that a reader never sees an empty database between
@@ -67,17 +79,28 @@ export async function akmIndex(options) {
67
79
  // inserts so readers never see an empty database mid-rebuild.
68
80
  const doFullDelete = options?.full || !isIncremental;
69
81
  const { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm } = await indexEntries(db, allStashDirs, isIncremental, builtAtMs, doFullDelete);
82
+ onProgress({
83
+ phase: "scan",
84
+ message: `Scanned ${scannedDirs} ${scannedDirs === 1 ? "directory" : "directories"} and skipped ${skippedDirs}.`,
85
+ });
70
86
  const tWalkEnd = Date.now();
71
87
  // Enhance entries with LLM if configured
72
88
  await enhanceDirsWithLlm(db, config, dirsNeedingLlm);
89
+ onProgress({
90
+ phase: "llm",
91
+ message: config.llm
92
+ ? `LLM enhancement reviewed ${dirsNeedingLlm.length} ${dirsNeedingLlm.length === 1 ? "directory" : "directories"}.`
93
+ : "LLM enhancement disabled.",
94
+ });
73
95
  const tLlmEnd = Date.now();
74
96
  // Rebuild FTS after all inserts
75
97
  rebuildFts(db);
98
+ onProgress({ phase: "fts", message: "Rebuilt full-text search index." });
76
99
  const tFtsEnd = Date.now();
77
100
  // Recompute utility scores from usage_events after FTS rebuild
78
101
  recomputeUtilityScores(db);
79
102
  // Generate embeddings if semantic search is enabled
80
- const hasEmbeddings = await generateEmbeddingsForDb(db, config);
103
+ const hasEmbeddings = await generateEmbeddingsForDb(db, config, onProgress);
81
104
  const tEmbedEnd = Date.now();
82
105
  // Update metadata
83
106
  setMeta(db, "builtAt", new Date().toISOString());
@@ -88,6 +111,8 @@ export async function akmIndex(options) {
88
111
  // Warn on every index run if using JS fallback with many entries
89
112
  warnIfVecMissing(db);
90
113
  const tEnd = Date.now();
114
+ const verification = verifyIndexState(db, config, totalEntries);
115
+ onProgress({ phase: "verify", message: verification.message });
91
116
  return {
92
117
  stashDir,
93
118
  totalEntries,
@@ -96,6 +121,7 @@ export async function akmIndex(options) {
96
121
  mode: isIncremental ? "incremental" : "full",
97
122
  directoriesScanned: scannedDirs,
98
123
  directoriesSkipped: skippedDirs,
124
+ verification,
99
125
  timing: {
100
126
  totalMs: tEnd - t0,
101
127
  walkMs: tWalkEnd - tWalkStart,
@@ -256,14 +282,22 @@ async function enhanceDirsWithLlm(db, config, dirsNeedingLlm) {
256
282
  })();
257
283
  }
258
284
  }
259
- async function generateEmbeddingsForDb(db, config) {
260
- if (!config.semanticSearch)
285
+ async function generateEmbeddingsForDb(db, config, onProgress) {
286
+ if (!config.semanticSearch) {
287
+ onProgress({ phase: "embeddings", message: "Semantic search disabled; skipping embeddings." });
261
288
  return false;
289
+ }
262
290
  try {
263
291
  const { embedBatch } = await import("./embedder.js");
264
292
  const allEntries = getAllEntriesForEmbedding(db);
265
- if (allEntries.length === 0)
293
+ if (allEntries.length === 0) {
294
+ onProgress({ phase: "embeddings", message: "Embeddings already up to date." });
266
295
  return true;
296
+ }
297
+ onProgress({
298
+ phase: "embeddings",
299
+ message: `Generating embeddings for ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"}.`,
300
+ });
267
301
  const texts = allEntries.map((e) => e.searchText);
268
302
  const embeddings = await embedBatch(texts, config.embedding);
269
303
  // Wrap all embedding upserts in a single transaction so partial
@@ -273,10 +307,18 @@ async function generateEmbeddingsForDb(db, config) {
273
307
  upsertEmbedding(db, allEntries[i].id, embeddings[i]);
274
308
  }
275
309
  })();
310
+ onProgress({
311
+ phase: "embeddings",
312
+ message: `Stored ${embeddings.length} embedding${embeddings.length === 1 ? "" : "s"}.`,
313
+ });
276
314
  return true;
277
315
  }
278
316
  catch (error) {
279
317
  warn("Embedding generation failed, continuing without:", error instanceof Error ? error.message : String(error));
318
+ onProgress({
319
+ phase: "embeddings",
320
+ message: `Embedding generation failed: ${error instanceof Error ? error.message : String(error)}`,
321
+ });
280
322
  return false;
281
323
  }
282
324
  }
@@ -297,6 +339,69 @@ function attachFileSize(entry, entryPath) {
297
339
  return entry;
298
340
  }
299
341
  }
342
+ function buildIndexSummaryMessage(options) {
343
+ const stashSourceLabel = options.stashSources === 1 ? "stash source" : "stash sources";
344
+ const semanticDetail = getSemanticSearchLabel(options.semanticSearch, options.embeddingProvider, options.vecAvailable);
345
+ return `Starting ${options.mode} index (${options.stashSources} ${stashSourceLabel}, semantic search: ${semanticDetail}, LLM: ${options.llmEnabled ? "enabled" : "disabled"}).`;
346
+ }
347
+ function getEmbeddingProvider(embedding) {
348
+ return isHttpUrl(embedding?.endpoint) ? "remote" : "local";
349
+ }
350
+ function getSemanticSearchLabel(semanticSearch, embeddingProvider, vecAvailable) {
351
+ if (!semanticSearch)
352
+ return "disabled";
353
+ return `${embeddingProvider} embeddings, ${vecAvailable ? "sqlite-vec" : "JS fallback"}`;
354
+ }
355
+ function verifyIndexState(db, config, totalEntries) {
356
+ const embeddingCount = getEmbeddingCount(db);
357
+ const vecAvailable = isVecAvailable(db);
358
+ const embeddingProvider = getEmbeddingProvider(config.embedding);
359
+ if (totalEntries === 0) {
360
+ return {
361
+ ok: true,
362
+ message: "Index ready. No assets were found yet.",
363
+ semanticSearchEnabled: config.semanticSearch,
364
+ embeddingProvider,
365
+ entryCount: totalEntries,
366
+ embeddingCount,
367
+ vecAvailable,
368
+ };
369
+ }
370
+ if (!config.semanticSearch) {
371
+ return {
372
+ ok: true,
373
+ message: "Keyword index ready. Semantic search is disabled.",
374
+ semanticSearchEnabled: false,
375
+ embeddingProvider,
376
+ entryCount: totalEntries,
377
+ embeddingCount,
378
+ vecAvailable,
379
+ };
380
+ }
381
+ if (embeddingCount >= totalEntries) {
382
+ return {
383
+ ok: true,
384
+ message: `Semantic search ready (${embeddingCount}/${totalEntries} embeddings, ${vecAvailable ? "sqlite-vec active" : "JS fallback active"}).`,
385
+ semanticSearchEnabled: true,
386
+ embeddingProvider,
387
+ entryCount: totalEntries,
388
+ embeddingCount,
389
+ vecAvailable,
390
+ };
391
+ }
392
+ return {
393
+ ok: false,
394
+ message: `Semantic search verification failed (${embeddingCount}/${totalEntries} embeddings available).`,
395
+ guidance: embeddingProvider === "remote"
396
+ ? "Check your embedding endpoint and credentials, then retry `akm index --full --verbose`."
397
+ : "Retry `akm index --full --verbose`. If it still fails, confirm local model downloads are permitted and see docs/configuration.md for local embedding dependency setup.",
398
+ semanticSearchEnabled: true,
399
+ embeddingProvider,
400
+ entryCount: totalEntries,
401
+ embeddingCount,
402
+ vecAvailable,
403
+ };
404
+ }
300
405
  function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
301
406
  // Check if file set changed (additions or deletions)
302
407
  const prevFileNames = new Set(previousEntries.map((ie) => ie.entry.filename).filter((e) => !!e));
@@ -13,7 +13,7 @@ const DEFAULT_MANUAL_ENTRIES_PATH = path.resolve("manual-entries.json");
13
13
  const DEFAULT_OUTPUT_PATH = path.resolve("index.json");
14
14
  const REQUIRED_KEYWORDS = ["akm-kit"];
15
15
  const GITHUB_TOPICS = ["akm-kit"];
16
- const EXCLUDED_REPOS = new Set(["itlackey/agentikit"]);
16
+ const EXCLUDED_REPOS = new Set(["itlackey/akm"]);
17
17
  const EXCLUDED_NPM_PACKAGES = new Set(["akm-cli"]);
18
18
  const EMPTY_INSPECTION = {};
19
19
  export async function buildRegistryIndex(options) {
package/dist/renderers.js CHANGED
@@ -399,4 +399,4 @@ export function registerBuiltinRenderers() {
399
399
  }
400
400
  }
401
401
  // ── Named exports for testing ────────────────────────────────────────────────
402
- export { skillMdRenderer, commandMdRenderer, agentMdRenderer, knowledgeMdRenderer, memoryMdRenderer, scriptSourceRenderer, INTERPRETER_MAP, SETUP_SIGNALS, };
402
+ export { agentMdRenderer, commandMdRenderer, INTERPRETER_MAP, knowledgeMdRenderer, memoryMdRenderer, SETUP_SIGNALS, scriptSourceRenderer, skillMdRenderer, };
@@ -3,16 +3,35 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fetchWithRetry, IS_WINDOWS } from "./common";
5
5
  import { githubHeaders } from "./github";
6
- const REPO = "itlackey/agentikit";
7
- export function detectInstallMethod() {
8
- // Bun-compiled binaries: Bun.main equals process.execPath
9
- if (typeof Bun !== "undefined" && Bun.main === process.execPath) {
10
- return "binary";
11
- }
6
+ const REPO = "itlackey/akm";
7
+ /** Read live runtime signals. */
8
+ export function getInstallSignals() {
9
+ return {
10
+ bunMain: typeof Bun !== "undefined" ? Bun.main : undefined,
11
+ importMetaDir: import.meta.dir ?? undefined,
12
+ hasAkmVersion: typeof AKM_VERSION !== "undefined",
13
+ };
14
+ }
15
+ // AKM_VERSION ambient type is declared in globals.d.ts
16
+ export function detectInstallMethod(signals) {
17
+ const s = signals ?? getInstallSignals();
12
18
  // npm/bun global install: import.meta.dir contains node_modules
13
- if (import.meta.dir?.includes("node_modules")) {
19
+ if (s.importMetaDir?.includes("node_modules")) {
14
20
  return "npm";
15
21
  }
22
+ // Bun-compiled binaries: Bun.main points to a virtual /$bunfs/ path,
23
+ // NOT process.execPath. The old check (Bun.main === process.execPath) was
24
+ // always false for compiled binaries, causing "unknown" for every binary user.
25
+ if (s.bunMain !== undefined) {
26
+ // Primary check: compiled binaries embed sources under /$bunfs/
27
+ if (s.bunMain.startsWith("/$bunfs/")) {
28
+ return "binary";
29
+ }
30
+ // Secondary check: AKM_VERSION is defined only in compiled builds (via --define)
31
+ if (s.hasAkmVersion) {
32
+ return "binary";
33
+ }
34
+ }
16
35
  return "unknown";
17
36
  }
18
37
  export function getAkmBinaryName() {
@@ -79,7 +98,7 @@ export async function performUpgrade(check, opts) {
79
98
  };
80
99
  }
81
100
  if (!latestVersion) {
82
- throw new Error("Unable to determine latest version from GitHub releases. Check https://github.com/itlackey/agentikit/releases");
101
+ throw new Error("Unable to determine latest version from GitHub releases. Check https://github.com/itlackey/akm/releases");
83
102
  }
84
103
  const tag = `v${latestVersion}`;
85
104
  const binaryName = getAkmBinaryName();
package/dist/setup.js CHANGED
@@ -6,8 +6,11 @@
6
6
  * Collects all choices and writes config once at the end.
7
7
  */
8
8
  import * as p from "@clack/prompts";
9
+ import { isHttpUrl } from "./common";
9
10
  import { DEFAULT_CONFIG, getConfigPath, loadConfig, saveConfig } from "./config";
11
+ import { closeDatabase, isVecAvailable, openDatabase } from "./db";
10
12
  import { detectAgentPlatforms, detectOllama, detectOpenViking } from "./detect";
13
+ import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "./embedder";
11
14
  import { akmIndex } from "./indexer";
12
15
  import { akmInit } from "./init";
13
16
  import { getDefaultStashDir } from "./paths";
@@ -20,6 +23,11 @@ const RECOMMENDED_GITHUB_REPOS = [
20
23
  hint: "community knowledge",
21
24
  },
22
25
  ];
26
+ // Approximate first-download sizes used in the setup note.
27
+ // LOCAL_MODEL_APPROX_SIZE_MB tracks the default local model (DEFAULT_LOCAL_MODEL).
28
+ const LOCAL_MODEL_APPROX_SIZE_MB = 130;
29
+ // SQLITE_VEC_APPROX_SIZE_MB reflects the optional sqlite-vec install footprint.
30
+ const SQLITE_VEC_APPROX_SIZE_MB = 5;
23
31
  // ── Helpers ─────────────────────────────────────────────────────────────────
24
32
  function bail() {
25
33
  p.cancel("Setup cancelled. No changes were saved.");
@@ -70,6 +78,112 @@ async function promptOrBack(fn) {
70
78
  return null;
71
79
  return result;
72
80
  }
81
+ function isRemoteEmbeddingConfig(embedding) {
82
+ return isHttpUrl(embedding?.endpoint);
83
+ }
84
+ /**
85
+ * @internal Exported for testing only.
86
+ */
87
+ export function describeSemanticSearchAssets(embedding) {
88
+ if (isRemoteEmbeddingConfig(embedding)) {
89
+ return [
90
+ `• Embedding endpoint: ${embedding?.provider ?? "custom"} / ${embedding?.model} (no local model download)`,
91
+ `• sqlite-vec acceleration: optional native extension (~${SQLITE_VEC_APPROX_SIZE_MB} MB when installed separately)`,
92
+ ];
93
+ }
94
+ return [
95
+ `• Local embedding model: ${embedding?.localModel ?? DEFAULT_LOCAL_MODEL} (~${LOCAL_MODEL_APPROX_SIZE_MB} MB download on first use)`,
96
+ `• sqlite-vec acceleration: optional native extension (~${SQLITE_VEC_APPROX_SIZE_MB} MB when installed separately)`,
97
+ ];
98
+ }
99
+ export async function stepSemanticSearch(current, embedding) {
100
+ const enabled = await prompt(() => p.confirm({
101
+ message: "Enable semantic search?",
102
+ initialValue: current.semanticSearch,
103
+ }));
104
+ if (!enabled) {
105
+ return { enabled: false, prepareAssets: false };
106
+ }
107
+ p.note(describeSemanticSearchAssets(embedding).join("\n"), "Semantic Search Assets");
108
+ const prepareAssets = await prompt(() => p.confirm({
109
+ message: isRemoteEmbeddingConfig(embedding)
110
+ ? "Check the embedding endpoint and verify semantic search now?"
111
+ : "Download and verify semantic-search assets now?",
112
+ initialValue: true,
113
+ }));
114
+ return { enabled: true, prepareAssets };
115
+ }
116
+ async function prepareSemanticSearchAssets(config) {
117
+ const remote = isRemoteEmbeddingConfig(config.embedding);
118
+ // For local embeddings, ensure the required package is installed first.
119
+ if (!remote) {
120
+ if (!(await isTransformersAvailable())) {
121
+ const spin = p.spinner();
122
+ spin.start("Installing @huggingface/transformers...");
123
+ try {
124
+ const proc = Bun.spawn(["bun", "add", "@huggingface/transformers"], {
125
+ stdout: "pipe",
126
+ stderr: "pipe",
127
+ });
128
+ await proc.exited;
129
+ if (proc.exitCode !== 0) {
130
+ const stderr = await new Response(proc.stderr).text();
131
+ throw new Error(stderr || `exit code ${proc.exitCode}`);
132
+ }
133
+ spin.stop("@huggingface/transformers installed.");
134
+ }
135
+ catch (err) {
136
+ const msg = err instanceof Error ? err.message : String(err);
137
+ spin.stop("Could not install @huggingface/transformers.");
138
+ p.log.warn(`Automatic install failed: ${msg}\n` +
139
+ "Install it manually with: bun add @huggingface/transformers\n" +
140
+ "Then re-run `akm setup` or `akm index --full --verbose`.");
141
+ return false;
142
+ }
143
+ }
144
+ }
145
+ const spin = p.spinner();
146
+ spin.start(remote
147
+ ? "Checking remote embedding endpoint..."
148
+ : `Downloading local embedding model (${config.embedding?.localModel ?? DEFAULT_LOCAL_MODEL})...`);
149
+ const result = await checkEmbeddingAvailability(config.embedding);
150
+ if (!result.available) {
151
+ spin.stop("Semantic-search assets could not be prepared.");
152
+ if (result.reason === "remote-unreachable") {
153
+ p.log.warn("The remote embedding endpoint is not reachable. Check your endpoint and credentials, then retry `akm index --full --verbose`.");
154
+ }
155
+ else if (result.reason === "missing-package") {
156
+ p.log.warn("@huggingface/transformers is not installed. Install it with: bun add @huggingface/transformers\n" +
157
+ "Then re-run `akm setup` or `akm index --full --verbose`.");
158
+ }
159
+ else {
160
+ p.log.warn(`The local embedding model could not be downloaded: ${result.message}\n` +
161
+ "Retry `akm index --full --verbose` after confirming local model downloads are permitted.");
162
+ }
163
+ return false;
164
+ }
165
+ spin.stop(remote ? "Remote embedding endpoint is ready." : "Local embedding model downloaded and ready.");
166
+ let db;
167
+ try {
168
+ db = openDatabase();
169
+ if (isVecAvailable(db)) {
170
+ p.log.info("sqlite-vec is available for fast vector search.");
171
+ }
172
+ else {
173
+ p.log.info("sqlite-vec is not available. Semantic search will use the JS fallback until the optional extension is installed.");
174
+ }
175
+ }
176
+ catch (error) {
177
+ const message = error instanceof Error ? error.message : String(error);
178
+ p.log.warn(`Could not open the local database or check for sqlite-vec. Semantic search will use the JS fallback. (${message})\n` +
179
+ "Check file permissions and available disk space in the cache directory, or run `akm index --full --verbose` to diagnose.");
180
+ }
181
+ finally {
182
+ if (db)
183
+ closeDatabase(db);
184
+ }
185
+ return true;
186
+ }
73
187
  // ── Steps ───────────────────────────────────────────────────────────────────
74
188
  async function stepStashDir(current) {
75
189
  const defaultDir = current.stashDir ?? getDefaultStashDir();
@@ -431,14 +545,17 @@ export async function runSetupWizard() {
431
545
  // Step 2: Ollama / Embedding / LLM
432
546
  p.log.step("Step 2: Embedding & LLM");
433
547
  const { embedding, llm } = await stepOllama(current);
434
- // Step 3: Registries
435
- p.log.step("Step 3: Registries");
548
+ // Step 3: Semantic search assets
549
+ p.log.step("Step 3: Semantic Search");
550
+ const semanticSearch = await stepSemanticSearch(current, embedding);
551
+ // Step 4: Registries
552
+ p.log.step("Step 4: Registries");
436
553
  const registries = await stepRegistries(current);
437
- // Step 4: Stash sources
438
- p.log.step("Step 4: Stash Sources");
554
+ // Step 5: Stash sources
555
+ p.log.step("Step 5: Stash Sources");
439
556
  const stashes = await stepStashSources(current);
440
- // Step 5: Agent platform detection
441
- p.log.step("Step 5: Agent Platform Detection");
557
+ // Step 6: Agent platform detection
558
+ p.log.step("Step 6: Agent Platform Detection");
442
559
  const platformStashes = await stepAgentPlatforms(current);
443
560
  // Merge platform stashes into main stashes list
444
561
  const allStashes = [...stashes];
@@ -456,7 +573,7 @@ export async function runSetupWizard() {
456
573
  registries,
457
574
  stashes: allStashes.length > 0 ? allStashes : undefined,
458
575
  // Preserve existing fields
459
- semanticSearch: current.semanticSearch,
576
+ semanticSearch: semanticSearch.enabled,
460
577
  installed: current.installed,
461
578
  output: current.output,
462
579
  };
@@ -466,6 +583,7 @@ export async function runSetupWizard() {
466
583
  `Stash directory: ${stashDir}`,
467
584
  `Embedding: ${embedding ? `${embedding.provider ?? "remote"} / ${embedding.model}` : "built-in local"}`,
468
585
  `LLM: ${llm ? `${llm.provider ?? "remote"} / ${llm.model}` : "disabled"}`,
586
+ `Semantic search: ${semanticSearch.enabled ? "enabled" : "disabled"}`,
469
587
  `Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
470
588
  `Stash sources: ${allStashes.length}`,
471
589
  ].join("\n"), "Configuration Summary");
@@ -479,12 +597,38 @@ export async function runSetupWizard() {
479
597
  saveConfig(newConfig);
480
598
  // Initialize stash directory
481
599
  await akmInit({ dir: stashDir });
600
+ if (semanticSearch.enabled) {
601
+ if (semanticSearch.prepareAssets) {
602
+ const ready = await prepareSemanticSearchAssets(newConfig);
603
+ if (!ready) {
604
+ // Asset preparation failed: disable semantic search and persist the update.
605
+ newConfig.semanticSearch = false;
606
+ saveConfig(newConfig);
607
+ p.log.warn("Semantic search has been disabled in the saved configuration. Re-run `akm setup` or `akm index --full --verbose` once the issue is resolved.");
608
+ }
609
+ }
610
+ else {
611
+ p.log.info("Semantic search will be enabled, but asset preparation was skipped. Run `akm index --full --verbose` later to verify it.");
612
+ }
613
+ }
482
614
  // Build search index
615
+ p.log.info("Building search index...");
483
616
  const spin = p.spinner();
484
617
  spin.start("Building search index...");
485
618
  try {
486
619
  const indexResult = await akmIndex({ stashDir });
487
620
  spin.stop(`Indexed ${indexResult.totalEntries} assets.`);
621
+ if (newConfig.semanticSearch) {
622
+ if (indexResult.verification.ok) {
623
+ p.log.success(indexResult.verification.message);
624
+ }
625
+ else {
626
+ p.log.warn(indexResult.verification.message);
627
+ if (indexResult.verification.guidance) {
628
+ p.log.info(indexResult.verification.guidance);
629
+ }
630
+ }
631
+ }
488
632
  }
489
633
  catch (err) {
490
634
  spin.stop("Indexing failed — you can run `akm index` manually later.");
@@ -137,4 +137,4 @@ function parseGitRepoUrl(rawUrl) {
137
137
  };
138
138
  }
139
139
  // ── Exports ─────────────────────────────────────────────────────────────────
140
- export { GitStashProvider, ensureGitMirror, getCachePaths, parseGitRepoUrl };
140
+ export { ensureGitMirror, GitStashProvider, getCachePaths, parseGitRepoUrl };
@@ -345,4 +345,4 @@ function inferTypeFromUri(uri) {
345
345
  return OV_TYPE_MAP[firstSegment] ?? "knowledge";
346
346
  }
347
347
  // ── Exports for testing ─────────────────────────────────────────────────────
348
- export { OpenVikingStashProvider, refToVikingUri, parseOVSearchResponse };
348
+ export { OpenVikingStashProvider, parseOVSearchResponse, refToVikingUri };
package/dist/version.js CHANGED
@@ -18,3 +18,4 @@ export const pkgVersion = (() => {
18
18
  }
19
19
  return "0.0.0-dev";
20
20
  })();
21
+ // AKM_VERSION ambient type is declared in globals.d.ts
package/package.json CHANGED
@@ -1,26 +1,34 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
- "description": "CLI tool to search, open, and run extension assets from an akm stash directory.",
5
+ "description": "akm (Agent Kit Manager) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
6
6
  "keywords": [
7
7
  "akm",
8
- "akm-kit",
8
+ "agent-kit-manager",
9
+ "akm-cli",
9
10
  "ai-agent",
11
+ "ai-tools",
10
12
  "agent-framework",
13
+ "package-manager",
11
14
  "developer-tools",
12
15
  "cli",
13
- "tools",
14
16
  "skills",
15
- "commands"
17
+ "commands",
18
+ "claude-code",
19
+ "opencode",
20
+ "mcp",
21
+ "ai-coding-assistant",
22
+ "agent-skills",
23
+ "skill-management"
16
24
  ],
17
- "homepage": "https://github.com/itlackey/agentikit#readme",
25
+ "homepage": "https://github.com/itlackey/akm#readme",
18
26
  "repository": {
19
27
  "type": "git",
20
- "url": "git+https://github.com/itlackey/agentikit.git"
28
+ "url": "git+https://github.com/itlackey/akm.git"
21
29
  },
22
30
  "bugs": {
23
- "url": "https://github.com/itlackey/agentikit/issues"
31
+ "url": "https://github.com/itlackey/akm/issues"
24
32
  },
25
33
  "license": "MPL-2.0",
26
34
  "files": [
@@ -39,7 +47,8 @@
39
47
  "lint": "bunx biome check src/ tests/",
40
48
  "lint:fix": "bunx biome check --write src/ tests/",
41
49
  "format": "bunx biome format --write src/ tests/",
42
- "prepublishOnly": "bun run build"
50
+ "prepublishOnly": "cp .github/README.npm.md README.md && bun run build",
51
+ "postpublish": "git checkout -- README.md"
43
52
  },
44
53
  "publishConfig": {
45
54
  "access": "public"
@@ -51,7 +60,6 @@
51
60
  "typescript": "^5.9.3"
52
61
  },
53
62
  "optionalDependencies": {
54
- "@xenova/transformers": "^2.17.0",
55
63
  "sqlite-vec": "0.1.7-alpha.2"
56
64
  },
57
65
  "engines": {
@@ -60,6 +68,7 @@
60
68
  "dependencies": {
61
69
  "@clack/prompts": "^1.1.0",
62
70
  "citty": "^0.2.1",
71
+ "@huggingface/transformers": "^3.8.1",
63
72
  "yaml": "^2.8.2"
64
73
  }
65
74
  }