akm-cli 0.9.0-rc.0 → 0.9.0-rc.13
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 +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -14,18 +14,26 @@
|
|
|
14
14
|
* implementation, not a "local vs. remote" distinction.
|
|
15
15
|
*/
|
|
16
16
|
import fs from "node:fs";
|
|
17
|
+
import path from "node:path";
|
|
17
18
|
import { buildActionFromContributors, defaultActionContributors } from "../../core/action-contributors.js";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
19
|
+
import { stashDirFor } from "../../core/asset/asset-placement.js";
|
|
20
|
+
import { displayRef } from "../../core/asset/resolve-ref.js";
|
|
20
21
|
import { getDbPath } from "../../core/paths.js";
|
|
22
|
+
import { defaultRendererRegistry } from "../../core/type-presentation.js";
|
|
21
23
|
import { warn } from "../../core/warn.js";
|
|
24
|
+
import { closeDatabase, openExistingDatabase } from "../../storage/repositories/index-connection.js";
|
|
25
|
+
import { getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getPositiveFeedbackCountsByIds, } from "../../storage/repositories/index-entries-repository.js";
|
|
26
|
+
import { searchFts } from "../../storage/repositories/index-fts-repository.js";
|
|
27
|
+
import { getMeta } from "../../storage/repositories/index-meta-repository.js";
|
|
28
|
+
import { searchVec } from "../../storage/repositories/index-vec-repository.js";
|
|
22
29
|
import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
|
|
23
|
-
import { closeDatabase, getAllEntries, getBaseBeliefStatesForDerivedTwins, getEntryById, getEntryCount, getMeta, getPositiveFeedbackCountsByIds, openExistingDatabase, sanitizeFtsQuery, searchFts, searchVec, } from "../db/db.js";
|
|
24
30
|
import { ensureIndex } from "../ensure-index.js";
|
|
25
|
-
import { collectGraphRelatedHit,
|
|
31
|
+
import { collectGraphRelatedHit, loadGraphBoostContext } from "../graph/graph-boost.js";
|
|
26
32
|
import { isProposedQuality } from "../passes/metadata.js";
|
|
27
33
|
import { resolveProjectContext } from "../walk/project-context.js";
|
|
34
|
+
import { parseRefPrefixQuery, parseRetiredTypePrefixQuery, sanitizeFtsQuery } from "./fts-query.js";
|
|
28
35
|
import { applyRankingRules, combineSearchScores, normalizeFtsScores } from "./ranking.js";
|
|
36
|
+
import { attachSearchHitAttribution, copySearchHitAttribution, getSearchHitAttribution } from "./search-attribution.js";
|
|
29
37
|
import { enrichSearchHit } from "./search-hit-enrichers.js";
|
|
30
38
|
import { buildEditHint, findSourceForPath, isEditable } from "./search-source.js";
|
|
31
39
|
import { deriveSemanticProviderFingerprint, getEffectiveSemanticStatus, isSemanticRuntimeReady, readSemanticStatus, } from "./semantic-status.js";
|
|
@@ -37,6 +45,9 @@ import { deriveSemanticProviderFingerprint, getEffectiveSemanticStatus, isSemant
|
|
|
37
45
|
* actionable without re-introducing read-triggered reindexing.
|
|
38
46
|
*/
|
|
39
47
|
const STALE_INDEX_HINT_MS = 7 * 24 * 60 * 60 * 1000;
|
|
48
|
+
function hasIndexedProvenance(entry) {
|
|
49
|
+
return Boolean(entry.itemRef && entry.bundleId && entry.conceptId);
|
|
50
|
+
}
|
|
40
51
|
function buildStaleIndexHint(db) {
|
|
41
52
|
try {
|
|
42
53
|
const builtAt = getMeta(db, "builtAt");
|
|
@@ -52,17 +63,22 @@ function buildStaleIndexHint(db) {
|
|
|
52
63
|
return undefined;
|
|
53
64
|
}
|
|
54
65
|
}
|
|
66
|
+
function indexedProvenance(entry) {
|
|
67
|
+
return { itemRef: entry.itemRef, bundleId: entry.bundleId, conceptId: entry.conceptId };
|
|
68
|
+
}
|
|
55
69
|
export function buildLocalAction(type, ref, registry = defaultRendererRegistry) {
|
|
56
70
|
return buildActionFromContributors({ type, ref }, defaultActionContributors(registry)) ?? `akm show ${ref}`;
|
|
57
71
|
}
|
|
58
|
-
function resolveSearchHitRef(entry,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
72
|
+
function resolveSearchHitRef(entry, provenance, defaultBundleId) {
|
|
73
|
+
return displayRef({
|
|
74
|
+
type: entry.type,
|
|
75
|
+
name: entry.name,
|
|
76
|
+
conceptId: provenance.conceptId,
|
|
77
|
+
bundleId: provenance.bundleId,
|
|
78
|
+
}, defaultBundleId);
|
|
63
79
|
}
|
|
64
80
|
function resolveSearchHitOrigin(source) {
|
|
65
|
-
return source?.
|
|
81
|
+
return source?.registryId ?? null;
|
|
66
82
|
}
|
|
67
83
|
/**
|
|
68
84
|
* Phase 2A / Rec 5: gate for the per-search `getPositiveFeedbackCountsByIds`
|
|
@@ -116,7 +132,26 @@ export async function searchLocal(input) {
|
|
|
116
132
|
}
|
|
117
133
|
}
|
|
118
134
|
if (config.semanticSearchMode === "auto" && semanticStatus === "blocked") {
|
|
119
|
-
|
|
135
|
+
if (!config.embedding?.endpoint || !config.embedding?.model) {
|
|
136
|
+
// F7/A2: same predicate as the `pending` branch above (#480) — a
|
|
137
|
+
// `blocked` status can outlive the provider config that produced it
|
|
138
|
+
// (e.g. the embedding config was later unset). This is not a fault;
|
|
139
|
+
// there is simply nothing configured to use.
|
|
140
|
+
warnings.push("Semantic search is enabled (semanticSearchMode='auto') but no embedding provider is configured. " +
|
|
141
|
+
'Either: (a) `akm config set embedding \'{"endpoint":"...","model":"..."}\'`, or ' +
|
|
142
|
+
"(b) `akm config set semanticSearchMode off` to use keyword-only search.");
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// F7/A2: surface the ACTUAL diagnostic instead of one fixed generic
|
|
146
|
+
// string. `rawStatus.reason`/`rawStatus.message` record why the
|
|
147
|
+
// semantic backend failed (auth, network, a stuck local-model
|
|
148
|
+
// download, …) — read at `readSemanticStatus()` above and, before
|
|
149
|
+
// this fix, discarded here in favor of a message that never varied.
|
|
150
|
+
const detail = rawStatus?.message ?? (rawStatus?.reason ? `reason: ${rawStatus.reason}` : undefined);
|
|
151
|
+
warnings.push(`Semantic search is blocked${detail ? ` (${detail})` : ""}. Using keyword search until the semantic ` +
|
|
152
|
+
"backend is healthy again. Run 'akm index --full' to retry, or " +
|
|
153
|
+
"`akm config set semanticSearchMode off` to silence this warning.");
|
|
154
|
+
}
|
|
120
155
|
}
|
|
121
156
|
// Bootstrap-only: builds the index inline when it cannot serve this stash.
|
|
122
157
|
// Content freshness is the writers' job (indexWrittenAssets + full runs);
|
|
@@ -145,16 +180,16 @@ export async function searchLocal(input) {
|
|
|
145
180
|
const staleHint = buildStaleIndexHint(db);
|
|
146
181
|
if (staleHint)
|
|
147
182
|
warnings.push(staleHint);
|
|
148
|
-
const { hits, embedMs, rankMs } = await searchDatabase(db, query, searchType, limit, stashDir, allSourceDirs, config, sources, rendererRegistry, filters, includeProposed, beliefFilter, restrictToSources, includeExcludedTypes, disableProjectContext, disableScopedUtility);
|
|
183
|
+
const { hits, embedMs, rankMs, usedSemantic } = await searchDatabase(db, query, searchType, limit, stashDir, allSourceDirs, config, sources, rendererRegistry, filters, includeProposed, beliefFilter, restrictToSources, includeExcludedTypes, disableProjectContext, disableScopedUtility);
|
|
149
184
|
return {
|
|
150
185
|
hits,
|
|
151
|
-
tip: hits.length === 0
|
|
152
|
-
? "No matching stash assets were found. Try a different query or run 'akm index' to rebuild."
|
|
153
|
-
: undefined,
|
|
186
|
+
tip: hits.length === 0 ? emptyResultTip(query) : undefined,
|
|
154
187
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
155
188
|
embedMs,
|
|
156
189
|
rankMs,
|
|
157
|
-
|
|
190
|
+
// Report the mode the search ACTUALLY used, carried explicitly from the
|
|
191
|
+
// vector scorer — not inferred from elapsed embedding milliseconds.
|
|
192
|
+
mode: usedSemantic ? "semantic" : "keyword",
|
|
158
193
|
};
|
|
159
194
|
}
|
|
160
195
|
finally {
|
|
@@ -169,58 +204,56 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
169
204
|
// `includeExcludedTypes`. When the config key is ABSENT a built-in default of
|
|
170
205
|
// ['session'] is applied; an explicit empty list disables exclusion.
|
|
171
206
|
const defaultExcludes = searchType === "any" && !includeExcludedTypes ? (config.search?.defaultExcludeTypes ?? ["session"]) : [];
|
|
207
|
+
// D4 — conceptId-prefix queries (`memories/projecta/`, `bundle//`,
|
|
208
|
+
// `bundle//skills/`) translate to a deterministic enumeration narrowed by
|
|
209
|
+
// conceptId, instead of degenerating into the AND-token FTS query their
|
|
210
|
+
// sanitized form would produce ("memories projecta" — noise). The branch
|
|
211
|
+
// fires only on the untyped path: an explicit `--type` flag expresses
|
|
212
|
+
// stronger intent and wins. The PREFIX is itself explicit intent, so
|
|
213
|
+
// `defaultExcludeTypes` does not apply — `sessions/` enumerates sessions
|
|
214
|
+
// exactly like `--type session` does, and `bundle//` means the whole bundle.
|
|
215
|
+
const refPrefix = searchType === "any" ? parseRefPrefixQuery(query) : null;
|
|
216
|
+
// Shared args for the two browse paths below; browse never runs semantic
|
|
217
|
+
// ranking, so both return usedSemantic: false.
|
|
218
|
+
const browseArgs = {
|
|
219
|
+
db,
|
|
220
|
+
query,
|
|
221
|
+
limit,
|
|
222
|
+
stashDir,
|
|
223
|
+
allSourceDirs,
|
|
224
|
+
sources,
|
|
225
|
+
config,
|
|
226
|
+
rendererRegistry,
|
|
227
|
+
filters,
|
|
228
|
+
includeProposed,
|
|
229
|
+
beliefFilter,
|
|
230
|
+
restrictToSources,
|
|
231
|
+
};
|
|
232
|
+
if (refPrefix) {
|
|
233
|
+
// Browse path (conceptId-prefix enumeration).
|
|
234
|
+
return {
|
|
235
|
+
...(await enumerateEntries({
|
|
236
|
+
...browseArgs,
|
|
237
|
+
excludeTypes: [],
|
|
238
|
+
conceptIdPrefix: refPrefix.conceptIdPrefix,
|
|
239
|
+
...(refPrefix.bundle !== undefined ? { bundle: refPrefix.bundle } : {}),
|
|
240
|
+
})),
|
|
241
|
+
usedSemantic: false,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
172
244
|
// Empty queries — including ones that sanitize down to no searchable FTS
|
|
173
245
|
// tokens such as "." — should enumerate matching entries instead of
|
|
174
246
|
// returning an empty result set from FTS.
|
|
175
247
|
if (!hasSearchableTokens) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
// Source filter: when the caller narrowed `sources` via `--source <name>`,
|
|
187
|
-
// drop entries whose filePath does not live under any of the requested
|
|
188
|
-
// sources. The FTS index spans every configured source, so without this
|
|
189
|
-
// filter a narrowed --source request would still leak results.
|
|
190
|
-
const sourceFiltered = restrictToSources
|
|
191
|
-
? uniqueEntries.filter((ie) => findSourceForPath(ie.filePath, sources) !== undefined)
|
|
192
|
-
: uniqueEntries;
|
|
193
|
-
// Scope filter: drop entries whose stored scope does not satisfy every
|
|
194
|
-
// supplied scope key. Filtering happens BEFORE the limit slice so a
|
|
195
|
-
// restrictive filter still returns up to `limit` results.
|
|
196
|
-
const scopeFiltered = filters
|
|
197
|
-
? sourceFiltered.filter((ie) => entryMatchesScope(ie.entry.scope, filters))
|
|
198
|
-
: sourceFiltered;
|
|
199
|
-
// Proposed-quality filter (v1 spec §4.2): exclude entries with
|
|
200
|
-
// `quality: "proposed"` unless the caller explicitly opts in.
|
|
201
|
-
const qualityFiltered = includeProposed
|
|
202
|
-
? scopeFiltered
|
|
203
|
-
: scopeFiltered.filter((ie) => !isProposedQuality(ie.entry.quality));
|
|
204
|
-
// 03-R3: derived twins inherit their base's demoting belief state here too,
|
|
205
|
-
// so the belief FILTER (and the reported hit state) stays consistent on the
|
|
206
|
-
// enumerate/browse path — not only on the FTS-scored path below.
|
|
207
|
-
inheritDerivedTwinBeliefStates(db, qualityFiltered);
|
|
208
|
-
const beliefFiltered = qualityFiltered.filter((ie) => matchBeliefFilter(ie.entry.beliefState, beliefFilter));
|
|
209
|
-
const selected = beliefFiltered.slice(0, limit);
|
|
210
|
-
const hits = await Promise.all(selected.map((ie) => buildDbHit({
|
|
211
|
-
entry: ie.entry,
|
|
212
|
-
path: ie.filePath,
|
|
213
|
-
score: 1,
|
|
214
|
-
query,
|
|
215
|
-
rankingMode: "fts",
|
|
216
|
-
defaultStashDir: stashDir,
|
|
217
|
-
allSourceDirs,
|
|
218
|
-
sources,
|
|
219
|
-
config,
|
|
220
|
-
rendererRegistry,
|
|
221
|
-
db,
|
|
222
|
-
})));
|
|
223
|
-
return { hits };
|
|
248
|
+
// Browse path (empty/unsearchable query).
|
|
249
|
+
return {
|
|
250
|
+
...(await enumerateEntries({
|
|
251
|
+
...browseArgs,
|
|
252
|
+
typeFilter: searchType === "any" ? undefined : searchType,
|
|
253
|
+
excludeTypes: defaultExcludes,
|
|
254
|
+
})),
|
|
255
|
+
usedSemantic: false,
|
|
256
|
+
};
|
|
224
257
|
}
|
|
225
258
|
// Start the async embedding request without awaiting, then run FTS
|
|
226
259
|
// synchronously while the HTTP/local embedding request is in-flight.
|
|
@@ -230,6 +263,12 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
230
263
|
const ftsResults = searchFts(db, query, limit * 3, typeFilter, defaultExcludes);
|
|
231
264
|
const embeddingScores = await embeddingPromise;
|
|
232
265
|
const embedMs = Date.now() - tEmbed0;
|
|
266
|
+
// The vector scorer returns a (possibly empty) Map when the embedding + vector
|
|
267
|
+
// search actually executed, or null when semantic was not runnable (disabled,
|
|
268
|
+
// no embeddings, or the embed call threw). This is the AUTHORITATIVE "semantic
|
|
269
|
+
// mode was used" signal — carried out to telemetry instead of guessing from
|
|
270
|
+
// elapsed milliseconds (which timed the concurrent FTS work too).
|
|
271
|
+
const usedSemantic = embeddingScores !== null;
|
|
233
272
|
const tRank0 = Date.now();
|
|
234
273
|
// ── Score normalization ──────────────────────────────────────────────
|
|
235
274
|
// Normalized BM25 + cosine similarity with weighted addition
|
|
@@ -253,11 +292,10 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
253
292
|
// not leak into default ('any') results. defaultExcludes is already []
|
|
254
293
|
// unless this is the untyped path without includeExcludedTypes.
|
|
255
294
|
excludeTypes: defaultExcludes,
|
|
256
|
-
});
|
|
295
|
+
}).filter(hasIndexedProvenance);
|
|
257
296
|
// ── Scoring Phase ──────────────────────────────────────────────────────
|
|
258
297
|
// Apply boosts as multiplicative factors (all boosts in a single phase
|
|
259
298
|
// so that sort order and displayed scores are always consistent).
|
|
260
|
-
//
|
|
261
299
|
// Ranking philosophy: the goal is to surface the MOST USEFUL result for the
|
|
262
300
|
// user's intent. An exact name match is the strongest signal. Actionable
|
|
263
301
|
// asset types (skills, commands, agents) are more useful than passive
|
|
@@ -280,7 +318,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
280
318
|
})();
|
|
281
319
|
// Resolve project-context tokens from the current working directory once
|
|
282
320
|
// per search invocation. Returns null when running from home dir / /tmp,
|
|
283
|
-
// or when the caller
|
|
321
|
+
// or when the caller passed `--no-project-context` (disableProjectContext).
|
|
284
322
|
const projectContext = disableProjectContext ? null : resolveProjectContext(process.cwd());
|
|
285
323
|
// Phase 2A / Rec 5: resolve forgetting-curve config and skip the feedback
|
|
286
324
|
// count query when the boost cannot make a difference (default ≤ 1.0 means
|
|
@@ -295,10 +333,11 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
295
333
|
// query here would be pure overhead. The boost > 1.0 sub-gate then skips the
|
|
296
334
|
// query when the configured boost is a no-op (1.5^count when boost==1 is 1).
|
|
297
335
|
const positiveFeedbackCounts = shouldQueryPositiveFeedbackCounts(utilityDecayRaw)
|
|
298
|
-
? getPositiveFeedbackCountsByIds(
|
|
336
|
+
? getPositiveFeedbackCountsByIds(scored.map((item) => item.id))
|
|
299
337
|
: undefined;
|
|
300
338
|
// Resolve per-project scope key for scoped utility scoring.
|
|
301
|
-
//
|
|
339
|
+
// `disableScopedUtility` (wired from `akm search --no-project-context`)
|
|
340
|
+
// opts out (e.g. for registry searches or tests).
|
|
302
341
|
let scopeKey;
|
|
303
342
|
try {
|
|
304
343
|
scopeKey = disableScopedUtility ? undefined : getCurrentWorkflowScopeKey();
|
|
@@ -323,8 +362,14 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
323
362
|
// Drop semantic-only hits (cosine-only, no FTS match) whose score falls
|
|
324
363
|
// below the configured floor. FTS hits and hybrid hits are always kept.
|
|
325
364
|
// Default floor: 0.2. Set search.minScore = 0 in config to disable.
|
|
365
|
+
// Judged on the PRE-ceiling score when a demoting belief state clamped the
|
|
366
|
+
// item (`preCeilingScore`): the belief ceilings can sit below this floor
|
|
367
|
+
// (archived 0.15 < 0.2), and a demotion must rank the hit last, not
|
|
368
|
+
// silently remove a result that would otherwise have listed.
|
|
326
369
|
const minScore = config.search?.minScore ?? 0.2;
|
|
327
|
-
const preFilter = minScore > 0
|
|
370
|
+
const preFilter = minScore > 0
|
|
371
|
+
? scored.filter((item) => item.rankingMode !== "semantic" || (item.preCeilingScore ?? item.score) >= minScore)
|
|
372
|
+
: scored;
|
|
328
373
|
// Deterministic tiebreaker on equal scores.
|
|
329
374
|
//
|
|
330
375
|
// CRITICAL: sort on the SAME clamped+rounded value the user sees (see the
|
|
@@ -341,34 +386,26 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
341
386
|
const displayScore = (s) => Math.round(Math.min(1, Math.max(0, s)) * 10000) / 10000;
|
|
342
387
|
preFilter.sort((a, b) => displayScore(b.score) - displayScore(a.score) || a.entry.name.localeCompare(b.entry.name));
|
|
343
388
|
// Deduplicate by file path — keep only the highest-scored entry per file.
|
|
344
|
-
// Multiple .stash.json entries can map to the same file (e.g. entries without
|
|
345
|
-
// a filename field all collapse to files[0]). Showing the same path/ref
|
|
346
|
-
// multiple times clutters results.
|
|
347
389
|
const deduped = deduplicateByPath(preFilter);
|
|
348
|
-
// Source
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
// `quality: "proposed"` unless the caller passed `--include-proposed`.
|
|
364
|
-
// Applied AFTER ranking for the same reason as scope filtering.
|
|
365
|
-
const qualityFiltered = includeProposed
|
|
366
|
-
? scopeFiltered
|
|
367
|
-
: scopeFiltered.filter((item) => !isProposedQuality(item.entry.quality));
|
|
368
|
-
const beliefFiltered = qualityFiltered.filter((item) => matchBeliefFilter(item.entry.beliefState, beliefFilter));
|
|
390
|
+
// Source → scope → proposed-quality → derived-twin belief inheritance →
|
|
391
|
+
// belief: the post-candidate filter chain shared with enumerateEntries (see
|
|
392
|
+
// applyEntryFilters). Applied AFTER ranking so filtering narrows the result
|
|
393
|
+
// set without touching the single FTS5+boosts scoring pipeline. The twin
|
|
394
|
+
// inheritance inside the chain re-runs here as an idempotent no-op — it
|
|
395
|
+
// already ran on the full candidate pool before ranking (:460) to feed the
|
|
396
|
+
// belief-state ranker.
|
|
397
|
+
const beliefFiltered = applyEntryFilters(deduped, {
|
|
398
|
+
db,
|
|
399
|
+
sources,
|
|
400
|
+
restrictToSources,
|
|
401
|
+
filters,
|
|
402
|
+
includeProposed,
|
|
403
|
+
beliefFilter,
|
|
404
|
+
});
|
|
369
405
|
const rankMs = Date.now() - tRank0;
|
|
370
406
|
const selected = beliefFiltered.slice(0, limit);
|
|
371
|
-
const hits = await Promise.all(selected.map((
|
|
407
|
+
const hits = await Promise.all(selected.map((ranked) => {
|
|
408
|
+
const { entry, filePath, score, rankingMode, utilityBoosted } = ranked;
|
|
372
409
|
// CLAUDE.md locks SearchHit.score in [0,1]. The boost loop above can
|
|
373
410
|
// exceed 1.0 (this was a pre-existing breach that #207's graph boost
|
|
374
411
|
// — up to ~1.05 additive contribution — made detectable); clamp here
|
|
@@ -377,7 +414,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
377
414
|
return buildDbHit({
|
|
378
415
|
entry,
|
|
379
416
|
path: filePath,
|
|
380
|
-
|
|
417
|
+
...indexedProvenance(ranked),
|
|
381
418
|
score: Math.round(finalScore * 10000) / 10000,
|
|
382
419
|
query,
|
|
383
420
|
rankingMode,
|
|
@@ -387,11 +424,148 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
|
|
|
387
424
|
config,
|
|
388
425
|
utilityBoosted,
|
|
389
426
|
graphContext,
|
|
427
|
+
attributionSource: ranked,
|
|
390
428
|
rendererRegistry,
|
|
391
429
|
db,
|
|
392
430
|
});
|
|
393
431
|
}));
|
|
394
|
-
return { embedMs, rankMs, hits };
|
|
432
|
+
return { embedMs, rankMs, hits, usedSemantic };
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* The no-hits tip. A query in the retired `<type>:` / `<type>:<prefix>/` browse
|
|
436
|
+
* grammar gets the conceptId spelling that replaces it: without this it comes
|
|
437
|
+
* back empty and silent, which is the failure D4 removed the grammar to avoid.
|
|
438
|
+
*/
|
|
439
|
+
function emptyResultTip(query) {
|
|
440
|
+
const generic = "No matching stash assets were found. Try a different query or run 'akm index' to rebuild.";
|
|
441
|
+
const retired = parseRetiredTypePrefixQuery(query);
|
|
442
|
+
if (!retired)
|
|
443
|
+
return generic;
|
|
444
|
+
const root = stashDirFor(retired.type);
|
|
445
|
+
if (!root)
|
|
446
|
+
return generic;
|
|
447
|
+
return `No matching stash assets were found. The '<type>:' browse grammar was removed in 0.9.0 — use the conceptId spelling: 'akm search "${root}/${retired.rest}"'.`;
|
|
448
|
+
}
|
|
449
|
+
// ── Enumeration (browse) path ────────────────────────────────────────────────
|
|
450
|
+
/**
|
|
451
|
+
* Enumerate index entries without FTS scoring — the browse path shared by
|
|
452
|
+
* empty/unsearchable queries and D4 conceptId-prefix queries (`memories/`,
|
|
453
|
+
* `bundle//`, `bundle//skills/`). Applies the same post-ranking filters as the scored
|
|
454
|
+
* path (source narrowing, scope, proposed-quality, belief) before the limit
|
|
455
|
+
* slice. Hits carry the fixed browse score 1 in type-then-name order — this is
|
|
456
|
+
* a deterministic listing, not a relevance ranking.
|
|
457
|
+
*/
|
|
458
|
+
async function enumerateEntries(opts) {
|
|
459
|
+
const { db, query, sources, config, rendererRegistry, filters, beliefFilter } = opts;
|
|
460
|
+
const allEntries = getAllEntries(db, opts.typeFilter, opts.excludeTypes).filter(hasIndexedProvenance);
|
|
461
|
+
// Explicit listing order: type, then name, then filePath. The underlying
|
|
462
|
+
// SELECT carries no ORDER BY, so its row order tracks the query plan and the
|
|
463
|
+
// index-insertion (file-walk) order — both machine-dependent. A browse
|
|
464
|
+
// listing must not change order across hosts or SQLite versions.
|
|
465
|
+
allEntries.sort((a, b) => a.entry.type.localeCompare(b.entry.type) ||
|
|
466
|
+
a.entry.name.localeCompare(b.entry.name) ||
|
|
467
|
+
a.filePath.localeCompare(b.filePath));
|
|
468
|
+
// D4: narrow to the requested bundle and subtree. Matching is against the
|
|
469
|
+
// conceptId — the spelling every emitted `ref` carries — so a ref copied out
|
|
470
|
+
// of search output round-trips back in. `startsWith` on the full
|
|
471
|
+
// slash-retaining prefix is exact: "memories/projecta/" cannot match a
|
|
472
|
+
// sibling "memories/projectalpha/…" scope.
|
|
473
|
+
const bundle = opts.bundle?.toLowerCase();
|
|
474
|
+
const bundleFiltered = bundle === undefined ? allEntries : allEntries.filter((ie) => ie.bundleId.toLowerCase() === bundle);
|
|
475
|
+
const conceptIdPrefix = opts.conceptIdPrefix?.toLowerCase() ?? "";
|
|
476
|
+
const prefixFiltered = conceptIdPrefix.length > 0
|
|
477
|
+
? bundleFiltered.filter((ie) => ie.conceptId.toLowerCase().startsWith(conceptIdPrefix))
|
|
478
|
+
: bundleFiltered;
|
|
479
|
+
// Deduplicate by file path — multiple entries can share the same file
|
|
480
|
+
const seenFilePaths = new Set();
|
|
481
|
+
const uniqueEntries = prefixFiltered.filter((ie) => {
|
|
482
|
+
if (seenFilePaths.has(ie.filePath))
|
|
483
|
+
return false;
|
|
484
|
+
seenFilePaths.add(ie.filePath);
|
|
485
|
+
return true;
|
|
486
|
+
});
|
|
487
|
+
// Source → scope → proposed-quality → derived-twin belief inheritance →
|
|
488
|
+
// belief: the post-candidate filter chain shared with searchDatabase's
|
|
489
|
+
// scored path (see applyEntryFilters). Filtering happens BEFORE the limit
|
|
490
|
+
// slice so a restrictive filter still returns up to `limit` results. On this
|
|
491
|
+
// path the twin inheritance is the ONLY place it runs (there is no ranking
|
|
492
|
+
// pass), keeping the belief filter and reported hit state consistent with
|
|
493
|
+
// the scored path.
|
|
494
|
+
const beliefFiltered = applyEntryFilters(uniqueEntries, {
|
|
495
|
+
db,
|
|
496
|
+
sources,
|
|
497
|
+
restrictToSources: opts.restrictToSources,
|
|
498
|
+
filters,
|
|
499
|
+
includeProposed: opts.includeProposed,
|
|
500
|
+
beliefFilter,
|
|
501
|
+
});
|
|
502
|
+
const selected = beliefFiltered.slice(0, opts.limit);
|
|
503
|
+
const hits = await Promise.all(selected.map((ie) => buildDbHit({
|
|
504
|
+
entry: ie.entry,
|
|
505
|
+
path: ie.filePath,
|
|
506
|
+
itemRef: ie.itemRef,
|
|
507
|
+
bundleId: ie.bundleId,
|
|
508
|
+
conceptId: ie.conceptId,
|
|
509
|
+
score: 1,
|
|
510
|
+
query,
|
|
511
|
+
rankingMode: "fts",
|
|
512
|
+
defaultStashDir: opts.stashDir,
|
|
513
|
+
allSourceDirs: opts.allSourceDirs,
|
|
514
|
+
sources,
|
|
515
|
+
config,
|
|
516
|
+
rendererRegistry,
|
|
517
|
+
db,
|
|
518
|
+
})));
|
|
519
|
+
return { hits };
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Post-candidate filter chain shared by BOTH search paths — the scored path
|
|
523
|
+
* (`searchDatabase`) and the browse path (`enumerateEntries`). Applies, in this
|
|
524
|
+
* exact order: source-narrowing → scope → proposed-quality → derived-twin
|
|
525
|
+
* belief inheritance → belief filter. Extracting the chain removes the two
|
|
526
|
+
* paths' formerly-duplicated filter sequences so the predicates, their order,
|
|
527
|
+
* and the twin-inheritance placement can never drift apart (plan §4.3).
|
|
528
|
+
*
|
|
529
|
+
* What this does NOT unify — and deliberately leaves divergent — is CANDIDATE-
|
|
530
|
+
* POOL construction, which is inherent search-vs-browse semantics: the scored
|
|
531
|
+
* path's pool is `searchFts`/vector matches for the query's own tokens (FTS
|
|
532
|
+
* indexes description/tags/searchHints/aliases, not raw body prose), while the
|
|
533
|
+
* enumerate path's pool is `getAllEntries` for the type, independent of query
|
|
534
|
+
* text. A derived twin sharing no indexed token with the query is therefore an
|
|
535
|
+
* enumerate-path candidate but never a scored-path candidate — see
|
|
536
|
+
* tests/fixtures/goldens/filter-behavior/scored-vs-enumerate.json, which pins
|
|
537
|
+
* that (retained) divergence.
|
|
538
|
+
*
|
|
539
|
+
* `inheritDerivedTwinBeliefStates` is idempotent, so running it here is safe on
|
|
540
|
+
* the scored path, which must ALSO call it before ranking (the belief-state
|
|
541
|
+
* ranker demotes inherited states): by the time this chain runs, those twins
|
|
542
|
+
* already carry a state and the call here is a no-op for them. The enumerate
|
|
543
|
+
* path never ranks, so this is the only place it inherits.
|
|
544
|
+
*/
|
|
545
|
+
function applyEntryFilters(items, opts) {
|
|
546
|
+
const { filters } = opts;
|
|
547
|
+
// Source filter: when the caller narrowed `sources` via `--from <name>`,
|
|
548
|
+
// drop entries whose filePath does not live under any requested source. The
|
|
549
|
+
// FTS/enumerate index spans every configured source, so without this filter a
|
|
550
|
+
// narrowed --from request would still leak results from other sources.
|
|
551
|
+
const sourceFiltered = opts.restrictToSources
|
|
552
|
+
? items.filter((item) => findSourceForPath(item.filePath, opts.sources) !== undefined)
|
|
553
|
+
: items;
|
|
554
|
+
// Scope filter: drop entries whose stored scope does not satisfy every
|
|
555
|
+
// supplied key.
|
|
556
|
+
const scopeFiltered = filters
|
|
557
|
+
? sourceFiltered.filter((item) => entryMatchesScope(item.entry.scope, filters))
|
|
558
|
+
: sourceFiltered;
|
|
559
|
+
// Proposed-quality filter (v1 spec §4.2): exclude `quality: "proposed"`
|
|
560
|
+
// entries unless the caller opts in.
|
|
561
|
+
const qualityFiltered = opts.includeProposed
|
|
562
|
+
? scopeFiltered
|
|
563
|
+
: scopeFiltered.filter((item) => !isProposedQuality(item.entry.quality));
|
|
564
|
+
// 03-R3: derived twins inherit their base's demoting belief state BEFORE the
|
|
565
|
+
// belief filter, so the filter (and the reported hit state) stays consistent
|
|
566
|
+
// across both paths.
|
|
567
|
+
inheritDerivedTwinBeliefStates(opts.db, qualityFiltered);
|
|
568
|
+
return qualityFiltered.filter((item) => matchBeliefFilter(item.entry.beliefState, opts.beliefFilter));
|
|
395
569
|
}
|
|
396
570
|
/**
|
|
397
571
|
* 03-R3: let each `.derived` twin inherit its base memory's demoting belief
|
|
@@ -466,7 +640,8 @@ async function tryVecScores(db, query, k, config) {
|
|
|
466
640
|
// ── Hit building ────────────────────────────────────────────────────────────
|
|
467
641
|
export async function buildDbHit(input) {
|
|
468
642
|
const rendererRegistry = input.rendererRegistry ?? defaultRendererRegistry;
|
|
469
|
-
const
|
|
643
|
+
const absolutePath = path.resolve(input.path);
|
|
644
|
+
const entryStashDir = findSourceForPath(absolutePath, input.sources)?.path ?? input.defaultStashDir;
|
|
470
645
|
// Quality and confidence boosts are now applied in the main scoring
|
|
471
646
|
// phase (searchDatabase). buildDbHit receives the already-final score and
|
|
472
647
|
// passes it through without further multiplication. We still compute the
|
|
@@ -477,23 +652,25 @@ export async function buildDbHit(input) {
|
|
|
477
652
|
const confidenceBoost = typeof input.entry.confidence === "number" ? Math.min(0.05, Math.max(0, input.entry.confidence) * 0.05) : 0;
|
|
478
653
|
// Round to 4 decimal places, no boost multiplication
|
|
479
654
|
const score = Math.round(input.score * 10000) / 10000;
|
|
480
|
-
const graphBoost = input.
|
|
655
|
+
const graphBoost = getSearchHitAttribution(input.attributionSource ?? {})?.graphExtraction?.boost ?? 0;
|
|
481
656
|
const whyMatched = buildWhyMatched(input.entry, input.query, input.rankingMode, qualityBoost, confidenceBoost, input.utilityBoosted, graphBoost);
|
|
482
|
-
const graphHit = input.graphContext ? collectGraphRelatedHit(input.graphContext,
|
|
483
|
-
const source = findSourceForPath(
|
|
484
|
-
const
|
|
485
|
-
|
|
657
|
+
const graphHit = input.graphContext ? collectGraphRelatedHit(input.graphContext, absolutePath) : null;
|
|
658
|
+
const source = findSourceForPath(absolutePath, input.sources);
|
|
659
|
+
const defaultBundleId = input.config?.defaultBundle ??
|
|
660
|
+
(source && path.resolve(source.path) === path.resolve(input.defaultStashDir)
|
|
661
|
+
? (input.bundleId ?? undefined)
|
|
662
|
+
: undefined);
|
|
663
|
+
const ref = resolveSearchHitRef(input.entry, input, defaultBundleId);
|
|
664
|
+
const editable = isEditable(absolutePath, input.config, input.sources);
|
|
486
665
|
const estimatedTokens = typeof input.entry.fileSize === "number" ? Math.round(input.entry.fileSize / 4) : undefined;
|
|
487
666
|
const hit = {
|
|
488
667
|
type: input.entry.type,
|
|
489
668
|
name: input.entry.name,
|
|
490
|
-
path:
|
|
669
|
+
path: absolutePath,
|
|
491
670
|
ref,
|
|
492
671
|
origin: resolveSearchHitOrigin(source),
|
|
493
672
|
editable,
|
|
494
|
-
...(!editable
|
|
495
|
-
? { editHint: buildEditHint(input.path, input.entry.type, input.entry.name, source?.registryId) }
|
|
496
|
-
: {}),
|
|
673
|
+
...(!editable ? { editHint: buildEditHint(ref) } : {}),
|
|
497
674
|
description: input.entry.description,
|
|
498
675
|
tags: input.entry.tags,
|
|
499
676
|
size: deriveSize(input.entry.fileSize),
|
|
@@ -508,6 +685,13 @@ export async function buildDbHit(input) {
|
|
|
508
685
|
...(input.entry.currentBeliefRefs ? { currentBeliefRefs: input.entry.currentBeliefRefs } : {}),
|
|
509
686
|
...(graphHit ? { graph: { entities: graphHit.entities, relations: graphHit.relations } } : {}),
|
|
510
687
|
};
|
|
688
|
+
if (input.attributionSource)
|
|
689
|
+
copySearchHitAttribution(input.attributionSource, hit);
|
|
690
|
+
if (input.entry.derivedFrom) {
|
|
691
|
+
attachSearchHitAttribution(hit, {
|
|
692
|
+
memoryInference: { exposure: "direct" },
|
|
693
|
+
});
|
|
694
|
+
}
|
|
511
695
|
await enrichSearchHit(hit, {
|
|
512
696
|
type: input.entry.type,
|
|
513
697
|
stashDir: entryStashDir,
|
|
@@ -605,7 +789,7 @@ function deduplicateByPath(items) {
|
|
|
605
789
|
});
|
|
606
790
|
}
|
|
607
791
|
/**
|
|
608
|
-
* Exact-match scope filter check.
|
|
792
|
+
* Exact-match scope filter check. Entries without a `scope` object only
|
|
609
793
|
* match when no filter is supplied — which is what the caller guards on
|
|
610
794
|
* before invoking this helper.
|
|
611
795
|
*/
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
* These transform a raw user query into an FTS5-safe MATCH expression. They
|
|
8
8
|
* touch no database state, so they are unit-testable with zero DB setup.
|
|
9
|
+
* `parseRefPrefixQuery` is the one non-FTS helper: it decides whether a raw
|
|
10
|
+
* query should bypass FTS entirely (SPEC-4 ref-prefix enumeration).
|
|
9
11
|
*/
|
|
10
12
|
/**
|
|
11
13
|
* Sanitize a raw user query into an FTS5-safe implicit-AND expression.
|
|
@@ -49,3 +51,65 @@ export function buildPrefixQuery(ftsQuery) {
|
|
|
49
51
|
return null;
|
|
50
52
|
return prefixTokens.join(" ");
|
|
51
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* D4 — parse a conceptId-prefix browse query.
|
|
56
|
+
*
|
|
57
|
+
* Decides whether a raw query is a subtree-enumeration request rather than an
|
|
58
|
+
* ordinary keyword search. Matching is deliberately conservative: the trimmed
|
|
59
|
+
* query must be EXACTLY
|
|
60
|
+
*
|
|
61
|
+
* - `<conceptId prefix>/` → that subtree in any bundle,
|
|
62
|
+
* - `<bundle>//` → one bundle entirely,
|
|
63
|
+
* - `<bundle>//<conceptId prefix>/` → that subtree of that bundle.
|
|
64
|
+
*
|
|
65
|
+
* The trailing slash is REQUIRED — and is RETAINED in `conceptIdPrefix` — so a
|
|
66
|
+
* plain `conceptId.startsWith(conceptIdPrefix)` check gives exact `/`-boundary
|
|
67
|
+
* subtree semantics (`"projecta/"` cannot match a sibling `projectalpha/…`
|
|
68
|
+
* scope). Bare refs like `memories/a/b` therefore stay ordinary searches
|
|
69
|
+
* (resolving one ref is `akm show` territory), and any interior whitespace
|
|
70
|
+
* disqualifies (prose mentioning a ref is still prose).
|
|
71
|
+
*
|
|
72
|
+
* The prefix matches the conceptId — the same string every emitted `ref`
|
|
73
|
+
* carries — so a ref copied out of search output round-trips back in as a
|
|
74
|
+
* prefix. Nothing here consults a type list: enumeration covers every
|
|
75
|
+
* adapter's items uniformly, which the retired `<type>:` grammar could not do.
|
|
76
|
+
*
|
|
77
|
+
* Returns `null` when the query is not a browse request.
|
|
78
|
+
*/
|
|
79
|
+
export function parseRefPrefixQuery(query) {
|
|
80
|
+
const trimmed = query.trim();
|
|
81
|
+
if (trimmed.length === 0 || /\s/.test(trimmed))
|
|
82
|
+
return null;
|
|
83
|
+
const separator = trimmed.indexOf("//");
|
|
84
|
+
if (separator < 0) {
|
|
85
|
+
return trimmed.endsWith("/") ? { conceptIdPrefix: trimmed } : null;
|
|
86
|
+
}
|
|
87
|
+
const bundle = trimmed.slice(0, separator);
|
|
88
|
+
if (bundle.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
const rest = trimmed.slice(separator + 2);
|
|
91
|
+
if (rest === "")
|
|
92
|
+
return { bundle, conceptIdPrefix: "" };
|
|
93
|
+
if (rest.endsWith("/") && !rest.includes("//"))
|
|
94
|
+
return { bundle, conceptIdPrefix: rest };
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Recognize the retired `<type>:` / `<type>:<prefix>/` browse grammar so the
|
|
99
|
+
* caller can name the replacement spelling rather than letting the query
|
|
100
|
+
* degrade silently into a keyword search — the exact silent failure D4 removes.
|
|
101
|
+
* Shape recognition only; mapping the type to its conceptId root belongs to the
|
|
102
|
+
* caller, which keeps this module dependency-free.
|
|
103
|
+
*/
|
|
104
|
+
export function parseRetiredTypePrefixQuery(query) {
|
|
105
|
+
const trimmed = query.trim();
|
|
106
|
+
if (trimmed.length === 0 || /\s/.test(trimmed) || trimmed.includes("//"))
|
|
107
|
+
return null;
|
|
108
|
+
const colon = trimmed.indexOf(":");
|
|
109
|
+
if (colon <= 0)
|
|
110
|
+
return null;
|
|
111
|
+
const rest = trimmed.slice(colon + 1);
|
|
112
|
+
if (rest !== "" && !rest.endsWith("/"))
|
|
113
|
+
return null;
|
|
114
|
+
return { type: trimmed.slice(0, colon), rest };
|
|
115
|
+
}
|