akm-cli 0.7.1 → 0.7.3
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 +35 -0
- package/dist/cli.js +62 -16
- package/dist/commands/history.js +2 -7
- package/dist/commands/info.js +2 -2
- package/dist/commands/installed-stashes.js +45 -1
- package/dist/commands/search.js +2 -2
- package/dist/commands/show.js +4 -19
- package/dist/commands/source-add.js +1 -1
- package/dist/core/common.js +16 -1
- package/dist/core/config.js +18 -3
- package/dist/indexer/db-search.js +33 -39
- package/dist/indexer/db.js +51 -1
- package/dist/indexer/graph-extraction.js +5 -3
- package/dist/indexer/indexer.js +334 -121
- package/dist/indexer/manifest.js +18 -23
- package/dist/indexer/memory-inference.js +47 -58
- package/dist/indexer/metadata.js +253 -21
- package/dist/indexer/search-source.js +11 -5
- package/dist/llm/client.js +61 -1
- package/dist/llm/embedder.js +8 -5
- package/dist/llm/embedders/local.js +8 -2
- package/dist/llm/embedders/remote.js +4 -2
- package/dist/llm/graph-extract.js +4 -4
- package/dist/llm/memory-infer.js +61 -33
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/output/cli-hints.js +5 -2
- package/dist/output/renderers.js +22 -49
- package/dist/registry/build-index.js +13 -18
- package/dist/setup/setup.js +238 -96
- package/dist/sources/providers/git.js +14 -2
- package/dist/sources/providers/website.js +4 -460
- package/dist/sources/website-ingest.js +470 -0
- package/dist/wiki/wiki.js +11 -1
- package/dist/workflows/parser.js +19 -4
- package/dist/workflows/runs.js +3 -3
- package/docs/README.md +10 -3
- package/docs/migration/release-notes/0.7.0.md +22 -0
- package/package.json +5 -2
package/dist/setup/setup.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
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 fs from "node:fs";
|
|
9
|
+
import os from "node:os";
|
|
8
10
|
import path from "node:path";
|
|
9
11
|
import * as p from "@clack/prompts";
|
|
10
12
|
import { akmInit } from "../commands/init";
|
|
@@ -19,15 +21,22 @@ import { probeLlmCapabilities } from "../llm/client";
|
|
|
19
21
|
import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder";
|
|
20
22
|
import { detectAgentPlatforms, detectOllama } from "./detect";
|
|
21
23
|
import { createSetupContext, runSetupSteps } from "./steps";
|
|
22
|
-
// ── Constants ───────────────────────────────────────────────────────────────
|
|
23
24
|
/**
|
|
24
25
|
* Recommended GitHub repositories shown during setup.
|
|
25
|
-
*
|
|
26
|
-
* Currently empty — populating from the akm-registry at runtime is a
|
|
27
|
-
* separate feature. The wizard prompt infrastructure is retained for that
|
|
28
|
-
* future use.
|
|
29
26
|
*/
|
|
30
|
-
const RECOMMENDED_GITHUB_REPOS = [
|
|
27
|
+
const RECOMMENDED_GITHUB_REPOS = [
|
|
28
|
+
{
|
|
29
|
+
url: "https://github.com/itlackey/akm-stash",
|
|
30
|
+
name: "itlackey/akm-stash",
|
|
31
|
+
hint: "official onboarding stash",
|
|
32
|
+
defaultSelected: true,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
url: "https://github.com/andrewyng/context-hub",
|
|
36
|
+
name: "andrewyng/context-hub",
|
|
37
|
+
hint: "optional community prompt and context stash",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
31
40
|
// Approximate first-download sizes used in the setup note.
|
|
32
41
|
// LOCAL_MODEL_APPROX_SIZE_MB tracks the default local model (DEFAULT_LOCAL_MODEL).
|
|
33
42
|
const LOCAL_MODEL_APPROX_SIZE_MB = 130;
|
|
@@ -83,6 +92,114 @@ async function promptOrBack(fn) {
|
|
|
83
92
|
return null;
|
|
84
93
|
return result;
|
|
85
94
|
}
|
|
95
|
+
function configuredSourceKey(source) {
|
|
96
|
+
return `${source.type}:${source.path ?? source.url ?? source.name ?? "unknown"}`;
|
|
97
|
+
}
|
|
98
|
+
function describeConfiguredSource(source) {
|
|
99
|
+
const target = source.path ?? source.url ?? "(unknown target)";
|
|
100
|
+
const typeLabel = source.type === "git" ? "Git" : source.type === "filesystem" ? "Filesystem" : source.type;
|
|
101
|
+
return {
|
|
102
|
+
value: configuredSourceKey(source),
|
|
103
|
+
label: source.name ?? target,
|
|
104
|
+
hint: `${typeLabel}: ${target}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function renderConfiguredSourceList(sources) {
|
|
108
|
+
return sources
|
|
109
|
+
.map((source) => {
|
|
110
|
+
const described = describeConfiguredSource(source);
|
|
111
|
+
return `- ${described.label} (${described.hint})`;
|
|
112
|
+
})
|
|
113
|
+
.join("\n");
|
|
114
|
+
}
|
|
115
|
+
function renderInstalledSourceList(installed) {
|
|
116
|
+
return installed.map((entry) => `- ${entry.id} (${entry.source})`).join("\n");
|
|
117
|
+
}
|
|
118
|
+
function cloneLlmConfig(llm) {
|
|
119
|
+
if (!llm)
|
|
120
|
+
return undefined;
|
|
121
|
+
return {
|
|
122
|
+
...llm,
|
|
123
|
+
...(llm.capabilities ? { capabilities: { ...llm.capabilities } } : {}),
|
|
124
|
+
...(llm.features ? { features: { ...llm.features } } : {}),
|
|
125
|
+
...(llm.extraParams ? { extraParams: { ...llm.extraParams } } : {}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
async function stepAdditionalSources(currentSources) {
|
|
129
|
+
const sources = [...currentSources];
|
|
130
|
+
let addMore = true;
|
|
131
|
+
while (addMore) {
|
|
132
|
+
const action = await prompt(() => p.select({
|
|
133
|
+
message: "Add another stash source?",
|
|
134
|
+
options: [
|
|
135
|
+
{ value: "done", label: "Done — no more sources" },
|
|
136
|
+
{ value: "github-repo", label: "GitHub repository", hint: "custom URL" },
|
|
137
|
+
{ value: "filesystem", label: "Filesystem path", hint: "local directory" },
|
|
138
|
+
],
|
|
139
|
+
initialValue: "done",
|
|
140
|
+
}));
|
|
141
|
+
if (action === "done") {
|
|
142
|
+
addMore = false;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if (action === "github-repo") {
|
|
146
|
+
const url = await promptOrBack(() => p.text({
|
|
147
|
+
message: "Enter the GitHub repository URL:",
|
|
148
|
+
placeholder: "https://github.com/owner/repo",
|
|
149
|
+
validate: (v) => {
|
|
150
|
+
if (!v?.trim())
|
|
151
|
+
return "URL cannot be empty";
|
|
152
|
+
},
|
|
153
|
+
}));
|
|
154
|
+
if (url === null)
|
|
155
|
+
continue;
|
|
156
|
+
const name = await promptOrBack(() => p.text({
|
|
157
|
+
message: "Give this stash a name (optional):",
|
|
158
|
+
placeholder: "my-repo",
|
|
159
|
+
}));
|
|
160
|
+
if (name === null)
|
|
161
|
+
continue;
|
|
162
|
+
const entry = { type: "git", url: url.trim() };
|
|
163
|
+
if (name.trim())
|
|
164
|
+
entry.name = name.trim();
|
|
165
|
+
if (!sources.some((s) => s.url === entry.url)) {
|
|
166
|
+
sources.push(entry);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
p.log.warn("This URL is already configured.");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (action === "filesystem") {
|
|
173
|
+
const fsPath = await promptOrBack(() => p.text({
|
|
174
|
+
message: "Enter the directory path:",
|
|
175
|
+
placeholder: "/path/to/stash",
|
|
176
|
+
validate: (v) => {
|
|
177
|
+
if (!v?.trim())
|
|
178
|
+
return "Path cannot be empty";
|
|
179
|
+
},
|
|
180
|
+
}));
|
|
181
|
+
if (fsPath === null)
|
|
182
|
+
continue;
|
|
183
|
+
const resolved = fsPath.trim();
|
|
184
|
+
const name = await promptOrBack(() => p.text({
|
|
185
|
+
message: "Give this stash a name (optional):",
|
|
186
|
+
placeholder: "my-stash",
|
|
187
|
+
}));
|
|
188
|
+
if (name === null)
|
|
189
|
+
continue;
|
|
190
|
+
const entry = { type: "filesystem", path: resolved };
|
|
191
|
+
if (name.trim())
|
|
192
|
+
entry.name = name.trim();
|
|
193
|
+
if (!sources.some((s) => s.path === entry.path)) {
|
|
194
|
+
sources.push(entry);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
p.log.warn("This path is already configured.");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return sources;
|
|
202
|
+
}
|
|
86
203
|
/**
|
|
87
204
|
* Quick connectivity check. Returns true if we can reach a public
|
|
88
205
|
* endpoint within 3 seconds, false otherwise. Used to skip network-
|
|
@@ -189,8 +306,10 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
189
306
|
}
|
|
190
307
|
spin.stop(remote ? "Remote embedding endpoint is ready." : "Local embedding model downloaded and ready.");
|
|
191
308
|
let db;
|
|
309
|
+
let probeDir;
|
|
192
310
|
try {
|
|
193
|
-
|
|
311
|
+
probeDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-setup-vec-probe-"));
|
|
312
|
+
db = openDatabase(path.join(probeDir, "probe.db"), config.embedding?.dimension ? { embeddingDim: config.embedding.dimension } : undefined);
|
|
194
313
|
if (isVecAvailable(db)) {
|
|
195
314
|
p.log.info("sqlite-vec is available for fast vector search.");
|
|
196
315
|
}
|
|
@@ -206,6 +325,14 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
206
325
|
finally {
|
|
207
326
|
if (db)
|
|
208
327
|
closeDatabase(db);
|
|
328
|
+
if (probeDir) {
|
|
329
|
+
try {
|
|
330
|
+
fs.rmSync(probeDir, { recursive: true, force: true });
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
/* ignore cleanup failure */
|
|
334
|
+
}
|
|
335
|
+
}
|
|
209
336
|
}
|
|
210
337
|
return { ok: true };
|
|
211
338
|
}
|
|
@@ -375,7 +502,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels) {
|
|
|
375
502
|
initialValue,
|
|
376
503
|
}));
|
|
377
504
|
if (choice === "keep")
|
|
378
|
-
return current.llm;
|
|
505
|
+
return cloneLlmConfig(current.llm);
|
|
379
506
|
if (choice === "none")
|
|
380
507
|
return undefined;
|
|
381
508
|
let llm;
|
|
@@ -466,7 +593,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels) {
|
|
|
466
593
|
}
|
|
467
594
|
return llm;
|
|
468
595
|
}
|
|
469
|
-
async function stepRegistries(current) {
|
|
596
|
+
export async function stepRegistries(current) {
|
|
470
597
|
const defaults = DEFAULT_CONFIG.registries ?? [];
|
|
471
598
|
const currentRegistries = current.registries ?? defaults;
|
|
472
599
|
const defaultUrls = new Set(defaults.map((r) => r.url));
|
|
@@ -507,119 +634,70 @@ async function stepRegistries(current) {
|
|
|
507
634
|
/**
|
|
508
635
|
* @internal Exported for testing only.
|
|
509
636
|
*/
|
|
510
|
-
export async function stepAddSources(current) {
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
637
|
+
export async function stepAddSources(current, options) {
|
|
638
|
+
const existingSources = [...(current.sources ?? current.stashes ?? [])];
|
|
639
|
+
const sources = [];
|
|
640
|
+
if (existingSources.length > 0) {
|
|
641
|
+
p.note(renderConfiguredSourceList(existingSources), "Configured stash sources");
|
|
642
|
+
const options = existingSources.map(describeConfiguredSource);
|
|
643
|
+
const selected = await prompt(() => p.multiselect({
|
|
644
|
+
message: "Configured stash sources — uncheck any you want to disable:",
|
|
645
|
+
options,
|
|
646
|
+
initialValues: options.map((option) => option.value),
|
|
647
|
+
required: false,
|
|
648
|
+
}));
|
|
649
|
+
for (const source of existingSources) {
|
|
650
|
+
if (selected.includes(configuredSourceKey(source))) {
|
|
651
|
+
sources.push(source);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if ((current.installed?.length ?? 0) > 0) {
|
|
656
|
+
p.note(renderInstalledSourceList(current.installed ?? []), "Installed managed stashes (preserved)");
|
|
514
657
|
}
|
|
515
658
|
// ── Recommended GitHub repos ───────────────────────────────────────────
|
|
516
659
|
// Skip the prompt entirely when there are no recommendations to show.
|
|
517
660
|
// The infrastructure is retained for a future registry-driven version.
|
|
518
661
|
if (RECOMMENDED_GITHUB_REPOS.length > 0) {
|
|
519
|
-
const existingUrls = new Set(
|
|
662
|
+
const existingUrls = new Set(sources.map((s) => s.url));
|
|
520
663
|
const repoOptions = RECOMMENDED_GITHUB_REPOS.map((r) => ({
|
|
521
664
|
value: r.url,
|
|
522
665
|
label: r.name,
|
|
523
666
|
hint: existingUrls.has(r.url) ? `${r.hint} (already added)` : r.hint,
|
|
524
667
|
}));
|
|
668
|
+
const initialValues = sources.length > 0
|
|
669
|
+
? repoOptions.filter((o) => existingUrls.has(o.value)).map((o) => o.value)
|
|
670
|
+
: RECOMMENDED_GITHUB_REPOS.filter((r) => r.defaultSelected).map((r) => r.url);
|
|
525
671
|
const selectedRepos = await prompt(() => p.multiselect({
|
|
526
672
|
message: "Recommended GitHub repositories — toggle to add or remove:",
|
|
527
673
|
options: repoOptions,
|
|
528
|
-
initialValues
|
|
674
|
+
initialValues,
|
|
529
675
|
required: false,
|
|
530
676
|
}));
|
|
531
677
|
// Add newly selected repos
|
|
532
678
|
for (const url of selectedRepos) {
|
|
533
679
|
if (!existingUrls.has(url)) {
|
|
534
680
|
const rec = RECOMMENDED_GITHUB_REPOS.find((r) => r.url === url);
|
|
535
|
-
|
|
681
|
+
sources.push({ type: "git", url, name: rec?.name });
|
|
536
682
|
existingUrls.add(url);
|
|
537
683
|
}
|
|
538
684
|
}
|
|
539
685
|
// Remove deselected repos that were previously configured
|
|
540
686
|
for (const rec of RECOMMENDED_GITHUB_REPOS) {
|
|
541
687
|
if (existingUrls.has(rec.url) && !selectedRepos.includes(rec.url)) {
|
|
542
|
-
const idx =
|
|
688
|
+
const idx = sources.findIndex((s) => s.url === rec.url);
|
|
543
689
|
if (idx !== -1) {
|
|
544
|
-
|
|
690
|
+
sources.splice(idx, 1);
|
|
545
691
|
existingUrls.delete(rec.url);
|
|
546
692
|
p.log.info(`Removed ${rec.name}.`);
|
|
547
693
|
}
|
|
548
694
|
}
|
|
549
695
|
}
|
|
550
696
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
while (addMore) {
|
|
554
|
-
const action = await prompt(() => p.select({
|
|
555
|
-
message: "Add another stash source?",
|
|
556
|
-
options: [
|
|
557
|
-
{ value: "github-repo", label: "GitHub repository", hint: "custom URL" },
|
|
558
|
-
{ value: "filesystem", label: "Filesystem path", hint: "local directory" },
|
|
559
|
-
{ value: "done", label: "Done — no more sources" },
|
|
560
|
-
],
|
|
561
|
-
}));
|
|
562
|
-
if (action === "done") {
|
|
563
|
-
addMore = false;
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
566
|
-
if (action === "github-repo") {
|
|
567
|
-
const url = await promptOrBack(() => p.text({
|
|
568
|
-
message: "Enter the GitHub repository URL:",
|
|
569
|
-
placeholder: "https://github.com/owner/repo",
|
|
570
|
-
validate: (v) => {
|
|
571
|
-
if (!v?.trim())
|
|
572
|
-
return "URL cannot be empty";
|
|
573
|
-
},
|
|
574
|
-
}));
|
|
575
|
-
if (url === null)
|
|
576
|
-
continue;
|
|
577
|
-
const name = await promptOrBack(() => p.text({
|
|
578
|
-
message: "Give this stash a name (optional):",
|
|
579
|
-
placeholder: "my-repo",
|
|
580
|
-
}));
|
|
581
|
-
if (name === null)
|
|
582
|
-
continue;
|
|
583
|
-
const entry = { type: "git", url: url.trim() };
|
|
584
|
-
if (name.trim())
|
|
585
|
-
entry.name = name.trim();
|
|
586
|
-
if (!stashes.some((s) => s.url === entry.url)) {
|
|
587
|
-
stashes.push(entry);
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
p.log.warn("This URL is already configured.");
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
if (action === "filesystem") {
|
|
594
|
-
const fsPath = await promptOrBack(() => p.text({
|
|
595
|
-
message: "Enter the directory path:",
|
|
596
|
-
placeholder: "/path/to/stash",
|
|
597
|
-
validate: (v) => {
|
|
598
|
-
if (!v?.trim())
|
|
599
|
-
return "Path cannot be empty";
|
|
600
|
-
},
|
|
601
|
-
}));
|
|
602
|
-
if (fsPath === null)
|
|
603
|
-
continue;
|
|
604
|
-
const resolved = fsPath.trim();
|
|
605
|
-
const name = await promptOrBack(() => p.text({
|
|
606
|
-
message: "Give this stash a name (optional):",
|
|
607
|
-
placeholder: "my-stash",
|
|
608
|
-
}));
|
|
609
|
-
if (name === null)
|
|
610
|
-
continue;
|
|
611
|
-
const entry = { type: "filesystem", path: resolved };
|
|
612
|
-
if (name.trim())
|
|
613
|
-
entry.name = name.trim();
|
|
614
|
-
if (!stashes.some((s) => s.path === entry.path)) {
|
|
615
|
-
stashes.push(entry);
|
|
616
|
-
}
|
|
617
|
-
else {
|
|
618
|
-
p.log.warn("This path is already configured.");
|
|
619
|
-
}
|
|
620
|
-
}
|
|
697
|
+
if (options?.promptForAdditional === false) {
|
|
698
|
+
return sources;
|
|
621
699
|
}
|
|
622
|
-
return
|
|
700
|
+
return stepAdditionalSources(sources);
|
|
623
701
|
}
|
|
624
702
|
async function stepAgentPlatforms(current) {
|
|
625
703
|
const platforms = detectAgentPlatforms();
|
|
@@ -627,7 +705,7 @@ async function stepAgentPlatforms(current) {
|
|
|
627
705
|
p.log.info("No agent platform configurations detected.");
|
|
628
706
|
return [];
|
|
629
707
|
}
|
|
630
|
-
const existingPaths = new Set((current.stashes ?? []).map((s) => s.path));
|
|
708
|
+
const existingPaths = new Set((current.sources ?? current.stashes ?? []).map((s) => s.path));
|
|
631
709
|
// Filter out platforms already configured
|
|
632
710
|
const newPlatforms = platforms.filter((pl) => !existingPaths.has(pl.path));
|
|
633
711
|
if (newPlatforms.length === 0) {
|
|
@@ -656,6 +734,60 @@ async function stepAgentPlatforms(current) {
|
|
|
656
734
|
}
|
|
657
735
|
return entries;
|
|
658
736
|
}
|
|
737
|
+
export async function stepAgentSelection(current, detections) {
|
|
738
|
+
const available = detections.filter((d) => d.available);
|
|
739
|
+
if (available.length === 0) {
|
|
740
|
+
return current.agent;
|
|
741
|
+
}
|
|
742
|
+
const initialValue = pickDefaultAgentProfile(detections, current.agent?.default) ?? available[0]?.name;
|
|
743
|
+
const selectedDefault = await prompt(() => p.select({
|
|
744
|
+
message: "Which detected agent CLI should be the default?",
|
|
745
|
+
options: [
|
|
746
|
+
...available.map((d) => ({
|
|
747
|
+
value: d.name,
|
|
748
|
+
label: d.name,
|
|
749
|
+
hint: d.resolvedPath ?? d.bin,
|
|
750
|
+
})),
|
|
751
|
+
{ value: "disabled", label: "Disabled", hint: "do not configure a default agent CLI" },
|
|
752
|
+
],
|
|
753
|
+
initialValue,
|
|
754
|
+
}));
|
|
755
|
+
if (selectedDefault === "disabled") {
|
|
756
|
+
if (!current.agent?.profiles && !current.agent?.timeoutMs) {
|
|
757
|
+
return undefined;
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
...(current.agent ?? {}),
|
|
761
|
+
default: undefined,
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
return {
|
|
765
|
+
...(current.agent ?? {}),
|
|
766
|
+
default: selectedDefault,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
export async function stepOutputConfig(current) {
|
|
770
|
+
const defaultOutput = current.output ?? DEFAULT_CONFIG.output ?? { format: "json", detail: "brief" };
|
|
771
|
+
const format = await prompt(() => p.select({
|
|
772
|
+
message: "Default output format?",
|
|
773
|
+
options: [
|
|
774
|
+
{ value: "json", label: "json", hint: "structured default" },
|
|
775
|
+
{ value: "text", label: "text", hint: "human-readable CLI output" },
|
|
776
|
+
{ value: "yaml", label: "yaml", hint: "structured text" },
|
|
777
|
+
],
|
|
778
|
+
initialValue: defaultOutput.format ?? "json",
|
|
779
|
+
}));
|
|
780
|
+
const detail = await prompt(() => p.select({
|
|
781
|
+
message: "Default output detail level?",
|
|
782
|
+
options: [
|
|
783
|
+
{ value: "brief", label: "brief", hint: "compact summaries" },
|
|
784
|
+
{ value: "normal", label: "normal", hint: "balanced detail" },
|
|
785
|
+
{ value: "full", label: "full", hint: "max available detail" },
|
|
786
|
+
],
|
|
787
|
+
initialValue: defaultOutput.detail ?? "brief",
|
|
788
|
+
}));
|
|
789
|
+
return { format: format, detail: detail };
|
|
790
|
+
}
|
|
659
791
|
/**
|
|
660
792
|
* Detect installed agent CLIs and produce an updated `agent` config block
|
|
661
793
|
* with a sensible `default` (the first detected profile that the user has
|
|
@@ -751,20 +883,20 @@ export function buildSetupSteps(options) {
|
|
|
751
883
|
id: "stash-sources",
|
|
752
884
|
label: "Stash Sources",
|
|
753
885
|
async run(ctx) {
|
|
754
|
-
const stashes = await stepAddSources(ctx.config);
|
|
755
|
-
const platforms = await stepAgentPlatforms(ctx.config);
|
|
886
|
+
const stashes = await stepAddSources(ctx.config, { promptForAdditional: false });
|
|
887
|
+
const platforms = await stepAgentPlatforms({ ...ctx.config, sources: stashes });
|
|
756
888
|
const merged = [...stashes];
|
|
757
889
|
for (const ps of platforms) {
|
|
758
890
|
if (!merged.some((s) => s.path === ps.path))
|
|
759
891
|
merged.push(ps);
|
|
760
892
|
}
|
|
761
|
-
|
|
893
|
+
const withAdditional = await stepAdditionalSources(merged);
|
|
894
|
+
ctx.apply({ sources: withAdditional.length > 0 ? withAdditional : undefined });
|
|
762
895
|
},
|
|
763
896
|
},
|
|
764
897
|
{
|
|
765
898
|
id: "agent-cli",
|
|
766
899
|
label: "Agent CLI",
|
|
767
|
-
nonInteractive: true,
|
|
768
900
|
async run(ctx) {
|
|
769
901
|
const result = stepAgentCliDetection(ctx.config);
|
|
770
902
|
const detected = result.detections.filter((d) => d.available);
|
|
@@ -775,7 +907,16 @@ export function buildSetupSteps(options) {
|
|
|
775
907
|
else {
|
|
776
908
|
p.log.info("No agent CLIs detected on PATH. Agent commands will be disabled until one is installed and `akm setup` is re-run.");
|
|
777
909
|
}
|
|
778
|
-
|
|
910
|
+
const agent = await stepAgentSelection({ ...ctx.config, agent: result.agent }, result.detections);
|
|
911
|
+
ctx.apply({ agent });
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
id: "output",
|
|
916
|
+
label: "Output Defaults",
|
|
917
|
+
async run(ctx) {
|
|
918
|
+
const output = await stepOutputConfig(ctx.config);
|
|
919
|
+
ctx.apply({ output });
|
|
779
920
|
},
|
|
780
921
|
},
|
|
781
922
|
];
|
|
@@ -811,7 +952,6 @@ export async function runSetupWizard() {
|
|
|
811
952
|
...ctx.config,
|
|
812
953
|
// Preserve fields the steps don't manage explicitly.
|
|
813
954
|
installed: current.installed,
|
|
814
|
-
output: current.output,
|
|
815
955
|
};
|
|
816
956
|
const semanticSearchMode = outcome.semantic;
|
|
817
957
|
const stashDir = newConfig.stashDir ?? current.stashDir ?? getDefaultStashDir();
|
|
@@ -828,6 +968,8 @@ export async function runSetupWizard() {
|
|
|
828
968
|
`Semantic search: ${semanticSearchMode.mode}`,
|
|
829
969
|
`Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
|
|
830
970
|
`Stash sources: ${allStashes.length}`,
|
|
971
|
+
`Agent default: ${newConfig.agent?.default ?? "disabled"}`,
|
|
972
|
+
`Output: ${newConfig.output?.format ?? "json"} / ${newConfig.output?.detail ?? "brief"}`,
|
|
831
973
|
].join("\n"), "Configuration Summary");
|
|
832
974
|
const shouldSave = await prompt(() => p.confirm({
|
|
833
975
|
message: "Save this configuration?",
|
|
@@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { TYPE_DIRS } from "../../core/asset-spec";
|
|
5
6
|
import { resolveStashDir } from "../../core/common";
|
|
6
7
|
import { loadConfig } from "../../core/config";
|
|
7
8
|
import { ConfigError, UsageError } from "../../core/errors";
|
|
@@ -123,7 +124,7 @@ async function ensureGitMirror(repo, cachePaths, options) {
|
|
|
123
124
|
* shared registry-index cache (12h TTL) and exposes the working tree as the
|
|
124
125
|
* stash content directory.
|
|
125
126
|
*/
|
|
126
|
-
async function syncMirroredRepo(config, options) {
|
|
127
|
+
export async function syncMirroredRepo(config, options) {
|
|
127
128
|
if (!config.url) {
|
|
128
129
|
throw new ConfigError("git stash entry requires a URL when no install ref is supplied");
|
|
129
130
|
}
|
|
@@ -303,7 +304,18 @@ function pullRepo(repoDir) {
|
|
|
303
304
|
}
|
|
304
305
|
function hasExtractedRepo(repoDir) {
|
|
305
306
|
try {
|
|
306
|
-
|
|
307
|
+
if (!fs.statSync(repoDir).isDirectory())
|
|
308
|
+
return false;
|
|
309
|
+
if (fs.statSync(path.join(repoDir, "content")).isDirectory())
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
/* fall through to root-layout detection */
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
if (!fs.statSync(repoDir).isDirectory())
|
|
317
|
+
return false;
|
|
318
|
+
return Object.values(TYPE_DIRS).some((dirName) => fs.existsSync(path.join(repoDir, dirName)));
|
|
307
319
|
}
|
|
308
320
|
catch {
|
|
309
321
|
return false;
|