akm-cli 0.9.0-beta.9 → 0.9.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +715 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +228 -292
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +291 -13
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +79 -5
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +403 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/json-schema.js +142 -0
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +261 -770
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +48 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +41 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +79 -6
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +83 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +206 -81
- package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
- package/dist/integrations/harnesses/aider/index.js +58 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
- package/dist/integrations/harnesses/amazonq/index.js +59 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
- package/dist/integrations/harnesses/claude/index.js +27 -23
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
- package/dist/integrations/harnesses/codex/index.js +63 -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 +60 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
- package/dist/integrations/harnesses/gemini/index.js +60 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +28 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +17 -16
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
- package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
- package/dist/integrations/harnesses/openhands/index.js +58 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
- package/dist/integrations/harnesses/pi/index.js +58 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -0
- package/dist/integrations/session-logs/index.js +40 -11
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +87 -15
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +6 -1
- package/dist/output/text/helpers.js +241 -2
- package/dist/output/text/workflow.js +4 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/schemas/akm-config.json +14225 -0
- package/dist/schemas/akm-workflow.json +328 -0
- package/dist/scripts/migrate-storage.js +2743 -8390
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/repositories/workflow-runs-repository.js +189 -1
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +5 -0
- package/dist/workflows/db.js +138 -4
- package/dist/workflows/exec/brief.js +484 -0
- package/dist/workflows/exec/native-executor.js +975 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1295 -0
- package/dist/workflows/exec/run-workflow.js +596 -0
- package/dist/workflows/exec/scheduler.js +100 -0
- package/dist/workflows/exec/step-work.js +1156 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +388 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +33 -0
- package/dist/workflows/ir/schema.js +4 -0
- package/dist/workflows/parser.js +3 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +760 -0
- package/dist/workflows/program/project.js +105 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/runs.js +248 -153
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
- package/dist/workflows/validate-summary.js +26 -10
- package/dist/workflows/validator.js +1 -1
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +7 -5
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
package/dist/indexer/indexer.js
CHANGED
|
@@ -10,9 +10,10 @@ import { getDbPath } from "../core/paths.js";
|
|
|
10
10
|
import { isVerbose, warn, warnVerbose } from "../core/warn.js";
|
|
11
11
|
import { resolveIndexPassLLM } from "../llm/index-passes.js";
|
|
12
12
|
import { takeWorkflowDocument } from "../workflows/runtime/document-cache.js";
|
|
13
|
-
import { clearStaleCacheEntries, closeDatabase, deleteEntriesByDir, deleteEntriesByIds, deleteEntriesByStashDir, deleteIndexDirStatesByStashDir, getAllEntriesForEmbedding, getEmbeddableEntryCount, getEmbeddingCount,
|
|
13
|
+
import { clearStaleCacheEntries, closeDatabase, deleteEntriesByDir, deleteEntriesByIds, deleteEntriesByStashDir, deleteIndexDirStatesByStashDir, getAllEntriesForEmbedding, getEmbeddableEntryCount, getEmbeddingCount, getEntryCount, getMeta, isVecAvailable, openExistingDatabase, openIndexDatabase, purgeEmbeddings, rebuildFts, relinkUsageEvents, setMeta, upsertEmbedding, upsertEntry, upsertIndexDirState, upsertUtilityScore, upsertWorkflowDocument, warnIfVecMissing, } from "./db/db.js";
|
|
14
14
|
import { deleteStoredGraph } from "./db/graph-db.js";
|
|
15
15
|
import { withIndexWriterLease } from "./index-writer-lock.js";
|
|
16
|
+
import { canUseIncrementalSkip, computeDirFingerprint, getCachedZeroRowDirState, getDirIndexState, inferZeroRowReason, } from "./passes/dir-staleness.js";
|
|
16
17
|
import { applyCuratedFrontmatter, applyWikiFrontmatter, generateMetadataFlat, isEnrichmentComplete, isWorkflowSkipWarning, loadStashFile, shouldIndexStashFile, } from "./passes/metadata.js";
|
|
17
18
|
import { buildSearchText } from "./search/search-fields.js";
|
|
18
19
|
import { classifySemanticFailure, clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus, } from "./search/semantic-status.js";
|
|
@@ -117,7 +118,7 @@ async function runWalkPhase(ctx) {
|
|
|
117
118
|
ctx.timing.tWalkEnd = Date.now();
|
|
118
119
|
throwIfAborted(signal);
|
|
119
120
|
// LLM enrichment for directories that need it
|
|
120
|
-
await enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal,
|
|
121
|
+
await enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal, reEnrich);
|
|
121
122
|
onProgress({
|
|
122
123
|
phase: "llm",
|
|
123
124
|
message: resolveIndexPassLLM("enrichment", config)
|
|
@@ -142,6 +143,7 @@ async function runEmbeddingPhase(ctx) {
|
|
|
142
143
|
*/
|
|
143
144
|
async function runFinalizePhase(ctx) {
|
|
144
145
|
const { db, config, sources, sourceDirs, isIncremental, stashDir, signal, onProgress } = ctx;
|
|
146
|
+
ctx.timing.tFinalizeStart = Date.now();
|
|
145
147
|
// Rebuild FTS after all inserts. Use incremental mode when this whole
|
|
146
148
|
// index run is incremental — only entries touched by `upsertEntry`
|
|
147
149
|
// since the last rebuild are re-indexed.
|
|
@@ -152,10 +154,13 @@ async function runFinalizePhase(ctx) {
|
|
|
152
154
|
});
|
|
153
155
|
ctx.timing.tFtsEnd = Date.now();
|
|
154
156
|
// Re-link detached usage_events and recompute utility scores.
|
|
157
|
+
onProgress({ phase: "finalize", message: "Relinking usage events." });
|
|
155
158
|
relinkUsageEvents(db);
|
|
159
|
+
onProgress({ phase: "finalize", message: "Recomputing utility scores." });
|
|
156
160
|
recomputeUtilityScores(db);
|
|
157
161
|
// Purge LLM cache entries for assets that no longer exist in the index.
|
|
158
162
|
try {
|
|
163
|
+
onProgress({ phase: "finalize", message: "Clearing stale LLM cache entries." });
|
|
159
164
|
clearStaleCacheEntries(db);
|
|
160
165
|
}
|
|
161
166
|
catch {
|
|
@@ -163,6 +168,7 @@ async function runFinalizePhase(ctx) {
|
|
|
163
168
|
}
|
|
164
169
|
// Regenerate each wiki's index.md from its pages' frontmatter. Best-effort.
|
|
165
170
|
try {
|
|
171
|
+
onProgress({ phase: "finalize", message: "Regenerating wiki indexes." });
|
|
166
172
|
const { regenerateAllWikiIndexes } = await import("../wiki/wiki.js");
|
|
167
173
|
regenerateAllWikiIndexes(stashDir);
|
|
168
174
|
}
|
|
@@ -179,6 +185,7 @@ async function runFinalizePhase(ctx) {
|
|
|
179
185
|
warnIfVecMissing(db);
|
|
180
186
|
const totalEntries = getEntryCount(db);
|
|
181
187
|
const semanticEntryCount = getEmbeddableEntryCount(db);
|
|
188
|
+
onProgress({ phase: "finalize", message: "Verifying semantic search state." });
|
|
182
189
|
const verification = verifyIndexState(db, config, semanticEntryCount, embeddingResult);
|
|
183
190
|
if (config.semanticSearchMode === "off") {
|
|
184
191
|
clearSemanticStatus();
|
|
@@ -196,8 +203,9 @@ async function runFinalizePhase(ctx) {
|
|
|
196
203
|
}
|
|
197
204
|
onProgress({ phase: "verify", message: verification.message });
|
|
198
205
|
// Store verification result and totalEntries on ctx for the caller to use
|
|
199
|
-
ctx.
|
|
200
|
-
ctx.
|
|
206
|
+
ctx.verification = verification;
|
|
207
|
+
ctx.totalEntries = totalEntries;
|
|
208
|
+
ctx.timing.tFinalizeEnd = Date.now();
|
|
201
209
|
// suppress unused warning — sources was previously used inline
|
|
202
210
|
void sources;
|
|
203
211
|
}
|
|
@@ -225,8 +233,34 @@ function runCleanPass(db, dryRun) {
|
|
|
225
233
|
};
|
|
226
234
|
}
|
|
227
235
|
// ── Indexer ──────────────────────────────────────────────────────────────────
|
|
236
|
+
// ── Test seam ────────────────────────────────────────────────────────────────
|
|
237
|
+
// Swap-and-restore override. Inert in production; only tests call the setter.
|
|
238
|
+
let akmIndexOverride;
|
|
239
|
+
/** TEST-ONLY. Swap the implementation of `akmIndex`; pass undefined to restore. */
|
|
240
|
+
export function _setAkmIndexForTests(fake) {
|
|
241
|
+
akmIndexOverride = fake;
|
|
242
|
+
}
|
|
228
243
|
export async function akmIndex(options) {
|
|
229
|
-
|
|
244
|
+
if (akmIndexOverride)
|
|
245
|
+
return akmIndexOverride(options);
|
|
246
|
+
return akmIndexReal(options);
|
|
247
|
+
}
|
|
248
|
+
async function akmIndexReal(options) {
|
|
249
|
+
const requestedAt = Date.now();
|
|
250
|
+
let acquiredAt = requestedAt;
|
|
251
|
+
return withIndexWriterLease({
|
|
252
|
+
purpose: "akm-index",
|
|
253
|
+
signal: options?.signal,
|
|
254
|
+
onWait: ({ waitedMs }) => {
|
|
255
|
+
options?.onProgress?.({
|
|
256
|
+
phase: "preflight",
|
|
257
|
+
message: `Waiting for index writer lease (${Math.round(waitedMs / 1000)}s elapsed).`,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
onAcquired: ({ waitedMs }) => {
|
|
261
|
+
acquiredAt = requestedAt + waitedMs;
|
|
262
|
+
},
|
|
263
|
+
}, async () => {
|
|
230
264
|
const stashDir = options?.stashDir || resolveStashDir();
|
|
231
265
|
const onProgress = options?.onProgress ?? (() => { });
|
|
232
266
|
const signal = options?.signal;
|
|
@@ -245,15 +279,22 @@ export async function akmIndex(options) {
|
|
|
245
279
|
warnOnUnmigratedVaults(stashDir);
|
|
246
280
|
// Ensure git stash caches are extracted before resolving stash dirs,
|
|
247
281
|
// so their content directories exist on disk for the walker to discover.
|
|
282
|
+
const sourceCacheStart = Date.now();
|
|
283
|
+
onProgress({ phase: "preflight", message: "Hydrating source caches." });
|
|
248
284
|
const { ensureSourceCaches, resolveSourceEntries } = await import("./search/search-source.js");
|
|
249
285
|
await ensureSourceCaches(config, { force: full });
|
|
286
|
+
const sourceCacheEnd = Date.now();
|
|
250
287
|
const allSourceEntries = resolveSourceEntries(stashDir, config);
|
|
251
288
|
const allSourceDirs = allSourceEntries.map((s) => s.path);
|
|
289
|
+
onProgress({
|
|
290
|
+
phase: "preflight",
|
|
291
|
+
message: `Resolved ${allSourceDirs.length} stash source${allSourceDirs.length === 1 ? "" : "s"}.`,
|
|
292
|
+
});
|
|
252
293
|
const t0 = Date.now();
|
|
253
294
|
// Open database — pass embedding dimension from config if available
|
|
254
295
|
const dbPath = getDbPath();
|
|
255
296
|
const embeddingDim = config.embedding?.dimension;
|
|
256
|
-
const db =
|
|
297
|
+
const db = openIndexDatabase(dbPath, embeddingDim ? { embeddingDim } : undefined);
|
|
257
298
|
try {
|
|
258
299
|
// Determine incremental vs full mode
|
|
259
300
|
const prevStashDir = getMeta(db, "stashDir");
|
|
@@ -278,6 +319,8 @@ export async function akmIndex(options) {
|
|
|
278
319
|
tLlmEnd: t0,
|
|
279
320
|
tFtsEnd: t0,
|
|
280
321
|
tEmbedEnd: t0,
|
|
322
|
+
tFinalizeStart: t0,
|
|
323
|
+
tFinalizeEnd: t0,
|
|
281
324
|
},
|
|
282
325
|
isIncremental,
|
|
283
326
|
builtAtMs,
|
|
@@ -288,7 +331,6 @@ export async function akmIndex(options) {
|
|
|
288
331
|
walkWarnings: [],
|
|
289
332
|
dirsNeedingLlm: [],
|
|
290
333
|
embeddingResult: null,
|
|
291
|
-
graphExtractionResult: null,
|
|
292
334
|
};
|
|
293
335
|
onProgress({
|
|
294
336
|
phase: "summary",
|
|
@@ -307,15 +349,23 @@ export async function akmIndex(options) {
|
|
|
307
349
|
await runEmbeddingPhase(ctx);
|
|
308
350
|
await runFinalizePhase(ctx);
|
|
309
351
|
// ────────────────────────────────────────────────────────────────────────
|
|
310
|
-
|
|
352
|
+
// runFinalizePhase always populates these before returning.
|
|
353
|
+
const verification = ctx.verification;
|
|
354
|
+
const totalEntries = ctx.totalEntries;
|
|
311
355
|
const { timing } = ctx;
|
|
312
356
|
// ── Clean pass ───────────────────────────────────────────────────────────
|
|
313
357
|
// After the normal index completes, remove entries whose source files no
|
|
314
358
|
// longer exist on disk. Remote entries (empty file_path) are skipped.
|
|
315
359
|
let cleanResult;
|
|
360
|
+
const cleanStart = Date.now();
|
|
316
361
|
if (clean) {
|
|
362
|
+
onProgress({
|
|
363
|
+
phase: "finalize",
|
|
364
|
+
message: dryRun ? "Scanning for stale index entries (dry run)." : "Removing stale index entries.",
|
|
365
|
+
});
|
|
317
366
|
cleanResult = runCleanPass(db, dryRun);
|
|
318
367
|
}
|
|
368
|
+
const cleanEnd = Date.now();
|
|
319
369
|
// ────────────────────────────────────────────────────────────────────────
|
|
320
370
|
return {
|
|
321
371
|
stashDir,
|
|
@@ -333,6 +383,12 @@ export async function akmIndex(options) {
|
|
|
333
383
|
llmMs: timing.tLlmEnd - timing.tWalkEnd,
|
|
334
384
|
embedMs: timing.tEmbedEnd - timing.tLlmEnd,
|
|
335
385
|
ftsMs: timing.tFtsEnd - timing.tEmbedEnd,
|
|
386
|
+
finalizeMs: timing.tFinalizeEnd - timing.tFinalizeStart,
|
|
387
|
+
cleanMs: clean ? cleanEnd - cleanStart : 0,
|
|
388
|
+
preflightMs: timing.t0 - requestedAt,
|
|
389
|
+
leaseWaitMs: acquiredAt - requestedAt,
|
|
390
|
+
sourceCacheMs: sourceCacheEnd - sourceCacheStart,
|
|
391
|
+
endToEndMs: Date.now() - requestedAt,
|
|
336
392
|
},
|
|
337
393
|
...(cleanResult !== undefined ? { clean: cleanResult } : {}),
|
|
338
394
|
};
|
|
@@ -342,14 +398,21 @@ export async function akmIndex(options) {
|
|
|
342
398
|
}
|
|
343
399
|
});
|
|
344
400
|
}
|
|
345
|
-
|
|
346
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Phase 1 (async): walk every source directory and pre-generate all metadata
|
|
403
|
+
* outside any transaction, producing the per-directory scan records that
|
|
404
|
+
* {@link persistDirRecords} later writes.
|
|
405
|
+
*
|
|
406
|
+
* generateMetadataFlat is async (uses dynamic import for the matcher/renderer
|
|
407
|
+
* registry), so it cannot run inside a db.transaction() callback — hence the
|
|
408
|
+
* split into a pure-ish scan pass and a synchronous persist pass.
|
|
409
|
+
*/
|
|
410
|
+
async function scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress) {
|
|
347
411
|
let scannedDirs = 0;
|
|
348
412
|
let skippedDirs = 0;
|
|
349
413
|
let generatedCount = 0;
|
|
350
414
|
const warnings = [];
|
|
351
415
|
const seenPaths = new Set();
|
|
352
|
-
const dirsNeedingLlm = [];
|
|
353
416
|
const dirRecords = [];
|
|
354
417
|
let processedDirs = 0;
|
|
355
418
|
let priorDirsChanged = hadRemovedSources;
|
|
@@ -369,6 +432,53 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
369
432
|
reportScanProgress(`${kind === "scan" ? "Rescanning" : "Skipping"} ${path.relative(currentStashDir, dirPath) || "."} ` +
|
|
370
433
|
`from ${currentStashDir}: ${reason.kind}${detail}${rowInfo}`);
|
|
371
434
|
};
|
|
435
|
+
// Duplicate-directory guard shared by the wiki-root and normal branches. A
|
|
436
|
+
// dir may surface via multiple stash roots; only the first occurrence is
|
|
437
|
+
// indexed, and the later ones are recorded as skips. Returns true when the
|
|
438
|
+
// dir was already seen (caller should skip further processing).
|
|
439
|
+
const markSeenOrSkipDuplicate = (dirPath, currentStashDir, files) => {
|
|
440
|
+
const resolved = path.resolve(dirPath);
|
|
441
|
+
if (seenPaths.has(resolved)) {
|
|
442
|
+
const reason = { kind: "duplicate-dir" };
|
|
443
|
+
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
|
|
444
|
+
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
447
|
+
seenPaths.add(resolved);
|
|
448
|
+
return false;
|
|
449
|
+
};
|
|
450
|
+
// Incremental freshness gate shared by both branches: consult the persisted
|
|
451
|
+
// dir state and record either a skip (unchanged + eligible for incremental
|
|
452
|
+
// skip) or a scan record carrying the candidate stash.
|
|
453
|
+
const recordFreshnessDecision = (dirPath, currentStashDir, stateFiles, stash) => {
|
|
454
|
+
const previousState = getDirIndexState(db, dirPath, stateFiles, builtAtMs);
|
|
455
|
+
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
456
|
+
skippedDirs++;
|
|
457
|
+
dirRecords.push({
|
|
458
|
+
dirPath,
|
|
459
|
+
currentStashDir,
|
|
460
|
+
files: stateFiles,
|
|
461
|
+
stash: null,
|
|
462
|
+
skip: true,
|
|
463
|
+
reason: previousState.reason,
|
|
464
|
+
});
|
|
465
|
+
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
scannedDirs++;
|
|
469
|
+
priorDirsChanged = true;
|
|
470
|
+
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
471
|
+
dirRecords.push({
|
|
472
|
+
dirPath,
|
|
473
|
+
currentStashDir,
|
|
474
|
+
files: stateFiles,
|
|
475
|
+
stash,
|
|
476
|
+
skip: false,
|
|
477
|
+
reason,
|
|
478
|
+
persistedRowCount: previousState.persistedRowCount,
|
|
479
|
+
});
|
|
480
|
+
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
481
|
+
};
|
|
372
482
|
for (const sourceAdded of allSourceEntries) {
|
|
373
483
|
const currentStashDir = sourceAdded.path;
|
|
374
484
|
const fileContexts = walkStashFlat(currentStashDir);
|
|
@@ -406,33 +516,9 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
406
516
|
}
|
|
407
517
|
}
|
|
408
518
|
for (const [dirPath, { files, entries }] of wikiDirGroups) {
|
|
409
|
-
if (
|
|
410
|
-
const reason = { kind: "duplicate-dir" };
|
|
411
|
-
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason });
|
|
412
|
-
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
seenPaths.add(path.resolve(dirPath));
|
|
416
|
-
const previousState = getDirIndexState(db, dirPath, files, builtAtMs);
|
|
417
|
-
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
418
|
-
skippedDirs++;
|
|
419
|
-
dirRecords.push({ dirPath, currentStashDir, files, stash: null, skip: true, reason: previousState.reason });
|
|
420
|
-
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
519
|
+
if (markSeenOrSkipDuplicate(dirPath, currentStashDir, files))
|
|
421
520
|
continue;
|
|
422
|
-
}
|
|
423
|
-
scannedDirs++;
|
|
424
|
-
priorDirsChanged = true;
|
|
425
|
-
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
426
|
-
dirRecords.push({
|
|
427
|
-
dirPath,
|
|
428
|
-
currentStashDir,
|
|
429
|
-
files,
|
|
430
|
-
stash: { entries },
|
|
431
|
-
skip: false,
|
|
432
|
-
reason,
|
|
433
|
-
persistedRowCount: previousState.persistedRowCount,
|
|
434
|
-
});
|
|
435
|
-
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
521
|
+
recordFreshnessDecision(dirPath, currentStashDir, files, { entries });
|
|
436
522
|
}
|
|
437
523
|
continue;
|
|
438
524
|
}
|
|
@@ -447,13 +533,8 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
447
533
|
}
|
|
448
534
|
for (const [dirPath, files] of dirGroups) {
|
|
449
535
|
const indexableFiles = files.filter((file) => shouldIndexStashFile(currentStashDir, file));
|
|
450
|
-
if (
|
|
451
|
-
const reason = { kind: "duplicate-dir" };
|
|
452
|
-
dirRecords.push({ dirPath, currentStashDir, files: indexableFiles, stash: null, skip: true, reason });
|
|
453
|
-
reportDirDecision("skip", dirPath, currentStashDir, reason);
|
|
536
|
+
if (markSeenOrSkipDuplicate(dirPath, currentStashDir, indexableFiles))
|
|
454
537
|
continue;
|
|
455
|
-
}
|
|
456
|
-
seenPaths.add(path.resolve(dirPath));
|
|
457
538
|
if (indexableFiles.length === 0) {
|
|
458
539
|
skippedDirs++;
|
|
459
540
|
const reason = { kind: "no-indexable-files" };
|
|
@@ -483,37 +564,17 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
483
564
|
if (generated.entries.length > 0) {
|
|
484
565
|
generatedCount += generated.entries.length;
|
|
485
566
|
}
|
|
486
|
-
|
|
487
|
-
if (isIncremental && !previousState.stale && canUseIncrementalSkip(previousState, priorDirsChanged)) {
|
|
488
|
-
skippedDirs++;
|
|
489
|
-
dirRecords.push({
|
|
490
|
-
dirPath,
|
|
491
|
-
currentStashDir,
|
|
492
|
-
files: staleFiles,
|
|
493
|
-
stash: null,
|
|
494
|
-
skip: true,
|
|
495
|
-
reason: previousState.reason,
|
|
496
|
-
});
|
|
497
|
-
reportDirDecision("skip", dirPath, currentStashDir, previousState.reason, previousState.persistedRowCount);
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
scannedDirs++;
|
|
501
|
-
priorDirsChanged = true;
|
|
502
|
-
const reason = isIncremental ? previousState.reason : { kind: "full-rebuild" };
|
|
503
|
-
dirRecords.push({
|
|
504
|
-
dirPath,
|
|
505
|
-
currentStashDir,
|
|
506
|
-
files: staleFiles,
|
|
507
|
-
stash,
|
|
508
|
-
skip: false,
|
|
509
|
-
reason,
|
|
510
|
-
persistedRowCount: previousState.persistedRowCount,
|
|
511
|
-
});
|
|
512
|
-
reportDirDecision("scan", dirPath, currentStashDir, reason, previousState.persistedRowCount);
|
|
567
|
+
recordFreshnessDecision(dirPath, currentStashDir, staleFiles, stash);
|
|
513
568
|
}
|
|
514
569
|
}
|
|
515
|
-
|
|
516
|
-
|
|
570
|
+
return { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings };
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Phase 2 (sync): write all pre-generated scan records inside a single
|
|
574
|
+
* transaction, returning the directories that still need LLM enrichment.
|
|
575
|
+
*/
|
|
576
|
+
function persistDirRecords(db, dirRecords, doFullDelete, warnings) {
|
|
577
|
+
const dirsNeedingLlm = [];
|
|
517
578
|
// Cross-stash dedup: track indexed assets by content identity
|
|
518
579
|
// (type + filename + description) so the same asset from a lower-priority
|
|
519
580
|
// stash root is skipped when a higher-priority root already covers it.
|
|
@@ -635,101 +696,17 @@ async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadR
|
|
|
635
696
|
}
|
|
636
697
|
});
|
|
637
698
|
insertTransaction();
|
|
638
|
-
return {
|
|
699
|
+
return { dirsNeedingLlm };
|
|
639
700
|
}
|
|
640
|
-
function
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
return { stale: true, reason: staleReason, persistedRowCount: prevEntries.length };
|
|
649
|
-
}
|
|
650
|
-
const cachedState = getIndexDirState(db, dirPath);
|
|
651
|
-
if (cachedState &&
|
|
652
|
-
cachedState.fileSetHash === fingerprint.fileSetHash &&
|
|
653
|
-
cachedState.fileMtimeMaxMs === fingerprint.fileMtimeMaxMs) {
|
|
654
|
-
return {
|
|
655
|
-
stale: false,
|
|
656
|
-
reason: { kind: "cached-zero-row-state", detail: cachedState.reason },
|
|
657
|
-
persistedRowCount: 0,
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
return {
|
|
661
|
-
stale: true,
|
|
662
|
-
reason: { kind: "no-previous-rows", detail: cachedState ? `cached=${cachedState.reason}` : undefined },
|
|
663
|
-
persistedRowCount: 0,
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
function getCachedZeroRowDirState(db, dirPath, files, builtAtMs, priorDirsChanged) {
|
|
667
|
-
const state = getDirIndexState(db, dirPath, files, builtAtMs);
|
|
668
|
-
if (state.stale || state.reason.kind !== "cached-zero-row-state")
|
|
669
|
-
return undefined;
|
|
670
|
-
if (!canUseIncrementalSkip(state, priorDirsChanged))
|
|
671
|
-
return undefined;
|
|
672
|
-
return state;
|
|
673
|
-
}
|
|
674
|
-
function canUseIncrementalSkip(state, priorDirsChanged) {
|
|
675
|
-
return !(priorDirsChanged &&
|
|
676
|
-
state.reason.kind === "cached-zero-row-state" &&
|
|
677
|
-
state.reason.detail === "deduped-zero-row");
|
|
678
|
-
}
|
|
679
|
-
function computeDirFingerprint(_dirPath, files) {
|
|
680
|
-
const normalizedFiles = [...new Set(files.map((file) => path.basename(file)))].sort();
|
|
681
|
-
let fileMtimeMaxMs = 0;
|
|
682
|
-
for (const file of files) {
|
|
683
|
-
try {
|
|
684
|
-
fileMtimeMaxMs = Math.max(fileMtimeMaxMs, fs.statSync(file).mtimeMs);
|
|
685
|
-
}
|
|
686
|
-
catch {
|
|
687
|
-
fileMtimeMaxMs = Number.POSITIVE_INFINITY;
|
|
688
|
-
break;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
return {
|
|
692
|
-
fileSetHash: normalizedFiles.join("\0"),
|
|
693
|
-
fileMtimeMaxMs,
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
function getDirStaleReason(_dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
697
|
-
const prevFileNames = new Set(previousEntries
|
|
698
|
-
.map((ie) => {
|
|
699
|
-
const fromPath = path.basename(ie.filePath);
|
|
700
|
-
return fromPath || ie.entry.filename;
|
|
701
|
-
})
|
|
702
|
-
.filter((e) => !!e));
|
|
703
|
-
const currFileNames = new Set(currentFiles.map((f) => path.basename(f)));
|
|
704
|
-
if (prevFileNames.size !== currFileNames.size) {
|
|
705
|
-
return { kind: "file-set-changed", detail: `${prevFileNames.size} -> ${currFileNames.size} files` };
|
|
706
|
-
}
|
|
707
|
-
for (const name of currFileNames) {
|
|
708
|
-
if (!prevFileNames.has(name))
|
|
709
|
-
return { kind: "file-set-changed", detail: name };
|
|
710
|
-
}
|
|
711
|
-
for (const file of currentFiles) {
|
|
712
|
-
try {
|
|
713
|
-
if (fs.statSync(file).mtimeMs > builtAtMs)
|
|
714
|
-
return { kind: "mtime-changed", detail: path.basename(file) };
|
|
715
|
-
}
|
|
716
|
-
catch {
|
|
717
|
-
return { kind: "missing-file", detail: path.basename(file) };
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
return undefined;
|
|
721
|
-
}
|
|
722
|
-
function inferZeroRowReason(stash, priorReason, warnings, dirPath, dedupedRows) {
|
|
723
|
-
if (dedupedRows > 0)
|
|
724
|
-
return "deduped-zero-row";
|
|
725
|
-
const workflowNoise = warnings.some((warning) => warning.startsWith("Skipped workflow ") && warning.includes(dirPath));
|
|
726
|
-
if (workflowNoise)
|
|
727
|
-
return "workflow-noise";
|
|
728
|
-
if (!stash || stash.entries.length === 0)
|
|
729
|
-
return "empty-generated-set";
|
|
730
|
-
return `zero-row:${priorReason?.kind ?? "unknown"}`;
|
|
701
|
+
async function indexEntries(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, doFullDelete = false, onProgress) {
|
|
702
|
+
// Phase 1 (async): walk directories and pre-generate all metadata outside the
|
|
703
|
+
// transaction.
|
|
704
|
+
const { dirRecords, scannedDirs, skippedDirs, generatedCount, warnings } = await scanSourceDirs(db, allSourceEntries, isIncremental, builtAtMs, hadRemovedSources, onProgress);
|
|
705
|
+
// Phase 2 (sync): write all pre-generated metadata inside a single transaction.
|
|
706
|
+
const { dirsNeedingLlm } = persistDirRecords(db, dirRecords, doFullDelete, warnings);
|
|
707
|
+
return { scannedDirs, skippedDirs, generatedCount, warnings, dirsNeedingLlm };
|
|
731
708
|
}
|
|
732
|
-
async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal,
|
|
709
|
+
async function enhanceDirsWithLlm(db, config, dirsNeedingLlm, onProgress, signal, reEnrich = false) {
|
|
733
710
|
// Resolve per-pass LLM config via the unified shim. Returns undefined when
|
|
734
711
|
// either no `akm.llm` is configured or the user opted this pass out via
|
|
735
712
|
// `index.enrichment.llm = false`. (#208)
|
|
@@ -899,21 +876,9 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal) {
|
|
|
899
876
|
const currentFingerprint = deriveSemanticProviderFingerprint(config.embedding);
|
|
900
877
|
const storedFingerprint = getMeta(db, "embeddingFingerprint");
|
|
901
878
|
if (storedFingerprint && storedFingerprint !== currentFingerprint) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
catch {
|
|
906
|
-
/* ignore */
|
|
907
|
-
}
|
|
908
|
-
if (isVecAvailable(db)) {
|
|
909
|
-
try {
|
|
910
|
-
db.exec("DELETE FROM entries_vec");
|
|
911
|
-
}
|
|
912
|
-
catch {
|
|
913
|
-
/* ignore */
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
setMeta(db, "hasEmbeddings", "0");
|
|
879
|
+
// Model/provider changed → stored vectors are incompatible. Clear them
|
|
880
|
+
// (same dimension, so keep the vec table); re-embedded by this index run.
|
|
881
|
+
purgeEmbeddings(db);
|
|
917
882
|
}
|
|
918
883
|
try {
|
|
919
884
|
const { embedBatch } = await import("../llm/embedder.js");
|
|
@@ -943,31 +908,44 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal) {
|
|
|
943
908
|
warnVerbose(`[embed] ${ref} (${chars} chars, est. ${tokens} tokens) → batch ${batchNum}/${totalBatches}`);
|
|
944
909
|
}
|
|
945
910
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
911
|
+
let heartbeatTimer;
|
|
912
|
+
try {
|
|
913
|
+
heartbeatTimer = setInterval(() => {
|
|
914
|
+
onProgress({
|
|
915
|
+
phase: "embeddings",
|
|
916
|
+
message: `Still generating embeddings for ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"}; waiting on embedding provider.`,
|
|
917
|
+
});
|
|
918
|
+
}, 15000);
|
|
919
|
+
const embeddings = await embedBatch(texts, config.embedding, signal);
|
|
920
|
+
throwIfAborted(signal);
|
|
921
|
+
// Wrap all embedding upserts in a single transaction so partial
|
|
922
|
+
// state is rolled back on failure rather than leaving the table half-filled.
|
|
923
|
+
let storedCount = 0;
|
|
924
|
+
let skippedCount = 0;
|
|
925
|
+
db.transaction(() => {
|
|
926
|
+
for (let i = 0; i < allEntries.length; i++) {
|
|
927
|
+
if (upsertEmbedding(db, allEntries[i].id, embeddings[i])) {
|
|
928
|
+
storedCount++;
|
|
929
|
+
}
|
|
930
|
+
else {
|
|
931
|
+
skippedCount++;
|
|
932
|
+
}
|
|
959
933
|
}
|
|
934
|
+
})();
|
|
935
|
+
if (skippedCount > 0) {
|
|
936
|
+
warn(`[embed] ${skippedCount} embedding${skippedCount === 1 ? "" : "s"} skipped (entry deleted between queue and write)`);
|
|
960
937
|
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
938
|
+
onProgress({
|
|
939
|
+
phase: "embeddings",
|
|
940
|
+
message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"}.`,
|
|
941
|
+
});
|
|
942
|
+
setMeta(db, "embeddingFingerprint", currentFingerprint);
|
|
943
|
+
return { success: true };
|
|
944
|
+
}
|
|
945
|
+
finally {
|
|
946
|
+
if (heartbeatTimer)
|
|
947
|
+
clearInterval(heartbeatTimer);
|
|
964
948
|
}
|
|
965
|
-
onProgress({
|
|
966
|
-
phase: "embeddings",
|
|
967
|
-
message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"}.`,
|
|
968
|
-
});
|
|
969
|
-
setMeta(db, "embeddingFingerprint", currentFingerprint);
|
|
970
|
-
return { success: true };
|
|
971
949
|
}
|
|
972
950
|
catch (error) {
|
|
973
951
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1078,7 +1056,7 @@ function resolveIndexedFiles(dirPath, files, stash) {
|
|
|
1078
1056
|
for (const entry of stash.entries) {
|
|
1079
1057
|
const entryPath = entry.filename
|
|
1080
1058
|
? path.join(dirPath, entry.filename)
|
|
1081
|
-
: matchEntryToFile(entry.name, fileBasenameMap
|
|
1059
|
+
: matchEntryToFile(entry.name, fileBasenameMap);
|
|
1082
1060
|
if (entryPath)
|
|
1083
1061
|
resolved.add(entryPath);
|
|
1084
1062
|
}
|
|
@@ -1197,7 +1175,7 @@ export function buildFileBasenameMap(files) {
|
|
|
1197
1175
|
* try matching the last segment
|
|
1198
1176
|
* 3. No implicit file fallback: ambiguous legacy entries are skipped
|
|
1199
1177
|
*/
|
|
1200
|
-
export function matchEntryToFile(entryName, fileMap
|
|
1178
|
+
export function matchEntryToFile(entryName, fileMap) {
|
|
1201
1179
|
// Exact match on entry name
|
|
1202
1180
|
const exact = fileMap.get(entryName);
|
|
1203
1181
|
if (exact)
|