akm-cli 0.9.0-rc.8 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1063 -44
- package/README.md +51 -25
- package/SECURITY.md +14 -1
- package/STABILITY.md +497 -0
- package/dist/akm +148 -35
- package/dist/{akm-migrate-storage → akm-migrate} +6 -9
- package/dist/assets/hints/cli-hints-full.md +223 -95
- package/dist/assets/hints/cli-hints-short.md +85 -22
- package/dist/assets/improve-strategies/default.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/stash-skeleton/README.md +6 -5
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
- package/dist/assets/tasks/core/extract.yml +1 -1
- package/dist/assets/tasks/core/version-check.yml +1 -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 +1 -3
- package/dist/assets/workflows/workflow-template.md +32 -15
- package/dist/cli/invocation.js +40 -15
- package/dist/cli/parse-args.js +0 -22
- package/dist/cli/retired-commands.js +121 -0
- package/dist/cli/shared.js +154 -22
- package/dist/cli/unknown-flags.js +236 -0
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +696 -258
- package/dist/commands/agent/agent-dispatch.js +14 -3
- package/dist/commands/agent/contribute-cli.js +73 -88
- package/dist/commands/completions.js +79 -22
- package/dist/commands/config-cli.js +17 -150
- package/dist/commands/env/env-cli.js +59 -143
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +36 -66
- package/dist/commands/env/secret.js +24 -57
- package/dist/commands/feedback-cli.js +141 -87
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +3 -4
- package/dist/commands/health/checks.js +85 -23
- package/dist/commands/health/html-report.js +7 -10
- package/dist/commands/health/improve-metrics.js +25 -83
- package/dist/commands/health/md-report.js +5 -9
- package/dist/commands/health/metrics.js +62 -20
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +4 -5
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +3 -48
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/commands/health/types-improve.js +7 -0
- package/dist/commands/health.js +99 -28
- package/dist/commands/improve/anti-collapse.js +2 -2
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +41 -40
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +140 -1000
- package/dist/commands/improve/distill/promote-memory.js +12 -12
- package/dist/commands/improve/distill/quality-gate.js +6 -6
- package/dist/commands/improve/distill.js +58 -69
- package/dist/commands/improve/eligibility.js +105 -57
- package/dist/commands/improve/extract-cli.js +14 -133
- package/dist/commands/improve/improve-cli.js +98 -114
- package/dist/commands/improve/improve-result-file.js +1 -28
- package/dist/commands/improve/improve-strategies.js +8 -5
- package/dist/commands/improve/improve.js +128 -91
- package/dist/commands/improve/loop-stages.js +182 -20
- package/dist/commands/improve/memory/derived-ref.js +45 -43
- package/dist/commands/improve/memory/memory-belief.js +1 -1
- package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
- package/dist/commands/improve/memory/memory-improve.js +6 -5
- package/dist/commands/improve/outcome-loop.js +22 -65
- package/dist/commands/improve/preparation.js +114 -123
- package/dist/commands/improve/proactive-maintenance.js +2 -5
- package/dist/commands/improve/reflect.js +56 -160
- package/dist/commands/improve/salience.js +11 -122
- package/dist/commands/improve/source-identity.js +10 -38
- package/dist/commands/lint/base-linter.js +20 -124
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +249 -43
- package/dist/commands/{events.js → log.js} +33 -38
- package/dist/commands/migrate-cli.js +92 -12
- package/dist/commands/migration-tool.js +46 -0
- package/dist/commands/observability-cli.js +70 -209
- package/dist/commands/proposal/drain.js +101 -29
- package/dist/commands/proposal/proposal-cli.js +76 -48
- package/dist/commands/proposal/proposal.js +54 -18
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +23 -15
- package/dist/commands/proposal/repository.js +701 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
- package/dist/commands/proposal/validators/proposal-validators.js +55 -7
- package/dist/commands/proposal/validators/proposals.js +4 -7
- package/dist/commands/read/curate.js +34 -53
- package/dist/commands/read/knowledge.js +150 -95
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +42 -15
- package/dist/commands/read/search-cli.js +180 -78
- package/dist/commands/read/search.js +58 -43
- package/dist/commands/read/show.js +197 -141
- package/dist/commands/registry-cli.js +12 -51
- package/dist/commands/remember.js +14 -57
- package/dist/commands/sources/add-cli.js +100 -31
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +7 -2
- package/dist/commands/sources/info.js +18 -5
- package/dist/commands/sources/init.js +12 -12
- package/dist/commands/sources/installed-stashes.js +382 -98
- package/dist/commands/sources/schema-repair.js +3 -2
- package/dist/commands/sources/self-update.js +131 -38
- package/dist/commands/sources/source-add.js +72 -17
- package/dist/commands/sources/source-clone.js +129 -45
- package/dist/commands/sources/source-manage.js +43 -23
- package/dist/commands/sources/sources-cli.js +57 -208
- package/dist/commands/sources/stash-cli.js +46 -53
- package/dist/commands/tasks/tasks-cli.js +91 -97
- package/dist/commands/tasks/tasks.js +276 -421
- package/dist/commands/workflow-cli.js +175 -450
- package/dist/core/adapter/adapters/akm-adapter.js +47 -28
- package/dist/core/adapter/adapters/akm-lint.js +42 -27
- package/dist/core/adapter/adapters/akm-metadata.js +15 -44
- package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
- package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
- package/dist/core/adapter/adapters/index.js +6 -6
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
- package/dist/core/adapter/adapters/okf-adapter.js +187 -19
- package/dist/core/adapter/adapters/shared.js +3 -19
- package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +6 -4
- package/dist/core/adapter/validate-context.js +214 -0
- package/dist/core/asset/akm-markdown.js +63 -0
- package/dist/core/asset/asset-placement.js +20 -6
- package/dist/core/asset/asset-ref.js +11 -9
- package/dist/core/asset/frontmatter-lint.js +30 -0
- package/dist/core/asset/frontmatter.js +37 -9
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +89 -18
- package/dist/core/asset/stash-meta.js +1 -1
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +152 -38
- package/dist/core/config/config-io.js +12 -1
- package/dist/core/config/config-schema.js +35 -8
- package/dist/core/config/config-sources.js +55 -11
- package/dist/core/config/config-walker.js +25 -9
- package/dist/core/config/config.js +9 -48
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +5 -1
- package/dist/core/config/schema/experimental.js +30 -0
- package/dist/core/config/schema/improve-processes.js +0 -6
- package/dist/core/config/schema/improve.js +21 -3
- package/dist/core/config/schema/index-config.js +8 -15
- package/dist/core/config/schema/output.js +4 -1
- package/dist/core/config/schema/setup.js +9 -18
- package/dist/core/config/schema/sources-bundles.js +49 -33
- package/dist/core/config/schema/workflow.js +3 -3
- package/dist/core/env-secret-ref.js +76 -46
- package/dist/core/errors.js +18 -12
- package/dist/core/events.js +46 -128
- package/dist/core/file-change.js +6 -5
- package/dist/core/fs-txn.js +83 -7
- package/dist/core/git-message.js +2 -2
- package/dist/core/improve-result.js +1 -100
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +2 -1
- package/dist/core/migration-operation.js +16 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/parse.js +4 -1
- package/dist/core/paths.js +17 -20
- package/dist/core/recognition-util.js +12 -14
- package/dist/core/redaction.js +34 -0
- package/dist/core/standards/resolve-standards-context.js +2 -14
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +2 -2
- package/dist/core/state/migrations.js +41 -18
- package/dist/core/state-db.js +5 -14
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +6 -4
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/type-presentation.js +3 -3
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +771 -95
- package/dist/indexer/bundle-identity-guard.js +3 -2
- package/dist/indexer/db/graph-db.js +0 -24
- package/dist/indexer/ensure-index.js +1 -0
- package/dist/indexer/graph/graph-boost.js +9 -34
- package/dist/indexer/graph/graph-extraction.js +8 -5
- package/dist/indexer/index-writer-lock.js +53 -17
- package/dist/indexer/index-written-assets.js +16 -22
- package/dist/indexer/indexer.js +497 -239
- package/dist/indexer/installations.js +14 -96
- package/dist/indexer/passes/dir-staleness.js +16 -9
- package/dist/indexer/passes/memory-inference.js +11 -9
- package/dist/indexer/passes/metadata.js +113 -47
- package/dist/indexer/scan/doc-to-entry.js +38 -1
- package/dist/indexer/scan/drain-dir.js +13 -23
- package/dist/indexer/search/db-search.js +99 -54
- package/dist/indexer/search/fts-query.js +47 -24
- package/dist/indexer/search/ranking-contributors.js +42 -20
- package/dist/indexer/search/ranking.js +18 -99
- package/dist/indexer/search/search-fields.js +7 -2
- package/dist/indexer/search/search-source.js +82 -93
- package/dist/indexer/usage/usage-events.js +0 -89
- package/dist/indexer/walk/file-context.js +2 -1
- package/dist/indexer/walk/matchers.js +30 -43
- package/dist/indexer/walk/path-resolver.js +7 -2
- package/dist/indexer/walk/walker.js +38 -12
- package/dist/integrations/agent/builders.js +0 -6
- package/dist/integrations/agent/config.js +2 -2
- package/dist/integrations/agent/detect.js +49 -19
- package/dist/integrations/agent/engine-fallback.js +76 -0
- package/dist/integrations/agent/profiles.js +14 -0
- package/dist/integrations/agent/prompts.js +12 -8
- package/dist/integrations/agent/runner-dispatch.js +4 -2
- package/dist/integrations/agent/runner.js +0 -1
- package/dist/integrations/agent/spawn.js +5 -6
- package/dist/integrations/github.js +1 -1
- package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
- package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
- package/dist/integrations/harnesses/claude/session-log.js +0 -10
- package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
- package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
- package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
- package/dist/integrations/harnesses/index.js +3 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
- package/dist/integrations/harnesses/opencode/session-log.js +0 -15
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
- package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
- package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
- package/dist/integrations/lockfile.js +101 -6
- package/dist/integrations/session-logs/index.js +3 -28
- package/dist/llm/client.js +136 -100
- package/dist/llm/embedders/remote.js +13 -5
- package/dist/llm/feature-gate.js +4 -12
- package/dist/llm/graph-extract.js +5 -11
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +5 -7
- package/dist/llm/structured-call.js +1 -1
- package/dist/llm/usage-persist.js +26 -5
- package/dist/llm/usage-telemetry.js +25 -2
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +259 -0
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -36
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +56 -83
- package/dist/output/shapes/migrate.js +8 -0
- package/dist/output/shapes/passthrough.js +7 -41
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes.js +2 -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 +164 -96
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/health-format.js +103 -0
- package/dist/output/text/health.js +7 -0
- package/dist/output/text/helpers.js +10 -8
- package/dist/output/text/lint-format.js +43 -0
- package/dist/output/text/{save.js → lint.js} +2 -2
- package/dist/output/text/migrate.js +88 -0
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +44 -72
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/show-directives.js +15 -7
- package/dist/output/text/status-list.js +32 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +24 -203
- package/dist/output/text/workflow.js +1 -7
- package/dist/output/text.js +16 -17
- 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 +13 -23
- package/dist/registry/resolve.js +42 -7
- package/dist/registry/semver.js +34 -84
- package/dist/runtime.js +2 -23
- package/dist/scripts/akm-migrate-node.js +60290 -0
- package/dist/scripts/akm-migrate.js +59628 -0
- package/dist/setup/detect.js +42 -15
- package/dist/setup/registry-stash-loader.js +2 -2
- package/dist/setup/setup.js +236 -136
- package/dist/setup/steps/connection.js +7 -9
- package/dist/setup/steps/platforms.js +9 -9
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +12 -13
- package/dist/setup/steps/stashdir.js +2 -3
- package/dist/setup/steps/tasks.js +237 -120
- package/dist/sources/freshness.js +1 -1
- 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 +25 -23
- package/dist/sources/providers/git-stash.js +395 -106
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +7 -4
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +6 -1
- package/dist/sources/resolve.js +6 -5
- package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
- package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
- package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
- package/dist/sources/snapshot-fetchers/github.js +100 -0
- package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
- package/dist/sources/snapshot-fetchers/registry.js +17 -1
- package/dist/sources/snapshot-fetchers/robots.js +348 -0
- package/dist/sources/snapshot-fetchers/rss.js +282 -0
- package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
- package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
- package/dist/sources/snapshot-fetchers/x.js +910 -0
- package/dist/storage/database.js +7 -0
- package/dist/storage/engines/sqlite-migrations.js +23 -111
- package/dist/storage/managed-db.js +2 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +27 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -12
- package/dist/storage/repositories/index-connection.js +17 -6
- package/dist/storage/repositories/index-entries-repository.js +151 -240
- package/dist/storage/repositories/index-entry-mapper.js +15 -11
- package/dist/storage/repositories/index-fts-repository.js +5 -2
- package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
- package/dist/storage/repositories/index-meta-repository.js +2 -3
- package/dist/storage/repositories/index-schema.js +10 -25
- package/dist/storage/repositories/index-utility-repository.js +15 -28
- package/dist/storage/repositories/index-vec-repository.js +6 -1
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +296 -59
- package/dist/storage/repositories/registry-cache.js +19 -0
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +15 -13
- package/dist/storage/repositories/workflow-runs-repository.js +52 -40
- package/dist/tasks/backends/cron.js +105 -15
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +85 -38
- package/dist/tasks/backends/schtasks.js +135 -15
- package/dist/tasks/embedded.js +56 -40
- package/dist/tasks/parser.js +7 -157
- package/dist/tasks/resolve-akm-bin.js +137 -59
- package/dist/tasks/runner.js +79 -42
- package/dist/tasks/scheduler-invocation.js +220 -10
- package/dist/tasks/schema.js +24 -1
- package/dist/tasks/task-id.js +1 -3
- package/dist/tasks/validator.js +20 -6
- package/dist/workflows/authoring/authoring.js +94 -143
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/exec/frozen-judge.js +28 -2
- package/dist/workflows/exec/native-executor.js +77 -57
- package/dist/workflows/exec/param-secrets.js +9 -9
- package/dist/workflows/exec/run-workflow.js +133 -79
- package/dist/workflows/exec/step-work.js +219 -346
- package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
- package/dist/workflows/ir/compile.js +141 -270
- package/dist/workflows/ir/freeze.js +40 -30
- package/dist/workflows/ir/params.js +135 -11
- package/dist/workflows/ir/plan-hash.js +1 -1
- package/dist/workflows/ir/schema.js +25 -26
- package/dist/workflows/parser.js +872 -307
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +7 -10
- package/dist/workflows/renderer.js +95 -68
- package/dist/workflows/resource-limits.js +2 -0
- package/dist/workflows/runtime/checkin.js +3 -3
- package/dist/workflows/runtime/plan-classifier.js +16 -75
- package/dist/workflows/runtime/runs.js +186 -127
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +2 -2
- package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +30 -36
- package/dist/workflows/validator.js +21 -62
- package/docs/README.md +68 -0
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.7.0.md +11 -11
- package/docs/migration/release-notes/0.9.0.md +208 -27
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +564 -208
- package/docs/migration/v0.9.0-troubleshooting.md +561 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/cli.md +2253 -0
- package/docs/reference/configuration.md +358 -0
- package/docs/reference/data-and-telemetry.md +105 -42
- package/docs/reference/workflows.md +647 -0
- package/package.json +22 -11
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +81 -128
- package/schemas/akm-workflow.json +74 -73
- package/dist/assets/tasks/core/backup.yml +0 -5
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/cli/config-migrate.js +0 -1806
- package/dist/cli/config-validate.js +0 -41
- package/dist/commands/backup-cli.js +0 -56
- package/dist/commands/bundle/bundle-cli.js +0 -68
- package/dist/commands/bundle/bundle.js +0 -219
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -489
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/mv-cli.js +0 -1221
- package/dist/commands/sources/history.js +0 -201
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/core/migration-backup.js +0 -1234
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/migrate/legacy/config-source-migration.js +0 -223
- package/dist/migrate/legacy/content-migration.js +0 -305
- package/dist/migrate/legacy/legacy-layout.js +0 -779
- package/dist/migrate/legacy/legacy-paths.js +0 -25
- package/dist/migrate/legacy/legacy-stash-json.js +0 -72
- package/dist/migrate/legacy/proposal-fs-import.js +0 -168
- package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
- package/dist/migrate/legacy/three-db-cutover.js +0 -841
- package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
- package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
- package/dist/migrate/legacy-ref-grammar.js +0 -214
- 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/registry/build-index.js +0 -382
- package/dist/schemas/akm-config.json +0 -4704
- package/dist/schemas/akm-task.json +0 -87
- package/dist/schemas/akm-workflow.json +0 -372
- package/dist/scripts/migrate-storage.js +0 -3816
- package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/cli.js +0 -53
- package/dist/workflows/exec/brief.js +0 -481
- package/dist/workflows/exec/report.js +0 -1460
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -813
- package/dist/workflows/program/project.js +0 -104
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* `index.db` entries repository — CRUD, lookup, re-key, delete-cascade, and the
|
|
6
6
|
* usage-event relink + workflow-document + tag-set reads that key on `entries`.
|
|
7
7
|
*
|
|
8
|
-
* Owns ALL raw SQL against the `entries` table
|
|
9
|
-
*
|
|
10
|
-
* leaf types + mapper modules rather than from the old `db.ts` hub.
|
|
8
|
+
* Owns ALL raw SQL against the `entries` table. The shared row/option shapes
|
|
9
|
+
* come from the leaf types + mapper modules.
|
|
11
10
|
*/
|
|
12
11
|
import fs from "node:fs";
|
|
13
12
|
import path from "node:path";
|
|
@@ -19,7 +18,7 @@ import { warn } from "../../core/warn.js";
|
|
|
19
18
|
import { buildSearchText } from "../../indexer/search/search-fields.js";
|
|
20
19
|
import { ENTRY_COLUMNS, rowToIndexedEntry } from "./index-entry-mapper.js";
|
|
21
20
|
import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
|
|
22
|
-
import { isVecAvailable } from "./index-vec-repository.js";
|
|
21
|
+
import { deleteEntryVectors, isVecAvailable } from "./index-vec-repository.js";
|
|
23
22
|
// ── Entry operations ────────────────────────────────────────────────────────
|
|
24
23
|
/**
|
|
25
24
|
* Insert or update an entry in the `entries` table. Returns the row id.
|
|
@@ -29,11 +28,12 @@ import { isVecAvailable } from "./index-vec-repository.js";
|
|
|
29
28
|
* reflect the changes.
|
|
30
29
|
*/
|
|
31
30
|
export function upsertEntry(db, entryKey, dirPath, filePath, stashDir, entry, searchText, provenance, contentHash) {
|
|
32
|
-
// Hot path during indexing — cache
|
|
33
|
-
//
|
|
34
|
-
// every call. The dirty-mark INSERT and the upsert-with-RETURNING
|
|
35
|
-
// share the same WeakMap so they live and die with the connection.
|
|
31
|
+
// Hot path during indexing — cache prepared statements per database
|
|
32
|
+
// connection so we don't pay the SQL parse/compile cost on every call.
|
|
36
33
|
const stmts = getUpsertStmts(db);
|
|
34
|
+
const previous = (provenance?.itemRef
|
|
35
|
+
? stmts.findByItemRef.get(provenance.itemRef)
|
|
36
|
+
: undefined) ?? stmts.findByEntryKey.get(entryKey);
|
|
37
37
|
// Phase 5A / Advantage D5: surface derived memory parent ref into the
|
|
38
38
|
// dedicated `derived_from` column so retrieval-time lookup (parent→child)
|
|
39
39
|
// does not have to scan + JSON-decode every memory row.
|
|
@@ -45,22 +45,29 @@ export function upsertEntry(db, entryKey, dirPath, filePath, stashDir, entry, se
|
|
|
45
45
|
// by the next full index). `content_hash` (F4a M-core-2) is `doc.hash` from
|
|
46
46
|
// the diff-persist writer; a NULL passed here PRESERVES any existing hash (the
|
|
47
47
|
// ON CONFLICT COALESCE below) so the LLM-enrichment re-upsert cannot wipe it.
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
const apply = () => {
|
|
49
|
+
const result = stmts.upsert.get(entryKey, dirPath, filePath, stashDir, JSON.stringify(entry), searchText, entry.type, derivedFrom, provenance?.itemRef ?? null, provenance?.bundleId ?? null, provenance?.componentId ?? null, provenance?.conceptId ?? null, provenance?.adapterId ?? null, entry.type, contentHash ?? null);
|
|
50
|
+
if (!result)
|
|
51
|
+
throw new Error("upsertEntry: entry_key not found after upsert");
|
|
52
|
+
if (previous?.id === result.id && previous.search_text !== searchText)
|
|
53
|
+
deleteEntryVectors(db, result.id);
|
|
54
|
+
// Mark this entry as FTS-dirty so `rebuildFts({ incremental: true })`
|
|
55
|
+
// only revisits entries that actually changed. INSERT OR IGNORE is
|
|
56
|
+
// idempotent across multiple upserts of the same row.
|
|
57
|
+
stmts.markDirty.run(result.id);
|
|
58
|
+
return result.id;
|
|
59
|
+
};
|
|
60
|
+
return previous && previous.search_text !== searchText ? db.transaction(apply)() : apply();
|
|
56
61
|
}
|
|
57
62
|
const upsertStmtsByDb = new WeakMap();
|
|
58
63
|
// The ON CONFLICT DO UPDATE column assignments — factored out so the two
|
|
59
|
-
// conflict targets below stay byte-identical. `
|
|
60
|
-
//
|
|
64
|
+
// conflict targets below stay byte-identical. `item_ref` is durable identity;
|
|
65
|
+
// update `entry_key` when adapter ownership changes its
|
|
66
|
+
// spelling so directory pruning retains the upserted row.
|
|
61
67
|
// `content_hash` COALESCEs so a NULL passed by the LLM-enhance re-upsert cannot
|
|
62
68
|
// wipe a previously-persisted hash.
|
|
63
69
|
const UPSERT_SET_CLAUSE = `SET
|
|
70
|
+
entry_key = excluded.entry_key,
|
|
64
71
|
dir_path = excluded.dir_path,
|
|
65
72
|
file_path = excluded.file_path,
|
|
66
73
|
stash_dir = excluded.stash_dir,
|
|
@@ -75,53 +82,14 @@ const UPSERT_SET_CLAUSE = `SET
|
|
|
75
82
|
adapter_id = excluded.adapter_id,
|
|
76
83
|
type = excluded.type,
|
|
77
84
|
content_hash = COALESCE(excluded.content_hash, content_hash)`;
|
|
78
|
-
/**
|
|
79
|
-
* Whether `entries.item_ref` currently carries its UNIQUE index (the v19
|
|
80
|
-
* `idx_entries_item_ref`, built by `ensureUniqueItemRefIndex`). On a
|
|
81
|
-
* partially-migrated DB with a duplicate non-NULL item_ref, that build falls
|
|
82
|
-
* back to a NON-unique index — and `ON CONFLICT(item_ref)` is only a legal
|
|
83
|
-
* conflict target when a UNIQUE index/constraint backs the column (otherwise
|
|
84
|
-
* the upsert throws AT PREPARE TIME). This gate lets {@link getUpsertStmts}
|
|
85
|
-
* degrade to the always-present `entry_key` target until a rebuild restores
|
|
86
|
-
* uniqueness. Best-effort: any probe failure treats item_ref as non-unique.
|
|
87
|
-
*/
|
|
88
|
-
function itemRefHasUniqueIndex(db) {
|
|
89
|
-
try {
|
|
90
|
-
const indexes = db.prepare("PRAGMA index_list(entries)").all();
|
|
91
|
-
return indexes.some((idx) => idx.name === "idx_entries_item_ref" && Number(idx.unique) === 1);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
85
|
function getUpsertStmts(db) {
|
|
98
86
|
const existing = upsertStmtsByDb.get(db);
|
|
99
87
|
if (existing)
|
|
100
88
|
return existing;
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// EXISTING row with a NULL `item_ref` (SQLite treats NULLs as distinct in a
|
|
106
|
-
// UNIQUE index, so an item_ref-only target would miss the row, fall through
|
|
107
|
-
// to a plain INSERT, and ABORT on the `entry_key NOT NULL UNIQUE` constraint).
|
|
108
|
-
// Concretely, the out-of-scope LLM metadata-enhance re-upsert (indexer.ts
|
|
109
|
-
// `enhanceDirsWithLlm`) re-writes already-indexed rows without provenance →
|
|
110
|
-
// NULL item_ref; the `entry_key` arm keeps that re-upsert an UPDATE, not a
|
|
111
|
-
// crash. Both arms run the IDENTICAL assignment block, so the outcome is the
|
|
112
|
-
// same regardless of which constraint matches. The `entry_key` fallback is
|
|
113
|
-
// deletable once every write path sets `item_ref`.
|
|
114
|
-
//
|
|
115
|
-
// When item_ref lacks its UNIQUE index (the ensureUniqueItemRefIndex fallback
|
|
116
|
-
// on a partially-migrated DB), `ON CONFLICT(item_ref)` is not a legal target
|
|
117
|
-
// and would fail at prepare time — so we degrade to the entry_key-only upsert
|
|
118
|
-
// (behaviour-identical to the pre-repoint key) until the next rebuild restores
|
|
119
|
-
// uniqueness; without this the very rebuild meant to heal the duplicate could
|
|
120
|
-
// not run.
|
|
121
|
-
const conflictClause = itemRefHasUniqueIndex(db)
|
|
122
|
-
? `ON CONFLICT(item_ref) DO UPDATE ${UPSERT_SET_CLAUSE}
|
|
123
|
-
ON CONFLICT(entry_key) DO UPDATE ${UPSERT_SET_CLAUSE}`
|
|
124
|
-
: `ON CONFLICT(entry_key) DO UPDATE ${UPSERT_SET_CLAUSE}`;
|
|
89
|
+
// Durable identity is the primary conflict target. `entry_key` remains the
|
|
90
|
+
// internal conflict key for low-level entries without provenance.
|
|
91
|
+
const conflictClause = `ON CONFLICT(item_ref) DO UPDATE ${UPSERT_SET_CLAUSE}
|
|
92
|
+
ON CONFLICT(entry_key) DO UPDATE ${UPSERT_SET_CLAUSE}`;
|
|
125
93
|
const stmts = {
|
|
126
94
|
// RETURNING id handles ON CONFLICT DO UPDATE correctly — no second
|
|
127
95
|
// SELECT round-trip needed (last_insert_rowid() is unreliable for
|
|
@@ -136,6 +104,8 @@ function getUpsertStmts(db) {
|
|
|
136
104
|
RETURNING id
|
|
137
105
|
`),
|
|
138
106
|
markDirty: db.prepare("INSERT OR IGNORE INTO entries_fts_dirty (entry_id) VALUES (?)"),
|
|
107
|
+
findByItemRef: db.prepare("SELECT id, search_text FROM entries WHERE item_ref = ? ORDER BY id ASC LIMIT 1"),
|
|
108
|
+
findByEntryKey: db.prepare("SELECT id, search_text FROM entries WHERE entry_key = ? LIMIT 1"),
|
|
139
109
|
};
|
|
140
110
|
upsertStmtsByDb.set(db, stmts);
|
|
141
111
|
return stmts;
|
|
@@ -153,25 +123,18 @@ function getUpsertStmts(db) {
|
|
|
153
123
|
export function getDerivedForParent(db, parentRef, stashDir) {
|
|
154
124
|
if (!parentRef)
|
|
155
125
|
return null;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
.prepare(`SELECT ${ENTRY_COLUMNS}
|
|
126
|
+
const sourceScope = stashDir ? "AND stash_dir = ?" : "";
|
|
127
|
+
const row = db
|
|
128
|
+
.prepare(`SELECT ${ENTRY_COLUMNS}
|
|
160
129
|
FROM entries
|
|
161
130
|
WHERE derived_from = ?
|
|
162
131
|
${sourceScope}
|
|
163
132
|
ORDER BY id DESC
|
|
164
133
|
LIMIT 1`)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return null;
|
|
168
|
-
return rowToIndexedEntry(row, "getDerivedForParent");
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
/* `derived_from` column may not exist on legacy DBs that haven't been
|
|
172
|
-
rebuilt; treat as "no derived child". */
|
|
134
|
+
.get(parentRef, ...(stashDir ? [stashDir] : []));
|
|
135
|
+
if (!row)
|
|
173
136
|
return null;
|
|
174
|
-
|
|
137
|
+
return rowToIndexedEntry(row, "getDerivedForParent");
|
|
175
138
|
}
|
|
176
139
|
/**
|
|
177
140
|
* 03-R3: for the given derived-twin row ids, fetch each twin's BASE memory
|
|
@@ -198,33 +161,21 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
|
|
|
198
161
|
const chunk = twinIds.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
199
162
|
const placeholders = chunk.map(() => "?").join(",");
|
|
200
163
|
bestEffort(() => {
|
|
201
|
-
// Chunk-5 flip F1: a twin's identity suffix is `.derived` on BOTH the
|
|
202
|
-
// legacy `entry_key` (`<stash>:memory:<name>.derived`) and the new
|
|
203
|
-
// `item_ref` (`<bundle>//memories/<name>.derived`). Join the base by
|
|
204
|
-
// stripping that suffix on EITHER column so both fully-migrated rows
|
|
205
|
-
// (item_ref path) and NULL-`item_ref` rows (legacy path) inherit. On a
|
|
206
|
-
// consistent index both predicates select the SAME base row, so the OR
|
|
207
|
-
// never double-counts; `substr(NULL, …)` is NULL and matches nothing, so
|
|
208
|
-
// a NULL `item_ref` simply falls through to the entry_key predicate.
|
|
209
|
-
// F5: the `entry_key` arm is the deletable legacy shim.
|
|
210
164
|
const rows = db
|
|
211
165
|
.prepare(`SELECT twin.id AS twin_id, json_extract(base.entry_json, '$.beliefState') AS belief
|
|
212
166
|
FROM entries twin
|
|
213
167
|
JOIN entries base
|
|
214
168
|
ON base.entry_type = 'memory'
|
|
215
|
-
|
|
216
|
-
base.entry_key = substr(twin.entry_key, 1, length(twin.entry_key) - length('.derived'))
|
|
217
|
-
OR base.item_ref = substr(twin.item_ref, 1, length(twin.item_ref) - length('.derived'))
|
|
218
|
-
)
|
|
169
|
+
AND base.item_ref = substr(twin.item_ref, 1, length(twin.item_ref) - length('.derived'))
|
|
219
170
|
WHERE twin.id IN (${placeholders})
|
|
220
|
-
AND
|
|
171
|
+
AND twin.item_ref LIKE '%.derived'
|
|
221
172
|
AND json_extract(base.entry_json, '$.beliefState') IS NOT NULL`)
|
|
222
173
|
.all(...chunk);
|
|
223
174
|
for (const r of rows) {
|
|
224
175
|
if (typeof r.belief === "string" && r.belief.trim().length > 0)
|
|
225
176
|
out.set(r.twin_id, r.belief.trim());
|
|
226
177
|
}
|
|
227
|
-
}, "
|
|
178
|
+
}, "belief-state inheritance is best-effort");
|
|
228
179
|
}
|
|
229
180
|
return out;
|
|
230
181
|
}
|
|
@@ -242,10 +193,8 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
|
|
|
242
193
|
* name; the row is marked FTS-dirty for the caller's
|
|
243
194
|
* `rebuildFts({incremental: true})`.
|
|
244
195
|
*
|
|
245
|
-
* `usage_events.entry_ref` rows for the old
|
|
246
|
-
*
|
|
247
|
-
* origin-qualified `origin//type:name` spelling (search/show writers persist
|
|
248
|
-
* either, see {@link getRetrievalCounts}). Without this, events keep the old
|
|
196
|
+
* Bundle-qualified `usage_events.entry_ref` rows for the old conceptId are
|
|
197
|
+
* rewritten to the new item ref. Without this, events keep the old
|
|
249
198
|
* ref, `relinkUsageEvents` finds no matching entry after the next full
|
|
250
199
|
* rebuild, and the utility history the re-key exists to preserve silently
|
|
251
200
|
* resets. DETACHED orphan events already sitting at the new ref (entry_id
|
|
@@ -266,14 +215,13 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
|
|
|
266
215
|
* full `akm index` picks the file up as a fresh entry).
|
|
267
216
|
*/
|
|
268
217
|
export function rekeyEntryInPlace(db, opts) {
|
|
269
|
-
|
|
270
|
-
// always has it and it is the UNIQUE identity column until the re-key.
|
|
218
|
+
const oldItemRef = `${opts.sourceName}//${opts.oldRef}`;
|
|
271
219
|
const row = db
|
|
272
|
-
.prepare("SELECT id, stash_dir, entry_json, search_text, entry_type
|
|
273
|
-
.get(
|
|
220
|
+
.prepare("SELECT id, stash_dir, entry_json, search_text, entry_type FROM entries WHERE item_ref = ?")
|
|
221
|
+
.get(oldItemRef);
|
|
274
222
|
if (!row)
|
|
275
223
|
return null;
|
|
276
|
-
if (
|
|
224
|
+
if (path.resolve(row.stash_dir) !== path.resolve(opts.sourceRoot)) {
|
|
277
225
|
throw new Error(`Refusing to re-key entry ${opts.oldEntryKey}: source root does not match.`);
|
|
278
226
|
}
|
|
279
227
|
// Patch the JSON payload. On corrupt entry_json still re-key key + paths so
|
|
@@ -293,19 +241,11 @@ export function rekeyEntryInPlace(db, opts) {
|
|
|
293
241
|
catch {
|
|
294
242
|
/* corrupt entry_json — key/path-only re-key */
|
|
295
243
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// crosses bundles); a NULL item_ref (write-back row) stays NULL and is healed
|
|
300
|
-
// on the next full index, exactly like the legacy columns before the flip.
|
|
301
|
-
let newItemRef = null;
|
|
302
|
-
let newConceptId = null;
|
|
303
|
-
if (row.item_ref) {
|
|
304
|
-
const boundary = row.item_ref.indexOf("//");
|
|
305
|
-
const bundle = boundary >= 0 ? row.item_ref.slice(0, boundary) : undefined;
|
|
306
|
-
newConceptId = conceptIdFromTypeName(row.entry_type, opts.newName);
|
|
307
|
-
newItemRef = bundle !== undefined ? `${bundle}//${newConceptId}` : newConceptId;
|
|
244
|
+
const expectedNewRef = conceptIdFromTypeName(row.entry_type, opts.newName);
|
|
245
|
+
if (opts.newRef !== expectedNewRef) {
|
|
246
|
+
throw new Error(`Refusing to re-key entry ${opts.oldEntryKey}: target ref does not match the entry type and name.`);
|
|
308
247
|
}
|
|
248
|
+
const newItemRef = `${opts.sourceName}//${opts.newRef}`;
|
|
309
249
|
db.transaction(() => {
|
|
310
250
|
const stale = db.prepare("SELECT id FROM entries WHERE entry_key = ?").get(opts.newEntryKey);
|
|
311
251
|
if (stale && stale.id !== row.id) {
|
|
@@ -317,13 +257,10 @@ export function rekeyEntryInPlace(db, opts) {
|
|
|
317
257
|
deleteRelatedRows(db, [{ id: stale.id }]);
|
|
318
258
|
db.prepare("DELETE FROM entries WHERE id = ?").run(stale.id);
|
|
319
259
|
}
|
|
320
|
-
db.prepare("UPDATE entries SET entry_key = ?, dir_path = ?, file_path = ?, entry_json = ?, search_text = ? WHERE id = ?").run(opts.newEntryKey, path.dirname(opts.newFilePath), opts.newFilePath, entryJson, searchText, row.id);
|
|
260
|
+
db.prepare("UPDATE entries SET entry_key = ?, dir_path = ?, file_path = ?, entry_json = ?, search_text = ?, item_ref = ?, concept_id = ? WHERE id = ?").run(opts.newEntryKey, path.dirname(opts.newFilePath), opts.newFilePath, entryJson, searchText, newItemRef, opts.newRef, row.id);
|
|
321
261
|
if (opts.newDerivedFrom !== undefined) {
|
|
322
262
|
db.prepare("UPDATE entries SET derived_from = ? WHERE id = ?").run(opts.newDerivedFrom, row.id);
|
|
323
263
|
}
|
|
324
|
-
if (newItemRef !== null) {
|
|
325
|
-
db.prepare("UPDATE entries SET item_ref = ?, concept_id = ? WHERE id = ?").run(newItemRef, newConceptId, row.id);
|
|
326
|
-
}
|
|
327
264
|
db.prepare("INSERT OR IGNORE INTO entries_fts_dirty (entry_id) VALUES (?)").run(row.id);
|
|
328
265
|
})();
|
|
329
266
|
// Re-point usage history at the new ref. Chunk-8 WI-8.3: usage_events lives in
|
|
@@ -336,28 +273,15 @@ export function rekeyEntryInPlace(db, opts) {
|
|
|
336
273
|
}
|
|
337
274
|
/**
|
|
338
275
|
* Rewrite `usage_events.entry_ref` from `opts.oldRef` to `opts.newRef` in
|
|
339
|
-
* state.db (
|
|
340
|
-
* spellings). DETACHED orphan events (entry_id NULL) already sitting AT the new
|
|
276
|
+
* state.db. DETACHED orphan events (entry_id NULL) already sitting AT the new
|
|
341
277
|
* ref are evicted first so the moved asset never adopts a deleted stranger's
|
|
342
|
-
* history (live-asset-wins). Best-effort + guarded on state.db's existence
|
|
343
|
-
* legacy state.db predating usage_events is tolerated.
|
|
278
|
+
* history (live-asset-wins). Best-effort + guarded on state.db's existence.
|
|
344
279
|
*/
|
|
345
|
-
/** The conceptId for a legacy `type:name` ref (`memory:foo` → `memories/foo`), or undefined. */
|
|
346
|
-
function conceptIdFromTypeNameRef(ref) {
|
|
347
|
-
const colon = ref.indexOf(":");
|
|
348
|
-
if (colon <= 0)
|
|
349
|
-
return undefined;
|
|
350
|
-
return conceptIdFromTypeName(ref.slice(0, colon), ref.slice(colon + 1));
|
|
351
|
-
}
|
|
352
280
|
function rewriteUsageEventRefForMove(opts) {
|
|
353
281
|
if (!fs.existsSync(getStateDbPath()))
|
|
354
282
|
return;
|
|
355
|
-
//
|
|
356
|
-
// (`<bundle>//<conceptId>`)
|
|
357
|
-
// only on rows written before the cutover. Rewrite BOTH: `opts.oldRef`/`newRef`
|
|
358
|
-
// are the legacy `type:name` forms, so the conceptId siblings are derived here.
|
|
359
|
-
const oldConcept = conceptIdFromTypeNameRef(opts.oldRef);
|
|
360
|
-
const newConcept = conceptIdFromTypeNameRef(opts.newRef);
|
|
283
|
+
// `usage_events.entry_ref` is the fully-qualified item_ref
|
|
284
|
+
// (`<bundle>//<conceptId>`).
|
|
361
285
|
const rename = (stateDb, oldR, newR) => {
|
|
362
286
|
stateDb.prepare("DELETE FROM usage_events WHERE entry_id IS NULL AND entry_ref = ?").run(newR);
|
|
363
287
|
stateDb.prepare("UPDATE usage_events SET entry_ref = ? WHERE entry_ref = ?").run(newR, oldR);
|
|
@@ -365,33 +289,13 @@ function rewriteUsageEventRefForMove(opts) {
|
|
|
365
289
|
try {
|
|
366
290
|
withStateDb((stateDb) => {
|
|
367
291
|
stateDb.transaction(() => {
|
|
368
|
-
|
|
369
|
-
const origins = new Set([opts.sourceName]);
|
|
370
|
-
if (opts.sourceName === "stash" && opts.includeLegacyBare)
|
|
371
|
-
origins.add("local");
|
|
372
|
-
for (const origin of origins) {
|
|
373
|
-
rename(stateDb, `${origin}//${opts.oldRef}`, `${origin}//${opts.newRef}`);
|
|
374
|
-
// The item_ref spelling (`<bundle>//<conceptId>`) — the durable
|
|
375
|
-
// post-cutover key. Origins double as the bundle id for the stash.
|
|
376
|
-
if (oldConcept !== undefined && newConcept !== undefined)
|
|
377
|
-
rename(stateDb, `${origin}//${oldConcept}`, `${origin}//${newConcept}`);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
if (opts.includeLegacyBare || !opts.sourceName) {
|
|
381
|
-
rename(stateDb, opts.oldRef, opts.newRef);
|
|
382
|
-
if (oldConcept !== undefined && newConcept !== undefined)
|
|
383
|
-
rename(stateDb, oldConcept, newConcept);
|
|
384
|
-
}
|
|
292
|
+
rename(stateDb, `${opts.sourceName}//${opts.oldRef}`, `${opts.sourceName}//${opts.newRef}`);
|
|
385
293
|
})();
|
|
386
294
|
});
|
|
387
295
|
}
|
|
388
296
|
catch (error) {
|
|
389
297
|
const message = error instanceof Error ? error.message : String(error);
|
|
390
|
-
|
|
391
|
-
/no such column:\s*(?:usage_events\.)?(?:entry_id|entry_ref)\b/i.test(message) ||
|
|
392
|
-
/table\s+usage_events\s+has no column named\s+(?:entry_id|entry_ref)\b/i.test(message);
|
|
393
|
-
if (!missingLegacyUsageSchema)
|
|
394
|
-
throw error;
|
|
298
|
+
throw new Error(`Failed to rewrite usage events for move: ${message}`, { cause: error });
|
|
395
299
|
}
|
|
396
300
|
}
|
|
397
301
|
/**
|
|
@@ -435,7 +339,7 @@ export function getPositiveFeedbackCountsByIds(ids) {
|
|
|
435
339
|
}
|
|
436
340
|
}
|
|
437
341
|
});
|
|
438
|
-
}, "
|
|
342
|
+
}, "positive feedback counts are best-effort");
|
|
439
343
|
return result;
|
|
440
344
|
}
|
|
441
345
|
function deleteEntriesWhere(db, column, value) {
|
|
@@ -448,6 +352,16 @@ function deleteEntriesWhere(db, column, value) {
|
|
|
448
352
|
export function deleteEntriesByDir(db, dirPath) {
|
|
449
353
|
deleteEntriesWhere(db, "dir_path", dirPath);
|
|
450
354
|
}
|
|
355
|
+
export function deleteEntriesByDirAndStash(db, dirPath, stashDir, options = {}) {
|
|
356
|
+
return db.transaction(() => {
|
|
357
|
+
const ids = db
|
|
358
|
+
.prepare("SELECT id FROM entries WHERE dir_path = ? AND stash_dir = ?")
|
|
359
|
+
.all(dirPath, stashDir);
|
|
360
|
+
deleteRelatedRows(db, ids, options);
|
|
361
|
+
db.prepare("DELETE FROM entries WHERE dir_path = ? AND stash_dir = ?").run(dirPath, stashDir);
|
|
362
|
+
return ids.map((row) => row.id);
|
|
363
|
+
})();
|
|
364
|
+
}
|
|
451
365
|
/**
|
|
452
366
|
* Delete every entry (and its child rows) under a source's stash root.
|
|
453
367
|
*
|
|
@@ -471,28 +385,28 @@ export function deleteEntriesByStashDir(db, stashDir) {
|
|
|
471
385
|
* to prune only the DEPARTED rows. The net row-state of `dir_path` is identical
|
|
472
386
|
* to delete-then-reinsert; the win is that unchanged rows keep their id.
|
|
473
387
|
*
|
|
474
|
-
* Keyed on `entry_key`
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
* conflict target precisely. Dir-scoped (not stash-dir-scoped) so it is safe
|
|
478
|
-
* under the per-dir incremental freshness gate — untouched (skipped) sibling
|
|
479
|
-
* directories are never in scope.
|
|
388
|
+
* Keyed on `entry_key` because it mirrors the directory upsert key precisely.
|
|
389
|
+
* Both directory- and source-scoped so overlapping source roots cannot prune
|
|
390
|
+
* one another's rows.
|
|
480
391
|
*/
|
|
481
|
-
export function deleteEntriesByDirExceptKeys(db, dirPath, keepKeys) {
|
|
482
|
-
db.transaction(() => {
|
|
483
|
-
const rows = db
|
|
392
|
+
export function deleteEntriesByDirExceptKeys(db, dirPath, stashDir, keepKeys, options = {}) {
|
|
393
|
+
return db.transaction(() => {
|
|
394
|
+
const rows = db
|
|
395
|
+
.prepare("SELECT id, entry_key FROM entries WHERE dir_path = ? AND stash_dir = ?")
|
|
396
|
+
.all(dirPath, stashDir);
|
|
484
397
|
const doomed = rows.filter((r) => !keepKeys.has(r.entry_key));
|
|
485
398
|
if (doomed.length === 0)
|
|
486
|
-
return;
|
|
487
|
-
deleteRelatedRows(db, doomed);
|
|
399
|
+
return [];
|
|
400
|
+
deleteRelatedRows(db, doomed, options);
|
|
488
401
|
for (let i = 0; i < doomed.length; i += SQLITE_CHUNK_SIZE) {
|
|
489
402
|
const chunk = doomed.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
490
403
|
const placeholders = chunk.map(() => "?").join(",");
|
|
491
404
|
db.prepare(`DELETE FROM entries WHERE id IN (${placeholders})`).run(...chunk.map((r) => r.id));
|
|
492
405
|
}
|
|
406
|
+
return doomed.map((row) => row.id);
|
|
493
407
|
})();
|
|
494
408
|
}
|
|
495
|
-
function deleteRelatedRows(db, ids) {
|
|
409
|
+
function deleteRelatedRows(db, ids, options = {}) {
|
|
496
410
|
if (ids.length === 0)
|
|
497
411
|
return;
|
|
498
412
|
const numericIds = ids.map((r) => r.id);
|
|
@@ -518,22 +432,11 @@ function deleteRelatedRows(db, ids) {
|
|
|
518
432
|
bestEffort(() => db.prepare(`DELETE FROM utility_scores WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores for entries");
|
|
519
433
|
bestEffort(() => db.prepare(`DELETE FROM utility_scores_scoped WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores_scoped for entries");
|
|
520
434
|
}
|
|
521
|
-
//
|
|
522
|
-
//
|
|
523
|
-
//
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (fs.existsSync(getStateDbPath())) {
|
|
527
|
-
bestEffort(() => {
|
|
528
|
-
withStateDb((stateDb) => {
|
|
529
|
-
for (let i = 0; i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
|
|
530
|
-
const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
531
|
-
const placeholders = chunk.map(() => "?").join(",");
|
|
532
|
-
stateDb.prepare(`DELETE FROM usage_events WHERE entry_id IN (${placeholders})`).run(...chunk);
|
|
533
|
-
}
|
|
534
|
-
});
|
|
535
|
-
}, "delete usage_events (state.db) for entries");
|
|
536
|
-
}
|
|
435
|
+
// usage_events lives in state.db, outside this transaction. Index persistence
|
|
436
|
+
// disables this cleanup and runs it only after its index.db transaction
|
|
437
|
+
// commits; standalone delete callers retain the immediate behavior.
|
|
438
|
+
if (options.cleanupUsageEvents !== false)
|
|
439
|
+
deleteUsageEventsByEntryIds(numericIds);
|
|
537
440
|
// #624-P1: graph_files is NO LONGER keyed on entries.id, so deleting an
|
|
538
441
|
// entries row must NOT wipe the extracted graph (that is the whole point —
|
|
539
442
|
// the graph survives a reindex when body_hash is unchanged). We therefore do
|
|
@@ -567,6 +470,19 @@ function deleteRelatedRows(db, ids) {
|
|
|
567
470
|
.run(stashRoot, stashRoot, stashRoot, stashRoot), "sync graph_meta counts after entries delete");
|
|
568
471
|
}
|
|
569
472
|
}
|
|
473
|
+
export function deleteUsageEventsByEntryIds(entryIds) {
|
|
474
|
+
if (entryIds.length === 0 || !fs.existsSync(getStateDbPath()))
|
|
475
|
+
return;
|
|
476
|
+
bestEffort(() => {
|
|
477
|
+
withStateDb((stateDb) => {
|
|
478
|
+
for (let i = 0; i < entryIds.length; i += SQLITE_CHUNK_SIZE) {
|
|
479
|
+
const chunk = entryIds.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
480
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
481
|
+
stateDb.prepare(`DELETE FROM usage_events WHERE entry_id IN (${placeholders})`).run(...chunk);
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}, "delete usage_events (state.db) for entries");
|
|
485
|
+
}
|
|
570
486
|
/**
|
|
571
487
|
* Delete entries by their primary key IDs, along with all related rows
|
|
572
488
|
* (embeddings, entries_vec, entries_fts, utility_scores, usage_events).
|
|
@@ -621,7 +537,7 @@ export function getAllEntries(db, entryType, excludeTypes) {
|
|
|
621
537
|
/**
|
|
622
538
|
* Resolve a single `entries.id` from a new-grammar `[bundle//]conceptId` ref,
|
|
623
539
|
* keying on the canonical stored `item_ref` (ref-grammar decision D-R1/D-R4).
|
|
624
|
-
*
|
|
540
|
+
* The optional `stashDir` scopes the
|
|
625
541
|
* match to one source root.
|
|
626
542
|
*/
|
|
627
543
|
export function findEntryIdByRef(db, ref, stashDir) {
|
|
@@ -632,9 +548,8 @@ function withMdVariants(name) {
|
|
|
632
548
|
return name.endsWith(".md") ? [name, name.slice(0, -3)] : [name, `${name}.md`];
|
|
633
549
|
}
|
|
634
550
|
/**
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
* legacy `entry_key` predicate for NULL-`item_ref` rows.
|
|
551
|
+
* Current (`[bundle//]conceptId`) id lookup: match `item_ref` exactly when
|
|
552
|
+
* bundle-qualified or by `//conceptId` suffix when short.
|
|
638
553
|
*/
|
|
639
554
|
function findEntryIdByBundleRef(db, ref, stashDir) {
|
|
640
555
|
const parsed = parseBundleRef(ref);
|
|
@@ -669,9 +584,7 @@ function findEntryIdByBundleRef(db, ref, stashDir) {
|
|
|
669
584
|
* precedence-ordered scan would pick — while staying a pure config-free leaf
|
|
670
585
|
* (true installation-priority resolution against an injected bundle list is
|
|
671
586
|
* `resolveRef`'s job; this DB helper takes no config handle). The exact-bundle
|
|
672
|
-
* arm is single-row under the UNIQUE `item_ref` index
|
|
673
|
-
* keeps it deterministic on a partially-migrated DB whose fallback index is
|
|
674
|
-
* non-unique.
|
|
587
|
+
* arm is single-row under the UNIQUE `item_ref` index.
|
|
675
588
|
*/
|
|
676
589
|
function matchIdByItemRef(db, bundle, conceptId, stashDir) {
|
|
677
590
|
const scope = stashDir ? "AND stash_dir = ?" : "";
|
|
@@ -697,11 +610,29 @@ export function getEntryCount(db) {
|
|
|
697
610
|
const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get();
|
|
698
611
|
return row.cnt;
|
|
699
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Per-asset-type entry counts (keyed by `entry_type`, e.g. "skill",
|
|
615
|
+
* "knowledge", "memory"). Used by `akm info` to break down the aggregate
|
|
616
|
+
* `indexStats.entryCount` (R-057). Rows with a null/empty `entry_type` are
|
|
617
|
+
* omitted rather than surfaced under a synthetic key.
|
|
618
|
+
*/
|
|
619
|
+
export function getEntryCountByType(db) {
|
|
620
|
+
const rows = db
|
|
621
|
+
.prepare("SELECT entry_type AS type, COUNT(*) AS cnt FROM entries WHERE entry_type IS NOT NULL GROUP BY entry_type")
|
|
622
|
+
.all();
|
|
623
|
+
const out = {};
|
|
624
|
+
for (const row of rows) {
|
|
625
|
+
out[row.type] = row.cnt;
|
|
626
|
+
}
|
|
627
|
+
return out;
|
|
628
|
+
}
|
|
700
629
|
export function getEmbeddableEntryCount(db) {
|
|
701
630
|
return getEntryCount(db);
|
|
702
631
|
}
|
|
703
632
|
export function getEntryById(db, id) {
|
|
704
|
-
const row = db
|
|
633
|
+
const row = db
|
|
634
|
+
.prepare("SELECT file_path, stash_dir, entry_json, item_ref, bundle_id, concept_id FROM entries WHERE id = ?")
|
|
635
|
+
.get(id);
|
|
705
636
|
if (!row)
|
|
706
637
|
return undefined;
|
|
707
638
|
// Guard against corrupt JSON
|
|
@@ -713,12 +644,29 @@ export function getEntryById(db, id) {
|
|
|
713
644
|
warn(`[db] getEntryById: skipping entry id=${id} — corrupt entry_json`);
|
|
714
645
|
return undefined;
|
|
715
646
|
}
|
|
716
|
-
return {
|
|
647
|
+
return {
|
|
648
|
+
filePath: row.file_path,
|
|
649
|
+
stashDir: row.stash_dir,
|
|
650
|
+
entry,
|
|
651
|
+
itemRef: row.item_ref,
|
|
652
|
+
bundleId: row.bundle_id,
|
|
653
|
+
conceptId: row.concept_id,
|
|
654
|
+
};
|
|
717
655
|
}
|
|
718
656
|
export function getEntriesByDir(db, dirPath) {
|
|
719
657
|
const rows = db.prepare(`SELECT ${ENTRY_COLUMNS} FROM entries WHERE dir_path = ?`).all(dirPath);
|
|
720
658
|
return parseEntryRows(rows, "getEntriesByDir");
|
|
721
659
|
}
|
|
660
|
+
/** Return every directory previously indexed for one physical source root. */
|
|
661
|
+
export function getIndexedDirPathsByStashDir(db, stashDir) {
|
|
662
|
+
const rows = db.prepare("SELECT DISTINCT dir_path FROM entries WHERE stash_dir = ?").all(stashDir);
|
|
663
|
+
return rows.map((row) => row.dir_path);
|
|
664
|
+
}
|
|
665
|
+
/** Return every persisted source owner for one physical directory. */
|
|
666
|
+
export function getIndexedStashDirsByDir(db, dirPath) {
|
|
667
|
+
const rows = db.prepare("SELECT DISTINCT stash_dir FROM entries WHERE dir_path = ?").all(dirPath);
|
|
668
|
+
return rows.map((row) => row.stash_dir);
|
|
669
|
+
}
|
|
722
670
|
/**
|
|
723
671
|
* Resolve a single `entries.id` by exact `file_path` (the canonical on-disk
|
|
724
672
|
* path), or `undefined` if no row matches.
|
|
@@ -848,14 +796,15 @@ export function getItemRefById(db, id) {
|
|
|
848
796
|
return row?.item_ref ?? null;
|
|
849
797
|
}
|
|
850
798
|
/**
|
|
851
|
-
* Resolve a `usage_events.entry_ref` to its live `entries.id`.
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
* short conceptId (no bundle) falls back to the default-stash scope.
|
|
799
|
+
* Resolve a `usage_events.entry_ref` to its live `entries.id`. `entry_ref` is
|
|
800
|
+
* the fully-qualified `bundle//conceptId` `item_ref` spelling, so this keys
|
|
801
|
+
* directly on the globally-unique `item_ref`. Bare durable refs are invalid and
|
|
802
|
+
* remain detached.
|
|
856
803
|
*/
|
|
857
|
-
function resolveUsageEventEntryId(db, ref
|
|
858
|
-
|
|
804
|
+
function resolveUsageEventEntryId(db, ref) {
|
|
805
|
+
if (parseBundleRef(ref).bundle === undefined)
|
|
806
|
+
return undefined;
|
|
807
|
+
return findEntryIdByRef(db, ref);
|
|
859
808
|
}
|
|
860
809
|
/**
|
|
861
810
|
* Re-link detached usage_events to their current entry_ids via entry_ref.
|
|
@@ -869,7 +818,7 @@ function resolveUsageEventEntryId(db, ref, options) {
|
|
|
869
818
|
* distinct linked entry_ids in usage_events is small — and the re-resolution
|
|
870
819
|
* reads `entries` from `indexDb`.
|
|
871
820
|
*/
|
|
872
|
-
export function relinkUsageEvents(indexDb, stateDb,
|
|
821
|
+
export function relinkUsageEvents(indexDb, stateDb, _options = {}) {
|
|
873
822
|
bestEffort(() => {
|
|
874
823
|
// Step 1: null out stale entry_ids (entry was deleted, re-keyed, etc).
|
|
875
824
|
// Leaving them in place would let `recomputeUtilityScores` aggregate by an
|
|
@@ -888,10 +837,8 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
|
|
|
888
837
|
});
|
|
889
838
|
nullTx();
|
|
890
839
|
}
|
|
891
|
-
// Step 2: re-resolve each
|
|
892
|
-
//
|
|
893
|
-
// historical/default root. This keeps duplicate refs from adopting whichever
|
|
894
|
-
// entries row SQLite happens to return first while retaining indexed lookups.
|
|
840
|
+
// Step 2: re-resolve each fully-qualified ref. Bare rows are not current
|
|
841
|
+
// durable identities and remain detached.
|
|
895
842
|
const refs = stateDb
|
|
896
843
|
.prepare("SELECT DISTINCT entry_ref AS ref FROM usage_events WHERE entry_id IS NULL AND entry_ref IS NOT NULL")
|
|
897
844
|
.all();
|
|
@@ -900,7 +847,7 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
|
|
|
900
847
|
for (const { ref } of refs) {
|
|
901
848
|
let id;
|
|
902
849
|
try {
|
|
903
|
-
id = resolveUsageEventEntryId(indexDb, ref
|
|
850
|
+
id = resolveUsageEventEntryId(indexDb, ref);
|
|
904
851
|
}
|
|
905
852
|
catch (err) {
|
|
906
853
|
if (err instanceof Error && err.name === "UsageError")
|
|
@@ -914,39 +861,3 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
|
|
|
914
861
|
relinkTx();
|
|
915
862
|
}, "usage_events table may not exist yet during entry_id re-resolution");
|
|
916
863
|
}
|
|
917
|
-
/**
|
|
918
|
-
* Walk indexed entries and collect a deduplicated set of tags. When
|
|
919
|
-
* `entryType` is provided, only entries of that type contribute tags.
|
|
920
|
-
*
|
|
921
|
-
* Pure read; never mutates the DB. Used by `akm lessons coverage` (Phase 7A)
|
|
922
|
-
* to compute the diff between all-asset tags and lesson tags. Tags are
|
|
923
|
-
* normalised by trimming and lower-casing, and blank tags are dropped.
|
|
924
|
-
*
|
|
925
|
-
* SQL owner: this module owns ALL raw SQL against the `entries` table (WS5),
|
|
926
|
-
* so the `lessons coverage` read lives here rather than leaking into cli.ts.
|
|
927
|
-
* The result set is fully materialised (`.all()` then iterate) before return.
|
|
928
|
-
*/
|
|
929
|
-
export function collectTagSetFromEntries(db, entryType) {
|
|
930
|
-
const tags = new Set();
|
|
931
|
-
const stmt = entryType
|
|
932
|
-
? db.prepare("SELECT entry_json FROM entries WHERE entry_type = ?")
|
|
933
|
-
: db.prepare("SELECT entry_json FROM entries");
|
|
934
|
-
const rows = (entryType ? stmt.all(entryType) : stmt.all());
|
|
935
|
-
for (const row of rows) {
|
|
936
|
-
let parsed;
|
|
937
|
-
try {
|
|
938
|
-
parsed = JSON.parse(row.entry_json);
|
|
939
|
-
}
|
|
940
|
-
catch {
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
if (!Array.isArray(parsed.tags))
|
|
944
|
-
continue;
|
|
945
|
-
for (const tag of parsed.tags) {
|
|
946
|
-
if (typeof tag === "string" && tag.trim().length > 0) {
|
|
947
|
-
tags.add(tag.trim().toLowerCase());
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return tags;
|
|
952
|
-
}
|