akm-cli 0.7.2 → 0.7.4
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 +29 -0
- package/dist/cli.js +43 -11
- package/dist/commands/history.js +2 -7
- package/dist/commands/info.js +2 -2
- package/dist/commands/installed-stashes.js +44 -0
- package/dist/commands/search.js +2 -2
- package/dist/commands/show.js +4 -19
- package/dist/core/config.js +13 -1
- package/dist/indexer/db-search.js +17 -38
- package/dist/indexer/db.js +51 -1
- package/dist/indexer/indexer.js +312 -115
- package/dist/indexer/manifest.js +18 -23
- package/dist/indexer/metadata.js +253 -21
- package/dist/indexer/search-source.js +10 -4
- package/dist/output/cli-hints.js +3 -2
- package/dist/output/renderers.js +22 -49
- package/dist/registry/build-index.js +13 -18
- package/dist/setup/setup.js +216 -84
- package/dist/sources/providers/git.js +14 -2
- 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 +3 -3
- package/docs/migration/release-notes/0.7.0.md +8 -0
- package/docs/migration/release-notes/0.7.3.md +16 -0
- package/docs/migration/release-notes/0.7.4.md +17 -0
- package/package.json +2 -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";
|
|
@@ -90,6 +92,114 @@ async function promptOrBack(fn) {
|
|
|
90
92
|
return null;
|
|
91
93
|
return result;
|
|
92
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
|
+
}
|
|
93
203
|
/**
|
|
94
204
|
* Quick connectivity check. Returns true if we can reach a public
|
|
95
205
|
* endpoint within 3 seconds, false otherwise. Used to skip network-
|
|
@@ -196,8 +306,10 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
196
306
|
}
|
|
197
307
|
spin.stop(remote ? "Remote embedding endpoint is ready." : "Local embedding model downloaded and ready.");
|
|
198
308
|
let db;
|
|
309
|
+
let probeDir;
|
|
199
310
|
try {
|
|
200
|
-
|
|
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);
|
|
201
313
|
if (isVecAvailable(db)) {
|
|
202
314
|
p.log.info("sqlite-vec is available for fast vector search.");
|
|
203
315
|
}
|
|
@@ -213,6 +325,14 @@ async function prepareSemanticSearchAssets(config) {
|
|
|
213
325
|
finally {
|
|
214
326
|
if (db)
|
|
215
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
|
+
}
|
|
216
336
|
}
|
|
217
337
|
return { ok: true };
|
|
218
338
|
}
|
|
@@ -382,7 +502,7 @@ export async function stepLlm(current, ollamaEndpoint, ollamaChatModels) {
|
|
|
382
502
|
initialValue,
|
|
383
503
|
}));
|
|
384
504
|
if (choice === "keep")
|
|
385
|
-
return current.llm;
|
|
505
|
+
return cloneLlmConfig(current.llm);
|
|
386
506
|
if (choice === "none")
|
|
387
507
|
return undefined;
|
|
388
508
|
let llm;
|
|
@@ -514,10 +634,26 @@ export async function stepRegistries(current) {
|
|
|
514
634
|
/**
|
|
515
635
|
* @internal Exported for testing only.
|
|
516
636
|
*/
|
|
517
|
-
export async function stepAddSources(current) {
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
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)");
|
|
521
657
|
}
|
|
522
658
|
// ── Recommended GitHub repos ───────────────────────────────────────────
|
|
523
659
|
// Skip the prompt entirely when there are no recommendations to show.
|
|
@@ -558,78 +694,10 @@ export async function stepAddSources(current) {
|
|
|
558
694
|
}
|
|
559
695
|
}
|
|
560
696
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
while (addMore) {
|
|
564
|
-
const action = await prompt(() => p.select({
|
|
565
|
-
message: "Add another stash source?",
|
|
566
|
-
options: [
|
|
567
|
-
{ value: "github-repo", label: "GitHub repository", hint: "custom URL" },
|
|
568
|
-
{ value: "filesystem", label: "Filesystem path", hint: "local directory" },
|
|
569
|
-
{ value: "done", label: "Done — no more sources" },
|
|
570
|
-
],
|
|
571
|
-
}));
|
|
572
|
-
if (action === "done") {
|
|
573
|
-
addMore = false;
|
|
574
|
-
break;
|
|
575
|
-
}
|
|
576
|
-
if (action === "github-repo") {
|
|
577
|
-
const url = await promptOrBack(() => p.text({
|
|
578
|
-
message: "Enter the GitHub repository URL:",
|
|
579
|
-
placeholder: "https://github.com/owner/repo",
|
|
580
|
-
validate: (v) => {
|
|
581
|
-
if (!v?.trim())
|
|
582
|
-
return "URL cannot be empty";
|
|
583
|
-
},
|
|
584
|
-
}));
|
|
585
|
-
if (url === null)
|
|
586
|
-
continue;
|
|
587
|
-
const name = await promptOrBack(() => p.text({
|
|
588
|
-
message: "Give this stash a name (optional):",
|
|
589
|
-
placeholder: "my-repo",
|
|
590
|
-
}));
|
|
591
|
-
if (name === null)
|
|
592
|
-
continue;
|
|
593
|
-
const entry = { type: "git", url: url.trim() };
|
|
594
|
-
if (name.trim())
|
|
595
|
-
entry.name = name.trim();
|
|
596
|
-
if (!sources.some((s) => s.url === entry.url)) {
|
|
597
|
-
sources.push(entry);
|
|
598
|
-
}
|
|
599
|
-
else {
|
|
600
|
-
p.log.warn("This URL is already configured.");
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (action === "filesystem") {
|
|
604
|
-
const fsPath = await promptOrBack(() => p.text({
|
|
605
|
-
message: "Enter the directory path:",
|
|
606
|
-
placeholder: "/path/to/stash",
|
|
607
|
-
validate: (v) => {
|
|
608
|
-
if (!v?.trim())
|
|
609
|
-
return "Path cannot be empty";
|
|
610
|
-
},
|
|
611
|
-
}));
|
|
612
|
-
if (fsPath === null)
|
|
613
|
-
continue;
|
|
614
|
-
const resolved = fsPath.trim();
|
|
615
|
-
const name = await promptOrBack(() => p.text({
|
|
616
|
-
message: "Give this stash a name (optional):",
|
|
617
|
-
placeholder: "my-stash",
|
|
618
|
-
}));
|
|
619
|
-
if (name === null)
|
|
620
|
-
continue;
|
|
621
|
-
const entry = { type: "filesystem", path: resolved };
|
|
622
|
-
if (name.trim())
|
|
623
|
-
entry.name = name.trim();
|
|
624
|
-
if (!sources.some((s) => s.path === entry.path)) {
|
|
625
|
-
sources.push(entry);
|
|
626
|
-
}
|
|
627
|
-
else {
|
|
628
|
-
p.log.warn("This path is already configured.");
|
|
629
|
-
}
|
|
630
|
-
}
|
|
697
|
+
if (options?.promptForAdditional === false) {
|
|
698
|
+
return sources;
|
|
631
699
|
}
|
|
632
|
-
return sources;
|
|
700
|
+
return stepAdditionalSources(sources);
|
|
633
701
|
}
|
|
634
702
|
async function stepAgentPlatforms(current) {
|
|
635
703
|
const platforms = detectAgentPlatforms();
|
|
@@ -637,7 +705,7 @@ async function stepAgentPlatforms(current) {
|
|
|
637
705
|
p.log.info("No agent platform configurations detected.");
|
|
638
706
|
return [];
|
|
639
707
|
}
|
|
640
|
-
const existingPaths = new Set((current.stashes ?? []).map((s) => s.path));
|
|
708
|
+
const existingPaths = new Set((current.sources ?? current.stashes ?? []).map((s) => s.path));
|
|
641
709
|
// Filter out platforms already configured
|
|
642
710
|
const newPlatforms = platforms.filter((pl) => !existingPaths.has(pl.path));
|
|
643
711
|
if (newPlatforms.length === 0) {
|
|
@@ -666,6 +734,60 @@ async function stepAgentPlatforms(current) {
|
|
|
666
734
|
}
|
|
667
735
|
return entries;
|
|
668
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
|
+
}
|
|
669
791
|
/**
|
|
670
792
|
* Detect installed agent CLIs and produce an updated `agent` config block
|
|
671
793
|
* with a sensible `default` (the first detected profile that the user has
|
|
@@ -761,20 +883,20 @@ export function buildSetupSteps(options) {
|
|
|
761
883
|
id: "stash-sources",
|
|
762
884
|
label: "Stash Sources",
|
|
763
885
|
async run(ctx) {
|
|
764
|
-
const stashes = await stepAddSources(ctx.config);
|
|
765
|
-
const platforms = await stepAgentPlatforms(ctx.config);
|
|
886
|
+
const stashes = await stepAddSources(ctx.config, { promptForAdditional: false });
|
|
887
|
+
const platforms = await stepAgentPlatforms({ ...ctx.config, sources: stashes });
|
|
766
888
|
const merged = [...stashes];
|
|
767
889
|
for (const ps of platforms) {
|
|
768
890
|
if (!merged.some((s) => s.path === ps.path))
|
|
769
891
|
merged.push(ps);
|
|
770
892
|
}
|
|
771
|
-
|
|
893
|
+
const withAdditional = await stepAdditionalSources(merged);
|
|
894
|
+
ctx.apply({ sources: withAdditional.length > 0 ? withAdditional : undefined });
|
|
772
895
|
},
|
|
773
896
|
},
|
|
774
897
|
{
|
|
775
898
|
id: "agent-cli",
|
|
776
899
|
label: "Agent CLI",
|
|
777
|
-
nonInteractive: true,
|
|
778
900
|
async run(ctx) {
|
|
779
901
|
const result = stepAgentCliDetection(ctx.config);
|
|
780
902
|
const detected = result.detections.filter((d) => d.available);
|
|
@@ -785,7 +907,16 @@ export function buildSetupSteps(options) {
|
|
|
785
907
|
else {
|
|
786
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.");
|
|
787
909
|
}
|
|
788
|
-
|
|
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 });
|
|
789
920
|
},
|
|
790
921
|
},
|
|
791
922
|
];
|
|
@@ -821,7 +952,6 @@ export async function runSetupWizard() {
|
|
|
821
952
|
...ctx.config,
|
|
822
953
|
// Preserve fields the steps don't manage explicitly.
|
|
823
954
|
installed: current.installed,
|
|
824
|
-
output: current.output,
|
|
825
955
|
};
|
|
826
956
|
const semanticSearchMode = outcome.semantic;
|
|
827
957
|
const stashDir = newConfig.stashDir ?? current.stashDir ?? getDefaultStashDir();
|
|
@@ -838,6 +968,8 @@ export async function runSetupWizard() {
|
|
|
838
968
|
`Semantic search: ${semanticSearchMode.mode}`,
|
|
839
969
|
`Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
|
|
840
970
|
`Stash sources: ${allStashes.length}`,
|
|
971
|
+
`Agent default: ${newConfig.agent?.default ?? "disabled"}`,
|
|
972
|
+
`Output: ${newConfig.output?.format ?? "json"} / ${newConfig.output?.detail ?? "brief"}`,
|
|
841
973
|
].join("\n"), "Configuration Summary");
|
|
842
974
|
const shouldSave = await prompt(() => p.confirm({
|
|
843
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;
|
package/dist/wiki/wiki.js
CHANGED
|
@@ -894,7 +894,17 @@ export function regenerateWikiIndex(stashDir, name) {
|
|
|
894
894
|
}
|
|
895
895
|
lines.push("");
|
|
896
896
|
}
|
|
897
|
-
|
|
897
|
+
const next = `${lines.join("\n")}\n`;
|
|
898
|
+
const indexPath = path.join(wikiDir, INDEX_MD);
|
|
899
|
+
try {
|
|
900
|
+
const current = fs.readFileSync(indexPath, "utf8");
|
|
901
|
+
if (current === next)
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
/* missing file -> write below */
|
|
906
|
+
}
|
|
907
|
+
fs.writeFileSync(indexPath, next, "utf8");
|
|
898
908
|
return true;
|
|
899
909
|
}
|
|
900
910
|
catch {
|
package/dist/workflows/parser.js
CHANGED
|
@@ -24,10 +24,25 @@ const SUBSECTION_COMPLETION_CRITERIA = "Completion Criteria";
|
|
|
24
24
|
* the matcher and parser cannot drift.
|
|
25
25
|
*/
|
|
26
26
|
export function looksLikeWorkflow(body) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
const structuralBody = stripFencedCodeBlocks(body);
|
|
28
|
+
return (/^#\s+Workflow:\s+/m.test(structuralBody) &&
|
|
29
|
+
/^##\s+Step:\s+/m.test(structuralBody) &&
|
|
30
|
+
/^Step ID:\s+/m.test(structuralBody) &&
|
|
31
|
+
/^###\s+Instructions\s*$/m.test(structuralBody));
|
|
32
|
+
}
|
|
33
|
+
function stripFencedCodeBlocks(body) {
|
|
34
|
+
let inFence = false;
|
|
35
|
+
const lines = body.split(/\r?\n/);
|
|
36
|
+
const stripped = [];
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (/^\s*```/.test(line) || /^\s*~~~/.test(line)) {
|
|
39
|
+
inFence = !inFence;
|
|
40
|
+
stripped.push("");
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
stripped.push(inFence ? "" : line);
|
|
44
|
+
}
|
|
45
|
+
return stripped.join("\n");
|
|
31
46
|
}
|
|
32
47
|
export function parseWorkflow(markdown, source) {
|
|
33
48
|
const errors = [];
|
package/dist/workflows/runs.js
CHANGED
|
@@ -5,7 +5,7 @@ import { loadConfig } from "../core/config";
|
|
|
5
5
|
import { NotFoundError, UsageError } from "../core/errors";
|
|
6
6
|
import { appendEvent } from "../core/events";
|
|
7
7
|
import { getDbPath } from "../core/paths";
|
|
8
|
-
import { closeDatabase,
|
|
8
|
+
import { closeDatabase, openExistingDatabase } from "../indexer/db";
|
|
9
9
|
import { resolveSourceEntries } from "../indexer/search-source";
|
|
10
10
|
import { resolveSourcesForOrigin } from "../registry/origin-resolve";
|
|
11
11
|
import { resolveAssetPath } from "../sources/resolve";
|
|
@@ -264,7 +264,7 @@ function readWorkflowDocumentFromIndex(sourcePath, ref) {
|
|
|
264
264
|
const dbPath = getDbPath();
|
|
265
265
|
if (!fs.existsSync(dbPath))
|
|
266
266
|
return null;
|
|
267
|
-
const db =
|
|
267
|
+
const db = openExistingDatabase(dbPath);
|
|
268
268
|
try {
|
|
269
269
|
const parsed = parseAssetRef(ref);
|
|
270
270
|
const entryKey = `${sourcePath}:${parsed.type}:${parsed.name}`;
|
|
@@ -315,7 +315,7 @@ function resolveWorkflowEntryId(sourcePath, ref) {
|
|
|
315
315
|
const dbPath = getDbPath();
|
|
316
316
|
if (!fs.existsSync(dbPath))
|
|
317
317
|
return null;
|
|
318
|
-
const db =
|
|
318
|
+
const db = openExistingDatabase(dbPath);
|
|
319
319
|
try {
|
|
320
320
|
const parsed = parseAssetRef(ref);
|
|
321
321
|
const entryKey = `${sourcePath}:${parsed.type}:${parsed.name}`;
|
package/docs/README.md
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
## Upgrading
|
|
12
12
|
|
|
13
|
-
- [v1 migration guide](migration/v1.md) -- The path from 0.x to v1.0
|
|
14
|
-
- [Release notes (latest: 0.7.0)](migration/release-notes/0.7.0.md) -- Per-release notes drop into `migration/release-notes
|
|
13
|
+
- [v1 migration guide](migration/v1.md) -- The path from 0.x to v1.0, including the `.stash.json` removal scheduled for v0.8.0
|
|
14
|
+
- [Release notes (latest: 0.7.0)](migration/release-notes/0.7.0.md) -- Per-release notes drop into `migration/release-notes/`, including current pre-release removals
|
|
15
15
|
- [v0.5 → v0.6 migration guide](migration/v0.5-to-v0.6.md) -- Every breaking change with before/after code, publisher checklist, and troubleshooting
|
|
16
16
|
|
|
17
17
|
## Reference
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- [CLI](cli.md) -- All `akm` commands and flags
|
|
20
20
|
- [Registry](registry.md) -- Registries, search, hosting, and managing sources
|
|
21
21
|
- [Configuration](configuration.md) -- Providers, settings, and Ollama setup
|
|
22
|
-
- [Filesystem](technical/filesystem.md) -- Directory layout
|
|
22
|
+
- [Filesystem](technical/filesystem.md) -- Directory layout plus `.stash.json` deprecation and migration notes
|
|
23
23
|
|
|
24
24
|
## Official Ecosystem Repositories
|
|
25
25
|
|
|
@@ -17,6 +17,14 @@ If you are coming from 0.6.x, the
|
|
|
17
17
|
canonical upgrade reference. This file is the executive summary of
|
|
18
18
|
what's new in 0.7.0.
|
|
19
19
|
|
|
20
|
+
For stash authors on the 0.7.x pre-release line: `.stash.json` remains supported
|
|
21
|
+
for compatibility in this release, but it is deprecated and will be removed in
|
|
22
|
+
v0.8.0. That timeline is intentional: during this aggressive pre-release
|
|
23
|
+
phase-out window, compatibility shims do not stay around until 1.0 unless they
|
|
24
|
+
still earn their cost. Prefer frontmatter for markdown assets and structured
|
|
25
|
+
code comments for scripts, and migrate any remaining `.stash.json` metadata
|
|
26
|
+
before taking the 0.8 upgrade.
|
|
27
|
+
|
|
20
28
|
## Major new surfaces in 0.7.0
|
|
21
29
|
|
|
22
30
|
### Proposal queue (`akm proposal *`) — new (#225, #226, #233)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Migration notes for akm v0.7.3
|
|
2
|
+
|
|
3
|
+
0.7.3 includes stability improvements, security hardening, and UX refinements. Key changes include:
|
|
4
|
+
|
|
5
|
+
- **`akm index --enrich` opt-in for LLM passes** — index-time enrichment work such as metadata enhancement, memory inference, and graph extraction now runs only when explicitly requested with `--enrich`. Default indexing is faster and no longer surprises operators with LLM-backed work during normal maintenance runs.
|
|
6
|
+
- **Config backup snapshots before writes** — config writes now create AKM cache backups so setup/config flows have a recovery path if a config is overwritten or corrupted during development or testing.
|
|
7
|
+
- **Setup wizard UX refresh** — `akm setup` now better reflects the real configured state: source prompts are ordered more sensibly, configured and preserved stash information is surfaced, agent defaults can be selected explicitly (including disabled), and post-setup indexing does not implicitly enable enrichment.
|
|
8
|
+
- **CI workflows updated for current GitHub Actions runtimes** — CI, release, and publishing workflows now use current action majors (`checkout@v5`, `cache@v5`, `setup-node@v5`, `upload-artifact@v5`, `download-artifact@v6`) to stay off deprecated Node 20 action runtimes.
|
|
9
|
+
- **Embedding-dimension drift on read-only DB opens** — read/telemetry paths no longer mutate the live index schema with the default embedding dimension. `akm info`, search/show parity paths, and related readers now preserve the configured embedding shape instead of downgrading vector tables.
|
|
10
|
+
- **Incremental index churn across multiple source layouts** — incremental indexing is now significantly more stable for filename-less legacy metadata, wiki-root sources, repo-root git stash layouts, non-indexed companion files, and cross-source dedupe cases.
|
|
11
|
+
- **Git source indexing for repo-root stashes** — git-backed sources no longer assume a `<repo>/content` subtree; repo-root stash layouts are indexed correctly and cached mirrors are treated as fresh instead of being needlessly refreshed.
|
|
12
|
+
- **`show` metadata no longer depends on `.stash.json`** — command and skill summary/show metadata now comes from file-local frontmatter and renderer parsing rather than the deprecated disk fallback sidecar.
|
|
13
|
+
- **`.stash.json` no longer drives incremental stale detection** — editing `.stash.json` alone no longer forces directories to rescan during incremental indexing.
|
|
14
|
+
- **One-shot URL ingest for `akm import` and `akm wiki stash`** — both commands now accept a single HTTP/HTTPS URL in addition to file paths and stdin. `akm import <url>` fetches the exact page, converts it to markdown, and writes it into `knowledge/` using a URL-path-derived default name. `akm wiki stash <wiki> <url>` fetches the exact page, converts it to markdown, and writes it into `wikis/<wiki>/raw/`. Neither command registers a persistent website source or crawls linked pages.
|
|
15
|
+
|
|
16
|
+
For stash authors on the 0.7.x pre-release line: `.stash.json` remains supported for compatibility in this release, but it is deprecated and will be removed in v0.8.0. That timeline is intentional: during this aggressive pre-release phase-out window, compatibility shims do not stay around until 1.0 unless they still earn their cost. Prefer frontmatter for markdown assets and structured code comments for scripts, and migrate any remaining `.stash.json` metadata before taking the 0.8 upgrade.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Migration notes for akm v0.7.4
|
|
2
|
+
|
|
3
|
+
This is a patch release that fixes the publish process. No functional changes from v0.7.3.
|
|
4
|
+
|
|
5
|
+
0.7.4 includes the same features as 0.7.3:
|
|
6
|
+
- **`akm index --enrich` opt-in for LLM passes** — index-time enrichment work such as metadata enhancement, memory inference, and graph extraction now runs only when explicitly requested with `--enrich`. Default indexing is faster and no longer surprises operators with LLM-backed work during normal maintenance runs.
|
|
7
|
+
- **Config backup snapshots before writes** — config writes now create AKM cache backups so setup/config flows have a recovery path if a config is overwritten or corrupted during development or testing.
|
|
8
|
+
- **Setup wizard UX refresh** — `akm setup` now better reflects the real configured state: source prompts are ordered more sensibly, configured and preserved stash information is surfaced, agent defaults can be selected explicitly (including disabled), and post-setup indexing does not implicitly enable enrichment.
|
|
9
|
+
- **CI workflows updated for current GitHub Actions runtimes** — CI, release, and publishing workflows now use current action majors (`checkout@v5`, `cache@v5`, `setup-node@v5`, `upload-artifact@v5`, `download-artifact@v6`) to stay off deprecated Node 20 action runtimes.
|
|
10
|
+
- **Embedding-dimension drift on read-only DB opens** — read/telemetry paths no longer mutate the live index schema with the default embedding dimension. `akm info`, search/show parity paths, and related readers now preserve the configured embedding shape instead of downgrading vector tables.
|
|
11
|
+
- **Incremental index churn across multiple source layouts** — incremental indexing is now significantly more stable for filename-less legacy metadata, wiki-root sources, repo-root git stash layouts, non-indexed companion files, and cross-source dedupe cases.
|
|
12
|
+
- **Git source indexing for repo-root stashes** — git-backed sources no longer assume a `<repo>/content` subtree; repo-root stash layouts are indexed correctly and cached mirrors are treated as fresh instead of being needlessly refreshed.
|
|
13
|
+
- **`show` metadata no longer depends on `.stash.json`** — command and skill summary/show metadata now comes from file-local frontmatter and renderer parsing rather than the deprecated disk fallback sidecar.
|
|
14
|
+
- **`.stash.json` no longer drives incremental stale detection** — editing `.stash.json` alone no longer forces directories to rescan during incremental indexing.
|
|
15
|
+
- **One-shot URL ingest for `akm import` and `akm wiki stash`** — both commands now accept a single HTTP/HTTPS URL in addition to file paths and stdin. `akm import <url>` fetches the exact page, converts it to markdown, and writes it into `knowledge/` using a URL-path-derived default name. `akm wiki stash <wiki> <url>` fetches the exact page, converts it to markdown, and writes it into `wikis/<wiki>/raw/`. Neither command registers a persistent website source or crawls linked pages.
|
|
16
|
+
|
|
17
|
+
For stash authors on the 0.7.x pre-release line: `.stash.json` remains supported for compatibility in this release, but it is deprecated and will be removed in v0.8.0. That timeline is intentional: during this aggressive pre-release phase-out window, compatibility shims do not stay around until 1.0 unless they still earn their cost. Prefer frontmatter for markdown assets and structured code comments for scripts, and migrate any remaining `.stash.json` metadata before taking the 0.8 upgrade.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"type": "module",
|
|
5
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": [
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"typescript": "^5.9.3"
|
|
67
67
|
},
|
|
68
68
|
"optionalDependencies": {
|
|
69
|
-
"@huggingface/transformers": "
|
|
69
|
+
"@huggingface/transformers": "^4.2.0",
|
|
70
70
|
"sqlite-vec": "0.1.7-alpha.2"
|
|
71
71
|
},
|
|
72
72
|
"engines": {
|