akm-cli 0.7.0 → 0.7.2
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 +8 -0
- package/dist/{src/cli.js → cli.js} +22 -8
- package/dist/{src/commands → commands}/installed-stashes.js +1 -1
- package/dist/{src/commands → commands}/source-add.js +1 -1
- package/dist/{src/core → core}/common.js +16 -1
- package/dist/{src/core → core}/config.js +5 -2
- package/dist/{src/indexer → indexer}/db-search.js +16 -1
- package/dist/{src/indexer → indexer}/graph-extraction.js +5 -3
- package/dist/{src/indexer → indexer}/indexer.js +27 -11
- package/dist/{src/indexer → indexer}/memory-inference.js +47 -58
- package/dist/{src/indexer → indexer}/search-source.js +1 -1
- package/dist/{src/llm → llm}/client.js +61 -1
- package/dist/{src/llm → llm}/embedder.js +8 -5
- package/dist/{src/llm → llm}/embedders/local.js +8 -2
- package/dist/{src/llm → llm}/embedders/remote.js +4 -2
- package/dist/{src/llm → llm}/graph-extract.js +4 -4
- package/dist/llm/memory-infer.js +114 -0
- package/dist/{src/llm → llm}/metadata-enhance.js +2 -2
- package/dist/{src/output → output}/cli-hints.js +2 -0
- package/dist/{src/setup → setup}/setup.js +30 -20
- package/dist/sources/providers/website.js +27 -0
- package/dist/{src/sources/providers/website.js → sources/website-ingest.js} +38 -51
- package/docs/README.md +7 -0
- package/docs/migration/release-notes/0.7.0.md +14 -0
- package/package.json +11 -8
- package/dist/src/llm/memory-infer.js +0 -86
- package/dist/tests/add-website-source.test.js +0 -119
- package/dist/tests/agent/agent-config-loader.test.js +0 -70
- package/dist/tests/agent/agent-config.test.js +0 -221
- package/dist/tests/agent/agent-detect.test.js +0 -100
- package/dist/tests/agent/agent-spawn.test.js +0 -234
- package/dist/tests/agent-output.test.js +0 -186
- package/dist/tests/architecture/agent-no-llm-sdk-guard.test.js +0 -103
- package/dist/tests/architecture/agent-spawn-seam.test.js +0 -193
- package/dist/tests/architecture/llm-stateless-seam.test.js +0 -112
- package/dist/tests/asset-ref.test.js +0 -192
- package/dist/tests/asset-registry.test.js +0 -103
- package/dist/tests/asset-spec.test.js +0 -241
- package/dist/tests/bench/attribution.test.js +0 -996
- package/dist/tests/bench/cleanup-sigint.test.js +0 -83
- package/dist/tests/bench/cleanup.js +0 -234
- package/dist/tests/bench/cleanup.test.js +0 -166
- package/dist/tests/bench/cli.js +0 -1018
- package/dist/tests/bench/cli.test.js +0 -445
- package/dist/tests/bench/compare.test.js +0 -556
- package/dist/tests/bench/corpus.js +0 -317
- package/dist/tests/bench/corpus.test.js +0 -258
- package/dist/tests/bench/doctor.js +0 -525
- package/dist/tests/bench/driver.js +0 -401
- package/dist/tests/bench/driver.test.js +0 -584
- package/dist/tests/bench/environment.js +0 -233
- package/dist/tests/bench/environment.test.js +0 -199
- package/dist/tests/bench/evolve-metrics.js +0 -179
- package/dist/tests/bench/evolve-metrics.test.js +0 -187
- package/dist/tests/bench/evolve.js +0 -647
- package/dist/tests/bench/evolve.test.js +0 -624
- package/dist/tests/bench/failure-modes.test.js +0 -349
- package/dist/tests/bench/feedback-integrity.test.js +0 -457
- package/dist/tests/bench/leakage.test.js +0 -228
- package/dist/tests/bench/learning-curve.test.js +0 -134
- package/dist/tests/bench/metrics.js +0 -2395
- package/dist/tests/bench/metrics.test.js +0 -1150
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +0 -43
- package/dist/tests/bench/opencode-config.js +0 -194
- package/dist/tests/bench/opencode-config.test.js +0 -370
- package/dist/tests/bench/report.js +0 -1885
- package/dist/tests/bench/report.test.js +0 -1038
- package/dist/tests/bench/run-config.js +0 -355
- package/dist/tests/bench/run-config.test.js +0 -298
- package/dist/tests/bench/run-curate-test.js +0 -32
- package/dist/tests/bench/run-failing-tasks.js +0 -56
- package/dist/tests/bench/run-full-bench.js +0 -51
- package/dist/tests/bench/run-items36-targeted.js +0 -69
- package/dist/tests/bench/run-nano-quick.js +0 -42
- package/dist/tests/bench/run-waveg-targeted.js +0 -62
- package/dist/tests/bench/runner.js +0 -699
- package/dist/tests/bench/runner.test.js +0 -958
- package/dist/tests/bench/search-bridge.test.js +0 -331
- package/dist/tests/bench/tmp.js +0 -131
- package/dist/tests/bench/trajectory.js +0 -116
- package/dist/tests/bench/trajectory.test.js +0 -127
- package/dist/tests/bench/verifier.js +0 -114
- package/dist/tests/bench/verifier.test.js +0 -118
- package/dist/tests/bench/workflow-evaluator.js +0 -557
- package/dist/tests/bench/workflow-evaluator.test.js +0 -421
- package/dist/tests/bench/workflow-spec.js +0 -345
- package/dist/tests/bench/workflow-spec.test.js +0 -363
- package/dist/tests/bench/workflow-trace.js +0 -472
- package/dist/tests/bench/workflow-trace.test.js +0 -254
- package/dist/tests/benchmark-search-quality.js +0 -536
- package/dist/tests/benchmark-suite.js +0 -1441
- package/dist/tests/capture-cli.test.js +0 -112
- package/dist/tests/cli-errors.test.js +0 -204
- package/dist/tests/commands/events.test.js +0 -370
- package/dist/tests/commands/history.test.js +0 -418
- package/dist/tests/commands/import.test.js +0 -103
- package/dist/tests/commands/proposal-cli.test.js +0 -209
- package/dist/tests/commands/reflect-propose-cli.test.js +0 -333
- package/dist/tests/commands/remember.test.js +0 -97
- package/dist/tests/commands/scope-flags.test.js +0 -300
- package/dist/tests/commands/search.test.js +0 -537
- package/dist/tests/commands/show-indexer-parity.test.js +0 -117
- package/dist/tests/commands/show.test.js +0 -294
- package/dist/tests/common.test.js +0 -266
- package/dist/tests/completions.test.js +0 -142
- package/dist/tests/config-cli.test.js +0 -193
- package/dist/tests/config-llm-features.test.js +0 -139
- package/dist/tests/config.test.js +0 -569
- package/dist/tests/contracts/migration-baseline.test.js +0 -43
- package/dist/tests/contracts/reflect-propose-envelope.test.js +0 -139
- package/dist/tests/contracts/spec-helpers.js +0 -46
- package/dist/tests/contracts/v1-spec-section-11-proposal-queue.test.js +0 -228
- package/dist/tests/contracts/v1-spec-section-12-agent-config.test.js +0 -56
- package/dist/tests/contracts/v1-spec-section-13-lesson-type.test.js +0 -34
- package/dist/tests/contracts/v1-spec-section-14-llm-features.test.js +0 -94
- package/dist/tests/contracts/v1-spec-section-4-1-asset-types.test.js +0 -39
- package/dist/tests/contracts/v1-spec-section-4-2-quality-rules.test.js +0 -44
- package/dist/tests/contracts/v1-spec-section-5-configuration.test.js +0 -47
- package/dist/tests/contracts/v1-spec-section-6-orchestration.test.js +0 -40
- package/dist/tests/contracts/v1-spec-section-7-module-layout.test.js +0 -58
- package/dist/tests/contracts/v1-spec-section-8-extension-points.test.js +0 -34
- package/dist/tests/contracts/v1-spec-section-9-4-cli-surface.test.js +0 -75
- package/dist/tests/contracts/v1-spec-section-9-7-llm-agent-boundary.test.js +0 -36
- package/dist/tests/core/write-source.test.js +0 -366
- package/dist/tests/curate-command.test.js +0 -87
- package/dist/tests/db-scoring.test.js +0 -201
- package/dist/tests/db.test.js +0 -654
- package/dist/tests/distill-cli-flag.test.js +0 -208
- package/dist/tests/distill.test.js +0 -515
- package/dist/tests/docker-install.test.js +0 -120
- package/dist/tests/e2e.test.js +0 -1419
- package/dist/tests/embedder.test.js +0 -340
- package/dist/tests/embedding-model-config.test.js +0 -379
- package/dist/tests/feedback-command.test.js +0 -172
- package/dist/tests/file-context.test.js +0 -552
- package/dist/tests/fixtures/scripts/git/summarize-diff.js +0 -9
- package/dist/tests/fixtures/scripts/lint/eslint-check.js +0 -7
- package/dist/tests/fixtures/stashes/load.js +0 -166
- package/dist/tests/fixtures/stashes/load.test.js +0 -97
- package/dist/tests/fixtures/stashes/ranking-baseline/scripts/mem0-search.js +0 -12
- package/dist/tests/frontmatter.test.js +0 -190
- package/dist/tests/fts-field-weighting.test.js +0 -254
- package/dist/tests/fuzzy-search.test.js +0 -230
- package/dist/tests/git-provider-clone.test.js +0 -45
- package/dist/tests/github.test.js +0 -161
- package/dist/tests/graph-boost-ranking.test.js +0 -305
- package/dist/tests/graph-extraction.test.js +0 -282
- package/dist/tests/helpers/usage-events.js +0 -8
- package/dist/tests/index-pass-llm.test.js +0 -161
- package/dist/tests/indexer.test.js +0 -570
- package/dist/tests/info-command.test.js +0 -166
- package/dist/tests/init.test.js +0 -69
- package/dist/tests/install-script.test.js +0 -246
- package/dist/tests/integration/agent-real-profile.test.js +0 -94
- package/dist/tests/issue-36-repro.test.js +0 -304
- package/dist/tests/issues-191-194.test.js +0 -160
- package/dist/tests/lesson-lint.test.js +0 -111
- package/dist/tests/llm-client.test.js +0 -115
- package/dist/tests/llm-feature-gate.test.js +0 -151
- package/dist/tests/llm.test.js +0 -139
- package/dist/tests/lockfile.test.js +0 -216
- package/dist/tests/manifest.test.js +0 -205
- package/dist/tests/markdown.test.js +0 -126
- package/dist/tests/matchers-unit.test.js +0 -189
- package/dist/tests/memory-inference.test.js +0 -299
- package/dist/tests/merge-scoring.test.js +0 -136
- package/dist/tests/metadata.test.js +0 -313
- package/dist/tests/migration-help.test.js +0 -89
- package/dist/tests/origin-resolve.test.js +0 -124
- package/dist/tests/output-baseline.test.js +0 -218
- package/dist/tests/output-shapes-unit.test.js +0 -478
- package/dist/tests/parallel-search.test.js +0 -272
- package/dist/tests/parameter-metadata.test.js +0 -365
- package/dist/tests/paths.test.js +0 -177
- package/dist/tests/progressive-disclosure.test.js +0 -280
- package/dist/tests/proposals.test.js +0 -279
- package/dist/tests/proposed-quality.test.js +0 -271
- package/dist/tests/provider-registry.test.js +0 -32
- package/dist/tests/ranking-regression.test.js +0 -548
- package/dist/tests/reflect-propose.test.js +0 -455
- package/dist/tests/registry-build-index.test.js +0 -394
- package/dist/tests/registry-cli.test.js +0 -290
- package/dist/tests/registry-index-v2.test.js +0 -430
- package/dist/tests/registry-install.test.js +0 -728
- package/dist/tests/registry-providers/parity.test.js +0 -189
- package/dist/tests/registry-providers/skills-sh.test.js +0 -309
- package/dist/tests/registry-providers/static-index.test.js +0 -238
- package/dist/tests/registry-resolve.test.js +0 -126
- package/dist/tests/registry-search.test.js +0 -923
- package/dist/tests/remember-frontmatter.test.js +0 -378
- package/dist/tests/remember-unit.test.js +0 -123
- package/dist/tests/ripgrep-install.test.js +0 -251
- package/dist/tests/ripgrep-resolve.test.js +0 -108
- package/dist/tests/ripgrep.test.js +0 -163
- package/dist/tests/save-command.test.js +0 -94
- package/dist/tests/save-trust-qa-fixes.test.js +0 -270
- package/dist/tests/scoring-pipeline.test.js +0 -648
- package/dist/tests/search-include-proposed-cli.test.js +0 -118
- package/dist/tests/self-update.test.js +0 -442
- package/dist/tests/semantic-search-e2e.test.js +0 -512
- package/dist/tests/semantic-status.test.js +0 -471
- package/dist/tests/setup-run.integration.js +0 -877
- package/dist/tests/setup-wizard.test.js +0 -198
- package/dist/tests/setup.test.js +0 -131
- package/dist/tests/source-add.test.js +0 -11
- package/dist/tests/source-clone.test.js +0 -254
- package/dist/tests/source-manage.test.js +0 -366
- package/dist/tests/source-providers/filesystem.test.js +0 -82
- package/dist/tests/source-providers/git.test.js +0 -252
- package/dist/tests/source-providers/website.test.js +0 -128
- package/dist/tests/source-qa-fixes.test.js +0 -286
- package/dist/tests/source-registry.test.js +0 -350
- package/dist/tests/source-resolve.test.js +0 -100
- package/dist/tests/source-source.test.js +0 -281
- package/dist/tests/source.test.js +0 -533
- package/dist/tests/tar-utils-scan.test.js +0 -73
- package/dist/tests/toggle-components.test.js +0 -73
- package/dist/tests/usage-telemetry.test.js +0 -265
- package/dist/tests/utility-scoring.test.js +0 -558
- package/dist/tests/vault-load-error.test.js +0 -78
- package/dist/tests/vault-qa-fixes.test.js +0 -194
- package/dist/tests/vault.test.js +0 -429
- package/dist/tests/vector-search.test.js +0 -608
- package/dist/tests/walker.test.js +0 -252
- package/dist/tests/wave2-cluster-bc.test.js +0 -228
- package/dist/tests/wave2-cluster-d.test.js +0 -180
- package/dist/tests/wave2-cluster-e.test.js +0 -179
- package/dist/tests/wiki-qa-fixes.test.js +0 -270
- package/dist/tests/wiki.test.js +0 -529
- package/dist/tests/workflow-cli.test.js +0 -271
- package/dist/tests/workflow-markdown.test.js +0 -171
- package/dist/tests/workflow-path-escape.test.js +0 -132
- package/dist/tests/workflow-qa-fixes.test.js +0 -395
- package/dist/tests/workflows/indexer-rejection.test.js +0 -213
- /package/dist/{src/commands → commands}/completions.js +0 -0
- /package/dist/{src/commands → commands}/config-cli.js +0 -0
- /package/dist/{src/commands → commands}/curate.js +0 -0
- /package/dist/{src/commands → commands}/distill.js +0 -0
- /package/dist/{src/commands → commands}/events.js +0 -0
- /package/dist/{src/commands → commands}/history.js +0 -0
- /package/dist/{src/commands → commands}/info.js +0 -0
- /package/dist/{src/commands → commands}/init.js +0 -0
- /package/dist/{src/commands → commands}/install-audit.js +0 -0
- /package/dist/{src/commands → commands}/migration-help.js +0 -0
- /package/dist/{src/commands → commands}/proposal.js +0 -0
- /package/dist/{src/commands → commands}/propose.js +0 -0
- /package/dist/{src/commands → commands}/reflect.js +0 -0
- /package/dist/{src/commands → commands}/registry-search.js +0 -0
- /package/dist/{src/commands → commands}/remember.js +0 -0
- /package/dist/{src/commands → commands}/search.js +0 -0
- /package/dist/{src/commands → commands}/self-update.js +0 -0
- /package/dist/{src/commands → commands}/show.js +0 -0
- /package/dist/{src/commands → commands}/source-clone.js +0 -0
- /package/dist/{src/commands → commands}/source-manage.js +0 -0
- /package/dist/{src/commands → commands}/vault.js +0 -0
- /package/dist/{src/core → core}/asset-ref.js +0 -0
- /package/dist/{src/core → core}/asset-registry.js +0 -0
- /package/dist/{src/core → core}/asset-spec.js +0 -0
- /package/dist/{src/core → core}/errors.js +0 -0
- /package/dist/{src/core → core}/events.js +0 -0
- /package/dist/{src/core → core}/frontmatter.js +0 -0
- /package/dist/{src/core → core}/lesson-lint.js +0 -0
- /package/dist/{src/core → core}/markdown.js +0 -0
- /package/dist/{src/core → core}/paths.js +0 -0
- /package/dist/{src/core → core}/proposals.js +0 -0
- /package/dist/{src/core → core}/warn.js +0 -0
- /package/dist/{src/core → core}/write-source.js +0 -0
- /package/dist/{src/indexer → indexer}/db.js +0 -0
- /package/dist/{src/indexer → indexer}/file-context.js +0 -0
- /package/dist/{src/indexer → indexer}/graph-boost.js +0 -0
- /package/dist/{src/indexer → indexer}/manifest.js +0 -0
- /package/dist/{src/indexer → indexer}/matchers.js +0 -0
- /package/dist/{src/indexer → indexer}/metadata.js +0 -0
- /package/dist/{src/indexer → indexer}/search-fields.js +0 -0
- /package/dist/{src/indexer → indexer}/semantic-status.js +0 -0
- /package/dist/{src/indexer → indexer}/usage-events.js +0 -0
- /package/dist/{src/indexer → indexer}/walker.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/config.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/detect.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/index.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/profiles.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/prompts.js +0 -0
- /package/dist/{src/integrations → integrations}/agent/spawn.js +0 -0
- /package/dist/{src/integrations → integrations}/github.js +0 -0
- /package/dist/{src/integrations → integrations}/lockfile.js +0 -0
- /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
- /package/dist/{src/llm → llm}/embedders/types.js +0 -0
- /package/dist/{src/llm → llm}/feature-gate.js +0 -0
- /package/dist/{src/llm → llm}/index-passes.js +0 -0
- /package/dist/{src/output → output}/context.js +0 -0
- /package/dist/{src/output → output}/renderers.js +0 -0
- /package/dist/{src/output → output}/shapes.js +0 -0
- /package/dist/{src/output → output}/text.js +0 -0
- /package/dist/{src/registry → registry}/build-index.js +0 -0
- /package/dist/{src/registry → registry}/create-provider-registry.js +0 -0
- /package/dist/{src/registry → registry}/factory.js +0 -0
- /package/dist/{src/registry → registry}/origin-resolve.js +0 -0
- /package/dist/{src/registry → registry}/providers/index.js +0 -0
- /package/dist/{src/registry → registry}/providers/skills-sh.js +0 -0
- /package/dist/{src/registry → registry}/providers/static-index.js +0 -0
- /package/dist/{src/registry → registry}/providers/types.js +0 -0
- /package/dist/{src/registry → registry}/resolve.js +0 -0
- /package/dist/{src/registry → registry}/types.js +0 -0
- /package/dist/{src/setup → setup}/detect.js +0 -0
- /package/dist/{src/setup → setup}/ripgrep-install.js +0 -0
- /package/dist/{src/setup → setup}/ripgrep-resolve.js +0 -0
- /package/dist/{src/setup → setup}/steps.js +0 -0
- /package/dist/{src/sources → sources}/include.js +0 -0
- /package/dist/{src/sources → sources}/provider-factory.js +0 -0
- /package/dist/{src/sources → sources}/provider.js +0 -0
- /package/dist/{src/sources → sources}/providers/filesystem.js +0 -0
- /package/dist/{src/sources → sources}/providers/git.js +0 -0
- /package/dist/{src/sources → sources}/providers/index.js +0 -0
- /package/dist/{src/sources → sources}/providers/install-types.js +0 -0
- /package/dist/{src/sources → sources}/providers/npm.js +0 -0
- /package/dist/{src/sources → sources}/providers/provider-utils.js +0 -0
- /package/dist/{src/sources → sources}/providers/sync-from-ref.js +0 -0
- /package/dist/{src/sources → sources}/providers/tar-utils.js +0 -0
- /package/dist/{src/sources → sources}/resolve.js +0 -0
- /package/dist/{src/sources → sources}/types.js +0 -0
- /package/dist/{src/templates → templates}/wiki-templates.js +0 -0
- /package/dist/{src/version.js → version.js} +0 -0
- /package/dist/{src/wiki → wiki}/wiki.js +0 -0
- /package/dist/{src/workflows → workflows}/authoring.js +0 -0
- /package/dist/{src/workflows → workflows}/cli.js +0 -0
- /package/dist/{src/workflows → workflows}/db.js +0 -0
- /package/dist/{src/workflows → workflows}/document-cache.js +0 -0
- /package/dist/{src/workflows → workflows}/parser.js +0 -0
- /package/dist/{src/workflows → workflows}/renderer.js +0 -0
- /package/dist/{src/workflows → workflows}/runs.js +0 -0
- /package/dist/{src/workflows → workflows}/schema.js +0 -0
- /package/dist/{src/workflows → workflows}/validator.js +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { fetchWithRetry, ResponseTooLargeError, readBodyWithByteCap } from "
|
|
5
|
-
import { ConfigError, UsageError } from "
|
|
6
|
-
import { getRegistryIndexCacheDir } from "
|
|
7
|
-
import { warn } from "
|
|
8
|
-
import {
|
|
9
|
-
import { isExpired, sanitizeString } from "./provider-utils";
|
|
4
|
+
import { fetchWithRetry, ResponseTooLargeError, readBodyWithByteCap } from "../core/common";
|
|
5
|
+
import { ConfigError, UsageError } from "../core/errors";
|
|
6
|
+
import { getRegistryIndexCacheDir } from "../core/paths";
|
|
7
|
+
import { warn } from "../core/warn";
|
|
8
|
+
import { isExpired, sanitizeString } from "./providers/provider-utils";
|
|
10
9
|
/** Refresh website snapshots every 12 hours to balance freshness with scraping load. */
|
|
11
10
|
const CACHE_REFRESH_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
12
11
|
/** Allow up to 7 days of stale snapshots when refresh fails so search remains available during outages. */
|
|
@@ -28,36 +27,7 @@ const WEBSITE_PAGE_BYTE_CAP = 5 * 1024 * 1024;
|
|
|
28
27
|
* whole crawl and return what we have when time runs out.
|
|
29
28
|
*/
|
|
30
29
|
const WEBSITE_CRAWL_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
31
|
-
|
|
32
|
-
* Website source provider — scrapes pages into a local mirror so the FTS5
|
|
33
|
-
* indexer can walk them. Implements the v1 {@link SourceProvider} interface
|
|
34
|
-
* (spec §2.1): `{ name, kind, init, path, sync }`.
|
|
35
|
-
*
|
|
36
|
-
* Reading is the indexer's job — this class doesn't implement `search` or
|
|
37
|
-
* `show`.
|
|
38
|
-
*/
|
|
39
|
-
class WebsiteSourceProvider {
|
|
40
|
-
kind = "website";
|
|
41
|
-
name;
|
|
42
|
-
#config;
|
|
43
|
-
#url;
|
|
44
|
-
constructor(config) {
|
|
45
|
-
this.#config = config;
|
|
46
|
-
this.name = config.name ?? "website";
|
|
47
|
-
this.#url = validateWebsiteUrl(config.url ?? "");
|
|
48
|
-
}
|
|
49
|
-
async init(_ctx) {
|
|
50
|
-
// URL validation already happens in the constructor; nothing else to do.
|
|
51
|
-
}
|
|
52
|
-
path() {
|
|
53
|
-
return getCachePaths(this.#url).stashDir;
|
|
54
|
-
}
|
|
55
|
-
async sync() {
|
|
56
|
-
await ensureWebsiteMirror(this.#config, { requireStashDir: true });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
registerSourceProvider("website", (config) => new WebsiteSourceProvider(config));
|
|
60
|
-
function getCachePaths(siteUrl) {
|
|
30
|
+
export function getWebsiteCachePaths(siteUrl) {
|
|
61
31
|
const key = createHash("sha256").update(normalizeSiteUrl(siteUrl)).digest("hex").slice(0, 16);
|
|
62
32
|
const rootDir = path.join(getRegistryIndexCacheDir(), `website-${key}`);
|
|
63
33
|
return {
|
|
@@ -66,10 +36,10 @@ function getCachePaths(siteUrl) {
|
|
|
66
36
|
manifestPath: path.join(rootDir, "manifest.json"),
|
|
67
37
|
};
|
|
68
38
|
}
|
|
69
|
-
async function ensureWebsiteMirror(config, options) {
|
|
39
|
+
export async function ensureWebsiteMirror(config, options) {
|
|
70
40
|
const rawUrl = config.url ?? "";
|
|
71
41
|
const normalizedUrl = validateWebsiteUrl(rawUrl);
|
|
72
|
-
const cachePaths =
|
|
42
|
+
const cachePaths = getWebsiteCachePaths(normalizedUrl);
|
|
73
43
|
const requireStashDir = options?.requireStashDir === true;
|
|
74
44
|
const force = options?.force === true;
|
|
75
45
|
let mtime = 0;
|
|
@@ -106,7 +76,6 @@ function hasExtractedSite(stashDir) {
|
|
|
106
76
|
const knowledgeDir = path.join(stashDir, "knowledge");
|
|
107
77
|
if (!fs.statSync(stashDir).isDirectory() || !fs.statSync(knowledgeDir).isDirectory())
|
|
108
78
|
return false;
|
|
109
|
-
// Check top-level and one level of subdirectories for .md files
|
|
110
79
|
for (const entry of fs.readdirSync(knowledgeDir, { withFileTypes: true })) {
|
|
111
80
|
if (entry.isFile() && entry.name.endsWith(".md"))
|
|
112
81
|
return true;
|
|
@@ -142,6 +111,22 @@ async function scrapeWebsiteToStash(startUrl, stashDir, options) {
|
|
|
142
111
|
fs.writeFileSync(filePath, buildMarkdownSnapshot(page, slug), "utf8");
|
|
143
112
|
}
|
|
144
113
|
}
|
|
114
|
+
export async function fetchWebsiteMarkdownSnapshot(rawUrl) {
|
|
115
|
+
const normalizedUrl = validateWebsiteInputUrl(rawUrl);
|
|
116
|
+
const fetched = await fetchWebsitePage(normalizedUrl);
|
|
117
|
+
if (!fetched) {
|
|
118
|
+
throw new UsageError(`No content could be fetched from ${normalizedUrl}`);
|
|
119
|
+
}
|
|
120
|
+
const preferredName = deriveImportPath(fetched.page.url);
|
|
121
|
+
const slug = preferredName.split("/").pop() ?? preferredName;
|
|
122
|
+
return {
|
|
123
|
+
url: fetched.page.url,
|
|
124
|
+
title: fetched.page.title,
|
|
125
|
+
markdown: fetched.page.markdown,
|
|
126
|
+
preferredName,
|
|
127
|
+
content: buildMarkdownSnapshot(fetched.page, slug || "website"),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
145
130
|
async function crawlWebsite(startUrl, options) {
|
|
146
131
|
const start = new URL(normalizeSiteUrl(startUrl));
|
|
147
132
|
const allowedOrigin = start.origin;
|
|
@@ -199,10 +184,8 @@ async function fetchWebsitePage(pageUrl) {
|
|
|
199
184
|
body = await readBodyWithByteCap(response, WEBSITE_PAGE_BYTE_CAP);
|
|
200
185
|
}
|
|
201
186
|
catch (err) {
|
|
202
|
-
if (err instanceof ResponseTooLargeError)
|
|
203
|
-
// Skip oversized pages rather than aborting the whole crawl.
|
|
187
|
+
if (err instanceof ResponseTooLargeError)
|
|
204
188
|
return null;
|
|
205
|
-
}
|
|
206
189
|
throw err;
|
|
207
190
|
}
|
|
208
191
|
const finalUrl = normalizeCrawlUrl(response.url || pageUrl) ?? pageUrl;
|
|
@@ -250,10 +233,10 @@ function buildMarkdownSnapshot(page, slug) {
|
|
|
250
233
|
"",
|
|
251
234
|
].join("\n");
|
|
252
235
|
}
|
|
253
|
-
function validateWebsiteUrl(rawUrl) {
|
|
236
|
+
export function validateWebsiteUrl(rawUrl) {
|
|
254
237
|
return validateWebsiteUrlWithError(rawUrl, ConfigError);
|
|
255
238
|
}
|
|
256
|
-
function validateWebsiteInputUrl(rawUrl) {
|
|
239
|
+
export function validateWebsiteInputUrl(rawUrl) {
|
|
257
240
|
return validateWebsiteUrlWithError(rawUrl, UsageError);
|
|
258
241
|
}
|
|
259
242
|
function validateWebsiteUrlWithError(rawUrl, ErrorType) {
|
|
@@ -299,10 +282,6 @@ function normalizeCrawlUrl(rawUrl) {
|
|
|
299
282
|
return null;
|
|
300
283
|
}
|
|
301
284
|
}
|
|
302
|
-
/** Convert a page URL into a relative file path preserving the URL hierarchy.
|
|
303
|
-
* e.g. https://example.com/docs/guide → docs/guide
|
|
304
|
-
* https://example.com/ → index
|
|
305
|
-
*/
|
|
306
285
|
function urlToRelativePath(rawUrl) {
|
|
307
286
|
const parsed = new URL(rawUrl);
|
|
308
287
|
const segments = parsed.pathname
|
|
@@ -318,6 +297,17 @@ function urlToRelativePath(rawUrl) {
|
|
|
318
297
|
}
|
|
319
298
|
return segments.length > 0 ? segments.join("/") : "index";
|
|
320
299
|
}
|
|
300
|
+
function deriveImportPath(rawUrl) {
|
|
301
|
+
const parsed = new URL(rawUrl);
|
|
302
|
+
const relativePath = urlToRelativePath(rawUrl);
|
|
303
|
+
if (relativePath !== "index")
|
|
304
|
+
return relativePath;
|
|
305
|
+
const host = slugifySegment(parsed.hostname) || "website";
|
|
306
|
+
if (!parsed.search)
|
|
307
|
+
return host;
|
|
308
|
+
const querySuffix = slugifySegment(parsed.search.slice(1));
|
|
309
|
+
return querySuffix ? `${host}-${querySuffix}` : host;
|
|
310
|
+
}
|
|
321
311
|
function slugifySegment(value) {
|
|
322
312
|
return sanitizeString(value, 200)
|
|
323
313
|
.toLowerCase()
|
|
@@ -459,8 +449,6 @@ function decodeHtmlEntities(value) {
|
|
|
459
449
|
});
|
|
460
450
|
}
|
|
461
451
|
function isAssetLikePath(pathname) {
|
|
462
|
-
// Keep this list intentionally conservative so docs paths are still crawled
|
|
463
|
-
// unless they clearly point at static assets/binaries.
|
|
464
452
|
return /\.(css|js|json|png|jpe?g|gif|svg|ico|webp|pdf|zip|tar|gz|mp4|mp3|woff2?)$/i.test(pathname);
|
|
465
453
|
}
|
|
466
454
|
function isSafeLinkUrl(url) {
|
|
@@ -480,4 +468,3 @@ function safeCodePointToString(value) {
|
|
|
480
468
|
return undefined;
|
|
481
469
|
}
|
|
482
470
|
}
|
|
483
|
-
export { ensureWebsiteMirror, getCachePaths, validateWebsiteInputUrl, validateWebsiteUrl, WebsiteSourceProvider };
|
package/docs/README.md
CHANGED
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
- [Configuration](configuration.md) -- Providers, settings, and Ollama setup
|
|
22
22
|
- [Filesystem](technical/filesystem.md) -- Directory layout and `.stash.json` schema
|
|
23
23
|
|
|
24
|
+
## Official Ecosystem Repositories
|
|
25
|
+
|
|
26
|
+
- [itlackey/akm-stash](https://github.com/itlackey/akm-stash) -- the official onboarding stash with ready-made assets you can install with `akm add`
|
|
27
|
+
- [itlackey/akm-registry](https://github.com/itlackey/akm-registry) -- the official registry index that powers built-in discovery
|
|
28
|
+
- [itlackey/akm-plugins](https://github.com/itlackey/akm-plugins) -- optional integrations for tools like OpenCode
|
|
29
|
+
- [itlackey/akm-bench](https://github.com/itlackey/akm-bench) -- the standalone benchmark and evaluation repo for akm
|
|
30
|
+
|
|
24
31
|
## Internals
|
|
25
32
|
|
|
26
33
|
- [Search](technical/search.md) -- Hybrid search architecture and scoring
|
|
@@ -150,6 +150,20 @@ This is the surface 0.7.0 commits to maintain through 0.8.x / 0.9.x.
|
|
|
150
150
|
Renaming or removing any of these commands at 1.0 GA would be a major
|
|
151
151
|
version bump.
|
|
152
152
|
|
|
153
|
+
## Post-0.7.0 additive surfaces
|
|
154
|
+
|
|
155
|
+
These landed after the 0.7.0 cut without changing the existing command grammar:
|
|
156
|
+
|
|
157
|
+
- `akm import <url>` now fetches one HTTP/HTTPS URL, converts it to markdown,
|
|
158
|
+
and writes it into `knowledge/` using a URL-path-derived default name.
|
|
159
|
+
- `akm wiki stash <name> <url>` now fetches one HTTP/HTTPS URL, converts it to
|
|
160
|
+
markdown, and writes it into `wikis/<name>/raw/`.
|
|
161
|
+
- Both flows are one-shot ingest only: they do not register a persistent
|
|
162
|
+
website source and they do not crawl linked pages.
|
|
163
|
+
- Website source sync (`akm add <url> --provider website`) and one-shot URL
|
|
164
|
+
ingest now share the same `src/sources/website-ingest.ts` module for URL
|
|
165
|
+
validation, fetch/convert, and mirror generation behavior.
|
|
166
|
+
|
|
153
167
|
## Pre-prod hardening (PR #275)
|
|
154
168
|
|
|
155
169
|
PR #275 batched five issues plus the bench tmp follow-up that operators
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
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": [
|
|
@@ -42,10 +42,13 @@
|
|
|
42
42
|
"akm": "dist/cli.js"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "rm -rf dist && bun run tsc --project ./tsconfig.json
|
|
45
|
+
"build": "rm -rf dist && bun run tsc --project ./tsconfig.build.json",
|
|
46
46
|
"check": "bun run lint && bunx tsc --noEmit && bun test ./tests",
|
|
47
47
|
"check:changed": "bun test tests/output-baseline.test.ts tests/e2e.test.ts tests/stash-search.test.ts && bun run lint && bunx tsc --noEmit",
|
|
48
48
|
"test": "bun test ./tests",
|
|
49
|
+
"lint:devto-posts": "bun scripts/lint-devto-posts.ts",
|
|
50
|
+
"lint:devto-posts:fix": "bun scripts/lint-devto-posts.ts --fix",
|
|
51
|
+
"publish:devto": "npx -y @sinedied/devto-cli push \"docs/posts/**/*.md\" --token \"$DEVTO_TOKEN\" --repo \"$GITHUB_REPOSITORY\" --branch \"${GITHUB_REF_NAME:-main}\" --reconcile",
|
|
49
52
|
"release:check": "./tests/release-check.sh",
|
|
50
53
|
"lint": "bunx biome check src/ tests/",
|
|
51
54
|
"lint:fix": "bunx biome check --write src/ tests/",
|
|
@@ -57,9 +60,9 @@
|
|
|
57
60
|
"access": "public"
|
|
58
61
|
},
|
|
59
62
|
"devDependencies": {
|
|
60
|
-
"@biomejs/biome": "^2.4.
|
|
61
|
-
"@types/node": "^22.
|
|
62
|
-
"bun-types": "^1.3.
|
|
63
|
+
"@biomejs/biome": "^2.4.14",
|
|
64
|
+
"@types/node": "^22.19.17",
|
|
65
|
+
"bun-types": "^1.3.13",
|
|
63
66
|
"typescript": "^5.9.3"
|
|
64
67
|
},
|
|
65
68
|
"optionalDependencies": {
|
|
@@ -70,9 +73,9 @@
|
|
|
70
73
|
"bun": ">=1.0.0"
|
|
71
74
|
},
|
|
72
75
|
"dependencies": {
|
|
73
|
-
"@clack/prompts": "^1.
|
|
74
|
-
"citty": "^0.2.
|
|
76
|
+
"@clack/prompts": "^1.3.0",
|
|
77
|
+
"citty": "^0.2.2",
|
|
75
78
|
"dotenv": "^17.4.2",
|
|
76
|
-
"yaml": "^2.8.
|
|
79
|
+
"yaml": "^2.8.4"
|
|
77
80
|
}
|
|
78
81
|
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM helper for the `akm index` memory-inference pass (#201).
|
|
3
|
-
*
|
|
4
|
-
* Splits a single memory body into a list of atomic facts. The pass itself
|
|
5
|
-
* (in `src/indexer/memory-inference.ts`) is responsible for deciding which
|
|
6
|
-
* memories are pending, persisting the resulting atomic memories with the
|
|
7
|
-
* correct frontmatter (`inferred: true`, `source: <parent-ref>`), and
|
|
8
|
-
* marking the parent as processed for idempotency.
|
|
9
|
-
*
|
|
10
|
-
* This module is intentionally tiny and stateless so tests can stub it via
|
|
11
|
-
* `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
|
|
12
|
-
*
|
|
13
|
-
* Locked v1 contract (#208): the LLM connection always comes from the
|
|
14
|
-
* shared `akm.llm` block — never from a per-pass override. Callers obtain
|
|
15
|
-
* the connection via `resolveIndexPassLLM("memory", config)` and pass it
|
|
16
|
-
* straight through.
|
|
17
|
-
*/
|
|
18
|
-
import { toErrorMessage } from "../core/common";
|
|
19
|
-
import { warn } from "../core/warn";
|
|
20
|
-
import { chatCompletion, parseJsonResponse } from "./client";
|
|
21
|
-
/** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
|
|
22
|
-
const MAX_BODY_CHARS = 4000;
|
|
23
|
-
/** Hard cap on the number of atomic facts returned per memory. */
|
|
24
|
-
const MAX_FACTS_PER_MEMORY = 16;
|
|
25
|
-
/** Hard timeout for the LLM call. The index run must not hang on a misbehaving endpoint. */
|
|
26
|
-
const LLM_TIMEOUT_MS = 30_000;
|
|
27
|
-
const SYSTEM_PROMPT = "You split a developer memory into atomic, self-contained facts. " +
|
|
28
|
-
"Return only valid JSON. No prose, no markdown fences.";
|
|
29
|
-
const USER_PROMPT_PREFIX = `Split the memory below into a JSON array of short, self-contained atomic facts.
|
|
30
|
-
|
|
31
|
-
Rules:
|
|
32
|
-
- Output ONLY a JSON object: {"facts": ["fact one", "fact two", ...]}.
|
|
33
|
-
- Each fact is a single complete sentence, decontextualized so it stands alone.
|
|
34
|
-
- Drop pleasantries, meta-commentary, and timestamps.
|
|
35
|
-
- Preserve technical specifics (names, versions, identifiers) verbatim.
|
|
36
|
-
- If the memory is already a single atomic fact, return it as the only entry.
|
|
37
|
-
- Limit to at most ${MAX_FACTS_PER_MEMORY} facts.
|
|
38
|
-
|
|
39
|
-
Memory:
|
|
40
|
-
`;
|
|
41
|
-
/**
|
|
42
|
-
* Split a single memory body into atomic facts via the configured LLM.
|
|
43
|
-
*
|
|
44
|
-
* Returns `[]` on any failure (timeout, invalid JSON, empty response). Errors
|
|
45
|
-
* are logged via `warn()` but never thrown — a failed split for one memory
|
|
46
|
-
* must not abort the rest of the index pass.
|
|
47
|
-
*/
|
|
48
|
-
export async function splitMemoryIntoAtomicFacts(llmConfig, body) {
|
|
49
|
-
const trimmedBody = body.trim();
|
|
50
|
-
if (!trimmedBody)
|
|
51
|
-
return [];
|
|
52
|
-
const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
|
|
53
|
-
let timeoutHandle;
|
|
54
|
-
try {
|
|
55
|
-
const raw = await Promise.race([
|
|
56
|
-
chatCompletion(llmConfig, [
|
|
57
|
-
{ role: "system", content: SYSTEM_PROMPT },
|
|
58
|
-
{ role: "user", content: userPrompt },
|
|
59
|
-
], { maxTokens: 768, temperature: 0.1 }),
|
|
60
|
-
new Promise((_, reject) => {
|
|
61
|
-
timeoutHandle = setTimeout(() => reject(new Error("memory inference timed out")), LLM_TIMEOUT_MS);
|
|
62
|
-
}),
|
|
63
|
-
]);
|
|
64
|
-
if (!raw)
|
|
65
|
-
return [];
|
|
66
|
-
const parsed = parseJsonResponse(raw);
|
|
67
|
-
if (!parsed || !Array.isArray(parsed.facts)) {
|
|
68
|
-
warn("memory inference: invalid JSON response from LLM; skipping memory.");
|
|
69
|
-
return [];
|
|
70
|
-
}
|
|
71
|
-
const facts = parsed.facts
|
|
72
|
-
.filter((f) => typeof f === "string")
|
|
73
|
-
.map((f) => f.trim())
|
|
74
|
-
.filter((f) => f.length > 0)
|
|
75
|
-
.slice(0, MAX_FACTS_PER_MEMORY);
|
|
76
|
-
return facts;
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
warn(`memory inference failed: ${toErrorMessage(err)}`);
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
finally {
|
|
83
|
-
if (timeoutHandle !== undefined)
|
|
84
|
-
clearTimeout(timeoutHandle);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { afterAll, afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
const CLI = path.join(__dirname, "..", "src", "cli.ts");
|
|
7
|
-
const tempDirs = [];
|
|
8
|
-
const servers = [];
|
|
9
|
-
const CLI_TIMEOUT_MS = 30_000;
|
|
10
|
-
const TEST_TIMEOUT_MS = 60_000;
|
|
11
|
-
function makeTempDir(prefix) {
|
|
12
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
13
|
-
tempDirs.push(dir);
|
|
14
|
-
return dir;
|
|
15
|
-
}
|
|
16
|
-
function createWorkingStash() {
|
|
17
|
-
const dir = makeTempDir("akm-add-website-stash-");
|
|
18
|
-
for (const sub of ["skills", "commands", "agents", "knowledge", "scripts"]) {
|
|
19
|
-
fs.mkdirSync(path.join(dir, sub), { recursive: true });
|
|
20
|
-
}
|
|
21
|
-
return dir;
|
|
22
|
-
}
|
|
23
|
-
function serveWebsite() {
|
|
24
|
-
const server = Bun.serve({
|
|
25
|
-
port: 0,
|
|
26
|
-
fetch(request) {
|
|
27
|
-
const url = new URL(request.url);
|
|
28
|
-
if (url.pathname === "/") {
|
|
29
|
-
return new Response("<html><head><title>Example Docs</title></head><body><h1>Example Docs</h1><p>Welcome to the docs.</p><a href='/getting-started'>Getting started</a></body></html>", {
|
|
30
|
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
if (url.pathname === "/getting-started") {
|
|
34
|
-
return new Response("<html><body><h1>Getting started</h1><p>Run setup first.</p></body></html>", {
|
|
35
|
-
headers: { "Content-Type": "text/html; charset=utf-8" },
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return new Response("not found", { status: 404 });
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
servers.push(server);
|
|
42
|
-
return `http://127.0.0.1:${server.port}`;
|
|
43
|
-
}
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
for (const server of servers.splice(0)) {
|
|
46
|
-
server.stop(true);
|
|
47
|
-
}
|
|
48
|
-
for (const dir of tempDirs.splice(0)) {
|
|
49
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
afterAll(() => {
|
|
53
|
-
for (const server of servers.splice(0)) {
|
|
54
|
-
server.stop(true);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
describe("akm add website", () => {
|
|
58
|
-
test("adds a website stash source, caches markdown, and indexes it", async () => {
|
|
59
|
-
const stashDir = createWorkingStash();
|
|
60
|
-
const xdgCache = makeTempDir("akm-add-website-cache-");
|
|
61
|
-
const xdgConfig = makeTempDir("akm-add-website-config-");
|
|
62
|
-
const websiteUrl = serveWebsite();
|
|
63
|
-
const configDir = path.join(xdgConfig, "akm");
|
|
64
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
65
|
-
fs.writeFileSync(path.join(configDir, "config.json"), `${JSON.stringify({ semanticSearchMode: "off" }, null, 2)}\n`);
|
|
66
|
-
const child = spawn("bun", [CLI, "add", websiteUrl, "--name", "docs-site", "--format=json"], {
|
|
67
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
68
|
-
env: {
|
|
69
|
-
...process.env,
|
|
70
|
-
AKM_STASH_DIR: stashDir,
|
|
71
|
-
XDG_CACHE_HOME: xdgCache,
|
|
72
|
-
XDG_CONFIG_HOME: xdgConfig,
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
let stdout = "";
|
|
76
|
-
let stderr = "";
|
|
77
|
-
child.stdout.on("data", (chunk) => {
|
|
78
|
-
stdout += String(chunk);
|
|
79
|
-
});
|
|
80
|
-
child.stderr.on("data", (chunk) => {
|
|
81
|
-
stderr += String(chunk);
|
|
82
|
-
});
|
|
83
|
-
const exitCode = await new Promise((resolve, reject) => {
|
|
84
|
-
const timer = setTimeout(() => {
|
|
85
|
-
child.kill("SIGKILL");
|
|
86
|
-
reject(new Error("CLI website add timed out"));
|
|
87
|
-
}, CLI_TIMEOUT_MS);
|
|
88
|
-
child.on("error", (err) => {
|
|
89
|
-
clearTimeout(timer);
|
|
90
|
-
reject(err);
|
|
91
|
-
});
|
|
92
|
-
child.on("close", (code) => {
|
|
93
|
-
clearTimeout(timer);
|
|
94
|
-
resolve(code ?? 1);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
expect(exitCode).toBe(0);
|
|
98
|
-
expect(stderr.trim()).toBe("");
|
|
99
|
-
const parsed = JSON.parse(stdout.trim());
|
|
100
|
-
const normalizedWebsiteUrl = `${websiteUrl}/`;
|
|
101
|
-
expect(parsed.sourceAdded).toBeDefined();
|
|
102
|
-
expect(parsed.sourceAdded?.type).toBe("website");
|
|
103
|
-
expect(parsed.sourceAdded?.url).toBe(normalizedWebsiteUrl);
|
|
104
|
-
expect(parsed.sourceAdded?.name).toBe("docs-site");
|
|
105
|
-
expect(parsed.index?.totalEntries).toBeGreaterThanOrEqual(2);
|
|
106
|
-
const configPath = path.join(xdgConfig, "akm", "config.json");
|
|
107
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
108
|
-
expect(config.sources).toContainEqual({
|
|
109
|
-
type: "website",
|
|
110
|
-
url: normalizedWebsiteUrl,
|
|
111
|
-
name: "docs-site",
|
|
112
|
-
});
|
|
113
|
-
expect(parsed.sourceAdded?.stashRoot).toBeDefined();
|
|
114
|
-
const knowledgeFiles = fs.readdirSync(path.join(parsed.sourceAdded?.stashRoot, "knowledge")).sort();
|
|
115
|
-
expect(knowledgeFiles).toEqual(["getting-started.md", "index.md"]);
|
|
116
|
-
const homeDoc = fs.readFileSync(path.join(parsed.sourceAdded?.stashRoot, "knowledge", "index.md"), "utf8");
|
|
117
|
-
expect(homeDoc).toContain("Example Docs");
|
|
118
|
-
}, { timeout: TEST_TIMEOUT_MS });
|
|
119
|
-
});
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration test: the AkmConfig loader propagates the `agent` block
|
|
3
|
-
* through `loadConfig()` (and through the on-disk JSONC parser, exercising
|
|
4
|
-
* the `pickKnownKeys` path).
|
|
5
|
-
*
|
|
6
|
-
* The acceptance criterion "config schema accepts an optional agent block"
|
|
7
|
-
* lives at the loader boundary, not just the parser; this test pins the
|
|
8
|
-
* end-to-end shape.
|
|
9
|
-
*/
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
-
import fs from "node:fs";
|
|
12
|
-
import os from "node:os";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
let tmpHome;
|
|
15
|
-
let originalHome;
|
|
16
|
-
let originalXdg;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), "akm-agent-cfg-"));
|
|
19
|
-
originalHome = process.env.HOME;
|
|
20
|
-
originalXdg = process.env.XDG_CONFIG_HOME;
|
|
21
|
-
process.env.HOME = tmpHome;
|
|
22
|
-
process.env.XDG_CONFIG_HOME = path.join(tmpHome, ".config");
|
|
23
|
-
});
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
if (originalHome === undefined)
|
|
26
|
-
delete process.env.HOME;
|
|
27
|
-
else
|
|
28
|
-
process.env.HOME = originalHome;
|
|
29
|
-
if (originalXdg === undefined)
|
|
30
|
-
delete process.env.XDG_CONFIG_HOME;
|
|
31
|
-
else
|
|
32
|
-
process.env.XDG_CONFIG_HOME = originalXdg;
|
|
33
|
-
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
34
|
-
});
|
|
35
|
-
describe("AkmConfig loader — agent block", () => {
|
|
36
|
-
test("loads agent.default + agent.profiles from disk", async () => {
|
|
37
|
-
const { getConfigPath, loadUserConfig, resetConfigCache } = await import("../../src/core/config");
|
|
38
|
-
const cfgPath = getConfigPath();
|
|
39
|
-
fs.mkdirSync(path.dirname(cfgPath), { recursive: true });
|
|
40
|
-
fs.writeFileSync(cfgPath, JSON.stringify({
|
|
41
|
-
semanticSearchMode: "auto",
|
|
42
|
-
agent: {
|
|
43
|
-
default: "claude",
|
|
44
|
-
timeoutMs: 45000,
|
|
45
|
-
profiles: {
|
|
46
|
-
claude: { args: ["--print"] },
|
|
47
|
-
rover: { bin: "rover-cli", parseOutput: "json" },
|
|
48
|
-
},
|
|
49
|
-
// Unknown key — must not throw at load.
|
|
50
|
-
mystery: 1,
|
|
51
|
-
},
|
|
52
|
-
}, null, 2));
|
|
53
|
-
resetConfigCache();
|
|
54
|
-
const cfg = loadUserConfig();
|
|
55
|
-
expect(cfg.agent?.default).toBe("claude");
|
|
56
|
-
expect(cfg.agent?.timeoutMs).toBe(45000);
|
|
57
|
-
expect(cfg.agent?.profiles?.claude?.args).toEqual(["--print"]);
|
|
58
|
-
expect(cfg.agent?.profiles?.rover?.bin).toBe("rover-cli");
|
|
59
|
-
expect(cfg.agent?.profiles?.rover?.parseOutput).toBe("json");
|
|
60
|
-
});
|
|
61
|
-
test("agent block absent → cfg.agent is undefined → requireAgentProfile throws", async () => {
|
|
62
|
-
const { loadUserConfig, resetConfigCache } = await import("../../src/core/config");
|
|
63
|
-
const { requireAgentProfile } = await import("../../src/integrations/agent/config");
|
|
64
|
-
const { ConfigError } = await import("../../src/core/errors");
|
|
65
|
-
resetConfigCache();
|
|
66
|
-
const cfg = loadUserConfig();
|
|
67
|
-
expect(cfg.agent).toBeUndefined();
|
|
68
|
-
expect(() => requireAgentProfile(cfg.agent)).toThrow(ConfigError);
|
|
69
|
-
});
|
|
70
|
-
});
|