akm-cli 0.6.1 → 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/CHANGELOG.md +66 -0
- package/dist/{cli.js → src/cli.js} +712 -34
- package/dist/{commands → src/commands}/config-cli.js +47 -4
- package/dist/src/commands/distill.js +283 -0
- package/dist/src/commands/events.js +108 -0
- package/dist/src/commands/history.js +191 -0
- package/dist/{commands → src/commands}/installed-stashes.js +1 -1
- package/dist/src/commands/proposal.js +119 -0
- package/dist/src/commands/propose.js +171 -0
- package/dist/src/commands/reflect.js +193 -0
- package/dist/{commands → src/commands}/registry-search.js +71 -7
- package/dist/{commands → src/commands}/remember.js +12 -0
- package/dist/{commands → src/commands}/search.js +104 -4
- package/dist/{commands → src/commands}/self-update.js +4 -3
- package/dist/{commands → src/commands}/show.js +73 -0
- package/dist/{commands → src/commands}/source-add.js +5 -1
- package/dist/{commands → src/commands}/source-manage.js +7 -1
- package/dist/{core → src/core}/asset-ref.js +5 -5
- package/dist/{core → src/core}/asset-spec.js +12 -0
- package/dist/{core → src/core}/common.js +1 -1
- package/dist/{core → src/core}/config.js +203 -121
- package/dist/{core → src/core}/errors.js +4 -0
- package/dist/src/core/events.js +239 -0
- package/dist/src/core/lesson-lint.js +86 -0
- package/dist/src/core/proposals.js +406 -0
- package/dist/src/core/warn.js +72 -0
- package/dist/{core → src/core}/write-source.js +80 -5
- package/dist/{indexer → src/indexer}/db-search.js +114 -24
- package/dist/{indexer → src/indexer}/db.js +76 -23
- package/dist/{indexer → src/indexer}/file-context.js +0 -3
- package/dist/src/indexer/graph-boost.js +179 -0
- package/dist/src/indexer/graph-extraction.js +212 -0
- package/dist/{indexer → src/indexer}/indexer.js +88 -7
- package/dist/{indexer → src/indexer}/matchers.js +1 -1
- package/dist/src/indexer/memory-inference.js +263 -0
- package/dist/{indexer → src/indexer}/metadata.js +111 -3
- package/dist/{indexer → src/indexer}/search-source.js +4 -2
- package/dist/src/integrations/agent/config.js +292 -0
- package/dist/src/integrations/agent/detect.js +94 -0
- package/dist/src/integrations/agent/index.js +17 -0
- package/dist/src/integrations/agent/profiles.js +65 -0
- package/dist/src/integrations/agent/prompts.js +167 -0
- package/dist/src/integrations/agent/spawn.js +272 -0
- package/dist/{integrations → src/integrations}/github.js +9 -3
- package/dist/{integrations → src/integrations}/lockfile.js +0 -26
- package/dist/{llm → src/llm}/client.js +33 -2
- package/dist/{llm → src/llm}/embedders/remote.js +37 -3
- package/dist/src/llm/feature-gate.js +108 -0
- package/dist/src/llm/graph-extract.js +107 -0
- package/dist/src/llm/index-passes.js +35 -0
- package/dist/src/llm/memory-infer.js +86 -0
- package/dist/{output → src/output}/cli-hints.js +15 -2
- package/dist/{output → src/output}/renderers.js +63 -2
- package/dist/src/output/shapes.js +523 -0
- package/dist/src/output/text.js +1116 -0
- package/dist/{registry → src/registry}/build-index.js +19 -8
- package/dist/{registry → src/registry}/factory.js +0 -8
- package/dist/{registry → src/registry}/providers/static-index.js +6 -3
- package/dist/{registry → src/registry}/resolve.js +68 -2
- package/dist/{setup → src/setup}/setup.js +52 -5
- package/dist/{sources → src/sources}/providers/git.js +7 -15
- package/dist/{wiki → src/wiki}/wiki.js +54 -6
- package/dist/{workflows → src/workflows}/runs.js +37 -3
- package/dist/tests/add-website-source.test.js +119 -0
- package/dist/tests/agent/agent-config-loader.test.js +70 -0
- package/dist/tests/agent/agent-config.test.js +221 -0
- package/dist/tests/agent/agent-detect.test.js +100 -0
- package/dist/tests/agent/agent-spawn.test.js +234 -0
- package/dist/tests/agent-output.test.js +186 -0
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +103 -0
- package/dist/tests/architecture/agent-spawn-seam.test.js +193 -0
- package/dist/tests/architecture/llm-stateless-seam.test.js +112 -0
- package/dist/tests/asset-ref.test.js +192 -0
- package/dist/tests/asset-registry.test.js +103 -0
- package/dist/tests/asset-spec.test.js +241 -0
- package/dist/tests/bench/attribution.test.js +996 -0
- package/dist/tests/bench/cleanup-sigint.test.js +83 -0
- package/dist/tests/bench/cleanup.js +234 -0
- package/dist/tests/bench/cleanup.test.js +166 -0
- package/dist/tests/bench/cli.js +1018 -0
- package/dist/tests/bench/cli.test.js +445 -0
- package/dist/tests/bench/compare.test.js +556 -0
- package/dist/tests/bench/corpus.js +317 -0
- package/dist/tests/bench/corpus.test.js +258 -0
- package/dist/tests/bench/doctor.js +525 -0
- package/dist/tests/bench/driver.js +401 -0
- package/dist/tests/bench/driver.test.js +584 -0
- package/dist/tests/bench/environment.js +233 -0
- package/dist/tests/bench/environment.test.js +199 -0
- package/dist/tests/bench/evolve-metrics.js +179 -0
- package/dist/tests/bench/evolve-metrics.test.js +187 -0
- package/dist/tests/bench/evolve.js +647 -0
- package/dist/tests/bench/evolve.test.js +624 -0
- package/dist/tests/bench/failure-modes.test.js +349 -0
- package/dist/tests/bench/feedback-integrity.test.js +457 -0
- package/dist/tests/bench/leakage.test.js +228 -0
- package/dist/tests/bench/learning-curve.test.js +134 -0
- package/dist/tests/bench/metrics.js +2395 -0
- package/dist/tests/bench/metrics.test.js +1150 -0
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
- 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 +1885 -0
- package/dist/tests/bench/report.test.js +1038 -0
- 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 +699 -0
- package/dist/tests/bench/runner.test.js +958 -0
- package/dist/tests/bench/search-bridge.test.js +331 -0
- package/dist/tests/bench/tmp.js +131 -0
- package/dist/tests/bench/trajectory.js +116 -0
- package/dist/tests/bench/trajectory.test.js +127 -0
- package/dist/tests/bench/verifier.js +114 -0
- package/dist/tests/bench/verifier.test.js +118 -0
- package/dist/tests/bench/workflow-evaluator.js +557 -0
- package/dist/tests/bench/workflow-evaluator.test.js +421 -0
- package/dist/tests/bench/workflow-spec.js +345 -0
- package/dist/tests/bench/workflow-spec.test.js +363 -0
- package/dist/tests/bench/workflow-trace.js +472 -0
- package/dist/tests/bench/workflow-trace.test.js +254 -0
- package/dist/tests/benchmark-search-quality.js +536 -0
- package/dist/tests/benchmark-suite.js +1441 -0
- package/dist/tests/capture-cli.test.js +112 -0
- package/dist/tests/cli-errors.test.js +204 -0
- package/dist/tests/commands/events.test.js +370 -0
- package/dist/tests/commands/history.test.js +418 -0
- package/dist/tests/commands/import.test.js +103 -0
- package/dist/tests/commands/proposal-cli.test.js +209 -0
- package/dist/tests/commands/reflect-propose-cli.test.js +333 -0
- package/dist/tests/commands/remember.test.js +97 -0
- package/dist/tests/commands/scope-flags.test.js +300 -0
- package/dist/tests/commands/search.test.js +537 -0
- package/dist/tests/commands/show-indexer-parity.test.js +117 -0
- package/dist/tests/commands/show.test.js +294 -0
- package/dist/tests/common.test.js +266 -0
- package/dist/tests/completions.test.js +142 -0
- package/dist/tests/config-cli.test.js +193 -0
- package/dist/tests/config-llm-features.test.js +139 -0
- package/dist/tests/config.test.js +569 -0
- package/dist/tests/contracts/migration-baseline.test.js +43 -0
- package/dist/tests/contracts/reflect-propose-envelope.test.js +139 -0
- package/dist/tests/contracts/spec-helpers.js +46 -0
- package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +228 -0
- package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +56 -0
- package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +94 -0
- package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +39 -0
- package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +44 -0
- package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +47 -0
- package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +40 -0
- package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +58 -0
- package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +34 -0
- package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +75 -0
- package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +36 -0
- package/dist/tests/core/write-source.test.js +366 -0
- package/dist/tests/curate-command.test.js +87 -0
- package/dist/tests/db-scoring.test.js +201 -0
- package/dist/tests/db.test.js +654 -0
- package/dist/tests/distill-cli-flag.test.js +208 -0
- package/dist/tests/distill.test.js +515 -0
- package/dist/tests/docker-install.test.js +120 -0
- package/dist/tests/e2e.test.js +1419 -0
- package/dist/tests/embedder.test.js +340 -0
- package/dist/tests/embedding-model-config.test.js +379 -0
- package/dist/tests/feedback-command.test.js +172 -0
- package/dist/tests/file-context.test.js +552 -0
- package/dist/tests/fixtures/scripts/git/summarize-diff.js +9 -0
- package/dist/tests/fixtures/scripts/lint/eslint-check.js +7 -0
- package/dist/tests/fixtures/stashes/load.js +166 -0
- package/dist/tests/fixtures/stashes/load.test.js +97 -0
- package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +12 -0
- package/dist/tests/frontmatter.test.js +190 -0
- package/dist/tests/fts-field-weighting.test.js +254 -0
- package/dist/tests/fuzzy-search.test.js +230 -0
- package/dist/tests/git-provider-clone.test.js +45 -0
- package/dist/tests/github.test.js +161 -0
- package/dist/tests/graph-boost-ranking.test.js +305 -0
- package/dist/tests/graph-extraction.test.js +282 -0
- package/dist/tests/helpers/usage-events.js +8 -0
- package/dist/tests/index-pass-llm.test.js +161 -0
- package/dist/tests/indexer.test.js +570 -0
- package/dist/tests/info-command.test.js +166 -0
- package/dist/tests/init.test.js +69 -0
- package/dist/tests/install-script.test.js +246 -0
- package/dist/tests/integration/agent-real-profile.test.js +94 -0
- package/dist/tests/issue-36-repro.test.js +304 -0
- package/dist/tests/issues-191-194.test.js +160 -0
- package/dist/tests/lesson-lint.test.js +111 -0
- package/dist/tests/llm-client.test.js +115 -0
- package/dist/tests/llm-feature-gate.test.js +151 -0
- package/dist/tests/llm.test.js +139 -0
- package/dist/tests/lockfile.test.js +216 -0
- package/dist/tests/manifest.test.js +205 -0
- package/dist/tests/markdown.test.js +126 -0
- package/dist/tests/matchers-unit.test.js +189 -0
- package/dist/tests/memory-inference.test.js +299 -0
- package/dist/tests/merge-scoring.test.js +136 -0
- package/dist/tests/metadata.test.js +313 -0
- package/dist/tests/migration-help.test.js +89 -0
- package/dist/tests/origin-resolve.test.js +124 -0
- package/dist/tests/output-baseline.test.js +218 -0
- package/dist/tests/output-shapes-unit.test.js +478 -0
- package/dist/tests/parallel-search.test.js +272 -0
- package/dist/tests/parameter-metadata.test.js +365 -0
- package/dist/tests/paths.test.js +177 -0
- package/dist/tests/progressive-disclosure.test.js +280 -0
- package/dist/tests/proposals.test.js +279 -0
- package/dist/tests/proposed-quality.test.js +271 -0
- package/dist/tests/provider-registry.test.js +32 -0
- package/dist/tests/ranking-regression.test.js +548 -0
- package/dist/tests/reflect-propose.test.js +455 -0
- package/dist/tests/registry-build-index.test.js +394 -0
- package/dist/tests/registry-cli.test.js +290 -0
- package/dist/tests/registry-index-v2.test.js +430 -0
- package/dist/tests/registry-install.test.js +728 -0
- package/dist/tests/registry-providers/parity.test.js +189 -0
- package/dist/tests/registry-providers/skills-sh.test.js +309 -0
- package/dist/tests/registry-providers/static-index.test.js +238 -0
- package/dist/tests/registry-resolve.test.js +126 -0
- package/dist/tests/registry-search.test.js +923 -0
- package/dist/tests/remember-frontmatter.test.js +378 -0
- package/dist/tests/remember-unit.test.js +123 -0
- package/dist/tests/ripgrep-install.test.js +251 -0
- package/dist/tests/ripgrep-resolve.test.js +108 -0
- package/dist/tests/ripgrep.test.js +163 -0
- package/dist/tests/save-command.test.js +94 -0
- package/dist/tests/save-trust-qa-fixes.test.js +270 -0
- package/dist/tests/scoring-pipeline.test.js +648 -0
- package/dist/tests/search-include-proposed-cli.test.js +118 -0
- package/dist/tests/self-update.test.js +442 -0
- package/dist/tests/semantic-search-e2e.test.js +512 -0
- package/dist/tests/semantic-status.test.js +471 -0
- package/dist/tests/setup-run.integration.js +877 -0
- package/dist/tests/setup-wizard.test.js +198 -0
- package/dist/tests/setup.test.js +131 -0
- package/dist/tests/source-add.test.js +11 -0
- package/dist/tests/source-clone.test.js +254 -0
- package/dist/tests/source-manage.test.js +366 -0
- package/dist/tests/source-providers/filesystem.test.js +82 -0
- package/dist/tests/source-providers/git.test.js +252 -0
- package/dist/tests/source-providers/website.test.js +128 -0
- package/dist/tests/source-qa-fixes.test.js +286 -0
- package/dist/tests/source-registry.test.js +350 -0
- package/dist/tests/source-resolve.test.js +100 -0
- package/dist/tests/source-source.test.js +281 -0
- package/dist/tests/source.test.js +533 -0
- package/dist/tests/tar-utils-scan.test.js +73 -0
- package/dist/tests/toggle-components.test.js +73 -0
- package/dist/tests/usage-telemetry.test.js +265 -0
- package/dist/tests/utility-scoring.test.js +558 -0
- package/dist/tests/vault-load-error.test.js +78 -0
- package/dist/tests/vault-qa-fixes.test.js +194 -0
- package/dist/tests/vault.test.js +429 -0
- package/dist/tests/vector-search.test.js +608 -0
- package/dist/tests/walker.test.js +252 -0
- package/dist/tests/wave2-cluster-bc.test.js +228 -0
- package/dist/tests/wave2-cluster-d.test.js +180 -0
- package/dist/tests/wave2-cluster-e.test.js +179 -0
- package/dist/tests/wiki-qa-fixes.test.js +270 -0
- package/dist/tests/wiki.test.js +529 -0
- package/dist/tests/workflow-cli.test.js +271 -0
- package/dist/tests/workflow-markdown.test.js +171 -0
- package/dist/tests/workflow-path-escape.test.js +132 -0
- package/dist/tests/workflow-qa-fixes.test.js +395 -0
- package/dist/tests/workflows/indexer-rejection.test.js +213 -0
- package/docs/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +244 -0
- package/package.json +2 -2
- package/dist/core/warn.js +0 -27
- package/dist/output/shapes.js +0 -212
- package/dist/output/text.js +0 -520
- /package/dist/{commands → src/commands}/completions.js +0 -0
- /package/dist/{commands → src/commands}/curate.js +0 -0
- /package/dist/{commands → src/commands}/info.js +0 -0
- /package/dist/{commands → src/commands}/init.js +0 -0
- /package/dist/{commands → src/commands}/install-audit.js +0 -0
- /package/dist/{commands → src/commands}/migration-help.js +0 -0
- /package/dist/{commands → src/commands}/source-clone.js +0 -0
- /package/dist/{commands → src/commands}/vault.js +0 -0
- /package/dist/{core → src/core}/asset-registry.js +0 -0
- /package/dist/{core → src/core}/frontmatter.js +0 -0
- /package/dist/{core → src/core}/markdown.js +0 -0
- /package/dist/{core → src/core}/paths.js +0 -0
- /package/dist/{indexer → src/indexer}/manifest.js +0 -0
- /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
- /package/dist/{indexer → src/indexer}/semantic-status.js +0 -0
- /package/dist/{indexer → src/indexer}/usage-events.js +0 -0
- /package/dist/{indexer → src/indexer}/walker.js +0 -0
- /package/dist/{llm → src/llm}/embedder.js +0 -0
- /package/dist/{llm → src/llm}/embedders/cache.js +0 -0
- /package/dist/{llm → src/llm}/embedders/local.js +0 -0
- /package/dist/{llm → src/llm}/embedders/types.js +0 -0
- /package/dist/{llm → src/llm}/metadata-enhance.js +0 -0
- /package/dist/{output → src/output}/context.js +0 -0
- /package/dist/{registry → src/registry}/create-provider-registry.js +0 -0
- /package/dist/{registry → src/registry}/origin-resolve.js +0 -0
- /package/dist/{registry → src/registry}/providers/index.js +0 -0
- /package/dist/{registry → src/registry}/providers/skills-sh.js +0 -0
- /package/dist/{registry → src/registry}/providers/types.js +0 -0
- /package/dist/{registry → src/registry}/types.js +0 -0
- /package/dist/{setup → src/setup}/detect.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-install.js +0 -0
- /package/dist/{setup → src/setup}/ripgrep-resolve.js +0 -0
- /package/dist/{setup → src/setup}/steps.js +0 -0
- /package/dist/{sources → src/sources}/include.js +0 -0
- /package/dist/{sources → src/sources}/provider-factory.js +0 -0
- /package/dist/{sources → src/sources}/provider.js +0 -0
- /package/dist/{sources → src/sources}/providers/filesystem.js +0 -0
- /package/dist/{sources → src/sources}/providers/index.js +0 -0
- /package/dist/{sources → src/sources}/providers/install-types.js +0 -0
- /package/dist/{sources → src/sources}/providers/npm.js +0 -0
- /package/dist/{sources → src/sources}/providers/provider-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/sync-from-ref.js +0 -0
- /package/dist/{sources → src/sources}/providers/tar-utils.js +0 -0
- /package/dist/{sources → src/sources}/providers/website.js +0 -0
- /package/dist/{sources → src/sources}/resolve.js +0 -0
- /package/dist/{sources → src/sources}/types.js +0 -0
- /package/dist/{templates → src/templates}/wiki-templates.js +0 -0
- /package/dist/{version.js → src/version.js} +0 -0
- /package/dist/{workflows → src/workflows}/authoring.js +0 -0
- /package/dist/{workflows → src/workflows}/cli.js +0 -0
- /package/dist/{workflows → src/workflows}/db.js +0 -0
- /package/dist/{workflows → src/workflows}/document-cache.js +0 -0
- /package/dist/{workflows → src/workflows}/parser.js +0 -0
- /package/dist/{workflows → src/workflows}/renderer.js +0 -0
- /package/dist/{workflows → src/workflows}/schema.js +0 -0
- /package/dist/{workflows → src/workflows}/validator.js +0 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end semantic search test that uses real @huggingface/transformers
|
|
3
|
+
* embeddings and verifies vector search produces meaningful results.
|
|
4
|
+
*
|
|
5
|
+
* Gated behind AKM_SEMANTIC_TESTS=1 because first run downloads the model.
|
|
6
|
+
* Subsequent runs use the cached model and complete in ~1-2 seconds.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* AKM_SEMANTIC_TESTS=1 bun test tests/semantic-search-e2e.test.ts
|
|
10
|
+
*/
|
|
11
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
12
|
+
import fs from "node:fs";
|
|
13
|
+
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { resetConfigCache, saveConfig } from "../src/core/config";
|
|
16
|
+
import { getDbPath } from "../src/core/paths";
|
|
17
|
+
import { closeDatabase, EMBEDDING_DIM, getEmbeddingCount, getEntryCount, getMeta, openDatabase, } from "../src/indexer/db";
|
|
18
|
+
import { searchLocal } from "../src/indexer/db-search";
|
|
19
|
+
import { akmIndex } from "../src/indexer/indexer";
|
|
20
|
+
import { clearEmbeddingCache } from "../src/llm/embedder";
|
|
21
|
+
// ── Gate ───────────────────────────────────────────────────────────────────
|
|
22
|
+
const SEMANTIC_TESTS = !!process.env.AKM_SEMANTIC_TESTS;
|
|
23
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
24
|
+
const createdTmpDirs = [];
|
|
25
|
+
function createTmpDir(prefix) {
|
|
26
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
27
|
+
createdTmpDirs.push(dir);
|
|
28
|
+
return dir;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a stash directory populated with semantically distinct test assets.
|
|
32
|
+
* Each asset has a .stash.json with curated metadata and a content file.
|
|
33
|
+
*/
|
|
34
|
+
function createTestStash() {
|
|
35
|
+
const stashDir = createTmpDir("akm-semantic-e2e-stash-");
|
|
36
|
+
// 1. Deploy script — about deploying applications to production
|
|
37
|
+
const deployDir = path.join(stashDir, "scripts", "deploy");
|
|
38
|
+
fs.mkdirSync(deployDir, { recursive: true });
|
|
39
|
+
fs.writeFileSync(path.join(deployDir, "deploy.sh"), `#!/bin/bash
|
|
40
|
+
# Deploy application to production environment
|
|
41
|
+
# Handles blue-green deployment with health checks
|
|
42
|
+
|
|
43
|
+
set -euo pipefail
|
|
44
|
+
|
|
45
|
+
echo "Starting production deployment..."
|
|
46
|
+
kubectl apply -f k8s/deployment.yaml
|
|
47
|
+
kubectl rollout status deployment/app --timeout=300s
|
|
48
|
+
echo "Deployment complete. Running health checks..."
|
|
49
|
+
curl -sf http://app.internal/health || exit 1
|
|
50
|
+
echo "Application deployed successfully to production."
|
|
51
|
+
`);
|
|
52
|
+
fs.writeFileSync(path.join(deployDir, ".stash.json"), JSON.stringify({
|
|
53
|
+
entries: [
|
|
54
|
+
{
|
|
55
|
+
name: "deploy",
|
|
56
|
+
type: "script",
|
|
57
|
+
filename: "deploy.sh",
|
|
58
|
+
description: "Deploy application to production with blue-green strategy and health checks",
|
|
59
|
+
tags: ["deploy", "production", "kubernetes", "k8s", "rollout"],
|
|
60
|
+
searchHints: ["use when deploying to production", "blue-green deployment strategy"],
|
|
61
|
+
quality: "curated",
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}));
|
|
65
|
+
// 2. Docker knowledge — about container best practices
|
|
66
|
+
const dockerDir = path.join(stashDir, "knowledge", "docker-guide");
|
|
67
|
+
fs.mkdirSync(dockerDir, { recursive: true });
|
|
68
|
+
fs.writeFileSync(path.join(dockerDir, "docker-guide.md"), `# Docker Container Best Practices
|
|
69
|
+
|
|
70
|
+
## Image Building
|
|
71
|
+
- Use multi-stage builds to reduce image size
|
|
72
|
+
- Pin base image versions for reproducibility
|
|
73
|
+
- Minimize layers and clean up in the same RUN command
|
|
74
|
+
|
|
75
|
+
## Security
|
|
76
|
+
- Run containers as non-root user
|
|
77
|
+
- Scan images for vulnerabilities regularly
|
|
78
|
+
- Use read-only filesystem where possible
|
|
79
|
+
|
|
80
|
+
## Networking
|
|
81
|
+
- Use Docker networks for service isolation
|
|
82
|
+
- Expose only necessary ports
|
|
83
|
+
- Configure health checks for orchestration
|
|
84
|
+
|
|
85
|
+
## Resource Management
|
|
86
|
+
- Set memory and CPU limits
|
|
87
|
+
- Use restart policies for resilience
|
|
88
|
+
- Monitor container resource usage
|
|
89
|
+
`);
|
|
90
|
+
fs.writeFileSync(path.join(dockerDir, ".stash.json"), JSON.stringify({
|
|
91
|
+
entries: [
|
|
92
|
+
{
|
|
93
|
+
name: "docker-guide",
|
|
94
|
+
type: "knowledge",
|
|
95
|
+
filename: "docker-guide.md",
|
|
96
|
+
description: "Docker container best practices covering image building, security, networking, and resource management",
|
|
97
|
+
tags: ["docker", "container", "devops", "best-practices"],
|
|
98
|
+
searchHints: ["container best practices", "docker security guidelines"],
|
|
99
|
+
quality: "curated",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
}));
|
|
103
|
+
// 3. Testing knowledge — about unit testing patterns
|
|
104
|
+
const testingDir = path.join(stashDir, "knowledge", "testing-patterns");
|
|
105
|
+
fs.mkdirSync(testingDir, { recursive: true });
|
|
106
|
+
fs.writeFileSync(path.join(testingDir, "testing-patterns.md"), `# Unit Testing Patterns and Best Practices
|
|
107
|
+
|
|
108
|
+
## Test Structure
|
|
109
|
+
- Arrange, Act, Assert (AAA) pattern
|
|
110
|
+
- One assertion per test for clarity
|
|
111
|
+
- Use descriptive test names that explain the expected behavior
|
|
112
|
+
|
|
113
|
+
## Mocking and Stubbing
|
|
114
|
+
- Mock external dependencies, not the system under test
|
|
115
|
+
- Use dependency injection to enable testability
|
|
116
|
+
- Prefer fakes over mocks for complex interactions
|
|
117
|
+
|
|
118
|
+
## Test Coverage
|
|
119
|
+
- Aim for meaningful coverage, not just line coverage
|
|
120
|
+
- Test edge cases and error paths
|
|
121
|
+
- Use property-based testing for algorithmic code
|
|
122
|
+
|
|
123
|
+
## Integration Testing
|
|
124
|
+
- Test module boundaries and API contracts
|
|
125
|
+
- Use test containers for database integration tests
|
|
126
|
+
- Verify error handling across service boundaries
|
|
127
|
+
`);
|
|
128
|
+
fs.writeFileSync(path.join(testingDir, ".stash.json"), JSON.stringify({
|
|
129
|
+
entries: [
|
|
130
|
+
{
|
|
131
|
+
name: "testing-patterns",
|
|
132
|
+
type: "knowledge",
|
|
133
|
+
filename: "testing-patterns.md",
|
|
134
|
+
description: "Unit testing patterns including AAA, mocking strategies, coverage guidelines, and integration testing",
|
|
135
|
+
tags: ["testing", "unit-test", "tdd", "quality"],
|
|
136
|
+
searchHints: ["how to write unit tests", "testing best practices"],
|
|
137
|
+
quality: "curated",
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
}));
|
|
141
|
+
// 4. Git workflow skill — about version control
|
|
142
|
+
const gitDir = path.join(stashDir, "skills", "git-workflow");
|
|
143
|
+
fs.mkdirSync(gitDir, { recursive: true });
|
|
144
|
+
fs.writeFileSync(path.join(gitDir, "git-workflow.md"), `# Git Workflow Skill
|
|
145
|
+
|
|
146
|
+
## Branching Strategy
|
|
147
|
+
Use a trunk-based development model:
|
|
148
|
+
- Main branch is always deployable
|
|
149
|
+
- Feature branches are short-lived
|
|
150
|
+
- Use pull requests for code review
|
|
151
|
+
|
|
152
|
+
## Commit Messages
|
|
153
|
+
Follow conventional commits:
|
|
154
|
+
- feat: for new features
|
|
155
|
+
- fix: for bug fixes
|
|
156
|
+
- refactor: for code restructuring
|
|
157
|
+
|
|
158
|
+
## Merge Strategy
|
|
159
|
+
- Prefer rebase for linear history
|
|
160
|
+
- Use squash merges for feature branches
|
|
161
|
+
- Never force push to shared branches
|
|
162
|
+
`);
|
|
163
|
+
fs.writeFileSync(path.join(gitDir, ".stash.json"), JSON.stringify({
|
|
164
|
+
entries: [
|
|
165
|
+
{
|
|
166
|
+
name: "git-workflow",
|
|
167
|
+
type: "skill",
|
|
168
|
+
filename: "git-workflow.md",
|
|
169
|
+
description: "Git version control workflow covering branching strategy, commit conventions, and merge practices",
|
|
170
|
+
tags: ["git", "version-control", "branching", "workflow"],
|
|
171
|
+
searchHints: ["how to manage git branches", "commit message conventions"],
|
|
172
|
+
quality: "curated",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}));
|
|
176
|
+
// 5. Database migration command — about schema changes
|
|
177
|
+
const migrateDir = path.join(stashDir, "commands", "db-migrate");
|
|
178
|
+
fs.mkdirSync(migrateDir, { recursive: true });
|
|
179
|
+
fs.writeFileSync(path.join(migrateDir, "db-migrate.sh"), `#!/bin/bash
|
|
180
|
+
# Run database migrations safely with rollback support
|
|
181
|
+
set -euo pipefail
|
|
182
|
+
|
|
183
|
+
echo "Running database schema migration..."
|
|
184
|
+
npx prisma migrate deploy
|
|
185
|
+
echo "Migration complete. Verifying schema..."
|
|
186
|
+
npx prisma db pull --force
|
|
187
|
+
echo "Database migration finished successfully."
|
|
188
|
+
`);
|
|
189
|
+
fs.writeFileSync(path.join(migrateDir, ".stash.json"), JSON.stringify({
|
|
190
|
+
entries: [
|
|
191
|
+
{
|
|
192
|
+
name: "db-migrate",
|
|
193
|
+
type: "command",
|
|
194
|
+
filename: "db-migrate.sh",
|
|
195
|
+
description: "Run database schema migrations with rollback support using Prisma",
|
|
196
|
+
tags: ["database", "migration", "schema", "prisma"],
|
|
197
|
+
searchHints: ["migrate database schema", "run prisma migrations"],
|
|
198
|
+
quality: "curated",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
}));
|
|
202
|
+
return stashDir;
|
|
203
|
+
}
|
|
204
|
+
// ── Environment isolation ──────────────────────────────────────────────────
|
|
205
|
+
// Save and restore env vars at module scope to prevent leaking to other test files.
|
|
206
|
+
const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
|
|
207
|
+
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
|
|
208
|
+
const originalAkmStashDir = process.env.AKM_STASH_DIR;
|
|
209
|
+
let testCacheDir = "";
|
|
210
|
+
let testConfigDir = "";
|
|
211
|
+
function restoreEnv() {
|
|
212
|
+
if (originalXdgCacheHome === undefined)
|
|
213
|
+
delete process.env.XDG_CACHE_HOME;
|
|
214
|
+
else
|
|
215
|
+
process.env.XDG_CACHE_HOME = originalXdgCacheHome;
|
|
216
|
+
if (originalXdgConfigHome === undefined)
|
|
217
|
+
delete process.env.XDG_CONFIG_HOME;
|
|
218
|
+
else
|
|
219
|
+
process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
|
|
220
|
+
if (originalAkmStashDir === undefined)
|
|
221
|
+
delete process.env.AKM_STASH_DIR;
|
|
222
|
+
else
|
|
223
|
+
process.env.AKM_STASH_DIR = originalAkmStashDir;
|
|
224
|
+
resetConfigCache();
|
|
225
|
+
}
|
|
226
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
227
|
+
// Gated tests — require real @huggingface/transformers and ONNX runtime
|
|
228
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
229
|
+
describe.skipIf(!SEMANTIC_TESTS)("Semantic search end-to-end (real embeddings)", () => {
|
|
230
|
+
let stashDir;
|
|
231
|
+
let savedCacheDir;
|
|
232
|
+
let savedConfigDir;
|
|
233
|
+
beforeAll(async () => {
|
|
234
|
+
// Set up isolated cache and config directories
|
|
235
|
+
testCacheDir = createTmpDir("akm-semantic-e2e-cache-");
|
|
236
|
+
testConfigDir = createTmpDir("akm-semantic-e2e-config-");
|
|
237
|
+
process.env.XDG_CACHE_HOME = testCacheDir;
|
|
238
|
+
process.env.XDG_CONFIG_HOME = testConfigDir;
|
|
239
|
+
// Use the user's existing HuggingFace model cache to avoid re-downloading
|
|
240
|
+
if (!process.env.HF_HOME) {
|
|
241
|
+
process.env.HF_HOME = path.join(process.env.HOME ?? "/tmp", ".cache", "huggingface");
|
|
242
|
+
}
|
|
243
|
+
// Create test stash with semantically distinct assets
|
|
244
|
+
stashDir = createTestStash();
|
|
245
|
+
process.env.AKM_STASH_DIR = stashDir;
|
|
246
|
+
// Write config with semantic search enabled (default behavior)
|
|
247
|
+
resetConfigCache();
|
|
248
|
+
saveConfig({ semanticSearchMode: "auto" });
|
|
249
|
+
// Index the stash with real embeddings
|
|
250
|
+
// This will download the model on first run (cached at ~/.cache/huggingface)
|
|
251
|
+
savedCacheDir = testCacheDir;
|
|
252
|
+
savedConfigDir = testConfigDir;
|
|
253
|
+
const result = await akmIndex({ stashDir, full: true });
|
|
254
|
+
expect(result.totalEntries).toBeGreaterThan(0);
|
|
255
|
+
expect(result.verification.semanticSearchEnabled).toBeTruthy();
|
|
256
|
+
}, 120_000); // 2 minute timeout for model download on first run
|
|
257
|
+
// Restore env vars before each test in case the degradation describe
|
|
258
|
+
// (which runs in the same file) overwrites them between beforeAll and tests.
|
|
259
|
+
beforeEach(() => {
|
|
260
|
+
process.env.XDG_CACHE_HOME = savedCacheDir;
|
|
261
|
+
process.env.XDG_CONFIG_HOME = savedConfigDir;
|
|
262
|
+
process.env.AKM_STASH_DIR = stashDir;
|
|
263
|
+
resetConfigCache();
|
|
264
|
+
});
|
|
265
|
+
afterAll(() => {
|
|
266
|
+
restoreEnv();
|
|
267
|
+
clearEmbeddingCache();
|
|
268
|
+
});
|
|
269
|
+
test("index stores embeddings with correct metadata", () => {
|
|
270
|
+
const dbPath = getDbPath();
|
|
271
|
+
expect(fs.existsSync(dbPath)).toBe(true);
|
|
272
|
+
const db = openDatabase(dbPath);
|
|
273
|
+
try {
|
|
274
|
+
// Verify hasEmbeddings flag is set
|
|
275
|
+
expect(getMeta(db, "hasEmbeddings")).toBe("1");
|
|
276
|
+
// Verify embedding count matches entry count
|
|
277
|
+
const entryCount = getEntryCount(db);
|
|
278
|
+
const embeddingCount = getEmbeddingCount(db);
|
|
279
|
+
expect(entryCount).toBeGreaterThanOrEqual(5);
|
|
280
|
+
expect(embeddingCount).toBe(entryCount);
|
|
281
|
+
// Verify each embedding has the correct dimension (384 for bge-small-en-v1.5)
|
|
282
|
+
const rows = db.prepare("SELECT id, embedding FROM embeddings").all();
|
|
283
|
+
expect(rows.length).toBe(entryCount);
|
|
284
|
+
for (const row of rows) {
|
|
285
|
+
const f32 = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
|
|
286
|
+
expect(f32.length).toBe(EMBEDDING_DIM);
|
|
287
|
+
// Verify embeddings are non-zero (not degenerate)
|
|
288
|
+
const norm = Math.sqrt(Array.from(f32).reduce((s, v) => s + v * v, 0));
|
|
289
|
+
expect(norm).toBeGreaterThan(0.5);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
closeDatabase(db);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
test("deploy query ranks deploy script highest", async () => {
|
|
297
|
+
resetConfigCache();
|
|
298
|
+
const config = { semanticSearchMode: "auto" };
|
|
299
|
+
const result = await searchLocal({
|
|
300
|
+
query: "deploy application to production",
|
|
301
|
+
searchType: "any",
|
|
302
|
+
limit: 10,
|
|
303
|
+
stashDir,
|
|
304
|
+
sources: [{ path: stashDir }],
|
|
305
|
+
config,
|
|
306
|
+
});
|
|
307
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
308
|
+
// The deploy script should be the top result (or at least top 2)
|
|
309
|
+
const topHits = result.hits.slice(0, 2);
|
|
310
|
+
const deployHit = topHits.find((h) => h.name === "deploy" || h.name.includes("deploy"));
|
|
311
|
+
expect(deployHit).toBeDefined();
|
|
312
|
+
// Verify we got hybrid ranking (FTS + semantic combined)
|
|
313
|
+
const deployResult = result.hits.find((h) => h.name === "deploy" || h.name.includes("deploy"));
|
|
314
|
+
expect(deployResult).toBeDefined();
|
|
315
|
+
expect(deployResult?.score).toBeGreaterThan(0);
|
|
316
|
+
});
|
|
317
|
+
test("container query ranks docker guide highest", async () => {
|
|
318
|
+
resetConfigCache();
|
|
319
|
+
const config = { semanticSearchMode: "auto" };
|
|
320
|
+
const result = await searchLocal({
|
|
321
|
+
query: "container best practices",
|
|
322
|
+
searchType: "any",
|
|
323
|
+
limit: 10,
|
|
324
|
+
stashDir,
|
|
325
|
+
sources: [{ path: stashDir }],
|
|
326
|
+
config,
|
|
327
|
+
});
|
|
328
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
329
|
+
// The docker guide should rank high for container queries
|
|
330
|
+
const topHits = result.hits.slice(0, 3);
|
|
331
|
+
const dockerHit = topHits.find((h) => h.name === "docker-guide" || h.name.includes("docker"));
|
|
332
|
+
expect(dockerHit).toBeDefined();
|
|
333
|
+
});
|
|
334
|
+
test("testing query ranks testing patterns highest", async () => {
|
|
335
|
+
resetConfigCache();
|
|
336
|
+
const config = { semanticSearchMode: "auto" };
|
|
337
|
+
const result = await searchLocal({
|
|
338
|
+
query: "unit testing",
|
|
339
|
+
searchType: "any",
|
|
340
|
+
limit: 10,
|
|
341
|
+
stashDir,
|
|
342
|
+
sources: [{ path: stashDir }],
|
|
343
|
+
config,
|
|
344
|
+
});
|
|
345
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
346
|
+
// The testing patterns doc should rank high
|
|
347
|
+
const topHits = result.hits.slice(0, 3);
|
|
348
|
+
const testingHit = topHits.find((h) => h.name === "testing-patterns" || h.name.includes("testing"));
|
|
349
|
+
expect(testingHit).toBeDefined();
|
|
350
|
+
});
|
|
351
|
+
test("semantic similarity differentiates unrelated queries", async () => {
|
|
352
|
+
resetConfigCache();
|
|
353
|
+
const config = { semanticSearchMode: "auto" };
|
|
354
|
+
// Search for deployment — deploy script should score higher than testing doc
|
|
355
|
+
const deployResult = await searchLocal({
|
|
356
|
+
query: "deploy application to production",
|
|
357
|
+
searchType: "any",
|
|
358
|
+
limit: 10,
|
|
359
|
+
stashDir,
|
|
360
|
+
sources: [{ path: stashDir }],
|
|
361
|
+
config,
|
|
362
|
+
});
|
|
363
|
+
const deployScore = deployResult.hits.find((h) => h.name === "deploy" || h.name.includes("deploy"))?.score ?? 0;
|
|
364
|
+
const testingScoreInDeployQuery = deployResult.hits.find((h) => h.name === "testing-patterns" || h.name.includes("testing"))?.score ?? 0;
|
|
365
|
+
// Deploy should score higher than testing when searching for deployment
|
|
366
|
+
expect(deployScore).toBeGreaterThan(testingScoreInDeployQuery);
|
|
367
|
+
// Search for testing — testing doc should score higher than deploy script
|
|
368
|
+
const testResult = await searchLocal({
|
|
369
|
+
query: "how to write unit tests with mocking",
|
|
370
|
+
searchType: "any",
|
|
371
|
+
limit: 10,
|
|
372
|
+
stashDir,
|
|
373
|
+
sources: [{ path: stashDir }],
|
|
374
|
+
config,
|
|
375
|
+
});
|
|
376
|
+
const testingScore = testResult.hits.find((h) => h.name === "testing-patterns" || h.name.includes("testing"))?.score ?? 0;
|
|
377
|
+
const deployScoreInTestQuery = testResult.hits.find((h) => h.name === "deploy" || h.name.includes("deploy"))?.score ?? 0;
|
|
378
|
+
// Testing should score higher than deploy when searching for testing
|
|
379
|
+
expect(testingScore).toBeGreaterThan(deployScoreInTestQuery);
|
|
380
|
+
});
|
|
381
|
+
test("semantic search finds results even without exact keyword match", async () => {
|
|
382
|
+
resetConfigCache();
|
|
383
|
+
const config = { semanticSearchMode: "auto" };
|
|
384
|
+
// Use a paraphrase that doesn't share exact keywords with the deploy script
|
|
385
|
+
// "shipping code to live servers" is semantically similar to deploy but uses different words
|
|
386
|
+
const result = await searchLocal({
|
|
387
|
+
query: "shipping code to live servers",
|
|
388
|
+
searchType: "any",
|
|
389
|
+
limit: 10,
|
|
390
|
+
stashDir,
|
|
391
|
+
sources: [{ path: stashDir }],
|
|
392
|
+
config,
|
|
393
|
+
});
|
|
394
|
+
// We should still get results (semantic search should find relevant items)
|
|
395
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
396
|
+
// The deploy script should appear somewhere in results via semantic similarity
|
|
397
|
+
const deployHit = result.hits.find((h) => h.name === "deploy" || h.name.includes("deploy"));
|
|
398
|
+
// It may or may not be top-1 since FTS won't match, but it should appear
|
|
399
|
+
// in the results via vector search
|
|
400
|
+
if (deployHit) {
|
|
401
|
+
expect(deployHit.score).toBeGreaterThan(0);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
test("vector scores are present in hybrid results", async () => {
|
|
405
|
+
resetConfigCache();
|
|
406
|
+
const config = { semanticSearchMode: "auto" };
|
|
407
|
+
const result = await searchLocal({
|
|
408
|
+
query: "deploy application",
|
|
409
|
+
searchType: "any",
|
|
410
|
+
limit: 10,
|
|
411
|
+
stashDir,
|
|
412
|
+
sources: [{ path: stashDir }],
|
|
413
|
+
config,
|
|
414
|
+
});
|
|
415
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
416
|
+
// embedMs should be set when vector search ran
|
|
417
|
+
expect(result.embedMs).toBeDefined();
|
|
418
|
+
expect(result.embedMs).toBeGreaterThanOrEqual(0);
|
|
419
|
+
// All hits should have positive scores
|
|
420
|
+
for (const hit of result.hits) {
|
|
421
|
+
expect(hit.score).toBeGreaterThan(0);
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
426
|
+
// Ungated test — graceful degradation (always runs)
|
|
427
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
428
|
+
describe("Semantic search graceful degradation", () => {
|
|
429
|
+
let stashDir;
|
|
430
|
+
let degradationCacheDir;
|
|
431
|
+
let degradationConfigDir;
|
|
432
|
+
beforeAll(() => {
|
|
433
|
+
degradationCacheDir = createTmpDir("akm-semantic-degrade-cache-");
|
|
434
|
+
degradationConfigDir = createTmpDir("akm-semantic-degrade-config-");
|
|
435
|
+
process.env.XDG_CACHE_HOME = degradationCacheDir;
|
|
436
|
+
process.env.XDG_CONFIG_HOME = degradationConfigDir;
|
|
437
|
+
// Create a minimal stash
|
|
438
|
+
stashDir = createTmpDir("akm-semantic-degrade-stash-");
|
|
439
|
+
const skillDir = path.join(stashDir, "skills", "hello");
|
|
440
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
441
|
+
fs.writeFileSync(path.join(skillDir, "hello.md"), "# Hello Skill\n\nA simple hello world greeting skill.\n");
|
|
442
|
+
fs.writeFileSync(path.join(skillDir, ".stash.json"), JSON.stringify({
|
|
443
|
+
entries: [
|
|
444
|
+
{
|
|
445
|
+
name: "hello",
|
|
446
|
+
type: "skill",
|
|
447
|
+
filename: "hello.md",
|
|
448
|
+
description: "A simple hello world greeting skill",
|
|
449
|
+
tags: ["hello", "greeting"],
|
|
450
|
+
quality: "curated",
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
}));
|
|
454
|
+
process.env.AKM_STASH_DIR = stashDir;
|
|
455
|
+
});
|
|
456
|
+
beforeEach(() => {
|
|
457
|
+
process.env.XDG_CACHE_HOME = degradationCacheDir;
|
|
458
|
+
process.env.XDG_CONFIG_HOME = degradationConfigDir;
|
|
459
|
+
process.env.AKM_STASH_DIR = stashDir;
|
|
460
|
+
resetConfigCache();
|
|
461
|
+
});
|
|
462
|
+
afterAll(() => {
|
|
463
|
+
restoreEnv();
|
|
464
|
+
// Clean up all temp dirs from both describe blocks
|
|
465
|
+
for (const dir of createdTmpDirs) {
|
|
466
|
+
try {
|
|
467
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
/* ignore cleanup failures */
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
test("indexing succeeds with FTS only when semanticSearch is disabled", async () => {
|
|
475
|
+
resetConfigCache();
|
|
476
|
+
saveConfig({ semanticSearchMode: "off" });
|
|
477
|
+
const result = await akmIndex({ stashDir, full: true });
|
|
478
|
+
expect(result.totalEntries).toBeGreaterThan(0);
|
|
479
|
+
// When semantic search is disabled, verification should report it
|
|
480
|
+
expect(result.verification.semanticSearchEnabled).toBeFalsy();
|
|
481
|
+
// Check the database directly
|
|
482
|
+
const dbPath = getDbPath();
|
|
483
|
+
const db = openDatabase(dbPath);
|
|
484
|
+
try {
|
|
485
|
+
expect(getMeta(db, "hasEmbeddings")).toBe("0");
|
|
486
|
+
expect(getEntryCount(db)).toBeGreaterThan(0);
|
|
487
|
+
expect(getEmbeddingCount(db)).toBe(0);
|
|
488
|
+
}
|
|
489
|
+
finally {
|
|
490
|
+
closeDatabase(db);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
test("search returns FTS results when no embeddings exist", async () => {
|
|
494
|
+
resetConfigCache();
|
|
495
|
+
// Use config with semanticSearchMode: "auto" but there are no embeddings in DB
|
|
496
|
+
// (from previous test that indexed with semanticSearchMode: "off")
|
|
497
|
+
const config = { semanticSearchMode: "auto" };
|
|
498
|
+
const result = await searchLocal({
|
|
499
|
+
query: "hello",
|
|
500
|
+
searchType: "any",
|
|
501
|
+
limit: 10,
|
|
502
|
+
stashDir,
|
|
503
|
+
sources: [{ path: stashDir }],
|
|
504
|
+
config,
|
|
505
|
+
});
|
|
506
|
+
// Should still return results via FTS
|
|
507
|
+
expect(result.hits.length).toBeGreaterThan(0);
|
|
508
|
+
const helloHit = result.hits.find((h) => h.name === "hello");
|
|
509
|
+
expect(helloHit).toBeDefined();
|
|
510
|
+
expect(helloHit?.score).toBeGreaterThan(0);
|
|
511
|
+
});
|
|
512
|
+
});
|