akm-cli 0.2.1 → 0.3.0-rc2
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 +42 -53
- package/README.md +30 -92
- package/dist/cli.js +106 -140
- package/dist/config-cli.js +26 -8
- package/dist/config.js +12 -3
- package/dist/db.js +59 -2
- package/dist/embedder.js +32 -3
- package/dist/indexer.js +95 -19
- package/dist/info.js +10 -1
- package/dist/installed-kits.js +111 -34
- package/dist/local-search.js +35 -2
- package/dist/paths.js +3 -0
- package/dist/registry-build-index.js +1 -1
- package/dist/self-update.js +27 -8
- package/dist/semantic-status.js +137 -0
- package/dist/setup.js +90 -23
- package/dist/stash-add.js +0 -18
- package/dist/version.js +1 -0
- package/package.json +20 -10
package/dist/self-update.js
CHANGED
|
@@ -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/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 (
|
|
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/
|
|
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();
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { DEFAULT_LOCAL_MODEL } from "./embedder";
|
|
4
|
+
import { getCacheDir, getSemanticStatusPath } from "./paths";
|
|
5
|
+
export function deriveSemanticProviderFingerprint(embedding) {
|
|
6
|
+
if (embedding?.endpoint) {
|
|
7
|
+
return `remote:${embedding.endpoint}|${embedding.model}|${embedding.dimension ?? "default"}`;
|
|
8
|
+
}
|
|
9
|
+
return `local:${embedding?.localModel ?? DEFAULT_LOCAL_MODEL}`;
|
|
10
|
+
}
|
|
11
|
+
export function readSemanticStatus() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = JSON.parse(fs.readFileSync(getSemanticStatusPath(), "utf8"));
|
|
14
|
+
if ((raw.status === "pending" ||
|
|
15
|
+
raw.status === "ready-js" ||
|
|
16
|
+
raw.status === "ready-vec" ||
|
|
17
|
+
raw.status === "blocked") &&
|
|
18
|
+
typeof raw.providerFingerprint === "string" &&
|
|
19
|
+
typeof raw.lastCheckedAt === "string") {
|
|
20
|
+
const status = {
|
|
21
|
+
status: raw.status,
|
|
22
|
+
providerFingerprint: raw.providerFingerprint,
|
|
23
|
+
lastCheckedAt: raw.lastCheckedAt,
|
|
24
|
+
};
|
|
25
|
+
if (typeof raw.reason === "string")
|
|
26
|
+
status.reason = raw.reason;
|
|
27
|
+
if (typeof raw.message === "string")
|
|
28
|
+
status.message = raw.message;
|
|
29
|
+
if (typeof raw.entryCount === "number")
|
|
30
|
+
status.entryCount = raw.entryCount;
|
|
31
|
+
if (typeof raw.embeddingCount === "number")
|
|
32
|
+
status.embeddingCount = raw.embeddingCount;
|
|
33
|
+
return status;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// ignore corrupt or missing semantic status
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
export function writeSemanticStatus(status) {
|
|
42
|
+
const dir = getCacheDir();
|
|
43
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
44
|
+
const filePath = getSemanticStatusPath();
|
|
45
|
+
const tmpPath = path.join(dir, `semantic-status.json.tmp.${process.pid}.${Math.random().toString(36).slice(2)}`);
|
|
46
|
+
fs.writeFileSync(tmpPath, `${JSON.stringify(status, null, 2)}\n`, "utf8");
|
|
47
|
+
try {
|
|
48
|
+
fs.renameSync(tmpPath, filePath);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
try {
|
|
52
|
+
fs.unlinkSync(tmpPath);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* ignore cleanup failure */
|
|
56
|
+
}
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function clearSemanticStatus() {
|
|
61
|
+
try {
|
|
62
|
+
fs.unlinkSync(getSemanticStatusPath());
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// ignore missing file
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** How long a "blocked" status is retained before the system retries. 24 hours. */
|
|
69
|
+
export const BLOCKED_TTL_MS = 24 * 60 * 60 * 1000;
|
|
70
|
+
export function getEffectiveSemanticStatus(config, status = readSemanticStatus()) {
|
|
71
|
+
if (config.semanticSearchMode === "off")
|
|
72
|
+
return "disabled";
|
|
73
|
+
if (!status)
|
|
74
|
+
return "pending";
|
|
75
|
+
const fingerprint = deriveSemanticProviderFingerprint(config.embedding);
|
|
76
|
+
if (status.providerFingerprint !== fingerprint)
|
|
77
|
+
return "pending";
|
|
78
|
+
// Auto-recovery: if blocked status is older than BLOCKED_TTL_MS, treat as pending
|
|
79
|
+
// so the next index run will re-attempt semantic setup.
|
|
80
|
+
if (status.status === "blocked") {
|
|
81
|
+
const checkedAt = new Date(status.lastCheckedAt).getTime();
|
|
82
|
+
if (Number.isNaN(checkedAt) || Date.now() - checkedAt > BLOCKED_TTL_MS) {
|
|
83
|
+
return "pending";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return status.status;
|
|
87
|
+
}
|
|
88
|
+
export function isSemanticRuntimeReady(status) {
|
|
89
|
+
return status === "ready-js" || status === "ready-vec";
|
|
90
|
+
}
|
|
91
|
+
export function classifySemanticFailure(message) {
|
|
92
|
+
const lower = message.toLowerCase();
|
|
93
|
+
if (lower.includes("401") || lower.includes("403") || lower.includes("auth") || lower.includes("unauthorized")) {
|
|
94
|
+
return "remote-auth";
|
|
95
|
+
}
|
|
96
|
+
if (lower.includes("429") || lower.includes("rate limit") || lower.includes("quota")) {
|
|
97
|
+
return "remote-rate-limit";
|
|
98
|
+
}
|
|
99
|
+
if (lower.includes("eacces") || lower.includes("permission denied")) {
|
|
100
|
+
return "permission-denied";
|
|
101
|
+
}
|
|
102
|
+
// Native library / linker errors must be checked before the generic ONNX
|
|
103
|
+
// match because Alpine/musl linker errors often contain "onnxruntime" in
|
|
104
|
+
// the library path (e.g. onnxruntime_binding.node).
|
|
105
|
+
if (lower.includes("shared library") ||
|
|
106
|
+
lower.includes("glibc") ||
|
|
107
|
+
lower.includes("musl") ||
|
|
108
|
+
lower.includes("libc.so")) {
|
|
109
|
+
return "native-lib-missing";
|
|
110
|
+
}
|
|
111
|
+
if (lower.includes("onnx") || lower.includes("onnxruntime")) {
|
|
112
|
+
return "onnx-runtime-failed";
|
|
113
|
+
}
|
|
114
|
+
if (lower.includes("404") || lower.includes("model not found") || lower.includes("bad request")) {
|
|
115
|
+
return "remote-model";
|
|
116
|
+
}
|
|
117
|
+
if (lower.includes("transformers") || lower.includes("missing-package")) {
|
|
118
|
+
return "missing-package";
|
|
119
|
+
}
|
|
120
|
+
if (lower.includes("download")) {
|
|
121
|
+
return "local-model-download";
|
|
122
|
+
}
|
|
123
|
+
if (lower.includes("dimension mismatch")) {
|
|
124
|
+
return "dimension-mismatch";
|
|
125
|
+
}
|
|
126
|
+
if (lower.includes("db") || lower.includes("sqlite") || lower.includes("cache dir")) {
|
|
127
|
+
return "db-open";
|
|
128
|
+
}
|
|
129
|
+
if (lower.includes("timeout") ||
|
|
130
|
+
lower.includes("unreachable") ||
|
|
131
|
+
lower.includes("refused") ||
|
|
132
|
+
lower.includes("network") ||
|
|
133
|
+
lower.includes("fetch")) {
|
|
134
|
+
return "remote-network";
|
|
135
|
+
}
|
|
136
|
+
return "unknown";
|
|
137
|
+
}
|
package/dist/setup.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* registry selection, stash sources, and agent platform discovery.
|
|
6
6
|
* Collects all choices and writes config once at the end.
|
|
7
7
|
*/
|
|
8
|
+
import path from "node:path";
|
|
8
9
|
import * as p from "@clack/prompts";
|
|
9
10
|
import { isHttpUrl } from "./common";
|
|
10
11
|
import { DEFAULT_CONFIG, getConfigPath, loadConfig, saveConfig } from "./config";
|
|
@@ -14,6 +15,7 @@ import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailabl
|
|
|
14
15
|
import { akmIndex } from "./indexer";
|
|
15
16
|
import { akmInit } from "./init";
|
|
16
17
|
import { getDefaultStashDir } from "./paths";
|
|
18
|
+
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus } from "./semantic-status";
|
|
17
19
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
18
20
|
/** Recommended GitHub repositories shown during setup. */
|
|
19
21
|
const RECOMMENDED_GITHUB_REPOS = [
|
|
@@ -78,6 +80,22 @@ async function promptOrBack(fn) {
|
|
|
78
80
|
return null;
|
|
79
81
|
return result;
|
|
80
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Quick connectivity check. Returns true if we can reach a public
|
|
85
|
+
* endpoint within 3 seconds, false otherwise. Used to skip network-
|
|
86
|
+
* dependent setup steps gracefully when offline.
|
|
87
|
+
*
|
|
88
|
+
* @internal Exported for testing only.
|
|
89
|
+
*/
|
|
90
|
+
export async function isOnline() {
|
|
91
|
+
try {
|
|
92
|
+
await fetch("https://dns.google", { signal: AbortSignal.timeout(3000) });
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
81
99
|
function isRemoteEmbeddingConfig(embedding) {
|
|
82
100
|
return isHttpUrl(embedding?.endpoint);
|
|
83
101
|
}
|
|
@@ -99,10 +117,10 @@ export function describeSemanticSearchAssets(embedding) {
|
|
|
99
117
|
export async function stepSemanticSearch(current, embedding) {
|
|
100
118
|
const enabled = await prompt(() => p.confirm({
|
|
101
119
|
message: "Enable semantic search?",
|
|
102
|
-
initialValue: current.
|
|
120
|
+
initialValue: current.semanticSearchMode !== "off",
|
|
103
121
|
}));
|
|
104
122
|
if (!enabled) {
|
|
105
|
-
return {
|
|
123
|
+
return { mode: "off", prepareAssets: false };
|
|
106
124
|
}
|
|
107
125
|
p.note(describeSemanticSearchAssets(embedding).join("\n"), "Semantic Search Assets");
|
|
108
126
|
const prepareAssets = await prompt(() => p.confirm({
|
|
@@ -111,7 +129,7 @@ export async function stepSemanticSearch(current, embedding) {
|
|
|
111
129
|
: "Download and verify semantic-search assets now?",
|
|
112
130
|
initialValue: true,
|
|
113
131
|
}));
|
|
114
|
-
return {
|
|
132
|
+
return { mode: "auto", prepareAssets };
|
|
115
133
|
}
|
|
116
134
|
async function prepareSemanticSearchAssets(config) {
|
|
117
135
|
const remote = isRemoteEmbeddingConfig(config.embedding);
|
|
@@ -121,7 +139,9 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
121
139
|
const spin = p.spinner();
|
|
122
140
|
spin.start("Installing @huggingface/transformers...");
|
|
123
141
|
try {
|
|
142
|
+
const pkgRoot = path.resolve(import.meta.dir, "..");
|
|
124
143
|
const proc = Bun.spawn(["bun", "add", "@huggingface/transformers"], {
|
|
144
|
+
cwd: pkgRoot,
|
|
125
145
|
stdout: "pipe",
|
|
126
146
|
stderr: "pipe",
|
|
127
147
|
});
|
|
@@ -138,7 +158,7 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
138
158
|
p.log.warn(`Automatic install failed: ${msg}\n` +
|
|
139
159
|
"Install it manually with: bun add @huggingface/transformers\n" +
|
|
140
160
|
"Then re-run `akm setup` or `akm index --full --verbose`.");
|
|
141
|
-
return false;
|
|
161
|
+
return { ok: false, reason: "missing-package", message: `Automatic install failed: ${msg}` };
|
|
142
162
|
}
|
|
143
163
|
}
|
|
144
164
|
}
|
|
@@ -151,16 +171,18 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
151
171
|
spin.stop("Semantic-search assets could not be prepared.");
|
|
152
172
|
if (result.reason === "remote-unreachable") {
|
|
153
173
|
p.log.warn("The remote embedding endpoint is not reachable. Check your endpoint and credentials, then retry `akm index --full --verbose`.");
|
|
174
|
+
return { ok: false, reason: "remote-network", message: "The remote embedding endpoint is not reachable." };
|
|
154
175
|
}
|
|
155
176
|
else if (result.reason === "missing-package") {
|
|
156
177
|
p.log.warn("@huggingface/transformers is not installed. Install it with: bun add @huggingface/transformers\n" +
|
|
157
178
|
"Then re-run `akm setup` or `akm index --full --verbose`.");
|
|
179
|
+
return { ok: false, reason: "missing-package", message: "@huggingface/transformers is not installed." };
|
|
158
180
|
}
|
|
159
181
|
else {
|
|
160
182
|
p.log.warn(`The local embedding model could not be downloaded: ${result.message}\n` +
|
|
161
183
|
"Retry `akm index --full --verbose` after confirming local model downloads are permitted.");
|
|
184
|
+
return { ok: false, reason: "local-model-download", message: result.message };
|
|
162
185
|
}
|
|
163
|
-
return false;
|
|
164
186
|
}
|
|
165
187
|
spin.stop(remote ? "Remote embedding endpoint is ready." : "Local embedding model downloaded and ready.");
|
|
166
188
|
let db;
|
|
@@ -182,7 +204,7 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
182
204
|
if (db)
|
|
183
205
|
closeDatabase(db);
|
|
184
206
|
}
|
|
185
|
-
return true;
|
|
207
|
+
return { ok: true };
|
|
186
208
|
}
|
|
187
209
|
// ── Steps ───────────────────────────────────────────────────────────────────
|
|
188
210
|
async function stepStashDir(current) {
|
|
@@ -250,11 +272,20 @@ async function stepOllama(current) {
|
|
|
250
272
|
embedding = current.embedding;
|
|
251
273
|
}
|
|
252
274
|
else if (embChoice !== "local") {
|
|
253
|
-
// Ask for dimension — different models produce different sizes
|
|
275
|
+
// Ask for dimension — different models produce different sizes.
|
|
276
|
+
// Common dimensions: nomic-embed-text=768, mxbai-embed-large=1024,
|
|
277
|
+
// all-minilm/bge-small=384. Default based on selected model.
|
|
278
|
+
const knownDims = {
|
|
279
|
+
nomic: 768,
|
|
280
|
+
mxbai: 1024,
|
|
281
|
+
minilm: 384,
|
|
282
|
+
bge: 384,
|
|
283
|
+
};
|
|
284
|
+
const guessedDim = Object.entries(knownDims).find(([k]) => embChoice.includes(k))?.[1] ?? 384;
|
|
254
285
|
const dimChoice = await prompt(() => p.text({
|
|
255
|
-
message:
|
|
256
|
-
placeholder:
|
|
257
|
-
defaultValue:
|
|
286
|
+
message: `Embedding dimension for ${embChoice}:`,
|
|
287
|
+
placeholder: String(guessedDim),
|
|
288
|
+
defaultValue: String(guessedDim),
|
|
258
289
|
validate: (v) => {
|
|
259
290
|
const n = Number(v);
|
|
260
291
|
if (!Number.isInteger(n) || n <= 0)
|
|
@@ -542,12 +573,18 @@ export async function runSetupWizard() {
|
|
|
542
573
|
// Step 1: Stash directory
|
|
543
574
|
p.log.step("Step 1: Stash Directory");
|
|
544
575
|
const stashDir = await stepStashDir(current);
|
|
576
|
+
// Quick connectivity check — skip network-dependent steps when offline
|
|
577
|
+
const online = await isOnline();
|
|
578
|
+
if (!online) {
|
|
579
|
+
p.log.warn("No network connectivity detected. Skipping Ollama detection and remote embedding checks.\n" +
|
|
580
|
+
"Local-only setup will continue. Re-run `akm setup` when online for full configuration.");
|
|
581
|
+
}
|
|
545
582
|
// Step 2: Ollama / Embedding / LLM
|
|
546
583
|
p.log.step("Step 2: Embedding & LLM");
|
|
547
|
-
const { embedding, llm } = await stepOllama(current);
|
|
584
|
+
const { embedding, llm } = online ? await stepOllama(current) : { embedding: current.embedding, llm: current.llm };
|
|
548
585
|
// Step 3: Semantic search assets
|
|
549
586
|
p.log.step("Step 3: Semantic Search");
|
|
550
|
-
const
|
|
587
|
+
const semanticSearchMode = await stepSemanticSearch(current, embedding);
|
|
551
588
|
// Step 4: Registries
|
|
552
589
|
p.log.step("Step 4: Registries");
|
|
553
590
|
const registries = await stepRegistries(current);
|
|
@@ -573,7 +610,7 @@ export async function runSetupWizard() {
|
|
|
573
610
|
registries,
|
|
574
611
|
stashes: allStashes.length > 0 ? allStashes : undefined,
|
|
575
612
|
// Preserve existing fields
|
|
576
|
-
|
|
613
|
+
semanticSearchMode: semanticSearchMode.mode,
|
|
577
614
|
installed: current.installed,
|
|
578
615
|
output: current.output,
|
|
579
616
|
};
|
|
@@ -583,7 +620,7 @@ export async function runSetupWizard() {
|
|
|
583
620
|
`Stash directory: ${stashDir}`,
|
|
584
621
|
`Embedding: ${embedding ? `${embedding.provider ?? "remote"} / ${embedding.model}` : "built-in local"}`,
|
|
585
622
|
`LLM: ${llm ? `${llm.provider ?? "remote"} / ${llm.model}` : "disabled"}`,
|
|
586
|
-
`Semantic search: ${
|
|
623
|
+
`Semantic search: ${semanticSearchMode.mode}`,
|
|
587
624
|
`Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
|
|
588
625
|
`Stash sources: ${allStashes.length}`,
|
|
589
626
|
].join("\n"), "Configuration Summary");
|
|
@@ -597,18 +634,39 @@ export async function runSetupWizard() {
|
|
|
597
634
|
saveConfig(newConfig);
|
|
598
635
|
// Initialize stash directory
|
|
599
636
|
await akmInit({ dir: stashDir });
|
|
600
|
-
if (
|
|
601
|
-
|
|
637
|
+
if (semanticSearchMode.mode === "off") {
|
|
638
|
+
clearSemanticStatus();
|
|
639
|
+
}
|
|
640
|
+
if (semanticSearchMode.mode === "auto") {
|
|
641
|
+
if (semanticSearchMode.prepareAssets) {
|
|
602
642
|
const ready = await prepareSemanticSearchAssets(newConfig);
|
|
603
|
-
if (!ready) {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
643
|
+
if (!ready.ok) {
|
|
644
|
+
writeSemanticStatus({
|
|
645
|
+
status: "blocked",
|
|
646
|
+
reason: ready.reason,
|
|
647
|
+
message: ready.message,
|
|
648
|
+
providerFingerprint: deriveSemanticProviderFingerprint(newConfig.embedding),
|
|
649
|
+
lastCheckedAt: new Date().toISOString(),
|
|
650
|
+
});
|
|
651
|
+
p.log.warn("Semantic search remains set to auto, but is currently blocked. Re-run `akm index --full --verbose` once the issue is resolved.");
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
writeSemanticStatus({
|
|
655
|
+
status: "pending",
|
|
656
|
+
message: "Semantic prerequisites verified. Building the index to finish activation.",
|
|
657
|
+
providerFingerprint: deriveSemanticProviderFingerprint(newConfig.embedding),
|
|
658
|
+
lastCheckedAt: new Date().toISOString(),
|
|
659
|
+
});
|
|
608
660
|
}
|
|
609
661
|
}
|
|
610
662
|
else {
|
|
611
|
-
|
|
663
|
+
writeSemanticStatus({
|
|
664
|
+
status: "pending",
|
|
665
|
+
message: "Semantic search is enabled, but asset preparation was skipped.",
|
|
666
|
+
providerFingerprint: deriveSemanticProviderFingerprint(newConfig.embedding),
|
|
667
|
+
lastCheckedAt: new Date().toISOString(),
|
|
668
|
+
});
|
|
669
|
+
p.log.info("Semantic search is set to auto, but asset preparation was skipped. Run `akm index --full --verbose` later to verify it.");
|
|
612
670
|
}
|
|
613
671
|
}
|
|
614
672
|
// Build search index
|
|
@@ -618,7 +676,7 @@ export async function runSetupWizard() {
|
|
|
618
676
|
try {
|
|
619
677
|
const indexResult = await akmIndex({ stashDir });
|
|
620
678
|
spin.stop(`Indexed ${indexResult.totalEntries} assets.`);
|
|
621
|
-
if (newConfig.
|
|
679
|
+
if (newConfig.semanticSearchMode === "auto") {
|
|
622
680
|
if (indexResult.verification.ok) {
|
|
623
681
|
p.log.success(indexResult.verification.message);
|
|
624
682
|
}
|
|
@@ -633,6 +691,15 @@ export async function runSetupWizard() {
|
|
|
633
691
|
catch (err) {
|
|
634
692
|
spin.stop("Indexing failed — you can run `akm index` manually later.");
|
|
635
693
|
p.log.warn(String(err));
|
|
694
|
+
if (newConfig.semanticSearchMode === "auto") {
|
|
695
|
+
writeSemanticStatus({
|
|
696
|
+
status: "blocked",
|
|
697
|
+
reason: "index-failed",
|
|
698
|
+
message: String(err),
|
|
699
|
+
providerFingerprint: deriveSemanticProviderFingerprint(newConfig.embedding),
|
|
700
|
+
lastCheckedAt: new Date().toISOString(),
|
|
701
|
+
});
|
|
702
|
+
}
|
|
636
703
|
}
|
|
637
704
|
// API key reminder
|
|
638
705
|
if (embedding?.apiKey === undefined && embedding?.provider !== "ollama") {
|
package/dist/stash-add.js
CHANGED
|
@@ -7,24 +7,6 @@ import { akmIndex } from "./indexer";
|
|
|
7
7
|
import { upsertLockEntry } from "./lockfile";
|
|
8
8
|
import { detectStashRoot, installRegistryRef, upsertInstalledRegistryEntry } from "./registry-install";
|
|
9
9
|
import { parseRegistryRef } from "./registry-resolve";
|
|
10
|
-
export async function akmKitAdd(input) {
|
|
11
|
-
const ref = input.ref.trim();
|
|
12
|
-
if (!ref)
|
|
13
|
-
throw new UsageError("Registry ref is required. " + "Examples: `akm kit add @scope/kit`, `akm kit add github:owner/repo`");
|
|
14
|
-
const stashDir = resolveStashDir();
|
|
15
|
-
try {
|
|
16
|
-
const parsed = parseRegistryRef(ref);
|
|
17
|
-
if (parsed.source === "local") {
|
|
18
|
-
throw new UsageError(`Local directories should be added as stashes, not kits. Use \`akm stash add ${ref}\` instead.`);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch (err) {
|
|
22
|
-
if (err instanceof UsageError)
|
|
23
|
-
throw err;
|
|
24
|
-
// Not a local ref — fall through to registry install
|
|
25
|
-
}
|
|
26
|
-
return addRegistryKit(ref, stashDir);
|
|
27
|
-
}
|
|
28
10
|
export async function akmAdd(input) {
|
|
29
11
|
const ref = input.ref.trim();
|
|
30
12
|
if (!ref)
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-rc2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
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
|
-
"
|
|
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/
|
|
25
|
+
"homepage": "https://github.com/itlackey/akm#readme",
|
|
18
26
|
"repository": {
|
|
19
27
|
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/itlackey/
|
|
28
|
+
"url": "git+https://github.com/itlackey/akm.git"
|
|
21
29
|
},
|
|
22
30
|
"bugs": {
|
|
23
|
-
"url": "https://github.com/itlackey/
|
|
31
|
+
"url": "https://github.com/itlackey/akm/issues"
|
|
24
32
|
},
|
|
25
33
|
"license": "MPL-2.0",
|
|
26
34
|
"files": [
|
|
@@ -36,10 +44,12 @@
|
|
|
36
44
|
"check": "bun run lint && bunx tsc --noEmit && bun test ./tests",
|
|
37
45
|
"check:changed": "bun test tests/output-baseline.test.ts tests/e2e.test.ts tests/stash-search.test.ts && bun run lint && bunx tsc --noEmit",
|
|
38
46
|
"test": "bun test ./tests",
|
|
47
|
+
"release:check": "./tests/release-check.sh",
|
|
39
48
|
"lint": "bunx biome check src/ tests/",
|
|
40
49
|
"lint:fix": "bunx biome check --write src/ tests/",
|
|
41
50
|
"format": "bunx biome format --write src/ tests/",
|
|
42
|
-
"prepublishOnly": "bun run build"
|
|
51
|
+
"prepublishOnly": "cp .github/README.npm.md README.md && bun run build",
|
|
52
|
+
"postpublish": "git checkout -- README.md"
|
|
43
53
|
},
|
|
44
54
|
"publishConfig": {
|
|
45
55
|
"access": "public"
|
|
@@ -51,6 +61,7 @@
|
|
|
51
61
|
"typescript": "^5.9.3"
|
|
52
62
|
},
|
|
53
63
|
"optionalDependencies": {
|
|
64
|
+
"@huggingface/transformers": "^3.8.1",
|
|
54
65
|
"sqlite-vec": "0.1.7-alpha.2"
|
|
55
66
|
},
|
|
56
67
|
"engines": {
|
|
@@ -58,7 +69,6 @@
|
|
|
58
69
|
},
|
|
59
70
|
"dependencies": {
|
|
60
71
|
"@clack/prompts": "^1.1.0",
|
|
61
|
-
"@huggingface/transformers": "^3.8.1",
|
|
62
72
|
"citty": "^0.2.1",
|
|
63
73
|
"yaml": "^2.8.2"
|
|
64
74
|
}
|