akm-cli 0.6.1 → 0.7.0-rc1
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} +620 -26
- package/dist/{commands → src/commands}/config-cli.js +5 -4
- package/dist/src/commands/distill.js +283 -0
- package/dist/src/commands/events.js +108 -0
- package/dist/src/commands/history.js +120 -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 +2 -1
- package/dist/{commands → src/commands}/remember.js +12 -0
- package/dist/{commands → src/commands}/search.js +74 -1
- package/dist/{commands → src/commands}/self-update.js +4 -3
- package/dist/{commands → src/commands}/show.js +44 -0
- 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 +175 -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 +113 -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 +73 -6
- package/dist/src/indexer/memory-inference.js +263 -0
- package/dist/{indexer → src/indexer}/metadata.js +111 -3
- 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 +221 -0
- package/dist/{integrations → src/integrations}/lockfile.js +0 -26
- package/dist/{llm → src/llm}/client.js +33 -2
- 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}/renderers.js +60 -1
- package/dist/src/output/shapes.js +516 -0
- package/dist/{output → src/output}/text.js +447 -4
- package/dist/{registry → src/registry}/build-index.js +14 -4
- package/dist/{registry → src/registry}/factory.js +0 -8
- package/dist/{registry → src/registry}/providers/static-index.js +3 -2
- package/dist/{registry → src/registry}/resolve.js +68 -2
- package/dist/{setup → src/setup}/setup.js +43 -5
- package/dist/{sources → src/sources}/providers/git.js +7 -15
- 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 +995 -0
- package/dist/tests/bench/cleanup-sigint.test.js +83 -0
- package/dist/tests/bench/cleanup.js +203 -0
- package/dist/tests/bench/cleanup.test.js +166 -0
- package/dist/tests/bench/cli.js +683 -0
- package/dist/tests/bench/cli.test.js +177 -0
- package/dist/tests/bench/compare.test.js +556 -0
- package/dist/tests/bench/corpus.js +314 -0
- package/dist/tests/bench/corpus.test.js +258 -0
- package/dist/tests/bench/driver.js +346 -0
- package/dist/tests/bench/driver.test.js +443 -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 +580 -0
- package/dist/tests/bench/evolve.test.js +616 -0
- package/dist/tests/bench/failure-modes.test.js +300 -0
- package/dist/tests/bench/feedback-integrity.test.js +456 -0
- package/dist/tests/bench/leakage.test.js +125 -0
- package/dist/tests/bench/learning-curve.test.js +133 -0
- package/dist/tests/bench/metrics.js +2319 -0
- package/dist/tests/bench/metrics.test.js +1144 -0
- package/dist/tests/bench/no-os-tmpdir-invariant.test.js +43 -0
- package/dist/tests/bench/report.js +1821 -0
- package/dist/tests/bench/report.test.js +989 -0
- package/dist/tests/bench/runner.js +536 -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 +41 -0
- package/dist/tests/bench/trajectory.js +116 -0
- package/dist/tests/bench/trajectory.test.js +127 -0
- package/dist/tests/bench/verifier.js +109 -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 +358 -0
- package/dist/tests/bench/workflow-spec.test.js +363 -0
- package/dist/tests/bench/workflow-trace.js +438 -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 +203 -0
- package/dist/tests/commands/events.test.js +370 -0
- package/dist/tests/commands/history.test.js +223 -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 +544 -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 +1398 -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 +88 -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 +559 -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 +217 -0
- package/dist/tests/output-shapes-unit.test.js +476 -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 +378 -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 +204 -0
- package/dist/tests/registry-resolve.test.js +126 -0
- package/dist/tests/registry-search.test.js +723 -0
- package/dist/tests/remember-frontmatter.test.js +380 -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 +268 -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 +221 -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 +377 -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/{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-add.js +0 -0
- /package/dist/{commands → src/commands}/source-clone.js +0 -0
- /package/dist/{commands → src/commands}/source-manage.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}/matchers.js +0 -0
- /package/dist/{indexer → src/indexer}/search-fields.js +0 -0
- /package/dist/{indexer → src/indexer}/search-source.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/{integrations → src/integrations}/github.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/remote.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}/cli-hints.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/{wiki → src/wiki}/wiki.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}/runs.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
|
}
|
|
@@ -170,59 +179,28 @@ function pickKnownKeys(raw) {
|
|
|
170
179
|
else if (raw.semanticSearchMode === "off" || raw.semanticSearchMode === "auto") {
|
|
171
180
|
config.semanticSearchMode = raw.semanticSearchMode;
|
|
172
181
|
}
|
|
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
182
|
const embedding = parseEmbeddingConfig(raw.embedding);
|
|
192
183
|
if (embedding)
|
|
193
184
|
config.embedding = embedding;
|
|
194
185
|
const llm = parseLlmConfig(raw.llm);
|
|
195
186
|
if (llm)
|
|
196
187
|
config.llm = llm;
|
|
188
|
+
const index = parseIndexConfig(raw.index);
|
|
189
|
+
if (index)
|
|
190
|
+
config.index = index;
|
|
197
191
|
const installed = parseInstalledEntries(raw.installed);
|
|
198
192
|
if (installed)
|
|
199
193
|
config.installed = installed;
|
|
200
194
|
const registries = parseRegistriesConfig(raw.registries);
|
|
201
195
|
if (registries)
|
|
202
196
|
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
197
|
if (raw.stashInheritance === "replace" || raw.stashInheritance === "merge") {
|
|
206
198
|
config.stashInheritance = raw.stashInheritance;
|
|
207
199
|
}
|
|
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);
|
|
200
|
+
const sources = parseSourcesConfig(raw.sources);
|
|
214
201
|
if (sources) {
|
|
215
202
|
config.sources = sources;
|
|
216
203
|
}
|
|
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
204
|
const security = parseSecurityConfig(raw.security);
|
|
227
205
|
if (security)
|
|
228
206
|
config.security = security;
|
|
@@ -235,6 +213,11 @@ function pickKnownKeys(raw) {
|
|
|
235
213
|
if (typeof raw.defaultWriteTarget === "string" && raw.defaultWriteTarget.trim()) {
|
|
236
214
|
config.defaultWriteTarget = raw.defaultWriteTarget.trim();
|
|
237
215
|
}
|
|
216
|
+
if ("agent" in raw) {
|
|
217
|
+
const agent = parseAgentConfig(raw.agent);
|
|
218
|
+
if (agent)
|
|
219
|
+
config.agent = agent;
|
|
220
|
+
}
|
|
238
221
|
if (typeof raw.search === "object" && raw.search !== null && !Array.isArray(raw.search)) {
|
|
239
222
|
const searchRaw = raw.search;
|
|
240
223
|
const searchConfig = {};
|
|
@@ -248,16 +231,14 @@ function pickKnownKeys(raw) {
|
|
|
248
231
|
}
|
|
249
232
|
function readNormalizedConfig(configPath) {
|
|
250
233
|
const raw = readConfigObject(configPath);
|
|
251
|
-
const
|
|
252
|
-
const expanded = migrated ? expandEnvVars(migrated) : undefined;
|
|
234
|
+
const expanded = raw ? expandEnvVars(raw) : undefined;
|
|
253
235
|
return expanded ? pickKnownKeys(expanded) : undefined;
|
|
254
236
|
}
|
|
255
|
-
function readNormalizedConfigFromText(
|
|
237
|
+
function readNormalizedConfigFromText(text) {
|
|
256
238
|
const raw = parseConfigObjectFromText(text);
|
|
257
239
|
if (!raw)
|
|
258
240
|
return undefined;
|
|
259
|
-
const
|
|
260
|
-
const expanded = expandEnvVars(migrated);
|
|
241
|
+
const expanded = expandEnvVars(raw);
|
|
261
242
|
return pickKnownKeys(expanded);
|
|
262
243
|
}
|
|
263
244
|
function parseOutputConfig(value) {
|
|
@@ -337,31 +318,6 @@ function parseConfigObjectFromText(text) {
|
|
|
337
318
|
return undefined;
|
|
338
319
|
}
|
|
339
320
|
}
|
|
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
321
|
function writeConfigObject(configPath, config) {
|
|
366
322
|
const tmpPath = `${configPath}.tmp.${process.pid}.${Math.random().toString(36).slice(2)}`;
|
|
367
323
|
try {
|
|
@@ -443,7 +399,7 @@ function parseEmbeddingConfig(value) {
|
|
|
443
399
|
return undefined;
|
|
444
400
|
}
|
|
445
401
|
if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
|
|
446
|
-
|
|
402
|
+
warn(`[akm] Ignoring embedding config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
|
|
447
403
|
// Still return localModel-only config if localModel was set
|
|
448
404
|
if (localModel) {
|
|
449
405
|
return { endpoint: "", model: "", localModel };
|
|
@@ -453,7 +409,7 @@ function parseEmbeddingConfig(value) {
|
|
|
453
409
|
if (typeof obj.model !== "string" || !obj.model) {
|
|
454
410
|
// No remote model, but localModel may still be valid
|
|
455
411
|
if (localModel) {
|
|
456
|
-
|
|
412
|
+
warn(`[akm] Embedding endpoint "${obj.endpoint}" ignored: model is required for remote embeddings. Using local model only.`);
|
|
457
413
|
return { endpoint: "", model: "", localModel };
|
|
458
414
|
}
|
|
459
415
|
return undefined;
|
|
@@ -489,7 +445,7 @@ function parseLlmConfig(value) {
|
|
|
489
445
|
if (typeof obj.endpoint !== "string" || !obj.endpoint)
|
|
490
446
|
return undefined;
|
|
491
447
|
if (!obj.endpoint.startsWith("http://") && !obj.endpoint.startsWith("https://")) {
|
|
492
|
-
|
|
448
|
+
warn(`[akm] Ignoring llm config: endpoint must start with http:// or https://, got "${obj.endpoint}"`);
|
|
493
449
|
return undefined;
|
|
494
450
|
}
|
|
495
451
|
const model = typeof obj.model === "string" ? obj.model : "";
|
|
@@ -515,26 +471,122 @@ function parseLlmConfig(value) {
|
|
|
515
471
|
if (typeof obj.apiKey === "string" && obj.apiKey) {
|
|
516
472
|
result.apiKey = obj.apiKey;
|
|
517
473
|
}
|
|
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
474
|
if (typeof obj.capabilities === "object" && obj.capabilities !== null && !Array.isArray(obj.capabilities)) {
|
|
525
475
|
const capsRaw = obj.capabilities;
|
|
526
476
|
const caps = {};
|
|
527
477
|
if (typeof capsRaw.structuredOutput === "boolean")
|
|
528
478
|
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
479
|
if (Object.keys(caps).length > 0)
|
|
534
480
|
result.capabilities = caps;
|
|
535
481
|
}
|
|
482
|
+
if (typeof obj.features === "object" && obj.features !== null && !Array.isArray(obj.features)) {
|
|
483
|
+
const features = parseLlmFeatures(obj.features);
|
|
484
|
+
if (Object.keys(features).length > 0)
|
|
485
|
+
result.features = features;
|
|
486
|
+
}
|
|
536
487
|
return result;
|
|
537
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* v1 spec §14 — locked feature keys. Defined here so unknown keys can
|
|
491
|
+
* be warn-and-ignored at load time (per spec §14.3 / §9.2). The set is
|
|
492
|
+
* deliberately the *full* locked table even though only a subset has
|
|
493
|
+
* runtime parsing today; this lets users author future-flagged configs
|
|
494
|
+
* without spurious warnings.
|
|
495
|
+
*/
|
|
496
|
+
const LOCKED_LLM_FEATURE_KEYS = new Set([
|
|
497
|
+
"curate_rerank",
|
|
498
|
+
"feedback_distillation",
|
|
499
|
+
"memory_inference",
|
|
500
|
+
"graph_extraction",
|
|
501
|
+
]);
|
|
502
|
+
function parseLlmFeatures(raw) {
|
|
503
|
+
const out = {};
|
|
504
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
505
|
+
if (!LOCKED_LLM_FEATURE_KEYS.has(key)) {
|
|
506
|
+
warn(`[akm] Ignoring unknown llm.features key "${key}".`);
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (typeof value !== "boolean") {
|
|
510
|
+
warn(`[akm] Ignoring llm.features.${key}: expected boolean, got ${typeof value}.`);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
switch (key) {
|
|
514
|
+
case "memory_inference":
|
|
515
|
+
out.memory_inference = value;
|
|
516
|
+
break;
|
|
517
|
+
case "graph_extraction":
|
|
518
|
+
out.graph_extraction = value;
|
|
519
|
+
break;
|
|
520
|
+
case "curate_rerank":
|
|
521
|
+
out.curate_rerank = value;
|
|
522
|
+
break;
|
|
523
|
+
case "feedback_distillation":
|
|
524
|
+
out.feedback_distillation = value;
|
|
525
|
+
break;
|
|
526
|
+
// No default: LOCKED_LLM_FEATURE_KEYS is the source of truth for which
|
|
527
|
+
// keys are accepted. Adding a new locked key requires an arm here AND a
|
|
528
|
+
// field on LlmFeatureFlags above.
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return out;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Keys that, if present anywhere under `index.<pass>`, indicate the user is
|
|
535
|
+
* trying to supply a parallel LLM provider configuration. Per #208 this is
|
|
536
|
+
* deliberately rejected at load time so there is exactly one place to
|
|
537
|
+
* configure the LLM (`akm.llm`).
|
|
538
|
+
*/
|
|
539
|
+
const PROVIDER_CONFIG_KEYS = new Set([
|
|
540
|
+
"endpoint",
|
|
541
|
+
"model",
|
|
542
|
+
"provider",
|
|
543
|
+
"apiKey",
|
|
544
|
+
"baseUrl",
|
|
545
|
+
"temperature",
|
|
546
|
+
"maxTokens",
|
|
547
|
+
"capabilities",
|
|
548
|
+
]);
|
|
549
|
+
/**
|
|
550
|
+
* Parse the `index` config block. Each entry is a pass name → small object
|
|
551
|
+
* `{ llm?: boolean }`. Anything richer (a parallel provider config, unknown
|
|
552
|
+
* keys, non-boolean `llm`) throws `ConfigError("INVALID_CONFIG_FILE")` at
|
|
553
|
+
* load time so the failure is visible at startup, not on the next index run.
|
|
554
|
+
*/
|
|
555
|
+
function parseIndexConfig(value) {
|
|
556
|
+
if (value === undefined || value === null)
|
|
557
|
+
return undefined;
|
|
558
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
559
|
+
throw new ConfigError('Invalid `index` config: expected an object keyed by pass name (e.g. `{ "enrichment": { "llm": false } }`).', "INVALID_CONFIG_FILE");
|
|
560
|
+
}
|
|
561
|
+
const out = {};
|
|
562
|
+
for (const [passName, raw] of Object.entries(value)) {
|
|
563
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
564
|
+
throw new ConfigError(`Invalid \`index.${passName}\` config: expected an object like \`{ "llm": false }\`.`, "INVALID_CONFIG_FILE");
|
|
565
|
+
}
|
|
566
|
+
const passRaw = raw;
|
|
567
|
+
// Reject any provider-shaped key — there must be exactly one place to
|
|
568
|
+
// configure the LLM (#208). This is the duplicate-provider guard.
|
|
569
|
+
for (const key of Object.keys(passRaw)) {
|
|
570
|
+
if (PROVIDER_CONFIG_KEYS.has(key)) {
|
|
571
|
+
throw new ConfigError(`Duplicate LLM provider configuration: \`index.${passName}.${key}\` is not allowed. ` +
|
|
572
|
+
"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.');
|
|
573
|
+
}
|
|
574
|
+
if (key !== "llm") {
|
|
575
|
+
throw new ConfigError(`Unknown key \`index.${passName}.${key}\`. Per-pass entries only support \`llm\` (boolean opt-out).`, "INVALID_CONFIG_FILE");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const passConfig = {};
|
|
579
|
+
if ("llm" in passRaw) {
|
|
580
|
+
const llmFlag = passRaw.llm;
|
|
581
|
+
if (typeof llmFlag !== "boolean") {
|
|
582
|
+
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.");
|
|
583
|
+
}
|
|
584
|
+
passConfig.llm = llmFlag;
|
|
585
|
+
}
|
|
586
|
+
out[passName] = passConfig;
|
|
587
|
+
}
|
|
588
|
+
return out;
|
|
589
|
+
}
|
|
538
590
|
function parseInstalledEntries(value) {
|
|
539
591
|
if (!Array.isArray(value))
|
|
540
592
|
return undefined;
|
|
@@ -604,7 +656,7 @@ function parseRegistriesConfig(value) {
|
|
|
604
656
|
// which overrides the default. Only return undefined if the field was not an array.
|
|
605
657
|
return entries;
|
|
606
658
|
}
|
|
607
|
-
function
|
|
659
|
+
function parseSourcesConfig(value) {
|
|
608
660
|
if (!Array.isArray(value))
|
|
609
661
|
return undefined;
|
|
610
662
|
const entries = value
|
|
@@ -669,28 +721,17 @@ function parseInstallAuditAllowedFinding(value) {
|
|
|
669
721
|
finding.reason = reason;
|
|
670
722
|
return finding;
|
|
671
723
|
}
|
|
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
724
|
function parseSourceConfigEntry(value) {
|
|
683
725
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
684
726
|
return undefined;
|
|
685
727
|
const obj = value;
|
|
686
|
-
const
|
|
687
|
-
if (!
|
|
728
|
+
const type = asNonEmptyString(obj.type);
|
|
729
|
+
if (!type)
|
|
688
730
|
return undefined;
|
|
689
|
-
if (
|
|
731
|
+
if (type === "openviking") {
|
|
690
732
|
const name = asNonEmptyString(obj.name) ?? "unnamed";
|
|
691
733
|
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
734
|
}
|
|
693
|
-
const type = STASH_TYPE_ALIASES[rawType] ?? rawType;
|
|
694
735
|
const entry = { type };
|
|
695
736
|
const entryPath = asNonEmptyString(obj.path);
|
|
696
737
|
if (entryPath)
|
|
@@ -746,19 +787,15 @@ function deriveStashEntryName(entry) {
|
|
|
746
787
|
* entry is missing the fields its provider type requires (e.g. a
|
|
747
788
|
* `filesystem` entry with no `path`); callers should drop or warn for those.
|
|
748
789
|
*
|
|
749
|
-
* Unknown provider types fall back to `{ type: "filesystem", path: ... }`
|
|
750
|
-
*
|
|
751
|
-
* runtime value
|
|
790
|
+
* Unknown provider types fall back to `{ type: "filesystem", path: ... }` when
|
|
791
|
+
* a `path` is supplied, so future provider types still produce a usable
|
|
792
|
+
* runtime value.
|
|
752
793
|
*/
|
|
753
794
|
export function parseSourceSpec(entry) {
|
|
754
795
|
switch (entry.type) {
|
|
755
796
|
case "filesystem":
|
|
756
797
|
return entry.path ? { type: "filesystem", path: entry.path } : undefined;
|
|
757
798
|
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
799
|
return entry.url ? { type: "git", url: entry.url } : undefined;
|
|
763
800
|
case "website":
|
|
764
801
|
return entry.url
|
|
@@ -792,11 +829,9 @@ export function parseSourceSpec(entry) {
|
|
|
792
829
|
*/
|
|
793
830
|
export function resolveConfiguredSources(config) {
|
|
794
831
|
const entries = [];
|
|
795
|
-
|
|
796
|
-
// migrates in-memory; only one of the two should be set at runtime.
|
|
797
|
-
const stashes = config.sources ?? config.stashes ?? [];
|
|
832
|
+
const sources = config.sources ?? [];
|
|
798
833
|
// (1) Primary entry: explicit `primary: true` wins; fall back to top-level stashDir.
|
|
799
|
-
let primary =
|
|
834
|
+
let primary = sources.find((entry) => entry.primary === true);
|
|
800
835
|
if (!primary && config.stashDir) {
|
|
801
836
|
primary = { type: "filesystem", path: config.stashDir, primary: true };
|
|
802
837
|
}
|
|
@@ -805,8 +840,8 @@ export function resolveConfiguredSources(config) {
|
|
|
805
840
|
if (runtime)
|
|
806
841
|
entries.push(runtime);
|
|
807
842
|
}
|
|
808
|
-
// (2) Declared
|
|
809
|
-
for (const entry of
|
|
843
|
+
// (2) Declared sources (skip the primary entry — already added).
|
|
844
|
+
for (const entry of sources) {
|
|
810
845
|
if (entry === primary)
|
|
811
846
|
continue;
|
|
812
847
|
const runtime = toConfiguredSource(entry, false);
|
|
@@ -862,6 +897,20 @@ function parseRegistryConfigEntry(value) {
|
|
|
862
897
|
}
|
|
863
898
|
return entry;
|
|
864
899
|
}
|
|
900
|
+
function mergeAgentConfig(base, override) {
|
|
901
|
+
const merged = { ...base, ...override };
|
|
902
|
+
const baseProfiles = base.profiles;
|
|
903
|
+
const overrideProfiles = override.profiles;
|
|
904
|
+
if (baseProfiles && overrideProfiles) {
|
|
905
|
+
const profiles = { ...baseProfiles };
|
|
906
|
+
for (const [name, entry] of Object.entries(overrideProfiles)) {
|
|
907
|
+
const existing = baseProfiles[name];
|
|
908
|
+
profiles[name] = existing ? { ...existing, ...entry } : entry;
|
|
909
|
+
}
|
|
910
|
+
merged.profiles = profiles;
|
|
911
|
+
}
|
|
912
|
+
return merged;
|
|
913
|
+
}
|
|
865
914
|
function mergeSecurityConfig(base, override) {
|
|
866
915
|
if (!base && !override)
|
|
867
916
|
return undefined;
|
|
@@ -882,9 +931,8 @@ function mergeInstallAuditConfig(base, override) {
|
|
|
882
931
|
*
|
|
883
932
|
* Scalar fields follow normal override semantics. Known nested objects are
|
|
884
933
|
* 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.
|
|
934
|
+
* clobbering sibling settings. `sources` are additive by default, but a later
|
|
935
|
+
* layer can set `stashInheritance: "replace"` to drop inherited sources first.
|
|
888
936
|
*/
|
|
889
937
|
function mergeLoadedConfig(base, override) {
|
|
890
938
|
if (!override)
|
|
@@ -902,18 +950,26 @@ function mergeLoadedConfig(base, override) {
|
|
|
902
950
|
if (base.llm && override.llm) {
|
|
903
951
|
merged.llm = { ...base.llm, ...override.llm };
|
|
904
952
|
}
|
|
953
|
+
if (base.index || override.index) {
|
|
954
|
+
// Deep-merge per-pass entries so a project layer can opt one pass out
|
|
955
|
+
// without dropping siblings configured in user config.
|
|
956
|
+
const mergedIndex = { ...(base.index ?? {}) };
|
|
957
|
+
for (const [passName, passOverride] of Object.entries(override.index ?? {})) {
|
|
958
|
+
mergedIndex[passName] = { ...(mergedIndex[passName] ?? {}), ...passOverride };
|
|
959
|
+
}
|
|
960
|
+
if (Object.keys(mergedIndex).length > 0)
|
|
961
|
+
merged.index = mergedIndex;
|
|
962
|
+
}
|
|
905
963
|
if (base.security && override.security) {
|
|
906
964
|
merged.security = mergeSecurityConfig(base.security, override.security);
|
|
907
965
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
const baseSources = base.sources ?? base.stashes ?? [];
|
|
916
|
-
if (replaceStashes) {
|
|
966
|
+
if (base.agent && override.agent) {
|
|
967
|
+
merged.agent = mergeAgentConfig(base.agent, override.agent);
|
|
968
|
+
}
|
|
969
|
+
const replaceSources = override.stashInheritance === "replace";
|
|
970
|
+
const overrideSources = override.sources ?? [];
|
|
971
|
+
const baseSources = base.sources ?? [];
|
|
972
|
+
if (replaceSources) {
|
|
917
973
|
merged.sources = [...overrideSources];
|
|
918
974
|
}
|
|
919
975
|
else if (overrideSources.length > 0) {
|
|
@@ -922,8 +978,6 @@ function mergeLoadedConfig(base, override) {
|
|
|
922
978
|
else if (baseSources.length > 0) {
|
|
923
979
|
merged.sources = [...baseSources];
|
|
924
980
|
}
|
|
925
|
-
// Clear deprecated stashes field on the merged result — sources is canonical.
|
|
926
|
-
delete merged.stashes;
|
|
927
981
|
return merged;
|
|
928
982
|
}
|
|
929
983
|
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 {
|