akm-cli 0.6.0-rc1 → 0.6.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/CHANGELOG.md +21 -0
- package/README.md +9 -9
- package/dist/cli.js +181 -111
- package/dist/{completions.js → commands/completions.js} +1 -1
- package/dist/{config-cli.js → commands/config-cli.js} +109 -11
- package/dist/{curate.js → commands/curate.js} +8 -3
- package/dist/{info.js → commands/info.js} +15 -9
- package/dist/{init.js → commands/init.js} +4 -4
- package/dist/{install-audit.js → commands/install-audit.js} +4 -7
- package/dist/{installed-stashes.js → commands/installed-stashes.js} +77 -31
- package/dist/{migration-help.js → commands/migration-help.js} +2 -2
- package/dist/{registry-search.js → commands/registry-search.js} +8 -6
- package/dist/{remember.js → commands/remember.js} +55 -49
- package/dist/{stash-search.js → commands/search.js} +28 -69
- package/dist/{self-update.js → commands/self-update.js} +3 -3
- package/dist/{stash-show.js → commands/show.js} +103 -78
- package/dist/{stash-add.js → commands/source-add.js} +42 -32
- package/dist/{stash-clone.js → commands/source-clone.js} +12 -10
- package/dist/{stash-source-manage.js → commands/source-manage.js} +24 -24
- package/dist/{vault.js → commands/vault.js} +43 -0
- package/dist/{stash-ref.js → core/asset-ref.js} +4 -4
- package/dist/{asset-registry.js → core/asset-registry.js} +1 -1
- package/dist/{asset-spec.js → core/asset-spec.js} +1 -1
- package/dist/{config.js → core/config.js} +79 -31
- package/dist/core/errors.js +90 -0
- package/dist/{frontmatter.js → core/frontmatter.js} +5 -3
- package/dist/core/write-source.js +280 -0
- package/dist/{db-search.js → indexer/db-search.js} +25 -19
- package/dist/{db.js → indexer/db.js} +70 -47
- package/dist/{file-context.js → indexer/file-context.js} +3 -3
- package/dist/{indexer.js → indexer/indexer.js} +123 -31
- package/dist/{manifest.js → indexer/manifest.js} +10 -10
- package/dist/{matchers.js → indexer/matchers.js} +3 -6
- package/dist/{metadata.js → indexer/metadata.js} +9 -5
- package/dist/{search-source.js → indexer/search-source.js} +52 -41
- package/dist/{semantic-status.js → indexer/semantic-status.js} +2 -2
- package/dist/{walker.js → indexer/walker.js} +1 -1
- package/dist/{lockfile.js → integrations/lockfile.js} +1 -1
- package/dist/{llm-client.js → llm/client.js} +1 -1
- package/dist/{embedders → llm/embedders}/local.js +2 -2
- package/dist/{embedders → llm/embedders}/remote.js +1 -1
- package/dist/{embedders → llm/embedders}/types.js +1 -1
- package/dist/{metadata-enhance.js → llm/metadata-enhance.js} +2 -2
- package/dist/{cli-hints.js → output/cli-hints.js} +1 -0
- package/dist/{output-context.js → output/context.js} +21 -3
- package/dist/{renderers.js → output/renderers.js} +9 -65
- package/dist/{output-shapes.js → output/shapes.js} +18 -4
- package/dist/{output-text.js → output/text.js} +1 -1
- package/dist/{registry-build-index.js → registry/build-index.js} +16 -7
- package/dist/{create-provider-registry.js → registry/create-provider-registry.js} +6 -2
- package/dist/registry/factory.js +33 -0
- package/dist/{origin-resolve.js → registry/origin-resolve.js} +1 -1
- package/dist/{providers → registry/providers}/index.js +1 -1
- package/dist/{providers → registry/providers}/skills-sh.js +59 -3
- package/dist/{providers → registry/providers}/static-index.js +80 -12
- package/dist/registry/providers/types.js +25 -0
- package/dist/{registry-resolve.js → registry/resolve.js} +3 -3
- package/dist/{detect.js → setup/detect.js} +0 -27
- package/dist/{ripgrep-install.js → setup/ripgrep-install.js} +1 -1
- package/dist/{ripgrep-resolve.js → setup/ripgrep-resolve.js} +2 -2
- package/dist/{setup.js → setup/setup.js} +16 -56
- package/dist/{stash-include.js → sources/include.js} +1 -1
- package/dist/sources/provider-factory.js +36 -0
- package/dist/sources/provider.js +21 -0
- package/dist/sources/providers/filesystem.js +35 -0
- package/dist/{stash-providers → sources/providers}/git.js +53 -64
- package/dist/{stash-providers → sources/providers}/index.js +3 -4
- package/dist/sources/providers/install-types.js +14 -0
- package/dist/{stash-providers → sources/providers}/npm.js +42 -41
- package/dist/{stash-providers → sources/providers}/provider-utils.js +3 -3
- package/dist/{stash-providers → sources/providers}/sync-from-ref.js +2 -2
- package/dist/{stash-providers → sources/providers}/tar-utils.js +11 -8
- package/dist/{stash-providers → sources/providers}/website.js +29 -65
- package/dist/{stash-resolve.js → sources/resolve.js} +8 -7
- package/dist/{wiki.js → wiki/wiki.js} +12 -11
- package/dist/{workflow-authoring.js → workflows/authoring.js} +37 -14
- package/dist/{workflow-cli.js → workflows/cli.js} +2 -1
- package/dist/{workflow-db.js → workflows/db.js} +1 -1
- package/dist/workflows/document-cache.js +20 -0
- package/dist/workflows/parser.js +379 -0
- package/dist/workflows/renderer.js +78 -0
- package/dist/{workflow-runs.js → workflows/runs.js} +72 -28
- package/dist/workflows/schema.js +11 -0
- package/dist/workflows/validator.js +48 -0
- package/docs/migration/release-notes/0.6.0.md +69 -23
- package/package.json +1 -1
- package/dist/errors.js +0 -45
- package/dist/llm.js +0 -16
- package/dist/registry-factory.js +0 -19
- package/dist/ripgrep.js +0 -2
- package/dist/stash-provider-factory.js +0 -35
- package/dist/stash-provider.js +0 -3
- package/dist/stash-providers/filesystem.js +0 -71
- package/dist/stash-providers/openviking.js +0 -348
- package/dist/stash-types.js +0 -1
- package/dist/workflow-markdown.js +0 -260
- /package/dist/{common.js → core/common.js} +0 -0
- /package/dist/{markdown.js → core/markdown.js} +0 -0
- /package/dist/{paths.js → core/paths.js} +0 -0
- /package/dist/{warn.js → core/warn.js} +0 -0
- /package/dist/{search-fields.js → indexer/search-fields.js} +0 -0
- /package/dist/{usage-events.js → indexer/usage-events.js} +0 -0
- /package/dist/{github.js → integrations/github.js} +0 -0
- /package/dist/{embedder.js → llm/embedder.js} +0 -0
- /package/dist/{embedders → llm/embedders}/cache.js +0 -0
- /package/dist/{registry-provider.js → registry/types.js} +0 -0
- /package/dist/{setup-steps.js → setup/steps.js} +0 -0
- /package/dist/{registry-types.js → sources/types.js} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { fetchWithRetry } from "
|
|
4
|
-
import { getRegistryIndexCacheDir } from "
|
|
5
|
-
import { registerProvider } from "../
|
|
3
|
+
import { fetchWithRetry } from "../../core/common";
|
|
4
|
+
import { getRegistryIndexCacheDir } from "../../core/paths";
|
|
5
|
+
import { registerProvider } from "../factory";
|
|
6
6
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
7
7
|
/** Per-query cache TTL in milliseconds (15 minutes). */
|
|
8
8
|
const QUERY_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
@@ -32,6 +32,62 @@ class SkillsShProvider {
|
|
|
32
32
|
return { hits: [], warnings: [`Registry ${label}: ${message}`] };
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
// ── v1-spec §3.1 surface ────────────────────────────────────────────────
|
|
36
|
+
async searchKits(q) {
|
|
37
|
+
const result = await this.search({
|
|
38
|
+
query: q.text,
|
|
39
|
+
limit: q.limit ?? 20,
|
|
40
|
+
includeAssets: false,
|
|
41
|
+
});
|
|
42
|
+
return result.hits.map((hit) => ({
|
|
43
|
+
id: hit.id,
|
|
44
|
+
title: hit.title,
|
|
45
|
+
summary: hit.description,
|
|
46
|
+
installRef: hit.installRef,
|
|
47
|
+
score: hit.score,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
async searchAssets(q) {
|
|
51
|
+
const result = await this.search({
|
|
52
|
+
query: q.text,
|
|
53
|
+
limit: q.limit ?? 20,
|
|
54
|
+
includeAssets: true,
|
|
55
|
+
});
|
|
56
|
+
return (result.assetHits ?? []).map((hit) => ({
|
|
57
|
+
kitId: hit.stash.id,
|
|
58
|
+
type: hit.assetType,
|
|
59
|
+
name: hit.assetName,
|
|
60
|
+
summary: hit.description,
|
|
61
|
+
cloneRef: hit.action.replace(/^akm add\s+/, ""),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* skills.sh has no `getKit` API — every entry corresponds to a GitHub
|
|
66
|
+
* repository whose metadata we already include in the search result. We
|
|
67
|
+
* synthesize a manifest from the search hit when the caller knows the kit
|
|
68
|
+
* id; if not present in the most recent results, return null.
|
|
69
|
+
*/
|
|
70
|
+
async getKit(id) {
|
|
71
|
+
if (!id.startsWith("skills-sh:"))
|
|
72
|
+
return null;
|
|
73
|
+
const slug = id.slice("skills-sh:".length);
|
|
74
|
+
// Best-effort: the API gives us search-by-name; extract the leaf segment.
|
|
75
|
+
const segments = slug.split("/").filter(Boolean);
|
|
76
|
+
const leaf = segments[segments.length - 1] ?? slug;
|
|
77
|
+
const result = await this.search({ query: leaf, limit: 50, includeAssets: false });
|
|
78
|
+
const match = result.hits.find((hit) => hit.id === id);
|
|
79
|
+
if (!match)
|
|
80
|
+
return null;
|
|
81
|
+
return { id: match.id, installRef: match.installRef };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* skills.sh entries are always GitHub repositories. Claim only refs whose
|
|
85
|
+
* parsed source is `github`; defer everything else (npm tarballs, local
|
|
86
|
+
* paths, raw git URLs) to other registries.
|
|
87
|
+
*/
|
|
88
|
+
canHandle(ref) {
|
|
89
|
+
return ref.source === "github";
|
|
90
|
+
}
|
|
35
91
|
async fetchSkills(query, limit) {
|
|
36
92
|
// Check per-query cache first
|
|
37
93
|
const cachePath = this.queryCachePath(query, limit);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { fetchWithRetry, jsonWithByteCap, toErrorMessage } from "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { registerProvider } from "../
|
|
3
|
+
import { fetchWithRetry, jsonWithByteCap, toErrorMessage } from "../../core/common";
|
|
4
|
+
import { getRegistryIndexCacheDir } from "../../core/paths";
|
|
5
|
+
import { asString } from "../../integrations/github";
|
|
6
|
+
import { registerProvider } from "../factory";
|
|
7
7
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
8
8
|
/** Cache TTL in milliseconds (1 hour). */
|
|
9
9
|
const CACHE_TTL_MS = 60 * 60 * 1000;
|
|
@@ -18,6 +18,63 @@ class StaticIndexProvider {
|
|
|
18
18
|
}
|
|
19
19
|
async search(options) {
|
|
20
20
|
const warnings = [];
|
|
21
|
+
const allKits = await this.loadAllKits(warnings);
|
|
22
|
+
const hits = scoreKits(allKits, options.query, options.limit);
|
|
23
|
+
let assetHits;
|
|
24
|
+
if (options.includeAssets) {
|
|
25
|
+
const scored = scoreAssets(allKits, options.query, options.limit);
|
|
26
|
+
if (scored.length > 0)
|
|
27
|
+
assetHits = scored;
|
|
28
|
+
}
|
|
29
|
+
return { hits, assetHits, warnings: warnings.length > 0 ? warnings : undefined };
|
|
30
|
+
}
|
|
31
|
+
// ── v1-spec §3.1 surface ────────────────────────────────────────────────
|
|
32
|
+
async searchKits(q) {
|
|
33
|
+
const result = await this.search({
|
|
34
|
+
query: q.text,
|
|
35
|
+
limit: q.limit ?? 20,
|
|
36
|
+
includeAssets: false,
|
|
37
|
+
});
|
|
38
|
+
return result.hits.map(hitToKitResult);
|
|
39
|
+
}
|
|
40
|
+
async searchAssets(q) {
|
|
41
|
+
const result = await this.search({
|
|
42
|
+
query: q.text,
|
|
43
|
+
limit: q.limit ?? 20,
|
|
44
|
+
includeAssets: true,
|
|
45
|
+
});
|
|
46
|
+
return (result.assetHits ?? []).map(assetHitToPreview);
|
|
47
|
+
}
|
|
48
|
+
async getKit(id) {
|
|
49
|
+
const allKits = await this.loadAllKits([]);
|
|
50
|
+
const found = allKits.find(({ stash }) => stash.id === id);
|
|
51
|
+
if (!found)
|
|
52
|
+
return null;
|
|
53
|
+
const installRef = buildInstallRef(found.stash.source, found.stash.ref);
|
|
54
|
+
return {
|
|
55
|
+
id: found.stash.id,
|
|
56
|
+
installRef,
|
|
57
|
+
assets: found.stash.assets?.map((asset) => ({
|
|
58
|
+
kitId: found.stash.id,
|
|
59
|
+
type: asset.type,
|
|
60
|
+
name: asset.name,
|
|
61
|
+
summary: asset.description,
|
|
62
|
+
cloneRef: installRef,
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Static-index doesn't own a URL prefix — any `ParsedRegistryRef` could
|
|
68
|
+
* theoretically be backed by an entry in some static-index registry. We
|
|
69
|
+
* therefore claim every ref. The orchestrator picks the first matching
|
|
70
|
+
* provider, and `static-index` is registered first by `index.ts`, so this
|
|
71
|
+
* is effectively the default catch-all.
|
|
72
|
+
*/
|
|
73
|
+
canHandle(_ref) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
// ── Internals ───────────────────────────────────────────────────────────
|
|
77
|
+
async loadAllKits(warnings) {
|
|
21
78
|
const allKits = [];
|
|
22
79
|
try {
|
|
23
80
|
const index = await loadIndex(this.config);
|
|
@@ -32,16 +89,27 @@ class StaticIndexProvider {
|
|
|
32
89
|
const label = this.config.name ? `${this.config.name} (${this.config.url})` : this.config.url;
|
|
33
90
|
warnings.push(`Registry ${label}: ${toErrorMessage(err)}`);
|
|
34
91
|
}
|
|
35
|
-
|
|
36
|
-
let assetHits;
|
|
37
|
-
if (options.includeAssets) {
|
|
38
|
-
const scored = scoreAssets(allKits, options.query, options.limit);
|
|
39
|
-
if (scored.length > 0)
|
|
40
|
-
assetHits = scored;
|
|
41
|
-
}
|
|
42
|
-
return { hits, assetHits, warnings: warnings.length > 0 ? warnings : undefined };
|
|
92
|
+
return allKits;
|
|
43
93
|
}
|
|
44
94
|
}
|
|
95
|
+
function hitToKitResult(hit) {
|
|
96
|
+
return {
|
|
97
|
+
id: hit.id,
|
|
98
|
+
title: hit.title,
|
|
99
|
+
summary: hit.description,
|
|
100
|
+
installRef: hit.installRef,
|
|
101
|
+
score: hit.score,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function assetHitToPreview(hit) {
|
|
105
|
+
return {
|
|
106
|
+
kitId: hit.stash.id,
|
|
107
|
+
type: hit.assetType,
|
|
108
|
+
name: hit.assetName,
|
|
109
|
+
summary: hit.description,
|
|
110
|
+
cloneRef: hit.action.replace(/^akm add\s+/, ""),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
45
113
|
// ── Self-register ───────────────────────────────────────────────────────────
|
|
46
114
|
registerProvider("static-index", (config) => new StaticIndexProvider(config));
|
|
47
115
|
// ── Index loading with cache ────────────────────────────────────────────────
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry provider interface (v1 architecture spec §3.1).
|
|
3
|
+
*
|
|
4
|
+
* A `RegistryProvider` is a read-only catalog that lists installable kits and
|
|
5
|
+
* (optionally) previews assets within them. It is *not* a `SourceProvider`:
|
|
6
|
+
* registry providers do not materialise files to disk — they only answer
|
|
7
|
+
* discovery queries.
|
|
8
|
+
*
|
|
9
|
+
* The two built-in registry providers at v1 are:
|
|
10
|
+
*
|
|
11
|
+
* - `static-index` — reads the v2 JSON index schema (the official akm registry
|
|
12
|
+
* and any static-hosted team registry). The v2 schema is owned by this
|
|
13
|
+
* provider, not by core akm.
|
|
14
|
+
* - `skills-sh` — wraps the skills.sh REST API.
|
|
15
|
+
*
|
|
16
|
+
* Context Hub is **not** a registry provider — it is an ordinary git repository
|
|
17
|
+
* recommended via the official static-index registry (see CLAUDE.md).
|
|
18
|
+
*
|
|
19
|
+
* Note: the simple `search()` method is the v0.6 surface and remains the
|
|
20
|
+
* primary entry point used by the orchestrator. The `searchKits` /
|
|
21
|
+
* `searchAssets` / `getKit` / `canHandle` methods are the v1-spec contract
|
|
22
|
+
* (§3.1) which built-in providers also implement so the orchestrator can be
|
|
23
|
+
* iterated cleanly post-Phase 6 without reaching into provider-specific shapes.
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
@@ -3,9 +3,9 @@ import fs from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
-
import { fetchWithRetry, jsonWithByteCap } from "
|
|
7
|
-
import { UsageError } from "
|
|
8
|
-
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "
|
|
6
|
+
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
7
|
+
import { UsageError } from "../core/errors";
|
|
8
|
+
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
9
9
|
/**
|
|
10
10
|
* Validate that a URL is safe to pass to git.
|
|
11
11
|
* Allowlists https:, http:, ssh:, git: schemes and git@ SSH shorthand.
|
|
@@ -91,30 +91,3 @@ export function detectAgentPlatforms() {
|
|
|
91
91
|
path: path.join(home, p.relPath),
|
|
92
92
|
}));
|
|
93
93
|
}
|
|
94
|
-
// ── OpenViking Detection ────────────────────────────────────────────────────
|
|
95
|
-
/**
|
|
96
|
-
* Check if an OpenViking server is reachable at the given URL.
|
|
97
|
-
* Uses the lightweight /api/v1/fs/stat endpoint (GET) rather than
|
|
98
|
-
* the search endpoint which requires a running search index.
|
|
99
|
-
*/
|
|
100
|
-
export async function detectOpenViking(url) {
|
|
101
|
-
const normalized = url.replace(/\/+$/, "");
|
|
102
|
-
try {
|
|
103
|
-
// Any HTTP response (even non-2xx) from the API endpoint means the server is reachable.
|
|
104
|
-
// Only network errors / timeouts indicate the server is truly unavailable.
|
|
105
|
-
await fetch(`${normalized}/api/v1/fs/stat?uri=${encodeURIComponent("viking://")}`, {
|
|
106
|
-
signal: AbortSignal.timeout(5000),
|
|
107
|
-
});
|
|
108
|
-
return { available: true, url: normalized };
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
// stat endpoint unreachable — try root URL as fallback
|
|
112
|
-
try {
|
|
113
|
-
await fetch(normalized, { signal: AbortSignal.timeout(5000) });
|
|
114
|
-
return { available: true, url: normalized };
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
return { available: false, url: normalized };
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { IS_WINDOWS } from "
|
|
4
|
+
import { IS_WINDOWS } from "../core/common";
|
|
5
5
|
import { RG_BINARY, resolveRg } from "./ripgrep-resolve";
|
|
6
6
|
/**
|
|
7
7
|
* Platform and architecture detection for ripgrep binary downloads.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { IS_WINDOWS } from "
|
|
4
|
-
import { getBinDir } from "
|
|
3
|
+
import { IS_WINDOWS } from "../core/common";
|
|
4
|
+
import { getBinDir } from "../core/paths";
|
|
5
5
|
export const RG_BINARY = IS_WINDOWS ? "rg.exe" : "rg";
|
|
6
6
|
function canExecute(filePath) {
|
|
7
7
|
if (!fs.existsSync(filePath))
|
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import * as p from "@clack/prompts";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { akmIndex } from "
|
|
16
|
-
import {
|
|
17
|
-
import { probeLlmCapabilities } from "
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { createSetupContext, runSetupSteps } from "./
|
|
10
|
+
import { akmInit } from "../commands/init";
|
|
11
|
+
import { isHttpUrl } from "../core/common";
|
|
12
|
+
import { DEFAULT_CONFIG, getConfigPath, loadUserConfig, saveConfig } from "../core/config";
|
|
13
|
+
import { getDefaultStashDir } from "../core/paths";
|
|
14
|
+
import { closeDatabase, isVecAvailable, openDatabase } from "../indexer/db";
|
|
15
|
+
import { akmIndex } from "../indexer/indexer";
|
|
16
|
+
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus, } from "../indexer/semantic-status";
|
|
17
|
+
import { probeLlmCapabilities } from "../llm/client";
|
|
18
|
+
import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder";
|
|
19
|
+
import { detectAgentPlatforms, detectOllama } from "./detect";
|
|
20
|
+
import { createSetupContext, runSetupSteps } from "./steps";
|
|
21
21
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
22
22
|
/**
|
|
23
23
|
* Recommended GitHub repositories shown during setup.
|
|
@@ -141,7 +141,7 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
141
141
|
const spin = p.spinner();
|
|
142
142
|
spin.start("Installing @huggingface/transformers...");
|
|
143
143
|
try {
|
|
144
|
-
const pkgRoot = path.resolve(import.meta.dir, "
|
|
144
|
+
const pkgRoot = path.resolve(import.meta.dir, "../..");
|
|
145
145
|
const proc = Bun.spawn(["bun", "add", "@huggingface/transformers"], {
|
|
146
146
|
cwd: pkgRoot,
|
|
147
147
|
stdout: "pipe",
|
|
@@ -500,8 +500,8 @@ async function stepRegistries(current) {
|
|
|
500
500
|
/**
|
|
501
501
|
* @internal Exported for testing only.
|
|
502
502
|
*/
|
|
503
|
-
export async function
|
|
504
|
-
const stashes = [...(current.stashes ?? [])];
|
|
503
|
+
export async function stepAddSources(current) {
|
|
504
|
+
const stashes = [...(current.sources ?? current.stashes ?? [])];
|
|
505
505
|
if (stashes.length > 0) {
|
|
506
506
|
p.log.info(`You have ${stashes.length} existing stash source(s).`);
|
|
507
507
|
}
|
|
@@ -547,7 +547,6 @@ export async function stepStashSources(current) {
|
|
|
547
547
|
const action = await prompt(() => p.select({
|
|
548
548
|
message: "Add another stash source?",
|
|
549
549
|
options: [
|
|
550
|
-
{ value: "openviking", label: "OpenViking server", hint: "remote stash" },
|
|
551
550
|
{ value: "github-repo", label: "GitHub repository", hint: "custom URL" },
|
|
552
551
|
{ value: "filesystem", label: "Filesystem path", hint: "local directory" },
|
|
553
552
|
{ value: "done", label: "Done — no more sources" },
|
|
@@ -557,45 +556,6 @@ export async function stepStashSources(current) {
|
|
|
557
556
|
addMore = false;
|
|
558
557
|
break;
|
|
559
558
|
}
|
|
560
|
-
if (action === "openviking") {
|
|
561
|
-
const url = await promptOrBack(() => p.text({
|
|
562
|
-
message: "Enter the OpenViking server URL:",
|
|
563
|
-
placeholder: "https://your-openviking-server.example.com",
|
|
564
|
-
validate: (v) => {
|
|
565
|
-
if (!v?.trim())
|
|
566
|
-
return "URL cannot be empty";
|
|
567
|
-
if (!v.startsWith("http://") && !v.startsWith("https://"))
|
|
568
|
-
return "URL must start with http:// or https://";
|
|
569
|
-
},
|
|
570
|
-
}));
|
|
571
|
-
if (url === null)
|
|
572
|
-
continue;
|
|
573
|
-
const spin = p.spinner();
|
|
574
|
-
spin.start("Checking OpenViking server...");
|
|
575
|
-
const result = await detectOpenViking(url.trim());
|
|
576
|
-
if (result.available) {
|
|
577
|
-
spin.stop("Server is reachable");
|
|
578
|
-
}
|
|
579
|
-
else {
|
|
580
|
-
spin.stop("Server not reachable — adding anyway (it may be temporarily down)");
|
|
581
|
-
}
|
|
582
|
-
const name = await promptOrBack(() => p.text({
|
|
583
|
-
message: "Give this stash a name (optional):",
|
|
584
|
-
placeholder: "my-openviking",
|
|
585
|
-
}));
|
|
586
|
-
if (name === null)
|
|
587
|
-
continue;
|
|
588
|
-
// Use the normalized URL from detection (trailing slashes stripped)
|
|
589
|
-
const entry = { type: "openviking", url: result.url };
|
|
590
|
-
if (name.trim())
|
|
591
|
-
entry.name = name.trim();
|
|
592
|
-
if (!stashes.some((s) => s.url === entry.url)) {
|
|
593
|
-
stashes.push(entry);
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
p.log.warn("This URL is already configured.");
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
559
|
if (action === "github-repo") {
|
|
600
560
|
const url = await promptOrBack(() => p.text({
|
|
601
561
|
message: "Enter the GitHub repository URL:",
|
|
@@ -761,14 +721,14 @@ export function buildSetupSteps(options) {
|
|
|
761
721
|
id: "stash-sources",
|
|
762
722
|
label: "Stash Sources",
|
|
763
723
|
async run(ctx) {
|
|
764
|
-
const stashes = await
|
|
724
|
+
const stashes = await stepAddSources(ctx.config);
|
|
765
725
|
const platforms = await stepAgentPlatforms(ctx.config);
|
|
766
726
|
const merged = [...stashes];
|
|
767
727
|
for (const ps of platforms) {
|
|
768
728
|
if (!merged.some((s) => s.path === ps.path))
|
|
769
729
|
merged.push(ps);
|
|
770
730
|
}
|
|
771
|
-
ctx.apply({
|
|
731
|
+
ctx.apply({ sources: merged.length > 0 ? merged : undefined });
|
|
772
732
|
},
|
|
773
733
|
},
|
|
774
734
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { isWithin } from "
|
|
3
|
+
import { isWithin } from "../core/common";
|
|
4
4
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
5
5
|
/** Key to check in package.json for akm include configuration. */
|
|
6
6
|
const INCLUDE_CONFIG_KEYS = ["akm"];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source provider factory map.
|
|
3
|
+
*
|
|
4
|
+
* Maps source kind identifiers (e.g. "filesystem", "git", "website", "npm")
|
|
5
|
+
* to factory functions that build {@link SourceProvider} instances from a
|
|
6
|
+
* {@link SourceConfigEntry}.
|
|
7
|
+
*
|
|
8
|
+
* Distinct from the registry-discovery factory (`registry/factory.ts`).
|
|
9
|
+
* Both share `create-provider-registry.ts` for the underlying string→factory
|
|
10
|
+
* map.
|
|
11
|
+
*/
|
|
12
|
+
import { createProviderRegistry } from "../registry/create-provider-registry";
|
|
13
|
+
// ── Factory map ─────────────────────────────────────────────────────────────
|
|
14
|
+
const registry = createProviderRegistry();
|
|
15
|
+
export function registerSourceProvider(type, factory) {
|
|
16
|
+
registry.register(type, factory);
|
|
17
|
+
}
|
|
18
|
+
export function resolveSourceProviderFactory(type) {
|
|
19
|
+
return registry.resolve(type);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a {@link SourceProvider} for every enabled source in the config that
|
|
23
|
+
* has a registered factory.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveSourceProviders(config) {
|
|
26
|
+
const providers = [];
|
|
27
|
+
for (const entry of config.sources ?? config.stashes ?? []) {
|
|
28
|
+
if (entry.enabled === false)
|
|
29
|
+
continue;
|
|
30
|
+
const factory = registry.resolve(entry.type);
|
|
31
|
+
if (factory) {
|
|
32
|
+
providers.push(factory(entry));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return providers;
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourceProvider — minimal v1 interface (spec §2.1).
|
|
3
|
+
*
|
|
4
|
+
* A SourceProvider gets files into a directory. The indexer walks `path()`
|
|
5
|
+
* and reads files from disk. Search and show go through the indexer, not
|
|
6
|
+
* through provider methods.
|
|
7
|
+
*
|
|
8
|
+
* Three required members + one optional:
|
|
9
|
+
* - name configured source name
|
|
10
|
+
* - kind "filesystem" | "git" | "website" | "npm"
|
|
11
|
+
* - init(ctx) called once after construction
|
|
12
|
+
* - path() the directory the indexer walks (stable for instance lifetime)
|
|
13
|
+
* - sync?() refresh the directory from upstream (no-op for filesystem)
|
|
14
|
+
*
|
|
15
|
+
* All other writing/reading concerns live outside this interface:
|
|
16
|
+
* - Writes: src/core/write-source.ts (Phase 5)
|
|
17
|
+
* - Reads: src/indexer.ts (Phase 4)
|
|
18
|
+
* - Install: src/sources/providers/sync-from-ref.ts (install-time helpers,
|
|
19
|
+
* separate from configured-source plumbing)
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { resolveStashDir } from "../../core/common";
|
|
2
|
+
import { ConfigError } from "../../core/errors";
|
|
3
|
+
import { registerSourceProvider } from "../provider-factory";
|
|
4
|
+
/**
|
|
5
|
+
* Filesystem source — points at a directory the user already manages.
|
|
6
|
+
*
|
|
7
|
+
* Implements the v1 {@link SourceProvider} interface (spec §2.1, §2.4):
|
|
8
|
+
* just `{ name, kind, init, path }`. No `sync()` — content is the user's
|
|
9
|
+
* own directory, never refreshed by akm.
|
|
10
|
+
*/
|
|
11
|
+
class FilesystemSourceProvider {
|
|
12
|
+
kind = "filesystem";
|
|
13
|
+
name;
|
|
14
|
+
#stashDir;
|
|
15
|
+
constructor(entry) {
|
|
16
|
+
if (entry.type !== "filesystem") {
|
|
17
|
+
throw new ConfigError(`FilesystemSourceProvider invoked with type="${entry.type}"`);
|
|
18
|
+
}
|
|
19
|
+
this.#stashDir = entry.path ?? resolveStashDir();
|
|
20
|
+
if (!this.#stashDir) {
|
|
21
|
+
throw new ConfigError("filesystem source requires a `path`");
|
|
22
|
+
}
|
|
23
|
+
this.name = entry.name ?? this.#stashDir;
|
|
24
|
+
}
|
|
25
|
+
async init(_ctx) {
|
|
26
|
+
// Filesystem sources resolve their path eagerly in the constructor;
|
|
27
|
+
// init has nothing to do beyond letting the registry know we're ready.
|
|
28
|
+
}
|
|
29
|
+
path() {
|
|
30
|
+
return this.#stashDir;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// ── Self-register ───────────────────────────────────────────────────────────
|
|
34
|
+
registerSourceProvider("filesystem", (config) => new FilesystemSourceProvider(config));
|
|
35
|
+
export { FilesystemSourceProvider };
|