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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { parseAgentConfig } from "../integrations/agent/config";
|
|
4
5
|
import { filterNonEmptyStrings } from "./common";
|
|
5
6
|
import { ConfigError } from "./errors";
|
|
6
7
|
import { getConfigDir as _getConfigDir, getConfigPath as _getConfigPath } from "./paths";
|
|
@@ -73,7 +74,7 @@ export function loadUserConfig() {
|
|
|
73
74
|
cachedUserConfig.contentHash === contentHash) {
|
|
74
75
|
return cachedUserConfig.config;
|
|
75
76
|
}
|
|
76
|
-
const config = mergeLoadedConfig(DEFAULT_CONFIG, readNormalizedConfigFromText(
|
|
77
|
+
const config = mergeLoadedConfig(DEFAULT_CONFIG, readNormalizedConfigFromText(text));
|
|
77
78
|
const finalConfig = applyRuntimeEnvApiKeys(config);
|
|
78
79
|
cachedUserConfig = {
|
|
79
80
|
config: finalConfig,
|
|
@@ -144,6 +145,14 @@ export function updateConfig(partial) {
|
|
|
144
145
|
if (current.llm && partial.llm && partial.llm !== current.llm) {
|
|
145
146
|
merged.llm = { ...current.llm, ...partial.llm };
|
|
146
147
|
}
|
|
148
|
+
// Deep-merge index per-pass entries so partial updates don't wipe siblings.
|
|
149
|
+
if (current.index && partial.index && partial.index !== current.index) {
|
|
150
|
+
const mergedIndex = { ...current.index };
|
|
151
|
+
for (const [passName, passOverride] of Object.entries(partial.index)) {
|
|
152
|
+
mergedIndex[passName] = { ...(mergedIndex[passName] ?? {}), ...passOverride };
|
|
153
|
+
}
|
|
154
|
+
merged.index = mergedIndex;
|
|
155
|
+
}
|
|
147
156
|
if (current.security && partial.security && partial.security !== current.security) {
|
|
148
157
|
merged.security = mergeSecurityConfig(current.security, partial.security);
|
|
149
158
|
}
|
|
@@ -160,6 +169,9 @@ export function updateConfig(partial) {
|
|
|
160
169
|
*/
|
|
161
170
|
function pickKnownKeys(raw) {
|
|
162
171
|
const config = {};
|
|
172
|
+
if (Array.isArray(raw.stashes)) {
|
|
173
|
+
throw new ConfigError("The legacy `stashes[]` config key is no longer supported; rename it to `sources[]`.", "INVALID_CONFIG_FILE", `Edit ${_getConfigPath()} and replace \`stashes\` with \`sources\`.`);
|
|
174
|
+
}
|
|
163
175
|
if (typeof raw.stashDir === "string" && raw.stashDir.trim()) {
|
|
164
176
|
config.stashDir = raw.stashDir.trim();
|
|
165
177
|
}
|
|
@@ -170,59 +182,28 @@ function pickKnownKeys(raw) {
|
|
|
170
182
|
else if (raw.semanticSearchMode === "off" || raw.semanticSearchMode === "auto") {
|
|
171
183
|
config.semanticSearchMode = raw.semanticSearchMode;
|
|
172
184
|
}
|
|
173
|
-
else if (typeof raw.semanticSearch === "boolean") {
|
|
174
|
-
// Legacy config: older versions used `semanticSearch` (boolean) instead of `semanticSearchMode`
|
|
175
|
-
const legacySemanticSearch = raw.semanticSearch;
|
|
176
|
-
config.semanticSearchMode = legacySemanticSearch ? "auto" : "off";
|
|
177
|
-
}
|
|
178
|
-
// Migrate legacy searchPaths into sources
|
|
179
|
-
if (Array.isArray(raw.searchPaths)) {
|
|
180
|
-
const legacyPaths = raw.searchPaths.filter((d) => typeof d === "string");
|
|
181
|
-
if (legacyPaths.length > 0) {
|
|
182
|
-
const existing = config.sources ?? [];
|
|
183
|
-
const migrated = legacyPaths
|
|
184
|
-
.filter((p) => !existing.some((s) => s.type === "filesystem" && s.path === p))
|
|
185
|
-
.map((p) => ({ type: "filesystem", path: p }));
|
|
186
|
-
if (migrated.length > 0) {
|
|
187
|
-
config.sources = [...existing, ...migrated];
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
185
|
const embedding = parseEmbeddingConfig(raw.embedding);
|
|
192
186
|
if (embedding)
|
|
193
187
|
config.embedding = embedding;
|
|
194
188
|
const llm = parseLlmConfig(raw.llm);
|
|
195
189
|
if (llm)
|
|
196
190
|
config.llm = llm;
|
|
191
|
+
const index = parseIndexConfig(raw.index);
|
|
192
|
+
if (index)
|
|
193
|
+
config.index = index;
|
|
197
194
|
const installed = parseInstalledEntries(raw.installed);
|
|
198
195
|
if (installed)
|
|
199
196
|
config.installed = installed;
|
|
200
197
|
const registries = parseRegistriesConfig(raw.registries);
|
|
201
198
|
if (registries)
|
|
202
199
|
config.registries = registries;
|
|
203
|
-
// Prefer the new `stashInheritance` field; fall back to the legacy boolean
|
|
204
|
-
// `disableGlobalStashes` so existing config files keep working unchanged.
|
|
205
200
|
if (raw.stashInheritance === "replace" || raw.stashInheritance === "merge") {
|
|
206
201
|
config.stashInheritance = raw.stashInheritance;
|
|
207
202
|
}
|
|
208
|
-
|
|
209
|
-
config.stashInheritance = raw.disableGlobalStashes ? "replace" : "merge";
|
|
210
|
-
config.disableGlobalStashes = raw.disableGlobalStashes;
|
|
211
|
-
}
|
|
212
|
-
// Load `sources` (new key) first, then fall back to legacy `stashes` key.
|
|
213
|
-
const sources = parseStashesConfig(raw.sources);
|
|
203
|
+
const sources = parseSourcesConfig(raw.sources);
|
|
214
204
|
if (sources) {
|
|
215
205
|
config.sources = sources;
|
|
216
206
|
}
|
|
217
|
-
else {
|
|
218
|
-
const legacyStashes = parseStashesConfig(raw.stashes);
|
|
219
|
-
if (legacyStashes) {
|
|
220
|
-
// Backwards-compat fallback: configs that still carry `stashes[]` are
|
|
221
|
-
// normalized to `sources[]` after the raw file loader has had a chance to
|
|
222
|
-
// auto-migrate the on-disk key.
|
|
223
|
-
config.sources = legacyStashes;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
207
|
const security = parseSecurityConfig(raw.security);
|
|
227
208
|
if (security)
|
|
228
209
|
config.security = security;
|
|
@@ -235,6 +216,11 @@ function pickKnownKeys(raw) {
|
|
|
235
216
|
if (typeof raw.defaultWriteTarget === "string" && raw.defaultWriteTarget.trim()) {
|
|
236
217
|
config.defaultWriteTarget = raw.defaultWriteTarget.trim();
|
|
237
218
|
}
|
|
219
|
+
if ("agent" in raw) {
|
|
220
|
+
const agent = parseAgentConfig(raw.agent);
|
|
221
|
+
if (agent)
|
|
222
|
+
config.agent = agent;
|
|
223
|
+
}
|
|
238
224
|
if (typeof raw.search === "object" && raw.search !== null && !Array.isArray(raw.search)) {
|
|
239
225
|
const searchRaw = raw.search;
|
|
240
226
|
const searchConfig = {};
|
|
@@ -248,16 +234,14 @@ function pickKnownKeys(raw) {
|
|
|
248
234
|
}
|
|
249
235
|
function readNormalizedConfig(configPath) {
|
|
250
236
|
const raw = readConfigObject(configPath);
|
|
251
|
-
const
|
|
252
|
-
const expanded = migrated ? expandEnvVars(migrated) : undefined;
|
|
237
|
+
const expanded = raw ? expandEnvVars(raw) : undefined;
|
|
253
238
|
return expanded ? pickKnownKeys(expanded) : undefined;
|
|
254
239
|
}
|
|
255
|
-
function readNormalizedConfigFromText(
|
|
240
|
+
function readNormalizedConfigFromText(text) {
|
|
256
241
|
const raw = parseConfigObjectFromText(text);
|
|
257
242
|
if (!raw)
|
|
258
243
|
return undefined;
|
|
259
|
-
const
|
|
260
|
-
const expanded = expandEnvVars(migrated);
|
|
244
|
+
const expanded = expandEnvVars(raw);
|
|
261
245
|
return pickKnownKeys(expanded);
|
|
262
246
|
}
|
|
263
247
|
function parseOutputConfig(value) {
|
|
@@ -337,31 +321,6 @@ function parseConfigObjectFromText(text) {
|
|
|
337
321
|
return undefined;
|
|
338
322
|
}
|
|
339
323
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Best-effort on-disk config migration for the legacy `stashes` key.
|
|
342
|
-
*
|
|
343
|
-
* When a config file still uses `stashes` and does not already define
|
|
344
|
-
* `sources`, rewrite the file in place with `sources` replacing `stashes`,
|
|
345
|
-
* emit a one-time notice on success, and return the migrated object. If the
|
|
346
|
-
* rewrite fails, emit a warning and return the original object so the loader
|
|
347
|
-
* can still continue with an in-memory fallback.
|
|
348
|
-
*/
|
|
349
|
-
function maybeAutoMigrateLegacyStashes(configPath, raw) {
|
|
350
|
-
if (Object.hasOwn(raw, "sources") || !Object.hasOwn(raw, "stashes")) {
|
|
351
|
-
return raw;
|
|
352
|
-
}
|
|
353
|
-
const migrated = Object.fromEntries(Object.entries(raw).map(([key, value]) => (key === "stashes" ? ["sources", value] : [key, value])));
|
|
354
|
-
try {
|
|
355
|
-
writeConfigObject(configPath, migrated);
|
|
356
|
-
warn('Config migrated: "stashes" → "sources" in config.json');
|
|
357
|
-
return migrated;
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
warn('Failed to migrate "stashes" → "sources" in config.json; continuing with the legacy key in memory. ' +
|
|
361
|
-
"Check file permissions or rename the key manually if this persists.");
|
|
362
|
-
return raw;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
324
|
function writeConfigObject(configPath, config) {
|
|
366
325
|
const tmpPath = `${configPath}.tmp.${process.pid}.${Math.random().toString(36).slice(2)}`;
|
|
367
326
|
try {
|
|
@@ -443,7 +402,7 @@ function parseEmbeddingConfig(value) {
|
|
|
443
402
|
return undefined;
|
|
444
403
|
}
|
|
445
404
|
if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
|
|
446
|
-
|
|
405
|
+
warn(`[akm] Ignoring embedding config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
|
|
447
406
|
// Still return localModel-only config if localModel was set
|
|
448
407
|
if (localModel) {
|
|
449
408
|
return { endpoint: "", model: "", localModel };
|
|
@@ -453,7 +412,7 @@ function parseEmbeddingConfig(value) {
|
|
|
453
412
|
if (typeof obj.model !== "string" || !obj.model) {
|
|
454
413
|
// No remote model, but localModel may still be valid
|
|
455
414
|
if (localModel) {
|
|
456
|
-
|
|
415
|
+
warn(`[akm] Embedding endpoint "${obj.endpoint}" ignored: model is required for remote embeddings. Using local model only.`);
|
|
457
416
|
return { endpoint: "", model: "", localModel };
|
|
458
417
|
}
|
|
459
418
|
return undefined;
|
|
@@ -480,6 +439,28 @@ function parseEmbeddingConfig(value) {
|
|
|
480
439
|
if (localModel) {
|
|
481
440
|
result.localModel = localModel;
|
|
482
441
|
}
|
|
442
|
+
if ("contextLength" in obj) {
|
|
443
|
+
if (typeof obj.contextLength !== "number" ||
|
|
444
|
+
!Number.isFinite(obj.contextLength) ||
|
|
445
|
+
!Number.isInteger(obj.contextLength) ||
|
|
446
|
+
obj.contextLength <= 0) {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
result.contextLength = obj.contextLength;
|
|
450
|
+
}
|
|
451
|
+
if (typeof obj.ollamaOptions === "object" && obj.ollamaOptions !== null && !Array.isArray(obj.ollamaOptions)) {
|
|
452
|
+
const opts = obj.ollamaOptions;
|
|
453
|
+
const parsed = {};
|
|
454
|
+
if (typeof opts.num_ctx === "number" &&
|
|
455
|
+
Number.isFinite(opts.num_ctx) &&
|
|
456
|
+
Number.isInteger(opts.num_ctx) &&
|
|
457
|
+
opts.num_ctx > 0) {
|
|
458
|
+
parsed.num_ctx = opts.num_ctx;
|
|
459
|
+
}
|
|
460
|
+
if (Object.keys(parsed).length > 0) {
|
|
461
|
+
result.ollamaOptions = parsed;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
483
464
|
return result;
|
|
484
465
|
}
|
|
485
466
|
function parseLlmConfig(value) {
|
|
@@ -489,7 +470,7 @@ function parseLlmConfig(value) {
|
|
|
489
470
|
if (typeof obj.endpoint !== "string" || !obj.endpoint)
|
|
490
471
|
return undefined;
|
|
491
472
|
if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
|
|
492
|
-
|
|
473
|
+
warn(`[akm] Ignoring llm config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
|
|
493
474
|
return undefined;
|
|
494
475
|
}
|
|
495
476
|
const model = typeof obj.model === "string" ? obj.model : "";
|
|
@@ -515,26 +496,122 @@ function parseLlmConfig(value) {
|
|
|
515
496
|
if (typeof obj.apiKey === "string" && obj.apiKey) {
|
|
516
497
|
result.apiKey = obj.apiKey;
|
|
517
498
|
}
|
|
518
|
-
if (typeof obj.contextWindow === "number" &&
|
|
519
|
-
Number.isFinite(obj.contextWindow) &&
|
|
520
|
-
Number.isInteger(obj.contextWindow) &&
|
|
521
|
-
obj.contextWindow > 0) {
|
|
522
|
-
result.contextWindow = obj.contextWindow;
|
|
523
|
-
}
|
|
524
499
|
if (typeof obj.capabilities === "object" && obj.capabilities !== null && !Array.isArray(obj.capabilities)) {
|
|
525
500
|
const capsRaw = obj.capabilities;
|
|
526
501
|
const caps = {};
|
|
527
502
|
if (typeof capsRaw.structuredOutput === "boolean")
|
|
528
503
|
caps.structuredOutput = capsRaw.structuredOutput;
|
|
529
|
-
if (typeof capsRaw.longContext === "boolean")
|
|
530
|
-
caps.longContext = capsRaw.longContext;
|
|
531
|
-
if (typeof capsRaw.toolUse === "boolean")
|
|
532
|
-
caps.toolUse = capsRaw.toolUse;
|
|
533
504
|
if (Object.keys(caps).length > 0)
|
|
534
505
|
result.capabilities = caps;
|
|
535
506
|
}
|
|
507
|
+
if (typeof obj.features === "object" && obj.features !== null && !Array.isArray(obj.features)) {
|
|
508
|
+
const features = parseLlmFeatures(obj.features);
|
|
509
|
+
if (Object.keys(features).length > 0)
|
|
510
|
+
result.features = features;
|
|
511
|
+
}
|
|
536
512
|
return result;
|
|
537
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* v1 spec §14 — locked feature keys. Defined here so unknown keys can
|
|
516
|
+
* be warn-and-ignored at load time (per spec §14.3 / §9.2). The set is
|
|
517
|
+
* deliberately the *full* locked table even though only a subset has
|
|
518
|
+
* runtime parsing today; this lets users author future-flagged configs
|
|
519
|
+
* without spurious warnings.
|
|
520
|
+
*/
|
|
521
|
+
const LOCKED_LLM_FEATURE_KEYS = new Set([
|
|
522
|
+
"curate_rerank",
|
|
523
|
+
"feedback_distillation",
|
|
524
|
+
"memory_inference",
|
|
525
|
+
"graph_extraction",
|
|
526
|
+
]);
|
|
527
|
+
function parseLlmFeatures(raw) {
|
|
528
|
+
const out = {};
|
|
529
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
530
|
+
if (!LOCKED_LLM_FEATURE_KEYS.has(key)) {
|
|
531
|
+
warn(`[akm] Ignoring unknown llm.features key "${key}".`);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (typeof value !== "boolean") {
|
|
535
|
+
warn(`[akm] Ignoring llm.features.${key}: expected boolean, got ${typeof value}.`);
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
switch (key) {
|
|
539
|
+
case "memory_inference":
|
|
540
|
+
out.memory_inference = value;
|
|
541
|
+
break;
|
|
542
|
+
case "graph_extraction":
|
|
543
|
+
out.graph_extraction = value;
|
|
544
|
+
break;
|
|
545
|
+
case "curate_rerank":
|
|
546
|
+
out.curate_rerank = value;
|
|
547
|
+
break;
|
|
548
|
+
case "feedback_distillation":
|
|
549
|
+
out.feedback_distillation = value;
|
|
550
|
+
break;
|
|
551
|
+
// No default: LOCKED_LLM_FEATURE_KEYS is the source of truth for which
|
|
552
|
+
// keys are accepted. Adding a new locked key requires an arm here AND a
|
|
553
|
+
// field on LlmFeatureFlags above.
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Keys that, if present anywhere under `index.<pass>`, indicate the user is
|
|
560
|
+
* trying to supply a parallel LLM provider configuration. Per #208 this is
|
|
561
|
+
* deliberately rejected at load time so there is exactly one place to
|
|
562
|
+
* configure the LLM (`akm.llm`).
|
|
563
|
+
*/
|
|
564
|
+
const PROVIDER_CONFIG_KEYS = new Set([
|
|
565
|
+
"endpoint",
|
|
566
|
+
"model",
|
|
567
|
+
"provider",
|
|
568
|
+
"apiKey",
|
|
569
|
+
"baseUrl",
|
|
570
|
+
"temperature",
|
|
571
|
+
"maxTokens",
|
|
572
|
+
"capabilities",
|
|
573
|
+
]);
|
|
574
|
+
/**
|
|
575
|
+
* Parse the `index` config block. Each entry is a pass name → small object
|
|
576
|
+
* `{ llm?: boolean }`. Anything richer (a parallel provider config, unknown
|
|
577
|
+
* keys, non-boolean `llm`) throws `ConfigError("INVALID_CONFIG_FILE")` at
|
|
578
|
+
* load time so the failure is visible at startup, not on the next index run.
|
|
579
|
+
*/
|
|
580
|
+
function parseIndexConfig(value) {
|
|
581
|
+
if (value === undefined || value === null)
|
|
582
|
+
return undefined;
|
|
583
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
584
|
+
throw new ConfigError('Invalid `index` config: expected an object keyed by pass name (e.g. `{ "enrichment": { "llm": false } }`).', "INVALID_CONFIG_FILE");
|
|
585
|
+
}
|
|
586
|
+
const out = {};
|
|
587
|
+
for (const [passName, raw] of Object.entries(value)) {
|
|
588
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
589
|
+
throw new ConfigError(`Invalid \`index.${passName}\` config: expected an object like \`{ "llm": false }\`.`, "INVALID_CONFIG_FILE");
|
|
590
|
+
}
|
|
591
|
+
const passRaw = raw;
|
|
592
|
+
// Reject any provider-shaped key — there must be exactly one place to
|
|
593
|
+
// configure the LLM (#208). This is the duplicate-provider guard.
|
|
594
|
+
for (const key of Object.keys(passRaw)) {
|
|
595
|
+
if (PROVIDER_CONFIG_KEYS.has(key)) {
|
|
596
|
+
throw new ConfigError(`Duplicate LLM provider configuration: \`index.${passName}.${key}\` is not allowed. ` +
|
|
597
|
+
"Configure provider/model/endpoint under top-level `llm` only; per-pass entries support `{ llm: false }` opt-out.", "INVALID_CONFIG_FILE", 'Move provider settings to the top-level "llm" block, then set `index.<pass>.llm = false` to opt a single pass out.');
|
|
598
|
+
}
|
|
599
|
+
if (key !== "llm") {
|
|
600
|
+
throw new ConfigError(`Unknown key \`index.${passName}.${key}\`. Per-pass entries only support \`llm\` (boolean opt-out).`, "INVALID_CONFIG_FILE");
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const passConfig = {};
|
|
604
|
+
if ("llm" in passRaw) {
|
|
605
|
+
const llmFlag = passRaw.llm;
|
|
606
|
+
if (typeof llmFlag !== "boolean") {
|
|
607
|
+
throw new ConfigError(`Invalid \`index.${passName}.llm\`: expected a boolean (true to use \`akm.llm\`, false to opt out). Got ${typeof llmFlag}.`, "INVALID_CONFIG_FILE", "Per-pass alternative provider config is intentionally unsupported in v1 (#208). Use `false` to disable LLM for this pass.");
|
|
608
|
+
}
|
|
609
|
+
passConfig.llm = llmFlag;
|
|
610
|
+
}
|
|
611
|
+
out[passName] = passConfig;
|
|
612
|
+
}
|
|
613
|
+
return out;
|
|
614
|
+
}
|
|
538
615
|
function parseInstalledEntries(value) {
|
|
539
616
|
if (!Array.isArray(value))
|
|
540
617
|
return undefined;
|
|
@@ -567,6 +644,9 @@ function parseInstalledStashEntry(value) {
|
|
|
567
644
|
};
|
|
568
645
|
if (typeof obj.writable === "boolean")
|
|
569
646
|
entry.writable = obj.writable;
|
|
647
|
+
if (entry.writable === true && entry.source !== "git") {
|
|
648
|
+
throw new ConfigError(`writable: true is only supported on filesystem and git sources (got "${entry.source}" on installed entry "${entry.id}").`, "INVALID_CONFIG_FILE", "Remove `writable: true` from the installed entry or re-add it as a git source instead.");
|
|
649
|
+
}
|
|
570
650
|
const resolvedVersion = asNonEmptyString(obj.resolvedVersion);
|
|
571
651
|
if (resolvedVersion)
|
|
572
652
|
entry.resolvedVersion = resolvedVersion;
|
|
@@ -604,7 +684,7 @@ function parseRegistriesConfig(value) {
|
|
|
604
684
|
// which overrides the default. Only return undefined if the field was not an array.
|
|
605
685
|
return entries;
|
|
606
686
|
}
|
|
607
|
-
function
|
|
687
|
+
function parseSourcesConfig(value) {
|
|
608
688
|
if (!Array.isArray(value))
|
|
609
689
|
return undefined;
|
|
610
690
|
const entries = value
|
|
@@ -669,28 +749,17 @@ function parseInstallAuditAllowedFinding(value) {
|
|
|
669
749
|
finding.reason = reason;
|
|
670
750
|
return finding;
|
|
671
751
|
}
|
|
672
|
-
/**
|
|
673
|
-
* Legacy stash type aliases that are normalized to canonical types at
|
|
674
|
-
* config-load time. Both "context-hub" and "github" were never distinct
|
|
675
|
-
* provider types — they were always git stashes — so we normalize them in
|
|
676
|
-
* memory to "git" without rewriting `config.json` on disk.
|
|
677
|
-
*/
|
|
678
|
-
const STASH_TYPE_ALIASES = {
|
|
679
|
-
"context-hub": "git",
|
|
680
|
-
github: "git",
|
|
681
|
-
};
|
|
682
752
|
function parseSourceConfigEntry(value) {
|
|
683
753
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
684
754
|
return undefined;
|
|
685
755
|
const obj = value;
|
|
686
|
-
const
|
|
687
|
-
if (!
|
|
756
|
+
const type = asNonEmptyString(obj.type);
|
|
757
|
+
if (!type)
|
|
688
758
|
return undefined;
|
|
689
|
-
if (
|
|
759
|
+
if (type === "openviking") {
|
|
690
760
|
const name = asNonEmptyString(obj.name) ?? "unnamed";
|
|
691
761
|
throw new ConfigError(`openviking is not supported in akm v1. API-backed sources will return as a\nseparate QuerySource tier post-v1. Remove the source named "${name}" from your config file\nor downgrade to 0.6.x. See docs/migration/v1.md.`, "INVALID_CONFIG_FILE", `Run \`akm remove ${name}\` then re-run, or edit your config file directly at ${_getConfigPath()} to remove the openviking entry.`);
|
|
692
762
|
}
|
|
693
|
-
const type = STASH_TYPE_ALIASES[rawType] ?? rawType;
|
|
694
763
|
const entry = { type };
|
|
695
764
|
const entryPath = asNonEmptyString(obj.path);
|
|
696
765
|
if (entryPath)
|
|
@@ -746,19 +815,15 @@ function deriveStashEntryName(entry) {
|
|
|
746
815
|
* entry is missing the fields its provider type requires (e.g. a
|
|
747
816
|
* `filesystem` entry with no `path`); callers should drop or warn for those.
|
|
748
817
|
*
|
|
749
|
-
* Unknown provider types fall back to `{ type: "filesystem", path: ... }`
|
|
750
|
-
*
|
|
751
|
-
* runtime value
|
|
818
|
+
* Unknown provider types fall back to `{ type: "filesystem", path: ... }` when
|
|
819
|
+
* a `path` is supplied, so future provider types still produce a usable
|
|
820
|
+
* runtime value.
|
|
752
821
|
*/
|
|
753
822
|
export function parseSourceSpec(entry) {
|
|
754
823
|
switch (entry.type) {
|
|
755
824
|
case "filesystem":
|
|
756
825
|
return entry.path ? { type: "filesystem", path: entry.path } : undefined;
|
|
757
826
|
case "git":
|
|
758
|
-
case "context-hub":
|
|
759
|
-
case "github":
|
|
760
|
-
// Note: a configured `github` provider entry historically meant "git
|
|
761
|
-
// repo over the GitHub web URL", not the registry-install `github:` ref.
|
|
762
827
|
return entry.url ? { type: "git", url: entry.url } : undefined;
|
|
763
828
|
case "website":
|
|
764
829
|
return entry.url
|
|
@@ -792,11 +857,9 @@ export function parseSourceSpec(entry) {
|
|
|
792
857
|
*/
|
|
793
858
|
export function resolveConfiguredSources(config) {
|
|
794
859
|
const entries = [];
|
|
795
|
-
|
|
796
|
-
// migrates in-memory; only one of the two should be set at runtime.
|
|
797
|
-
const stashes = config.sources ?? config.stashes ?? [];
|
|
860
|
+
const sources = config.sources ?? [];
|
|
798
861
|
// (1) Primary entry: explicit `primary: true` wins; fall back to top-level stashDir.
|
|
799
|
-
let primary =
|
|
862
|
+
let primary = sources.find((entry) => entry.primary === true);
|
|
800
863
|
if (!primary && config.stashDir) {
|
|
801
864
|
primary = { type: "filesystem", path: config.stashDir, primary: true };
|
|
802
865
|
}
|
|
@@ -805,8 +868,8 @@ export function resolveConfiguredSources(config) {
|
|
|
805
868
|
if (runtime)
|
|
806
869
|
entries.push(runtime);
|
|
807
870
|
}
|
|
808
|
-
// (2) Declared
|
|
809
|
-
for (const entry of
|
|
871
|
+
// (2) Declared sources (skip the primary entry — already added).
|
|
872
|
+
for (const entry of sources) {
|
|
810
873
|
if (entry === primary)
|
|
811
874
|
continue;
|
|
812
875
|
const runtime = toConfiguredSource(entry, false);
|
|
@@ -862,6 +925,20 @@ function parseRegistryConfigEntry(value) {
|
|
|
862
925
|
}
|
|
863
926
|
return entry;
|
|
864
927
|
}
|
|
928
|
+
function mergeAgentConfig(base, override) {
|
|
929
|
+
const merged = { ...base, ...override };
|
|
930
|
+
const baseProfiles = base.profiles;
|
|
931
|
+
const overrideProfiles = override.profiles;
|
|
932
|
+
if (baseProfiles && overrideProfiles) {
|
|
933
|
+
const profiles = { ...baseProfiles };
|
|
934
|
+
for (const [name, entry] of Object.entries(overrideProfiles)) {
|
|
935
|
+
const existing = baseProfiles[name];
|
|
936
|
+
profiles[name] = existing ? { ...existing, ...entry } : entry;
|
|
937
|
+
}
|
|
938
|
+
merged.profiles = profiles;
|
|
939
|
+
}
|
|
940
|
+
return merged;
|
|
941
|
+
}
|
|
865
942
|
function mergeSecurityConfig(base, override) {
|
|
866
943
|
if (!base && !override)
|
|
867
944
|
return undefined;
|
|
@@ -882,9 +959,8 @@ function mergeInstallAuditConfig(base, override) {
|
|
|
882
959
|
*
|
|
883
960
|
* Scalar fields follow normal override semantics. Known nested objects are
|
|
884
961
|
* deep-merged so project config files can override individual fields without
|
|
885
|
-
* clobbering sibling settings. `
|
|
886
|
-
* layer can set `stashInheritance: "replace"`
|
|
887
|
-
* `disableGlobalStashes: true`) to drop inherited stashes first.
|
|
962
|
+
* clobbering sibling settings. `sources` are additive by default, but a later
|
|
963
|
+
* layer can set `stashInheritance: "replace"` to drop inherited sources first.
|
|
888
964
|
*/
|
|
889
965
|
function mergeLoadedConfig(base, override) {
|
|
890
966
|
if (!override)
|
|
@@ -902,18 +978,26 @@ function mergeLoadedConfig(base, override) {
|
|
|
902
978
|
if (base.llm && override.llm) {
|
|
903
979
|
merged.llm = { ...base.llm, ...override.llm };
|
|
904
980
|
}
|
|
981
|
+
if (base.index || override.index) {
|
|
982
|
+
// Deep-merge per-pass entries so a project layer can opt one pass out
|
|
983
|
+
// without dropping siblings configured in user config.
|
|
984
|
+
const mergedIndex = { ...(base.index ?? {}) };
|
|
985
|
+
for (const [passName, passOverride] of Object.entries(override.index ?? {})) {
|
|
986
|
+
mergedIndex[passName] = { ...(mergedIndex[passName] ?? {}), ...passOverride };
|
|
987
|
+
}
|
|
988
|
+
if (Object.keys(mergedIndex).length > 0)
|
|
989
|
+
merged.index = mergedIndex;
|
|
990
|
+
}
|
|
905
991
|
if (base.security && override.security) {
|
|
906
992
|
merged.security = mergeSecurityConfig(base.security, override.security);
|
|
907
993
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const baseSources = base.sources ?? base.stashes ?? [];
|
|
916
|
-
if (replaceStashes) {
|
|
994
|
+
if (base.agent && override.agent) {
|
|
995
|
+
merged.agent = mergeAgentConfig(base.agent, override.agent);
|
|
996
|
+
}
|
|
997
|
+
const replaceSources = override.stashInheritance === "replace";
|
|
998
|
+
const overrideSources = override.sources ?? [];
|
|
999
|
+
const baseSources = base.sources ?? [];
|
|
1000
|
+
if (replaceSources) {
|
|
917
1001
|
merged.sources = [...overrideSources];
|
|
918
1002
|
}
|
|
919
1003
|
else if (overrideSources.length > 0) {
|
|
@@ -922,8 +1006,6 @@ function mergeLoadedConfig(base, override) {
|
|
|
922
1006
|
else if (baseSources.length > 0) {
|
|
923
1007
|
merged.sources = [...baseSources];
|
|
924
1008
|
}
|
|
925
|
-
// Clear deprecated stashes field on the merged result — sources is canonical.
|
|
926
|
-
delete merged.stashes;
|
|
927
1009
|
return merged;
|
|
928
1010
|
}
|
|
929
1011
|
function applyRuntimeEnvApiKeys(config) {
|
|
@@ -28,6 +28,7 @@ const CONFIG_HINTS = {
|
|
|
28
28
|
};
|
|
29
29
|
/** Default hint for each UsageError code. */
|
|
30
30
|
const USAGE_HINTS = {
|
|
31
|
+
INVALID_FLAG_VALUE: "Run `akm <command> --help` to see accepted values.",
|
|
31
32
|
INVALID_SOURCE_VALUE: "Pick one of: stash, registry, both.",
|
|
32
33
|
INVALID_FORMAT_VALUE: "Pick one of: json, jsonl, text, yaml.",
|
|
33
34
|
INVALID_DETAIL_VALUE: "Pick one of: brief, normal, full, summary, agent.",
|
|
@@ -38,7 +39,10 @@ const USAGE_HINTS = {
|
|
|
38
39
|
};
|
|
39
40
|
/** Default hint for each NotFoundError code. */
|
|
40
41
|
const NOT_FOUND_HINTS = {
|
|
42
|
+
ASSET_NOT_FOUND: "Run `akm search <query>` or `akm index` to refresh the index.",
|
|
41
43
|
SOURCE_NOT_FOUND: "Run `akm list` to view your sources, then retry with one of those values.",
|
|
44
|
+
WORKFLOW_NOT_FOUND: "Run `akm workflow list --active` to see runs.",
|
|
45
|
+
FILE_NOT_FOUND: "Check the path exists and is readable.",
|
|
42
46
|
};
|
|
43
47
|
/** Raised when configuration or environment is invalid or missing. */
|
|
44
48
|
export class ConfigError extends Error {
|