akm-cli 0.5.0 → 0.6.0-rc1
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 +32 -5
- package/dist/asset-registry.js +29 -5
- package/dist/asset-spec.js +12 -5
- package/dist/cli-hints.js +300 -0
- package/dist/cli.js +218 -1357
- package/dist/common.js +147 -50
- package/dist/config.js +224 -13
- package/dist/create-provider-registry.js +1 -1
- package/dist/curate.js +258 -0
- package/dist/{local-search.js → db-search.js} +30 -19
- package/dist/db.js +168 -62
- package/dist/embedder.js +49 -273
- package/dist/embedders/cache.js +47 -0
- package/dist/embedders/local.js +152 -0
- package/dist/embedders/remote.js +121 -0
- package/dist/embedders/types.js +39 -0
- package/dist/errors.js +14 -3
- package/dist/frontmatter.js +61 -7
- package/dist/indexer.js +38 -7
- package/dist/info.js +2 -2
- package/dist/install-audit.js +16 -1
- package/dist/{installed-kits.js → installed-stashes.js} +48 -22
- package/dist/llm-client.js +92 -0
- package/dist/llm.js +14 -126
- package/dist/lockfile.js +28 -1
- package/dist/matchers.js +1 -1
- package/dist/metadata-enhance.js +53 -0
- package/dist/migration-help.js +75 -44
- package/dist/output-context.js +77 -0
- package/dist/output-shapes.js +198 -0
- package/dist/output-text.js +520 -0
- package/dist/paths.js +4 -4
- package/dist/providers/index.js +11 -0
- package/dist/providers/skills-sh.js +1 -1
- package/dist/providers/static-index.js +47 -45
- package/dist/registry-build-index.js +36 -29
- package/dist/registry-factory.js +2 -2
- package/dist/registry-resolve.js +8 -4
- package/dist/registry-search.js +62 -5
- package/dist/remember.js +172 -0
- package/dist/renderers.js +52 -0
- package/dist/search-source.js +73 -42
- package/dist/setup-steps.js +45 -0
- package/dist/setup.js +149 -76
- package/dist/stash-add.js +94 -38
- package/dist/stash-clone.js +4 -4
- package/dist/stash-provider-factory.js +2 -2
- package/dist/stash-provider.js +3 -1
- package/dist/stash-providers/filesystem.js +31 -1
- package/dist/stash-providers/git.js +209 -8
- package/dist/stash-providers/index.js +1 -0
- package/dist/stash-providers/npm.js +159 -0
- package/dist/stash-providers/provider-utils.js +162 -0
- package/dist/stash-providers/sync-from-ref.js +45 -0
- package/dist/stash-providers/tar-utils.js +151 -0
- package/dist/stash-providers/website.js +80 -4
- package/dist/stash-resolve.js +5 -5
- package/dist/stash-search.js +4 -4
- package/dist/stash-show.js +3 -3
- package/dist/wiki.js +6 -6
- package/dist/workflow-authoring.js +12 -4
- package/dist/workflow-markdown.js +9 -0
- package/dist/workflow-runs.js +12 -2
- package/docs/README.md +30 -0
- package/docs/migration/release-notes/0.0.13.md +4 -0
- package/docs/migration/release-notes/0.1.0.md +6 -0
- package/docs/migration/release-notes/0.2.0.md +6 -0
- package/docs/migration/release-notes/0.3.0.md +5 -0
- package/docs/migration/release-notes/0.5.0.md +6 -0
- package/docs/migration/release-notes/0.6.0.md +29 -0
- package/docs/migration/release-notes/README.md +21 -0
- package/package.json +3 -2
- package/dist/registry-install.js +0 -532
- /package/dist/{kit-include.js → stash-include.js} +0 -0
package/dist/renderers.js
CHANGED
|
@@ -411,6 +411,58 @@ const memoryMdRenderer = {
|
|
|
411
411
|
content: ctx.content(),
|
|
412
412
|
};
|
|
413
413
|
},
|
|
414
|
+
extractMetadata(entry, ctx) {
|
|
415
|
+
try {
|
|
416
|
+
const parsed = parseFrontmatter(ctx.content());
|
|
417
|
+
const fm = parsed.data;
|
|
418
|
+
// Description from frontmatter
|
|
419
|
+
const desc = toStringOrUndefined(fm.description);
|
|
420
|
+
if (desc && !entry.description) {
|
|
421
|
+
entry.description = desc;
|
|
422
|
+
entry.source = "frontmatter";
|
|
423
|
+
entry.confidence = 0.9;
|
|
424
|
+
}
|
|
425
|
+
// Tags from frontmatter
|
|
426
|
+
if (Array.isArray(fm.tags) && fm.tags.length > 0) {
|
|
427
|
+
const fmTags = fm.tags.filter((t) => typeof t === "string" && t.trim().length > 0);
|
|
428
|
+
if (fmTags.length > 0) {
|
|
429
|
+
entry.tags = Array.from(new Set([...(entry.tags ?? []), ...fmTags]));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Build searchHints from structured memory metadata fields
|
|
433
|
+
const hints = new Set(entry.searchHints ?? []);
|
|
434
|
+
const source = toStringOrUndefined(fm.source);
|
|
435
|
+
if (source)
|
|
436
|
+
hints.add(source);
|
|
437
|
+
// observed_at: prefer frontmatter value, fall back to file mtime
|
|
438
|
+
const fmObservedAt = toStringOrUndefined(fm.observed_at);
|
|
439
|
+
if (fmObservedAt) {
|
|
440
|
+
hints.add(`observed_at:${fmObservedAt}`);
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
// mtime fallback: format as ISO date (YYYY-MM-DD)
|
|
444
|
+
try {
|
|
445
|
+
const mtime = ctx.stat().mtime;
|
|
446
|
+
const isoDate = mtime.toISOString().slice(0, 10);
|
|
447
|
+
hints.add(`observed_at:${isoDate}`);
|
|
448
|
+
}
|
|
449
|
+
catch {
|
|
450
|
+
// Non-fatal: skip mtime fallback on stat error
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const expires = toStringOrUndefined(fm.expires);
|
|
454
|
+
if (expires)
|
|
455
|
+
hints.add(`expires:${expires}`);
|
|
456
|
+
if (fm.subjective === true)
|
|
457
|
+
hints.add("subjective");
|
|
458
|
+
if (hints.size > 0) {
|
|
459
|
+
entry.searchHints = Array.from(hints).filter(Boolean);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
// Non-fatal: skip metadata extraction on error
|
|
464
|
+
}
|
|
465
|
+
},
|
|
414
466
|
};
|
|
415
467
|
// ── 6. workflow-md ───────────────────────────────────────────────────────────
|
|
416
468
|
const workflowMdRenderer = {
|
package/dist/search-source.js
CHANGED
|
@@ -5,15 +5,25 @@ import { loadConfig } from "./config";
|
|
|
5
5
|
import { ensureGitMirror, getCachePaths, parseGitRepoUrl } from "./stash-providers/git";
|
|
6
6
|
import { ensureWebsiteMirror, getCachePaths as getWebsiteCachePaths } from "./stash-providers/website";
|
|
7
7
|
import { warn } from "./warn";
|
|
8
|
+
// Legacy "context-hub" / "github" type aliases are normalized to "git" at
|
|
9
|
+
// config-load time (see src/config.ts), so this set only contains the canonical
|
|
10
|
+
// type.
|
|
11
|
+
const GIT_STASH_TYPES = new Set(["git"]);
|
|
8
12
|
// ── Resolution ──────────────────────────────────────────────────────────────
|
|
9
13
|
/**
|
|
10
|
-
* Build the ordered list of stash sources
|
|
11
|
-
*
|
|
12
|
-
* 2. Additional stashes (filesystem and remote providers)
|
|
13
|
-
* 3. Installed kit paths (cache-managed, from registry)
|
|
14
|
+
* Build the ordered list of stash sources, walking every configured stash
|
|
15
|
+
* once. Iteration order:
|
|
14
16
|
*
|
|
15
|
-
* The
|
|
16
|
-
*
|
|
17
|
+
* 1. The primary stash directory (the entry marked `primary: true`, or the
|
|
18
|
+
* legacy top-level `stashDir`). Always emitted, even when the directory
|
|
19
|
+
* does not yet exist on disk, so callers can use it as the clone target.
|
|
20
|
+
* 2. Each entry in `config.stashes[]` (in declared order), excluding the
|
|
21
|
+
* one already emitted as the primary.
|
|
22
|
+
* 3. Each entry in `config.installed[]` (registry-managed stashes).
|
|
23
|
+
*
|
|
24
|
+
* Replaces the previous four-pass loop that walked `stashes[]` separately
|
|
25
|
+
* for each provider kind. Disabled entries (`enabled: false`) and entries
|
|
26
|
+
* whose disk path doesn't exist are filtered after deduplication.
|
|
17
27
|
*/
|
|
18
28
|
export function resolveStashSources(overrideStashDir, existingConfig) {
|
|
19
29
|
const stashDir = overrideStashDir ?? resolveStashDir();
|
|
@@ -36,48 +46,70 @@ export function resolveStashSources(overrideStashDir, existingConfig) {
|
|
|
36
46
|
});
|
|
37
47
|
}
|
|
38
48
|
};
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
// (1) + (2) Single pass over declared stashes — primary first if present,
|
|
50
|
+
// then the rest in declared order. The primary's directory is already
|
|
51
|
+
// injected as `sources[0]` above, so we only need to dedupe the source set.
|
|
52
|
+
const stashes = config.stashes ?? [];
|
|
53
|
+
const primaryIdx = stashes.findIndex((entry) => entry.primary === true);
|
|
54
|
+
const ordered = [];
|
|
55
|
+
if (primaryIdx >= 0) {
|
|
56
|
+
ordered.push(stashes[primaryIdx]);
|
|
57
|
+
stashes.forEach((entry, i) => {
|
|
58
|
+
if (i !== primaryIdx)
|
|
59
|
+
ordered.push(entry);
|
|
60
|
+
});
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
for (const entry of config.stashes ?? []) {
|
|
48
|
-
if (GIT_STASH_TYPES.has(entry.type) && entry.url && entry.enabled !== false) {
|
|
49
|
-
try {
|
|
50
|
-
const repo = parseGitRepoUrl(entry.url);
|
|
51
|
-
const cachePaths = getCachePaths(repo.canonicalUrl);
|
|
52
|
-
// The content/ subdirectory inside the extracted repo is the actual
|
|
53
|
-
// stash root containing DOC.md / SKILL.md files that the walker indexes.
|
|
54
|
-
const contentDir = path.join(cachePaths.repoDir, "content");
|
|
55
|
-
addSource(contentDir, entry.name, entry.wikiName);
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
warn(`Warning: failed to resolve git stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
62
|
+
else {
|
|
63
|
+
ordered.push(...stashes);
|
|
61
64
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
warn(`Warning: failed to resolve website stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
65
|
+
for (const entry of ordered) {
|
|
66
|
+
if (entry.enabled === false)
|
|
67
|
+
continue;
|
|
68
|
+
const dir = resolveEntryContentDir(entry);
|
|
69
|
+
if (dir == null)
|
|
70
|
+
continue;
|
|
71
|
+
addSource(dir, entry.name, entry.wikiName);
|
|
74
72
|
}
|
|
75
|
-
// Installed
|
|
73
|
+
// (3) Installed stashes (registry-managed). Always last.
|
|
76
74
|
for (const entry of config.installed ?? []) {
|
|
77
75
|
addSource(entry.stashRoot, entry.id, entry.wikiName);
|
|
78
76
|
}
|
|
79
77
|
return sources;
|
|
80
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the content directory the indexer should walk for a given config
|
|
81
|
+
* entry. Returns `undefined` if the entry has no walkable content (e.g. an
|
|
82
|
+
* `openviking` remote stash) so the caller can skip it.
|
|
83
|
+
*/
|
|
84
|
+
function resolveEntryContentDir(entry) {
|
|
85
|
+
if (entry.type === "filesystem" && entry.path) {
|
|
86
|
+
return entry.path;
|
|
87
|
+
}
|
|
88
|
+
if (GIT_STASH_TYPES.has(entry.type) && entry.url) {
|
|
89
|
+
try {
|
|
90
|
+
const repo = parseGitRepoUrl(entry.url);
|
|
91
|
+
const cachePaths = getCachePaths(repo.canonicalUrl);
|
|
92
|
+
// The content/ subdirectory inside the extracted repo is the actual
|
|
93
|
+
// stash root containing DOC.md / SKILL.md files that the walker indexes.
|
|
94
|
+
return path.join(cachePaths.repoDir, "content");
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
warn(`Warning: failed to resolve git stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (entry.type === "website" && entry.url) {
|
|
102
|
+
try {
|
|
103
|
+
return getWebsiteCachePaths(entry.url).stashDir;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
warn(`Warning: failed to resolve website stash cache for "${entry.url}": ${err instanceof Error ? err.message : String(err)}`);
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Remote-only providers (openviking) have no walkable directory.
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
81
113
|
/**
|
|
82
114
|
* Convenience: returns just the directory paths, preserving priority order.
|
|
83
115
|
*/
|
|
@@ -168,8 +200,7 @@ function isValidDirectory(dir) {
|
|
|
168
200
|
return false;
|
|
169
201
|
}
|
|
170
202
|
}
|
|
171
|
-
// ──
|
|
172
|
-
const GIT_STASH_TYPES = new Set(["context-hub", "github", "git"]);
|
|
203
|
+
// ── Stash cache integration ─────────────────────────────────────────────────
|
|
173
204
|
/**
|
|
174
205
|
* Ensure all cache-backed stash providers are refreshed so their cache
|
|
175
206
|
* directories exist on disk. Must be called (async) before
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable runner abstraction for `akm setup`.
|
|
3
|
+
*
|
|
4
|
+
* The interactive wizard in `setup.ts` historically ran a fixed series of
|
|
5
|
+
* step functions (`stepStashDir`, `stepOllama`, `stepLlm`, ...) inline.
|
|
6
|
+
* This module formalizes that pattern so steps can be:
|
|
7
|
+
* - reused by `akm init` (non-interactive preset, see Finding 31),
|
|
8
|
+
* - tested in isolation by passing a stub `SetupContext`, and
|
|
9
|
+
* - extended by plugins without touching the wizard call site.
|
|
10
|
+
*
|
|
11
|
+
* Steps mutate state through `SetupContext.apply()`, which accumulates a
|
|
12
|
+
* delta on top of the original config. `stepLlm` reading the embedding
|
|
13
|
+
* endpoint that `stepSemanticSearch` produced is the canonical example of
|
|
14
|
+
* why mutable accumulation is preferred over immutable returns.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Build a fresh `SetupContext` over a starting config. The returned context
|
|
18
|
+
* applies deltas in-place onto an internal accumulator and exposes the
|
|
19
|
+
* latest snapshot via `ctx.config`.
|
|
20
|
+
*/
|
|
21
|
+
export function createSetupContext(initial, options) {
|
|
22
|
+
let acc = { ...initial };
|
|
23
|
+
return {
|
|
24
|
+
get config() {
|
|
25
|
+
return acc;
|
|
26
|
+
},
|
|
27
|
+
nonInteractive: options.nonInteractive,
|
|
28
|
+
apply(delta) {
|
|
29
|
+
acc = { ...acc, ...delta };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Run a list of steps against a context. Steps marked interactive-only are
|
|
35
|
+
* skipped when `ctx.nonInteractive` is true. Returns the final accumulated
|
|
36
|
+
* config so callers can persist it without re-reading the context.
|
|
37
|
+
*/
|
|
38
|
+
export async function runSetupSteps(steps, ctx) {
|
|
39
|
+
for (const step of steps) {
|
|
40
|
+
if (ctx.nonInteractive && !step.nonInteractive)
|
|
41
|
+
continue;
|
|
42
|
+
await step.run(ctx);
|
|
43
|
+
}
|
|
44
|
+
return ctx.config;
|
|
45
|
+
}
|
package/dist/setup.js
CHANGED
|
@@ -17,15 +17,16 @@ import { akmInit } from "./init";
|
|
|
17
17
|
import { probeLlmCapabilities } from "./llm";
|
|
18
18
|
import { getDefaultStashDir } from "./paths";
|
|
19
19
|
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus } from "./semantic-status";
|
|
20
|
+
import { createSetupContext, runSetupSteps } from "./setup-steps";
|
|
20
21
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
];
|
|
22
|
+
/**
|
|
23
|
+
* Recommended GitHub repositories shown during setup.
|
|
24
|
+
*
|
|
25
|
+
* Currently empty — populating from the akm-registry at runtime is a
|
|
26
|
+
* separate feature. The wizard prompt infrastructure is retained for that
|
|
27
|
+
* future use.
|
|
28
|
+
*/
|
|
29
|
+
const RECOMMENDED_GITHUB_REPOS = [];
|
|
29
30
|
// Approximate first-download sizes used in the setup note.
|
|
30
31
|
// LOCAL_MODEL_APPROX_SIZE_MB tracks the default local model (DEFAULT_LOCAL_MODEL).
|
|
31
32
|
const LOCAL_MODEL_APPROX_SIZE_MB = 130;
|
|
@@ -136,7 +137,7 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
136
137
|
const remote = isRemoteEmbeddingConfig(config.embedding);
|
|
137
138
|
// For local embeddings, ensure the required package is installed first.
|
|
138
139
|
if (!remote) {
|
|
139
|
-
if (!
|
|
140
|
+
if (!isTransformersAvailable()) {
|
|
140
141
|
const spin = p.spinner();
|
|
141
142
|
spin.start("Installing @huggingface/transformers...");
|
|
142
143
|
try {
|
|
@@ -505,34 +506,38 @@ export async function stepStashSources(current) {
|
|
|
505
506
|
p.log.info(`You have ${stashes.length} existing stash source(s).`);
|
|
506
507
|
}
|
|
507
508
|
// ── Recommended GitHub repos ───────────────────────────────────────────
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
existingUrls.
|
|
509
|
+
// Skip the prompt entirely when there are no recommendations to show.
|
|
510
|
+
// The infrastructure is retained for a future registry-driven version.
|
|
511
|
+
if (RECOMMENDED_GITHUB_REPOS.length > 0) {
|
|
512
|
+
const existingUrls = new Set(stashes.map((s) => s.url));
|
|
513
|
+
const repoOptions = RECOMMENDED_GITHUB_REPOS.map((r) => ({
|
|
514
|
+
value: r.url,
|
|
515
|
+
label: r.name,
|
|
516
|
+
hint: existingUrls.has(r.url) ? `${r.hint} (already added)` : r.hint,
|
|
517
|
+
}));
|
|
518
|
+
const selectedRepos = await prompt(() => p.multiselect({
|
|
519
|
+
message: "Recommended GitHub repositories — toggle to add or remove:",
|
|
520
|
+
options: repoOptions,
|
|
521
|
+
initialValues: repoOptions.filter((o) => existingUrls.has(o.value)).map((o) => o.value),
|
|
522
|
+
required: false,
|
|
523
|
+
}));
|
|
524
|
+
// Add newly selected repos
|
|
525
|
+
for (const url of selectedRepos) {
|
|
526
|
+
if (!existingUrls.has(url)) {
|
|
527
|
+
const rec = RECOMMENDED_GITHUB_REPOS.find((r) => r.url === url);
|
|
528
|
+
stashes.push({ type: "git", url, name: rec?.name });
|
|
529
|
+
existingUrls.add(url);
|
|
530
|
+
}
|
|
526
531
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
532
|
+
// Remove deselected repos that were previously configured
|
|
533
|
+
for (const rec of RECOMMENDED_GITHUB_REPOS) {
|
|
534
|
+
if (existingUrls.has(rec.url) && !selectedRepos.includes(rec.url)) {
|
|
535
|
+
const idx = stashes.findIndex((s) => s.url === rec.url);
|
|
536
|
+
if (idx !== -1) {
|
|
537
|
+
stashes.splice(idx, 1);
|
|
538
|
+
existingUrls.delete(rec.url);
|
|
539
|
+
p.log.info(`Removed ${rec.name}.`);
|
|
540
|
+
}
|
|
536
541
|
}
|
|
537
542
|
}
|
|
538
543
|
}
|
|
@@ -685,60 +690,128 @@ async function stepAgentPlatforms(current) {
|
|
|
685
690
|
return entries;
|
|
686
691
|
}
|
|
687
692
|
// ── Main Wizard ─────────────────────────────────────────────────────────────
|
|
693
|
+
/**
|
|
694
|
+
* Build the canonical list of `SetupStep`s for the interactive wizard.
|
|
695
|
+
* Exposed (and exported) so tests and `akm init` can compose subsets.
|
|
696
|
+
*
|
|
697
|
+
* Each step wraps the existing `step*` functions, accumulating its result
|
|
698
|
+
* into the shared `SetupContext`. The `nonInteractive` flag controls
|
|
699
|
+
* inclusion in `akm init` (a non-interactive preset of `akm setup`).
|
|
700
|
+
*/
|
|
701
|
+
export function buildSetupSteps(options) {
|
|
702
|
+
const outcome = { semantic: options.semanticSearchOutcome };
|
|
703
|
+
// Local cache of Ollama-detected fields surfaced from the embedding step
|
|
704
|
+
// to the LLM step. Mutable by design — `stepLlm` needs them.
|
|
705
|
+
let ollamaEndpoint;
|
|
706
|
+
let ollamaChatModels;
|
|
707
|
+
const steps = [
|
|
708
|
+
{
|
|
709
|
+
id: "stash-dir",
|
|
710
|
+
label: "Stash Directory",
|
|
711
|
+
nonInteractive: true,
|
|
712
|
+
async run(ctx) {
|
|
713
|
+
const stashDir = await stepStashDir(ctx.config);
|
|
714
|
+
ctx.apply({ stashDir });
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
id: "embedding",
|
|
719
|
+
label: "Embedding",
|
|
720
|
+
async run(ctx) {
|
|
721
|
+
if (!options.online) {
|
|
722
|
+
ctx.apply({ embedding: ctx.config.embedding });
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const result = await stepOllama(ctx.config);
|
|
726
|
+
ollamaEndpoint = result.ollamaEndpoint;
|
|
727
|
+
ollamaChatModels = result.ollamaChatModels;
|
|
728
|
+
ctx.apply({ embedding: result.embedding });
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
id: "llm",
|
|
733
|
+
label: "LLM Provider",
|
|
734
|
+
async run(ctx) {
|
|
735
|
+
if (!options.online) {
|
|
736
|
+
ctx.apply({ llm: ctx.config.llm });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const llm = await stepLlm(ctx.config, ollamaEndpoint, ollamaChatModels);
|
|
740
|
+
ctx.apply({ llm });
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
id: "semantic-search",
|
|
745
|
+
label: "Semantic Search",
|
|
746
|
+
async run(ctx) {
|
|
747
|
+
const semantic = await stepSemanticSearch(ctx.config, ctx.config.embedding);
|
|
748
|
+
outcome.semantic = semantic;
|
|
749
|
+
ctx.apply({ semanticSearchMode: semantic.mode });
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
id: "registries",
|
|
754
|
+
label: "Registries",
|
|
755
|
+
async run(ctx) {
|
|
756
|
+
const registries = await stepRegistries(ctx.config);
|
|
757
|
+
ctx.apply({ registries });
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
id: "stash-sources",
|
|
762
|
+
label: "Stash Sources",
|
|
763
|
+
async run(ctx) {
|
|
764
|
+
const stashes = await stepStashSources(ctx.config);
|
|
765
|
+
const platforms = await stepAgentPlatforms(ctx.config);
|
|
766
|
+
const merged = [...stashes];
|
|
767
|
+
for (const ps of platforms) {
|
|
768
|
+
if (!merged.some((s) => s.path === ps.path))
|
|
769
|
+
merged.push(ps);
|
|
770
|
+
}
|
|
771
|
+
ctx.apply({ stashes: merged.length > 0 ? merged : undefined });
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
];
|
|
775
|
+
return { steps, outcome };
|
|
776
|
+
}
|
|
688
777
|
export async function runSetupWizard() {
|
|
689
778
|
p.intro("akm setup");
|
|
690
779
|
const current = loadUserConfig();
|
|
691
780
|
const configPath = getConfigPath();
|
|
692
|
-
// Step 1: Stash directory
|
|
693
|
-
p.log.step("Step 1: Stash Directory");
|
|
694
|
-
const stashDir = await stepStashDir(current);
|
|
695
781
|
// Quick connectivity check — skip network-dependent steps when offline
|
|
696
782
|
const online = await isOnline();
|
|
697
783
|
if (!online) {
|
|
698
784
|
p.log.warn("No network connectivity detected. Skipping Ollama detection and remote embedding checks.\n" +
|
|
699
785
|
"Local-only setup will continue. Re-run `akm setup` when online for full configuration.");
|
|
700
786
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
//
|
|
708
|
-
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
p.log.step("Step 5: Stash Sources");
|
|
718
|
-
const stashes = await stepStashSources(current);
|
|
719
|
-
// Step 6: Agent platform detection
|
|
720
|
-
p.log.step("Step 6: Agent Platform Detection");
|
|
721
|
-
const platformStashes = await stepAgentPlatforms(current);
|
|
722
|
-
// Merge platform stashes into main stashes list
|
|
723
|
-
const allStashes = [...stashes];
|
|
724
|
-
for (const ps of platformStashes) {
|
|
725
|
-
if (!allStashes.some((s) => s.path === ps.path)) {
|
|
726
|
-
allStashes.push(ps);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
// Build final config
|
|
787
|
+
const ctx = createSetupContext(current, { nonInteractive: false });
|
|
788
|
+
const { steps, outcome } = buildSetupSteps({
|
|
789
|
+
online,
|
|
790
|
+
semanticSearchOutcome: { mode: current.semanticSearchMode, prepareAssets: false },
|
|
791
|
+
});
|
|
792
|
+
// Wrap each step with a `p.log.step()` header so the wizard UI is
|
|
793
|
+
// unchanged. The canonical `runSetupSteps()` runner is used directly by
|
|
794
|
+
// `akm init` (non-interactive) and by tests.
|
|
795
|
+
const labeledSteps = steps.map((step) => ({
|
|
796
|
+
...step,
|
|
797
|
+
async run(stepCtx) {
|
|
798
|
+
p.log.step(step.label);
|
|
799
|
+
await step.run(stepCtx);
|
|
800
|
+
},
|
|
801
|
+
}));
|
|
802
|
+
await runSetupSteps(labeledSteps, ctx);
|
|
730
803
|
const newConfig = {
|
|
731
|
-
...
|
|
732
|
-
|
|
733
|
-
embedding,
|
|
734
|
-
llm,
|
|
735
|
-
registries,
|
|
736
|
-
stashes: allStashes.length > 0 ? allStashes : undefined,
|
|
737
|
-
// Preserve existing fields
|
|
738
|
-
semanticSearchMode: semanticSearchMode.mode,
|
|
804
|
+
...ctx.config,
|
|
805
|
+
// Preserve fields the steps don't manage explicitly.
|
|
739
806
|
installed: current.installed,
|
|
740
807
|
output: current.output,
|
|
741
808
|
};
|
|
809
|
+
const semanticSearchMode = outcome.semantic;
|
|
810
|
+
const stashDir = newConfig.stashDir ?? current.stashDir ?? getDefaultStashDir();
|
|
811
|
+
const embedding = newConfig.embedding;
|
|
812
|
+
const llm = newConfig.llm;
|
|
813
|
+
const registries = newConfig.registries;
|
|
814
|
+
const allStashes = newConfig.stashes ?? [];
|
|
742
815
|
// Confirm before saving
|
|
743
816
|
const effectiveRegistries = registries ?? DEFAULT_CONFIG.registries ?? [];
|
|
744
817
|
p.note([
|