akm-cli 0.7.0 → 0.7.1
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/package.json +8 -8
- 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/cli.js → cli.js} +0 -0
- /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}/installed-stashes.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-add.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}/common.js +0 -0
- /package/dist/{src/core → core}/config.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-search.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}/graph-extraction.js +0 -0
- /package/dist/{src/indexer → indexer}/indexer.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}/memory-inference.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}/search-source.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}/client.js +0 -0
- /package/dist/{src/llm → llm}/embedder.js +0 -0
- /package/dist/{src/llm → llm}/embedders/cache.js +0 -0
- /package/dist/{src/llm → llm}/embedders/local.js +0 -0
- /package/dist/{src/llm → llm}/embedders/remote.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}/graph-extract.js +0 -0
- /package/dist/{src/llm → llm}/index-passes.js +0 -0
- /package/dist/{src/llm → llm}/memory-infer.js +0 -0
- /package/dist/{src/llm → llm}/metadata-enhance.js +0 -0
- /package/dist/{src/output → output}/cli-hints.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}/setup.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}/providers/website.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,370 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { akmEventsList, akmEventsTail } from "../../src/commands/events";
|
|
7
|
-
import { saveConfig } from "../../src/core/config";
|
|
8
|
-
import { appendEvent, getEventsPath, readEvents, tailEvents } from "../../src/core/events";
|
|
9
|
-
const CLI = path.join(__dirname, "..", "..", "src", "cli.ts");
|
|
10
|
-
const tempDirs = [];
|
|
11
|
-
const savedEnv = {
|
|
12
|
-
AKM_STASH_DIR: process.env.AKM_STASH_DIR,
|
|
13
|
-
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
|
|
14
|
-
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
|
|
15
|
-
};
|
|
16
|
-
function makeTempDir(prefix) {
|
|
17
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
18
|
-
tempDirs.push(dir);
|
|
19
|
-
return dir;
|
|
20
|
-
}
|
|
21
|
-
function writeFile(filePath, content) {
|
|
22
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
23
|
-
fs.writeFileSync(filePath, content);
|
|
24
|
-
}
|
|
25
|
-
function runCli(args) {
|
|
26
|
-
const result = spawnSync("bun", [CLI, ...args], {
|
|
27
|
-
encoding: "utf8",
|
|
28
|
-
timeout: 30_000,
|
|
29
|
-
env: { ...process.env },
|
|
30
|
-
});
|
|
31
|
-
return {
|
|
32
|
-
status: result.status,
|
|
33
|
-
stdout: result.stdout ?? "",
|
|
34
|
-
stderr: result.stderr ?? "",
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
process.env.XDG_CACHE_HOME = makeTempDir("akm-events-cache-");
|
|
39
|
-
process.env.XDG_CONFIG_HOME = makeTempDir("akm-events-config-");
|
|
40
|
-
});
|
|
41
|
-
afterEach(() => {
|
|
42
|
-
if (savedEnv.AKM_STASH_DIR === undefined)
|
|
43
|
-
delete process.env.AKM_STASH_DIR;
|
|
44
|
-
else
|
|
45
|
-
process.env.AKM_STASH_DIR = savedEnv.AKM_STASH_DIR;
|
|
46
|
-
if (savedEnv.XDG_CACHE_HOME === undefined)
|
|
47
|
-
delete process.env.XDG_CACHE_HOME;
|
|
48
|
-
else
|
|
49
|
-
process.env.XDG_CACHE_HOME = savedEnv.XDG_CACHE_HOME;
|
|
50
|
-
if (savedEnv.XDG_CONFIG_HOME === undefined)
|
|
51
|
-
delete process.env.XDG_CONFIG_HOME;
|
|
52
|
-
else
|
|
53
|
-
process.env.XDG_CONFIG_HOME = savedEnv.XDG_CONFIG_HOME;
|
|
54
|
-
for (const dir of tempDirs.splice(0)) {
|
|
55
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
describe("appendEvent / readEvents", () => {
|
|
59
|
-
test("appends events as newline-delimited JSON", () => {
|
|
60
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
61
|
-
let now = 1_700_000_000_000;
|
|
62
|
-
const ctx = { filePath, now: () => now };
|
|
63
|
-
appendEvent({ eventType: "remember", ref: "memory:alpha", metadata: { tagCount: 2 } }, ctx);
|
|
64
|
-
now += 1000;
|
|
65
|
-
appendEvent({ eventType: "feedback", ref: "memory:alpha", metadata: { signal: "positive" } }, ctx);
|
|
66
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
67
|
-
const lines = content.trim().split("\n");
|
|
68
|
-
expect(lines).toHaveLength(2);
|
|
69
|
-
const first = JSON.parse(lines[0]);
|
|
70
|
-
expect(first.eventType).toBe("remember");
|
|
71
|
-
expect(first.ref).toBe("memory:alpha");
|
|
72
|
-
expect(first.schemaVersion).toBe(1);
|
|
73
|
-
expect(typeof first.ts).toBe("string");
|
|
74
|
-
});
|
|
75
|
-
test("readEvents returns parsed envelopes with monotonic byte offsets", () => {
|
|
76
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
77
|
-
const ctx = { filePath };
|
|
78
|
-
appendEvent({ eventType: "add", metadata: { target: "user/repo" } }, ctx);
|
|
79
|
-
appendEvent({ eventType: "remove", metadata: { target: "user/repo" } }, ctx);
|
|
80
|
-
const result = readEvents({}, ctx);
|
|
81
|
-
expect(result.events).toHaveLength(2);
|
|
82
|
-
expect(result.events[0]?.eventType).toBe("add");
|
|
83
|
-
expect(result.events[1]?.eventType).toBe("remove");
|
|
84
|
-
// ids are byte offsets; second must be larger than first
|
|
85
|
-
expect((result.events[1]?.id ?? 0) > (result.events[0]?.id ?? 0)).toBe(true);
|
|
86
|
-
expect(result.nextOffset).toBe(fs.statSync(filePath).size);
|
|
87
|
-
});
|
|
88
|
-
test("--since (byte offset) is durable across processes", () => {
|
|
89
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
90
|
-
const ctx = { filePath };
|
|
91
|
-
appendEvent({ eventType: "remember", ref: "memory:a" }, ctx);
|
|
92
|
-
const cursor = readEvents({}, ctx).nextOffset;
|
|
93
|
-
// Simulate "another process" appending more events
|
|
94
|
-
appendEvent({ eventType: "remember", ref: "memory:b" }, ctx);
|
|
95
|
-
appendEvent({ eventType: "remember", ref: "memory:c" }, ctx);
|
|
96
|
-
const next = readEvents({ sinceOffset: cursor }, ctx);
|
|
97
|
-
expect(next.events.map((e) => e.ref)).toEqual(["memory:b", "memory:c"]);
|
|
98
|
-
// Resuming from the new cursor yields no events when nothing was added.
|
|
99
|
-
const empty = readEvents({ sinceOffset: next.nextOffset }, ctx);
|
|
100
|
-
expect(empty.events).toEqual([]);
|
|
101
|
-
expect(empty.nextOffset).toBe(next.nextOffset);
|
|
102
|
-
});
|
|
103
|
-
test("`akm events list --since @offset:N` resumes across a real process boundary", () => {
|
|
104
|
-
// This is the cross-process durability contract: a producer writes N
|
|
105
|
-
// events, persists nextOffset to a temp file, appends MORE events, and
|
|
106
|
-
// then a SECOND `bun src/cli.ts events list` invocation reads the cursor
|
|
107
|
-
// from the file and must emit only the post-cursor events with no
|
|
108
|
-
// duplicates and no losses.
|
|
109
|
-
const cacheDir = makeTempDir("akm-events-xproc-cache-");
|
|
110
|
-
const configDir = makeTempDir("akm-events-xproc-config-");
|
|
111
|
-
const cursorFile = path.join(makeTempDir("akm-events-xproc-state-"), "cursor.txt");
|
|
112
|
-
// Drive both processes through the same XDG_CACHE_HOME so they share
|
|
113
|
-
// the same events.jsonl path (see env-isolation note in core/events.ts).
|
|
114
|
-
const childEnv = {
|
|
115
|
-
...process.env,
|
|
116
|
-
XDG_CACHE_HOME: cacheDir,
|
|
117
|
-
XDG_CONFIG_HOME: configDir,
|
|
118
|
-
};
|
|
119
|
-
const eventsPath = path.join(cacheDir, "akm", "events.jsonl");
|
|
120
|
-
const ctx = { filePath: eventsPath };
|
|
121
|
-
// 1. Producer writes events 0..2 (the "first batch").
|
|
122
|
-
appendEvent({ eventType: "remember", ref: "memory:e0" }, ctx);
|
|
123
|
-
appendEvent({ eventType: "remember", ref: "memory:e1" }, ctx);
|
|
124
|
-
appendEvent({ eventType: "remember", ref: "memory:e2" }, ctx);
|
|
125
|
-
// 2. Producer persists nextOffset to a temp file.
|
|
126
|
-
const cursor = readEvents({}, ctx).nextOffset;
|
|
127
|
-
fs.writeFileSync(cursorFile, String(cursor));
|
|
128
|
-
// 3. Producer appends MORE events (3..5) BEFORE the second process reads.
|
|
129
|
-
appendEvent({ eventType: "remember", ref: "memory:e3" }, ctx);
|
|
130
|
-
appendEvent({ eventType: "remember", ref: "memory:e4" }, ctx);
|
|
131
|
-
appendEvent({ eventType: "remember", ref: "memory:e5" }, ctx);
|
|
132
|
-
// 4. Spawn a SECOND bun process; it reads the cursor from the temp file
|
|
133
|
-
// and asks the CLI for events with `--since @offset:<cursor>`. This
|
|
134
|
-
// exercises a real exec boundary, not just in-process arithmetic.
|
|
135
|
-
const persisted = fs.readFileSync(cursorFile, "utf8").trim();
|
|
136
|
-
const child = spawnSync("bun", [CLI, "events", "list", "--since", `@offset:${persisted}`, "--format=json"], {
|
|
137
|
-
encoding: "utf8",
|
|
138
|
-
timeout: 30_000,
|
|
139
|
-
env: childEnv,
|
|
140
|
-
});
|
|
141
|
-
expect(child.status).toBe(0);
|
|
142
|
-
const parsed = JSON.parse(child.stdout);
|
|
143
|
-
// 5. Assert: exactly the post-cursor events, in order, no duplicates,
|
|
144
|
-
// no losses. The pre-cursor events MUST NOT appear.
|
|
145
|
-
expect(parsed.events.map((e) => e.ref)).toEqual(["memory:e3", "memory:e4", "memory:e5"]);
|
|
146
|
-
expect(parsed.totalCount).toBe(3);
|
|
147
|
-
expect(parsed.sinceOffset).toBe(Number(persisted));
|
|
148
|
-
expect(parsed.nextOffset).toBeGreaterThan(Number(persisted));
|
|
149
|
-
});
|
|
150
|
-
test("`akm events list --since @offset:` rejects malformed byte cursors", () => {
|
|
151
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
152
|
-
const ctx = { filePath };
|
|
153
|
-
expect(() => akmEventsList({ since: "@offset:not-a-number", ctx })).toThrow(/Invalid --since byte offset/);
|
|
154
|
-
expect(() => akmEventsList({ since: "@offset:-3", ctx })).toThrow(/Invalid --since/);
|
|
155
|
-
});
|
|
156
|
-
test("--since (timestamp) filter is monotonic across processes", () => {
|
|
157
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
158
|
-
let now = 1_700_000_000_000;
|
|
159
|
-
const ctx = { filePath, now: () => now };
|
|
160
|
-
appendEvent({ eventType: "remember", ref: "memory:a" }, ctx);
|
|
161
|
-
const cutoff = new Date(now + 500).toISOString();
|
|
162
|
-
now += 1000;
|
|
163
|
-
appendEvent({ eventType: "remember", ref: "memory:b" }, ctx);
|
|
164
|
-
const result = akmEventsList({ since: cutoff, ctx });
|
|
165
|
-
expect(result.totalCount).toBe(1);
|
|
166
|
-
expect(result.events[0]?.ref).toBe("memory:b");
|
|
167
|
-
});
|
|
168
|
-
test("--type and --ref filters work in combination", () => {
|
|
169
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
170
|
-
const ctx = { filePath };
|
|
171
|
-
appendEvent({ eventType: "remember", ref: "memory:a" }, ctx);
|
|
172
|
-
appendEvent({ eventType: "feedback", ref: "memory:a", metadata: { signal: "positive" } }, ctx);
|
|
173
|
-
appendEvent({ eventType: "feedback", ref: "memory:b", metadata: { signal: "negative" } }, ctx);
|
|
174
|
-
const filtered = akmEventsList({ type: "feedback", ref: "memory:a", ctx });
|
|
175
|
-
expect(filtered.totalCount).toBe(1);
|
|
176
|
-
expect(filtered.events[0]?.eventType).toBe("feedback");
|
|
177
|
-
expect(filtered.events[0]?.ref).toBe("memory:a");
|
|
178
|
-
});
|
|
179
|
-
test("malformed lines are skipped, valid ones still parse", () => {
|
|
180
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
181
|
-
const ctx = { filePath };
|
|
182
|
-
appendEvent({ eventType: "remember", ref: "memory:a" }, ctx);
|
|
183
|
-
fs.appendFileSync(filePath, "this is not json\n");
|
|
184
|
-
appendEvent({ eventType: "remember", ref: "memory:b" }, ctx);
|
|
185
|
-
const result = readEvents({}, ctx);
|
|
186
|
-
expect(result.events.map((e) => e.ref)).toEqual(["memory:a", "memory:b"]);
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
describe("tailEvents", () => {
|
|
190
|
-
test("emits historical events, then follows new appends until maxEvents", async () => {
|
|
191
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
192
|
-
const ctx = { filePath };
|
|
193
|
-
appendEvent({ eventType: "remember", ref: "memory:1" }, ctx);
|
|
194
|
-
const tailPromise = tailEvents({ intervalMs: 25, maxEvents: 3, maxDurationMs: 2_000 }, ctx);
|
|
195
|
-
// Give the tail loop a chance to start polling
|
|
196
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
197
|
-
appendEvent({ eventType: "remember", ref: "memory:2" }, ctx);
|
|
198
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
199
|
-
appendEvent({ eventType: "remember", ref: "memory:3" }, ctx);
|
|
200
|
-
const result = await tailPromise;
|
|
201
|
-
expect(result.events.map((e) => e.ref)).toEqual(["memory:1", "memory:2", "memory:3"]);
|
|
202
|
-
expect(result.reason).toBe("maxEvents");
|
|
203
|
-
});
|
|
204
|
-
test("tail keeps up with a concurrent writer without losing events", async () => {
|
|
205
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
206
|
-
const ctx = { filePath };
|
|
207
|
-
const TOTAL = 50;
|
|
208
|
-
// Writer: append TOTAL events with small jitter so the tail polling
|
|
209
|
-
// intervals span multiple writes. Run concurrently with the tail.
|
|
210
|
-
const writerPromise = (async () => {
|
|
211
|
-
for (let i = 0; i < TOTAL; i += 1) {
|
|
212
|
-
appendEvent({ eventType: "remember", ref: `memory:${i}`, metadata: { i } }, ctx);
|
|
213
|
-
if (i % 5 === 0)
|
|
214
|
-
await new Promise((r) => setTimeout(r, 5));
|
|
215
|
-
}
|
|
216
|
-
})();
|
|
217
|
-
// Tail with maxEvents = TOTAL so we resolve as soon as we've seen
|
|
218
|
-
// every event the writer produced.
|
|
219
|
-
const tailPromise = tailEvents({ intervalMs: 20, maxEvents: TOTAL, maxDurationMs: 5_000 }, ctx);
|
|
220
|
-
await writerPromise;
|
|
221
|
-
const result = await tailPromise;
|
|
222
|
-
expect(result.events).toHaveLength(TOTAL);
|
|
223
|
-
// Event order matches write order (each event has a unique `i`).
|
|
224
|
-
const indices = result.events.map((e) => e.metadata?.i);
|
|
225
|
-
expect(indices).toEqual(Array.from({ length: TOTAL }, (_, idx) => idx));
|
|
226
|
-
expect(result.reason).toBe("maxEvents");
|
|
227
|
-
});
|
|
228
|
-
test("tail terminates on AbortSignal", async () => {
|
|
229
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
230
|
-
const ctx = { filePath };
|
|
231
|
-
const ac = new AbortController();
|
|
232
|
-
setTimeout(() => ac.abort(), 80);
|
|
233
|
-
const result = await tailEvents({ intervalMs: 20, signal: ac.signal, maxDurationMs: 1_000 }, ctx);
|
|
234
|
-
expect(result.reason).toBe("signal");
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
describe("akm CLI mutation events", () => {
|
|
238
|
-
test("remember, feedback, and add each emit an event to events.jsonl", async () => {
|
|
239
|
-
const stashDir = makeTempDir("akm-events-stash-");
|
|
240
|
-
process.env.AKM_STASH_DIR = stashDir;
|
|
241
|
-
saveConfig({ semanticSearchMode: "off" });
|
|
242
|
-
// ─ remember ──────────────────────────────────────────────────────────
|
|
243
|
-
const remember = runCli(["remember", "first event captured", "--name", "alpha", "--format=json"]);
|
|
244
|
-
expect(remember.status).toBe(0);
|
|
245
|
-
// index so feedback can find the ref
|
|
246
|
-
const indexResult = runCli(["index", "--full", "--format=json"]);
|
|
247
|
-
expect(indexResult.status).toBe(0);
|
|
248
|
-
// ─ feedback ──────────────────────────────────────────────────────────
|
|
249
|
-
const feedback = runCli(["feedback", "memory:alpha", "--positive", "--format=json"]);
|
|
250
|
-
expect(feedback.status).toBe(0);
|
|
251
|
-
// ─ add (local directory source) ──────────────────────────────────────
|
|
252
|
-
const localSource = makeTempDir("akm-events-local-");
|
|
253
|
-
writeFile(path.join(localSource, "skills", "demo.md"), "# demo\n\nA demo skill.\n");
|
|
254
|
-
const add = runCli(["add", localSource, "--format=json"]);
|
|
255
|
-
expect(add.status).toBe(0);
|
|
256
|
-
// Confirm events.jsonl contains all three event types in order.
|
|
257
|
-
const eventsPath = getEventsPath();
|
|
258
|
-
expect(fs.existsSync(eventsPath)).toBe(true);
|
|
259
|
-
const lines = fs.readFileSync(eventsPath, "utf8").trim().split("\n");
|
|
260
|
-
const types = lines.map((line) => JSON.parse(line).eventType);
|
|
261
|
-
expect(types).toContain("remember");
|
|
262
|
-
expect(types).toContain("feedback");
|
|
263
|
-
expect(types).toContain("add");
|
|
264
|
-
});
|
|
265
|
-
test("`akm events list` returns the captured events in JSON envelope shape", async () => {
|
|
266
|
-
const stashDir = makeTempDir("akm-events-stash-");
|
|
267
|
-
process.env.AKM_STASH_DIR = stashDir;
|
|
268
|
-
saveConfig({ semanticSearchMode: "off" });
|
|
269
|
-
// Create a remember event via the CLI so events.jsonl exists.
|
|
270
|
-
const remember = runCli(["remember", "another event captured", "--name", "beta", "--format=json"]);
|
|
271
|
-
expect(remember.status).toBe(0);
|
|
272
|
-
const list = runCli(["events", "list", "--format=json"]);
|
|
273
|
-
expect(list.status).toBe(0);
|
|
274
|
-
const parsed = JSON.parse(list.stdout);
|
|
275
|
-
expect(parsed.totalCount).toBeGreaterThanOrEqual(1);
|
|
276
|
-
expect(Array.isArray(parsed.events)).toBe(true);
|
|
277
|
-
const events = parsed.events;
|
|
278
|
-
expect(events.some((e) => e.eventType === "remember")).toBe(true);
|
|
279
|
-
});
|
|
280
|
-
test("`akm events list --type feedback` filters by event type", async () => {
|
|
281
|
-
const stashDir = makeTempDir("akm-events-stash-");
|
|
282
|
-
process.env.AKM_STASH_DIR = stashDir;
|
|
283
|
-
saveConfig({ semanticSearchMode: "off" });
|
|
284
|
-
const remember = runCli(["remember", "filter test", "--name", "gamma", "--format=json"]);
|
|
285
|
-
expect(remember.status).toBe(0);
|
|
286
|
-
runCli(["index", "--full", "--format=json"]);
|
|
287
|
-
runCli(["feedback", "memory:gamma", "--positive", "--format=json"]);
|
|
288
|
-
const filtered = runCli(["events", "list", "--type", "feedback", "--format=json"]);
|
|
289
|
-
expect(filtered.status).toBe(0);
|
|
290
|
-
const parsed = JSON.parse(filtered.stdout);
|
|
291
|
-
const events = parsed.events;
|
|
292
|
-
expect(events.length).toBeGreaterThanOrEqual(1);
|
|
293
|
-
expect(events.every((e) => e.eventType === "feedback")).toBe(true);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
describe("akmEventsTail", () => {
|
|
297
|
-
test("--since timestamp emits only events at or after the cutoff", async () => {
|
|
298
|
-
const filePath = path.join(makeTempDir("akm-events-"), "events.jsonl");
|
|
299
|
-
let now = 1_700_000_000_000;
|
|
300
|
-
const ctx = { filePath, now: () => now };
|
|
301
|
-
appendEvent({ eventType: "remember", ref: "memory:before" }, ctx);
|
|
302
|
-
const cutoff = new Date(now + 500).toISOString();
|
|
303
|
-
now += 1000;
|
|
304
|
-
appendEvent({ eventType: "remember", ref: "memory:after" }, ctx);
|
|
305
|
-
const result = await akmEventsTail({
|
|
306
|
-
since: cutoff,
|
|
307
|
-
ctx,
|
|
308
|
-
intervalMs: 20,
|
|
309
|
-
maxEvents: 1,
|
|
310
|
-
maxDurationMs: 500,
|
|
311
|
-
});
|
|
312
|
-
expect(result.events).toHaveLength(1);
|
|
313
|
-
expect(result.events[0]?.ref).toBe("memory:after");
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
describe("akm events tail (streaming trailer)", () => {
|
|
317
|
-
// Blocker fix: `--format text|jsonl` previously dropped the resumable
|
|
318
|
-
// cursor entirely. After the streaming loop ends we must emit a single
|
|
319
|
-
// trailer line so callers can resume from `nextOffset`.
|
|
320
|
-
test("--format jsonl writes a discriminated trailer row to stdout", () => {
|
|
321
|
-
const cacheDir = makeTempDir("akm-events-trailer-jsonl-cache-");
|
|
322
|
-
const configDir = makeTempDir("akm-events-trailer-jsonl-config-");
|
|
323
|
-
const eventsPath = path.join(cacheDir, "akm", "events.jsonl");
|
|
324
|
-
const ctx = { filePath: eventsPath };
|
|
325
|
-
appendEvent({ eventType: "remember", ref: "memory:t1" }, ctx);
|
|
326
|
-
appendEvent({ eventType: "remember", ref: "memory:t2" }, ctx);
|
|
327
|
-
const child = spawnSync("bun", [
|
|
328
|
-
CLI,
|
|
329
|
-
"events",
|
|
330
|
-
"tail",
|
|
331
|
-
"--format=jsonl",
|
|
332
|
-
"--max-events",
|
|
333
|
-
"2",
|
|
334
|
-
"--max-duration-ms",
|
|
335
|
-
"2000",
|
|
336
|
-
"--interval-ms",
|
|
337
|
-
"20",
|
|
338
|
-
], {
|
|
339
|
-
encoding: "utf8",
|
|
340
|
-
timeout: 30_000,
|
|
341
|
-
env: { ...process.env, XDG_CACHE_HOME: cacheDir, XDG_CONFIG_HOME: configDir },
|
|
342
|
-
});
|
|
343
|
-
expect(child.status).toBe(0);
|
|
344
|
-
const lines = child.stdout.trim().split("\n").filter(Boolean);
|
|
345
|
-
expect(lines.length).toBeGreaterThanOrEqual(3); // 2 events + trailer
|
|
346
|
-
const last = JSON.parse(lines[lines.length - 1]);
|
|
347
|
-
expect(last._kind).toBe("trailer");
|
|
348
|
-
expect(last.schemaVersion).toBe(1);
|
|
349
|
-
expect(typeof last.nextOffset).toBe("number");
|
|
350
|
-
expect(last.totalCount).toBe(2);
|
|
351
|
-
expect(["maxEvents", "signal", "maxDuration"]).toContain(last.reason);
|
|
352
|
-
});
|
|
353
|
-
test("--format text writes a trailer line to stderr (stdout stays pristine)", () => {
|
|
354
|
-
const cacheDir = makeTempDir("akm-events-trailer-text-cache-");
|
|
355
|
-
const configDir = makeTempDir("akm-events-trailer-text-config-");
|
|
356
|
-
const eventsPath = path.join(cacheDir, "akm", "events.jsonl");
|
|
357
|
-
const ctx = { filePath: eventsPath };
|
|
358
|
-
appendEvent({ eventType: "remember", ref: "memory:tx1" }, ctx);
|
|
359
|
-
const child = spawnSync("bun", [CLI, "events", "tail", "--format=text", "--max-events", "1", "--max-duration-ms", "2000", "--interval-ms", "20"], {
|
|
360
|
-
encoding: "utf8",
|
|
361
|
-
timeout: 30_000,
|
|
362
|
-
env: { ...process.env, XDG_CACHE_HOME: cacheDir, XDG_CONFIG_HOME: configDir },
|
|
363
|
-
});
|
|
364
|
-
expect(child.status).toBe(0);
|
|
365
|
-
// stdout: pristine event lines, no trailer mixed in.
|
|
366
|
-
expect(child.stdout).not.toContain("[events-tail]");
|
|
367
|
-
// stderr: the trailer with reason + nextOffset + total.
|
|
368
|
-
expect(child.stderr).toMatch(/\[events-tail\] reason=\S+ nextOffset=\d+ total=\d+/);
|
|
369
|
-
});
|
|
370
|
-
});
|