akm-cli 0.7.0-rc1 → 0.7.0
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/dist/src/cli.js +100 -16
- package/dist/src/commands/config-cli.js +42 -0
- package/dist/src/commands/history.js +78 -7
- package/dist/src/commands/registry-search.js +69 -6
- package/dist/src/commands/search.js +30 -3
- package/dist/src/commands/show.js +29 -0
- package/dist/src/commands/source-add.js +5 -1
- package/dist/src/commands/source-manage.js +7 -1
- package/dist/src/core/config.js +28 -0
- package/dist/src/indexer/db-search.js +1 -0
- package/dist/src/indexer/indexer.js +16 -2
- package/dist/src/indexer/matchers.js +1 -1
- package/dist/src/indexer/search-source.js +4 -2
- package/dist/src/integrations/agent/profiles.js +1 -1
- package/dist/src/integrations/agent/spawn.js +67 -16
- package/dist/src/integrations/github.js +9 -3
- package/dist/src/llm/embedders/remote.js +37 -3
- package/dist/src/output/cli-hints.js +15 -2
- package/dist/src/output/renderers.js +3 -1
- package/dist/src/output/shapes.js +8 -1
- package/dist/src/output/text.js +156 -3
- package/dist/src/registry/build-index.js +5 -4
- package/dist/src/registry/providers/static-index.js +3 -1
- package/dist/src/setup/setup.js +9 -0
- package/dist/src/wiki/wiki.js +54 -6
- package/dist/src/workflows/runs.js +37 -3
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +1 -1
- package/dist/tests/bench/attribution.test.js +24 -23
- package/dist/tests/bench/cleanup.js +31 -0
- package/dist/tests/bench/cli.js +366 -31
- package/dist/tests/bench/cli.test.js +282 -14
- package/dist/tests/bench/corpus.js +3 -0
- package/dist/tests/bench/corpus.test.js +10 -10
- package/dist/tests/bench/doctor.js +525 -0
- package/dist/tests/bench/driver.js +77 -22
- package/dist/tests/bench/driver.test.js +142 -1
- package/dist/tests/bench/environment.js +233 -0
- package/dist/tests/bench/environment.test.js +199 -0
- package/dist/tests/bench/evolve.js +67 -0
- package/dist/tests/bench/evolve.test.js +12 -4
- package/dist/tests/bench/failure-modes.test.js +52 -3
- package/dist/tests/bench/feedback-integrity.test.js +3 -2
- package/dist/tests/bench/leakage.test.js +105 -2
- package/dist/tests/bench/learning-curve.test.js +3 -2
- package/dist/tests/bench/metrics.js +102 -26
- package/dist/tests/bench/metrics.test.js +10 -4
- package/dist/tests/bench/opencode-config.js +194 -0
- package/dist/tests/bench/opencode-config.test.js +370 -0
- package/dist/tests/bench/report.js +73 -9
- package/dist/tests/bench/report.test.js +59 -10
- package/dist/tests/bench/run-config.js +355 -0
- package/dist/tests/bench/run-config.test.js +298 -0
- package/dist/tests/bench/run-curate-test.js +32 -0
- package/dist/tests/bench/run-failing-tasks.js +56 -0
- package/dist/tests/bench/run-full-bench.js +51 -0
- package/dist/tests/bench/run-items36-targeted.js +69 -0
- package/dist/tests/bench/run-nano-quick.js +42 -0
- package/dist/tests/bench/run-waveg-targeted.js +62 -0
- package/dist/tests/bench/runner.js +257 -94
- package/dist/tests/bench/tmp.js +90 -0
- package/dist/tests/bench/trajectory.js +2 -2
- package/dist/tests/bench/verifier.js +6 -1
- package/dist/tests/bench/workflow-spec.js +11 -24
- package/dist/tests/bench/workflow-spec.test.js +1 -1
- package/dist/tests/bench/workflow-trace.js +34 -0
- package/dist/tests/cli-errors.test.js +1 -0
- package/dist/tests/commands/history.test.js +195 -0
- package/dist/tests/config.test.js +25 -0
- package/dist/tests/e2e.test.js +23 -2
- package/dist/tests/fixtures/stashes/load.js +1 -1
- package/dist/tests/fixtures/stashes/load.test.js +11 -2
- package/dist/tests/indexer.test.js +12 -1
- package/dist/tests/output-baseline.test.js +2 -1
- package/dist/tests/output-shapes-unit.test.js +3 -1
- package/dist/tests/registry-build-index.test.js +17 -1
- package/dist/tests/registry-providers/static-index.test.js +34 -0
- package/dist/tests/registry-search.test.js +200 -0
- package/dist/tests/remember-frontmatter.test.js +11 -13
- package/dist/tests/source-qa-fixes.test.js +18 -0
- package/dist/tests/source-registry.test.js +3 -3
- package/dist/tests/source-source.test.js +61 -1
- package/dist/tests/workflow-qa-fixes.test.js +18 -0
- package/package.json +1 -1
|
@@ -485,6 +485,206 @@ describe("AKM_REGISTRY_URL env var", () => {
|
|
|
485
485
|
srv2.close();
|
|
486
486
|
}
|
|
487
487
|
});
|
|
488
|
+
// Problem A: env-based override must preserve provider type
|
|
489
|
+
test("provider::url syntax routes to the declared provider type", async () => {
|
|
490
|
+
// Stand up a skills-sh-shaped endpoint
|
|
491
|
+
const skillsSrv = Bun.serve({
|
|
492
|
+
port: 0,
|
|
493
|
+
fetch() {
|
|
494
|
+
return new Response(JSON.stringify({
|
|
495
|
+
skills: [{ id: "org/tools/my-skill", name: "my-skill", installs: 200, source: "org/tools" }],
|
|
496
|
+
}), { headers: { "Content-Type": "application/json" } });
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
process.env.AKM_REGISTRY_URL = `skills-sh::http://localhost:${skillsSrv.port}`;
|
|
500
|
+
try {
|
|
501
|
+
const result = await searchRegistry("my-skill");
|
|
502
|
+
// skills-sh provider should have handled this — hits use skills-sh id format
|
|
503
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
504
|
+
expect(result.hits[0].id).toBe("skills-sh:org/tools/my-skill");
|
|
505
|
+
expect(result.hits[0].installRef).toBe("github:org/tools");
|
|
506
|
+
expect(result.warnings).toEqual([]);
|
|
507
|
+
}
|
|
508
|
+
finally {
|
|
509
|
+
skillsSrv.stop(true);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
test("bare URL in env var defaults to static-index provider", async () => {
|
|
513
|
+
const srv = serveIndex(FIXTURE_INDEX);
|
|
514
|
+
process.env.AKM_REGISTRY_URL = srv.url;
|
|
515
|
+
try {
|
|
516
|
+
const result = await searchRegistry("openkit");
|
|
517
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
518
|
+
// static-index uses the stash id directly
|
|
519
|
+
expect(result.hits[0].id).toBe("npm:@itlackey/openkit");
|
|
520
|
+
}
|
|
521
|
+
finally {
|
|
522
|
+
srv.close();
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
test("unknown provider type in env var produces warning, not crash", async () => {
|
|
526
|
+
process.env.AKM_REGISTRY_URL = `no-such-provider::http://127.0.0.1:1/index.json`;
|
|
527
|
+
const result = await searchRegistry("anything");
|
|
528
|
+
expect(result.hits).toEqual([]);
|
|
529
|
+
expect(result.warnings.length).toBe(1);
|
|
530
|
+
expect(result.warnings[0]).toContain("no-such-provider");
|
|
531
|
+
});
|
|
532
|
+
test("mixed provider types in comma-separated env var", async () => {
|
|
533
|
+
const staticSrv = serveIndex({
|
|
534
|
+
version: 3,
|
|
535
|
+
updatedAt: "2026-01-01T00:00:00Z",
|
|
536
|
+
stashes: [
|
|
537
|
+
{
|
|
538
|
+
id: "npm:env-static-stash",
|
|
539
|
+
name: "env-static-stash",
|
|
540
|
+
ref: "env-static-stash",
|
|
541
|
+
source: "npm",
|
|
542
|
+
tags: ["deploy"],
|
|
543
|
+
},
|
|
544
|
+
],
|
|
545
|
+
});
|
|
546
|
+
const skillsSrv = Bun.serve({
|
|
547
|
+
port: 0,
|
|
548
|
+
fetch() {
|
|
549
|
+
return new Response(JSON.stringify({
|
|
550
|
+
skills: [{ id: "user/tools/env-skill", name: "env-skill", installs: 100, source: "user/tools" }],
|
|
551
|
+
}), { headers: { "Content-Type": "application/json" } });
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
process.env.AKM_REGISTRY_URL = `${staticSrv.url},skills-sh::http://localhost:${skillsSrv.port}`;
|
|
555
|
+
try {
|
|
556
|
+
const result = await searchRegistry("env");
|
|
557
|
+
const ids = result.hits.map((h) => h.id);
|
|
558
|
+
expect(ids).toContain("npm:env-static-stash");
|
|
559
|
+
expect(ids).toContain("skills-sh:user/tools/env-skill");
|
|
560
|
+
expect(result.warnings).toEqual([]);
|
|
561
|
+
}
|
|
562
|
+
finally {
|
|
563
|
+
staticSrv.close();
|
|
564
|
+
skillsSrv.stop(true);
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
// ── Score normalization (Problem B) ─────────────────────────────────────────
|
|
569
|
+
describe("cross-provider score normalization", () => {
|
|
570
|
+
test("scores from all providers are in [0, 1] after normalization", async () => {
|
|
571
|
+
// static-index raw scores can exceed 1 (e.g. exact name + tag + description).
|
|
572
|
+
// After normalization, all scores in the merged response must be <= 1.
|
|
573
|
+
const srv = serveIndex(FIXTURE_INDEX);
|
|
574
|
+
try {
|
|
575
|
+
const result = await searchRegistry("openkit bun typescript starter", {
|
|
576
|
+
registries: [{ url: srv.url }],
|
|
577
|
+
});
|
|
578
|
+
for (const hit of result.hits) {
|
|
579
|
+
if (hit.score !== undefined) {
|
|
580
|
+
expect(hit.score).toBeGreaterThanOrEqual(0);
|
|
581
|
+
expect(hit.score).toBeLessThanOrEqual(1);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
finally {
|
|
586
|
+
srv.close();
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
test("top hit within a provider batch retains score = 1 after normalization", async () => {
|
|
590
|
+
// The highest-scored hit in each provider batch should map to exactly 1.0.
|
|
591
|
+
const srv = serveIndex(FIXTURE_INDEX);
|
|
592
|
+
try {
|
|
593
|
+
const result = await searchRegistry("openkit", {
|
|
594
|
+
registries: [{ url: srv.url }],
|
|
595
|
+
});
|
|
596
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
597
|
+
const topScore = result.hits[0].score;
|
|
598
|
+
expect(topScore).toBe(1);
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
srv.close();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
test("merged multi-provider results are ordered by normalized score", async () => {
|
|
605
|
+
// Provider A: static-index with a moderate-relevance match.
|
|
606
|
+
// Provider B: skills-sh with a high-installs match.
|
|
607
|
+
// After normalization each batch has max=1; the better-matched kit wins.
|
|
608
|
+
const staticSrv = serveIndex({
|
|
609
|
+
version: 3,
|
|
610
|
+
updatedAt: "2026-01-01T00:00:00Z",
|
|
611
|
+
stashes: [
|
|
612
|
+
{
|
|
613
|
+
id: "npm:exact-name-match",
|
|
614
|
+
name: "deploy",
|
|
615
|
+
description: "exact match",
|
|
616
|
+
ref: "exact-name-match",
|
|
617
|
+
source: "npm",
|
|
618
|
+
tags: ["deploy"],
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: "npm:partial-match",
|
|
622
|
+
name: "deployment-helper",
|
|
623
|
+
description: "partial",
|
|
624
|
+
ref: "partial-match",
|
|
625
|
+
source: "npm",
|
|
626
|
+
tags: [],
|
|
627
|
+
},
|
|
628
|
+
],
|
|
629
|
+
});
|
|
630
|
+
const skillsSrv = Bun.serve({
|
|
631
|
+
port: 0,
|
|
632
|
+
fetch() {
|
|
633
|
+
return new Response(JSON.stringify({
|
|
634
|
+
skills: [
|
|
635
|
+
{ id: "org/deploy-skill", name: "deploy-skill", installs: 1000, source: "org/deploy-skill" },
|
|
636
|
+
{ id: "org/other-skill", name: "other-skill", installs: 100, source: "org/other" },
|
|
637
|
+
],
|
|
638
|
+
}), { headers: { "Content-Type": "application/json" } });
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
try {
|
|
642
|
+
const result = await searchRegistry("deploy", {
|
|
643
|
+
registries: [{ url: staticSrv.url }, { url: `http://localhost:${skillsSrv.port}`, provider: "skills-sh" }],
|
|
644
|
+
});
|
|
645
|
+
// All scores in [0, 1]
|
|
646
|
+
for (const hit of result.hits) {
|
|
647
|
+
if (hit.score !== undefined) {
|
|
648
|
+
expect(hit.score).toBeGreaterThanOrEqual(0);
|
|
649
|
+
expect(hit.score).toBeLessThanOrEqual(1);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
// Results should be sorted descending
|
|
653
|
+
for (let i = 1; i < result.hits.length; i++) {
|
|
654
|
+
expect((result.hits[i - 1].score ?? 0) >= (result.hits[i].score ?? 0)).toBe(true);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
finally {
|
|
658
|
+
staticSrv.close();
|
|
659
|
+
skillsSrv.stop(true);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
test("single-hit provider batch normalizes to score 1", async () => {
|
|
663
|
+
const srv = serveIndex({
|
|
664
|
+
version: 3,
|
|
665
|
+
updatedAt: "2026-01-01T00:00:00Z",
|
|
666
|
+
stashes: [
|
|
667
|
+
{
|
|
668
|
+
id: "npm:only-stash",
|
|
669
|
+
name: "only-stash",
|
|
670
|
+
description: "only one",
|
|
671
|
+
ref: "only-stash",
|
|
672
|
+
source: "npm",
|
|
673
|
+
tags: ["unique"],
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
});
|
|
677
|
+
try {
|
|
678
|
+
const result = await searchRegistry("unique", {
|
|
679
|
+
registries: [{ url: srv.url }],
|
|
680
|
+
});
|
|
681
|
+
expect(result.hits.length).toBe(1);
|
|
682
|
+
expect(result.hits[0].score).toBe(1);
|
|
683
|
+
}
|
|
684
|
+
finally {
|
|
685
|
+
srv.close();
|
|
686
|
+
}
|
|
687
|
+
});
|
|
488
688
|
});
|
|
489
689
|
// ── Provenance tagging ──────────────────────────────────────────────────────
|
|
490
690
|
describe("provenance tagging", () => {
|
|
@@ -67,6 +67,13 @@ describe("zero-flag remember", () => {
|
|
|
67
67
|
const content = fs.readFileSync(json.path, "utf8");
|
|
68
68
|
expect(content.startsWith("---")).toBe(false);
|
|
69
69
|
});
|
|
70
|
+
test("reads stdin when --format json is present", () => {
|
|
71
|
+
const { result } = runCli(["remember", "--name", "from-stdin", "--format", "json"], { input: "stdin body" });
|
|
72
|
+
expect(result.status).toBe(0);
|
|
73
|
+
const json = JSON.parse(result.stdout);
|
|
74
|
+
expect(fs.readFileSync(json.path, "utf8")).toContain("stdin body");
|
|
75
|
+
expect(fs.readFileSync(json.path, "utf8")).not.toContain("\njson");
|
|
76
|
+
});
|
|
70
77
|
});
|
|
71
78
|
// ── CLI args (Mode 1) ────────────────────────────────────────────────────────
|
|
72
79
|
describe("remember --tag", () => {
|
|
@@ -227,21 +234,12 @@ describe("remember --auto", () => {
|
|
|
227
234
|
expect(parsed.data.observed_at).toBe("2026-01-15");
|
|
228
235
|
void result; // suppress unused variable warning
|
|
229
236
|
});
|
|
230
|
-
test("--auto without any tags from heuristics or CLI
|
|
237
|
+
test("--auto without any tags from heuristics or CLI still writes the memory", () => {
|
|
231
238
|
// Plain text body — no code block, no URL. Heuristics won't derive any tags.
|
|
232
239
|
const { result } = runCli(["remember", "Plain text note without any tags derivable", "--auto"]);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (result.status !== 0) {
|
|
237
|
-
const json = JSON.parse(result.stderr);
|
|
238
|
-
expect(json.error).toContain("tags");
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
// If some future heuristic adds tags for plain text, just verify the file is written
|
|
242
|
-
const json = JSON.parse(result.stdout);
|
|
243
|
-
expect(fs.existsSync(json.path)).toBe(true);
|
|
244
|
-
}
|
|
240
|
+
expect(result.status).toBe(0);
|
|
241
|
+
const json = JSON.parse(result.stdout);
|
|
242
|
+
expect(fs.existsSync(json.path)).toBe(true);
|
|
245
243
|
});
|
|
246
244
|
test("--auto + explicit --tag satisfies required-field check", () => {
|
|
247
245
|
const body = "No special content here";
|
|
@@ -14,7 +14,9 @@ import os from "node:os";
|
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { akmListSources, akmUpdate } from "../src/commands/installed-stashes";
|
|
16
16
|
import { akmAdd } from "../src/commands/source-add";
|
|
17
|
+
import { addStash } from "../src/commands/source-manage";
|
|
17
18
|
import { loadConfig, saveConfig } from "../src/core/config";
|
|
19
|
+
import { ConfigError } from "../src/core/errors";
|
|
18
20
|
const createdTmpDirs = [];
|
|
19
21
|
function createTmpDir(prefix = "akm-qa-") {
|
|
20
22
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
@@ -115,6 +117,22 @@ describe("issue #9: --name flag persisted for filesystem sources", () => {
|
|
|
115
117
|
expect(typeof added?.name).toBe("string");
|
|
116
118
|
});
|
|
117
119
|
});
|
|
120
|
+
describe("manual QA add validation", () => {
|
|
121
|
+
test("akmAdd rejects writable installs for npm refs before syncing", async () => {
|
|
122
|
+
saveConfig({ semanticSearchMode: "off" });
|
|
123
|
+
await expect(akmAdd({ ref: "npm:left-pad", writable: true })).rejects.toThrow(ConfigError);
|
|
124
|
+
});
|
|
125
|
+
test("addStash rejects openviking providers before persisting config", () => {
|
|
126
|
+
saveConfig({ semanticSearchMode: "off" });
|
|
127
|
+
expect(() => addStash({ target: "https://example.com", providerType: "openviking" })).toThrow(ConfigError);
|
|
128
|
+
expect(loadConfig().sources).toBeUndefined();
|
|
129
|
+
});
|
|
130
|
+
test("addStash rejects writable website sources before persisting config", () => {
|
|
131
|
+
saveConfig({ semanticSearchMode: "off" });
|
|
132
|
+
expect(() => addStash({ target: "https://example.com", providerType: "website", writable: true })).toThrow(ConfigError);
|
|
133
|
+
expect(loadConfig().sources).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
118
136
|
// ── Issue #10: filesystem kind = "filesystem" in list output ──────────────
|
|
119
137
|
describe("issue #10: filesystem kind in list output", () => {
|
|
120
138
|
test("filesystem source has kind='filesystem' in akmListSources", async () => {
|
|
@@ -155,9 +155,9 @@ describe("akmListSources", () => {
|
|
|
155
155
|
semanticSearchMode: "off",
|
|
156
156
|
installed: [
|
|
157
157
|
{
|
|
158
|
-
id: "github
|
|
159
|
-
source: "
|
|
160
|
-
ref: "github
|
|
158
|
+
id: "git:https://github.com/owner/repo",
|
|
159
|
+
source: "git",
|
|
160
|
+
ref: "git:https://github.com/owner/repo.git",
|
|
161
161
|
artifactUrl: "https://github.com/owner/repo.git",
|
|
162
162
|
stashRoot,
|
|
163
163
|
cacheDir,
|
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { saveConfig } from "../src/core/config";
|
|
6
|
-
import { findSourceForPath, getPrimarySource, isEditable, resolveAllStashDirs, resolveSourceEntries, } from "../src/indexer/search-source";
|
|
6
|
+
import { ensureSourceCaches, findSourceForPath, getPrimarySource, isEditable, resolveAllStashDirs, resolveSourceEntries, } from "../src/indexer/search-source";
|
|
7
7
|
const originalStashDir = process.env.AKM_STASH_DIR;
|
|
8
8
|
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
9
9
|
let testConfigDir = "";
|
|
@@ -219,3 +219,63 @@ describe("isEditable", () => {
|
|
|
219
219
|
expect(isEditable("/some/random/path/file.sh")).toBe(true);
|
|
220
220
|
});
|
|
221
221
|
});
|
|
222
|
+
// ── ensureSourceCaches ────────────────────────────────────────────────────────
|
|
223
|
+
describe("ensureSourceCaches", () => {
|
|
224
|
+
test("completes without error when sources[] is empty", async () => {
|
|
225
|
+
const config = { semanticSearchMode: "off", sources: [] };
|
|
226
|
+
await expect(ensureSourceCaches(config)).resolves.toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
test("completes without error when sources[] has filesystem entries (no sync needed)", async () => {
|
|
229
|
+
const config = {
|
|
230
|
+
semanticSearchMode: "off",
|
|
231
|
+
sources: [{ type: "filesystem", path: stashDir }],
|
|
232
|
+
};
|
|
233
|
+
await expect(ensureSourceCaches(config)).resolves.toBeUndefined();
|
|
234
|
+
});
|
|
235
|
+
test("reads from sources[] not stashes[] — git entries in sources[] are processed", async () => {
|
|
236
|
+
// A config where sources[] has a git entry and stashes is undefined.
|
|
237
|
+
// We can't run a real git mirror in unit tests, but we verify that the
|
|
238
|
+
// function does NOT throw even when the git URL is unreachable (it warns).
|
|
239
|
+
const config = {
|
|
240
|
+
semanticSearchMode: "off",
|
|
241
|
+
sources: [
|
|
242
|
+
{
|
|
243
|
+
type: "git",
|
|
244
|
+
url: "https://github.com/example/nonexistent-repo.git",
|
|
245
|
+
name: "test-git",
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
};
|
|
249
|
+
// Should resolve (not reject) — failures are warn-only
|
|
250
|
+
await expect(ensureSourceCaches(config)).resolves.toBeUndefined();
|
|
251
|
+
});
|
|
252
|
+
test("reads from sources[] not stashes[] — website entries in sources[] are processed", async () => {
|
|
253
|
+
// A config where sources[] has a website entry and stashes is undefined.
|
|
254
|
+
// The mirror will fail (unreachable host) but the function warns, not throws.
|
|
255
|
+
const config = {
|
|
256
|
+
semanticSearchMode: "off",
|
|
257
|
+
sources: [
|
|
258
|
+
{
|
|
259
|
+
type: "website",
|
|
260
|
+
url: "https://example.invalid/docs",
|
|
261
|
+
name: "test-website",
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
};
|
|
265
|
+
await expect(ensureSourceCaches(config)).resolves.toBeUndefined();
|
|
266
|
+
});
|
|
267
|
+
test("stashes[] entries are still processed for one-release backwards compat", async () => {
|
|
268
|
+
// stashes[] is deprecated but still accepted in the runtime shape for one release.
|
|
269
|
+
const config = {
|
|
270
|
+
semanticSearchMode: "off",
|
|
271
|
+
stashes: [
|
|
272
|
+
{
|
|
273
|
+
type: "git",
|
|
274
|
+
url: "https://github.com/example/nonexistent-repo.git",
|
|
275
|
+
name: "legacy-git",
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
};
|
|
279
|
+
await expect(ensureSourceCaches(config)).resolves.toBeUndefined();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -282,6 +282,24 @@ describe("workflow status with workflow ref", () => {
|
|
|
282
282
|
const err = JSON.parse(status.stderr);
|
|
283
283
|
expect(err.error).toContain("No workflow runs found");
|
|
284
284
|
});
|
|
285
|
+
test("next with an unknown run id returns WORKFLOW_NOT_FOUND", () => {
|
|
286
|
+
const env = createWorkflowEnv();
|
|
287
|
+
setupWorkflow(env);
|
|
288
|
+
const next = runCli(["workflow", "next", "bogus-run-id"], env);
|
|
289
|
+
expect(next.status).toBe(1);
|
|
290
|
+
const err = JSON.parse(next.stderr);
|
|
291
|
+
expect(err.code).toBe("WORKFLOW_NOT_FOUND");
|
|
292
|
+
expect(err.hint).toContain("akm workflow list --active");
|
|
293
|
+
});
|
|
294
|
+
test("status with an unknown run id returns WORKFLOW_NOT_FOUND", () => {
|
|
295
|
+
const env = createWorkflowEnv();
|
|
296
|
+
setupWorkflow(env);
|
|
297
|
+
const status = runCli(["workflow", "status", "bogus-run-id"], env);
|
|
298
|
+
expect(status.status).toBe(1);
|
|
299
|
+
const err = JSON.parse(status.stderr);
|
|
300
|
+
expect(err.code).toBe("WORKFLOW_NOT_FOUND");
|
|
301
|
+
expect(err.hint).toContain("akm workflow list --active");
|
|
302
|
+
});
|
|
285
303
|
});
|
|
286
304
|
// ---------------------------------------------------------------------------
|
|
287
305
|
// 7. workflow create name validation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.7.0
|
|
3
|
+
"version": "0.7.0",
|
|
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": [
|