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
|
@@ -4,27 +4,33 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Source operations: list, remove, update.
|
|
6
6
|
*
|
|
7
|
-
* Provides unified operations across all
|
|
8
|
-
* The CLI's `akm list`, `akm remove`, and `akm update` commands are wired here.
|
|
7
|
+
* Provides unified operations across all configured bundle source providers.
|
|
8
|
+
* The CLI's `akm bundle list`, `akm bundle remove`, and `akm bundle update` commands are wired here.
|
|
9
9
|
*
|
|
10
10
|
* 0.9.0 (spec §10.1/§10.2): the retired `installed[]` array is gone — a
|
|
11
11
|
* registry-managed source is now a `bundles.<slug>` entry (the desired locator)
|
|
12
12
|
* paired with a lock entry (the resolved `localRoot`/version). A bundle that has
|
|
13
13
|
* a lock entry is "managed" (installed from a registry and overwritten on
|
|
14
|
-
* `akm update`); a bundle with no lock is a plain filesystem/git/website source.
|
|
14
|
+
* `akm bundle update`); a bundle with no lock is a plain filesystem/git/website source.
|
|
15
15
|
*/
|
|
16
16
|
import fs from "node:fs";
|
|
17
17
|
import path from "node:path";
|
|
18
|
-
import { resolveStashDir } from "../../core/common.js";
|
|
18
|
+
import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
19
19
|
import { getSources, loadConfig } from "../../core/config/config.js";
|
|
20
|
-
import { NotFoundError, UsageError } from "../../core/errors.js";
|
|
20
|
+
import { ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
21
|
+
import { getDbPath } from "../../core/paths.js";
|
|
22
|
+
import { warn } from "../../core/warn.js";
|
|
23
|
+
import { withAssetMutationLease } from "../../indexer/index-writer-lock.js";
|
|
21
24
|
import { akmIndex } from "../../indexer/indexer.js";
|
|
22
|
-
import {
|
|
25
|
+
import { compareAndSwapLockfile, readLockfile } from "../../integrations/lockfile.js";
|
|
23
26
|
import { parseRegistryRef } from "../../registry/resolve.js";
|
|
27
|
+
import { sha256Hex } from "../../runtime.js";
|
|
24
28
|
import { parseGitRepoUrl, syncMirroredRepo } from "../../sources/providers/git.js";
|
|
25
29
|
import { syncFromRef } from "../../sources/providers/sync-from-ref.js";
|
|
26
30
|
import { ensureWebsiteMirror, shouldAllowPrivateWebsiteUrlForTests, } from "../../sources/snapshot-fetchers/website-ingest.js";
|
|
27
|
-
import {
|
|
31
|
+
import { closeDatabase, openReadonlyExistingDatabase } from "../../storage/repositories/index-connection.js";
|
|
32
|
+
import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
|
|
33
|
+
import { removeInstalledRegistryEntry } from "./source-add.js";
|
|
28
34
|
import { removeStash } from "./source-manage.js";
|
|
29
35
|
/** Enumerate the registry-managed installs (lock-backed bundles) in a config. */
|
|
30
36
|
function listManagedInstalls(config) {
|
|
@@ -35,6 +41,7 @@ function listManagedInstalls(config) {
|
|
|
35
41
|
const lock = locks.get(key);
|
|
36
42
|
if (!lock)
|
|
37
43
|
continue; // only lock-backed bundles are registry-managed
|
|
44
|
+
const componentWritable = Object.values(bundle.components ?? {})[0]?.writable;
|
|
38
45
|
out.push({
|
|
39
46
|
bundleKey: key,
|
|
40
47
|
installId: bundle.registryId ?? key,
|
|
@@ -43,12 +50,15 @@ function listManagedInstalls(config) {
|
|
|
43
50
|
localRoot: lock.localRoot ?? "",
|
|
44
51
|
resolvedVersion: lock.resolvedVersion,
|
|
45
52
|
resolvedRevision: lock.resolvedRevision,
|
|
46
|
-
writable: bundle.writable === true,
|
|
53
|
+
writable: componentWritable ?? bundle.writable === true,
|
|
54
|
+
requiredRoots: lock.localRoot
|
|
55
|
+
? Object.values(bundle.components ?? {}).map((component) => path.resolve(lock.localRoot, component.root ?? "."))
|
|
56
|
+
: [],
|
|
47
57
|
});
|
|
48
58
|
}
|
|
49
59
|
return out;
|
|
50
60
|
}
|
|
51
|
-
/** Resolve an `akm remove`/`akm update` target to a managed install, if any. */
|
|
61
|
+
/** Resolve an `akm bundle remove`/`akm bundle update` target to a managed install, if any. */
|
|
52
62
|
function resolveManagedTarget(config, target) {
|
|
53
63
|
const installs = listManagedInstalls(config);
|
|
54
64
|
const byId = installs.find((m) => m.installId === target || m.bundleKey === target);
|
|
@@ -75,49 +85,118 @@ function resolveManagedTarget(config, target) {
|
|
|
75
85
|
return installs.find((m) => m.installId === parsedId);
|
|
76
86
|
return undefined;
|
|
77
87
|
}
|
|
88
|
+
function describeBundleSource(entry) {
|
|
89
|
+
if (entry.path)
|
|
90
|
+
return { kind: "path", locator: entry.path };
|
|
91
|
+
if (entry.git)
|
|
92
|
+
return { kind: "git", locator: entry.git };
|
|
93
|
+
if (entry.website) {
|
|
94
|
+
return {
|
|
95
|
+
kind: "website",
|
|
96
|
+
locator: entry.website.url,
|
|
97
|
+
...(entry.website.maxPages !== undefined ? { maxPages: entry.website.maxPages } : {}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { kind: "npm", locator: entry.npm ?? "" };
|
|
101
|
+
}
|
|
102
|
+
function describeComponents(entry) {
|
|
103
|
+
return Object.entries(entry.components ?? {}).map(([name, component]) => ({
|
|
104
|
+
name,
|
|
105
|
+
...(component.root !== undefined ? { root: component.root } : {}),
|
|
106
|
+
...(component.adapter !== undefined ? { adapter: component.adapter } : {}),
|
|
107
|
+
...(component.writable !== undefined ? { writable: component.writable } : {}),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
function describeLock(entry) {
|
|
111
|
+
if (!entry)
|
|
112
|
+
return null;
|
|
113
|
+
return {
|
|
114
|
+
source: entry.source,
|
|
115
|
+
ref: entry.ref,
|
|
116
|
+
...(entry.resolvedVersion !== undefined ? { resolvedVersion: entry.resolvedVersion } : {}),
|
|
117
|
+
...(entry.resolvedRevision !== undefined ? { resolvedRevision: entry.resolvedRevision } : {}),
|
|
118
|
+
...(entry.integrity !== undefined ? { integrity: entry.integrity } : {}),
|
|
119
|
+
...(entry.localRoot !== undefined ? { localRoot: entry.localRoot } : {}),
|
|
120
|
+
...(entry.manifestDigest !== undefined ? { manifestDigest: entry.manifestDigest } : {}),
|
|
121
|
+
...(entry.adapterIds !== undefined ? { adapterIds: entry.adapterIds } : {}),
|
|
122
|
+
...(entry.installedAt !== undefined ? { installedAt: entry.installedAt } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function readBundleCounts() {
|
|
126
|
+
const counts = new Map();
|
|
127
|
+
const dbPath = getDbPath();
|
|
128
|
+
if (!fs.existsSync(dbPath))
|
|
129
|
+
return counts;
|
|
130
|
+
let db;
|
|
131
|
+
try {
|
|
132
|
+
db = openReadonlyExistingDatabase(dbPath);
|
|
133
|
+
if (!db)
|
|
134
|
+
return counts;
|
|
135
|
+
for (const row of getAllEntries(db)) {
|
|
136
|
+
if (!row.bundleId)
|
|
137
|
+
continue;
|
|
138
|
+
const count = counts.get(row.bundleId) ?? { itemCount: 0, byType: {} };
|
|
139
|
+
count.itemCount += 1;
|
|
140
|
+
count.byType[row.entry.type] = (count.byType[row.entry.type] ?? 0) + 1;
|
|
141
|
+
counts.set(row.bundleId, count);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
warn(`[akm bundle list] failed to read bundle counts from ${dbPath}: ${String(error)}`);
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
if (db) {
|
|
149
|
+
try {
|
|
150
|
+
closeDatabase(db);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// The inventory read is already complete; a close failure is non-fatal.
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return counts;
|
|
158
|
+
}
|
|
78
159
|
export async function akmListSources(input) {
|
|
79
160
|
const stashDir = input?.stashDir ?? resolveStashDir();
|
|
80
161
|
const config = loadConfig();
|
|
81
162
|
const kindFilter = input?.kind;
|
|
82
163
|
const locks = new Map(readLockfile().map((entry) => [entry.id, entry]));
|
|
164
|
+
const counts = readBundleCounts();
|
|
83
165
|
const sources = [];
|
|
84
|
-
// Every source is a bundle. A bundle with a lock entry is registry-managed;
|
|
85
|
-
// otherwise it is a plain filesystem/git/website source.
|
|
86
166
|
for (const bundle of getSources(config)) {
|
|
87
167
|
const key = bundle.name ?? bundle.path ?? bundle.url ?? "unknown";
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
const kind = "managed";
|
|
91
|
-
if (kindFilter && !kindFilter.includes(kind))
|
|
92
|
-
continue;
|
|
93
|
-
const root = lock.localRoot ?? bundle.path ?? "";
|
|
94
|
-
sources.push({
|
|
95
|
-
name: key,
|
|
96
|
-
kind,
|
|
97
|
-
...(root ? { path: root } : {}),
|
|
98
|
-
ref: lock.ref,
|
|
99
|
-
version: lock.resolvedVersion,
|
|
100
|
-
writable: bundle.writable === true,
|
|
101
|
-
status: { exists: root ? directoryExists(root) : false },
|
|
102
|
-
});
|
|
168
|
+
const configured = config.bundles?.[key];
|
|
169
|
+
if (!configured)
|
|
103
170
|
continue;
|
|
104
|
-
|
|
105
|
-
const kind = bundle.type
|
|
171
|
+
const lock = locks.get(key);
|
|
172
|
+
const kind = bundle.type;
|
|
106
173
|
if (kindFilter && !kindFilter.includes(kind))
|
|
107
174
|
continue;
|
|
108
|
-
const
|
|
175
|
+
const root = lock?.localRoot ?? bundle.path ?? "";
|
|
176
|
+
const componentWritable = Object.values(configured.components ?? {})[0]?.writable;
|
|
177
|
+
const bundleCounts = counts.get(key) ?? { itemCount: 0, byType: {} };
|
|
109
178
|
sources.push({
|
|
110
179
|
name: key,
|
|
111
180
|
kind,
|
|
112
|
-
|
|
181
|
+
default: key === config.defaultBundle,
|
|
182
|
+
source: describeBundleSource(configured),
|
|
183
|
+
...(root ? { path: root } : {}),
|
|
184
|
+
...(lock ? { ref: lock.ref } : {}),
|
|
113
185
|
provider: bundle.url != null ? bundle.type : undefined,
|
|
114
|
-
|
|
115
|
-
|
|
186
|
+
...(lock?.resolvedVersion !== undefined ? { version: lock.resolvedVersion } : {}),
|
|
187
|
+
writable: componentWritable ?? bundle.writable ?? kind === "filesystem",
|
|
188
|
+
...(configured.registryId !== undefined ? { registryId: configured.registryId } : {}),
|
|
189
|
+
components: describeComponents(configured),
|
|
190
|
+
lock: describeLock(lock),
|
|
191
|
+
itemCount: bundleCounts.itemCount,
|
|
192
|
+
byType: bundleCounts.byType,
|
|
193
|
+
status: { exists: root ? directoryExists(root) : true },
|
|
116
194
|
});
|
|
117
195
|
}
|
|
118
196
|
return {
|
|
119
197
|
schemaVersion: 1,
|
|
120
|
-
stashDir,
|
|
198
|
+
bundleDir: stashDir,
|
|
199
|
+
defaultBundle: config.defaultBundle ?? null,
|
|
121
200
|
sources,
|
|
122
201
|
totalSources: sources.length,
|
|
123
202
|
};
|
|
@@ -125,7 +204,7 @@ export async function akmListSources(input) {
|
|
|
125
204
|
export async function akmRemove(input) {
|
|
126
205
|
const target = input.target.trim();
|
|
127
206
|
if (!target)
|
|
128
|
-
throw new UsageError("Target is required. Provide the source id, ref, path, URL, or name (e.g. `akm remove npm:@scope/stash` or `akm remove ~/my-stash`).");
|
|
207
|
+
throw new UsageError("Target is required. Provide the source id, ref, path, URL, or name (e.g. `akm bundle remove npm:@scope/stash` or `akm bundle remove ~/my-stash`).");
|
|
129
208
|
const stashDir = input.stashDir ?? resolveStashDir();
|
|
130
209
|
const config = loadConfig();
|
|
131
210
|
// Registry-managed installs (lock-backed bundles) first.
|
|
@@ -133,12 +212,12 @@ export async function akmRemove(input) {
|
|
|
133
212
|
if (managed) {
|
|
134
213
|
const updatedConfig = await removeInstalledRegistryEntry(managed.installId);
|
|
135
214
|
if (managed.source !== "local" && managed.localRoot) {
|
|
136
|
-
cleanupDirectoryBestEffort(managed.localRoot);
|
|
215
|
+
cleanupDirectoryBestEffort(managed.localRoot, "remove");
|
|
137
216
|
}
|
|
138
217
|
const index = await akmIndex({ stashDir });
|
|
139
218
|
return {
|
|
140
219
|
schemaVersion: 1,
|
|
141
|
-
stashDir,
|
|
220
|
+
bundleDir: stashDir,
|
|
142
221
|
target,
|
|
143
222
|
removed: {
|
|
144
223
|
id: managed.installId,
|
|
@@ -149,7 +228,6 @@ export async function akmRemove(input) {
|
|
|
149
228
|
},
|
|
150
229
|
config: {
|
|
151
230
|
sourceCount: getSources(updatedConfig).length,
|
|
152
|
-
installedKitCount: readLockfile().length,
|
|
153
231
|
},
|
|
154
232
|
index: {
|
|
155
233
|
mode: index.mode,
|
|
@@ -169,7 +247,7 @@ export async function akmRemove(input) {
|
|
|
169
247
|
const updatedConfig = loadConfig();
|
|
170
248
|
return {
|
|
171
249
|
schemaVersion: 1,
|
|
172
|
-
stashDir,
|
|
250
|
+
bundleDir: stashDir,
|
|
173
251
|
target,
|
|
174
252
|
removed: {
|
|
175
253
|
id: removedEntry.name ?? removedEntry.path ?? removedEntry.url ?? target,
|
|
@@ -180,7 +258,6 @@ export async function akmRemove(input) {
|
|
|
180
258
|
},
|
|
181
259
|
config: {
|
|
182
260
|
sourceCount: getSources(updatedConfig).length,
|
|
183
|
-
installedKitCount: readLockfile().length,
|
|
184
261
|
},
|
|
185
262
|
index: {
|
|
186
263
|
mode: index.mode,
|
|
@@ -192,18 +269,19 @@ export async function akmRemove(input) {
|
|
|
192
269
|
}
|
|
193
270
|
// ── akmUpdate helpers ────────────────────────────────────────────────────────
|
|
194
271
|
/** Build a standard UpdateResponse summary block from the current config and index run. */
|
|
195
|
-
async function buildUpdateResponse(stashDir, target, all, processed,
|
|
196
|
-
const index = await akmIndex({ stashDir, ...(full ? { full: true } : {}) });
|
|
272
|
+
async function buildUpdateResponse(stashDir, target, all, processed, opts) {
|
|
273
|
+
const index = opts?.index ?? (await akmIndex({ stashDir, ...(opts?.full ? { full: true } : {}) }));
|
|
197
274
|
const finalConfig = loadConfig();
|
|
198
275
|
return {
|
|
199
276
|
schemaVersion: 1,
|
|
200
|
-
stashDir,
|
|
277
|
+
bundleDir: stashDir,
|
|
201
278
|
target,
|
|
202
279
|
all,
|
|
203
280
|
processed,
|
|
281
|
+
...(opts?.plainSynced?.length ? { plainSynced: opts.plainSynced } : {}),
|
|
282
|
+
...(opts?.skipped?.length ? { skipped: opts.skipped } : {}),
|
|
204
283
|
config: {
|
|
205
284
|
sourceCount: getSources(finalConfig).length,
|
|
206
|
-
installedKitCount: readLockfile().length,
|
|
207
285
|
},
|
|
208
286
|
index: {
|
|
209
287
|
mode: index.mode,
|
|
@@ -213,30 +291,87 @@ async function buildUpdateResponse(stashDir, target, all, processed, full = fals
|
|
|
213
291
|
},
|
|
214
292
|
};
|
|
215
293
|
}
|
|
216
|
-
/**
|
|
217
|
-
|
|
294
|
+
/**
|
|
295
|
+
* Sync a git-mirrored (plain) source in place. Returns the {@link UpdatePlainSyncedItem}
|
|
296
|
+
* record instead of building the full response, so `--all` can batch several of
|
|
297
|
+
* these into one response alongside managed installs (R-015).
|
|
298
|
+
*/
|
|
299
|
+
async function syncGitPlainSource(gitSource) {
|
|
218
300
|
await syncMirroredRepo(gitSource, { force: true, writable: gitSource.writable === true });
|
|
219
|
-
return
|
|
301
|
+
return { id: gitSource.name ?? gitSource.url ?? "", kind: "git", ref: gitSource.url ?? "" };
|
|
220
302
|
}
|
|
221
|
-
/** Re-crawl a website (plain) source
|
|
222
|
-
async function
|
|
303
|
+
/** Re-crawl a website (plain) source in place. See {@link syncGitPlainSource}. */
|
|
304
|
+
async function syncWebsitePlainSource(websiteSource) {
|
|
223
305
|
// TODO: full incremental re-crawl with delta tracking (#19)
|
|
224
306
|
await ensureWebsiteMirror(websiteSource, {
|
|
225
307
|
requireStashDir: true,
|
|
226
308
|
force: true,
|
|
227
309
|
...(shouldAllowPrivateWebsiteUrlForTests(websiteSource.url ?? "") ? { allowPrivateHosts: true } : {}),
|
|
228
310
|
});
|
|
229
|
-
return
|
|
311
|
+
return { id: websiteSource.name ?? websiteSource.url ?? "", kind: "website", ref: websiteSource.url ?? "" };
|
|
312
|
+
}
|
|
313
|
+
/** Sync a git-mirrored (plain) source and return an UpdateResponse (single-target path). */
|
|
314
|
+
async function updateGitSource(stashDir, target, all, gitSource) {
|
|
315
|
+
const synced = await syncGitPlainSource(gitSource);
|
|
316
|
+
return buildUpdateResponse(stashDir, target, all, [], { full: true, plainSynced: [synced] });
|
|
317
|
+
}
|
|
318
|
+
/** Re-crawl a website (plain) source and return an UpdateResponse (single-target path). */
|
|
319
|
+
async function updateWebsiteSource(stashDir, target, all, websiteSource) {
|
|
320
|
+
const synced = await syncWebsitePlainSource(websiteSource);
|
|
321
|
+
return buildUpdateResponse(stashDir, target, all, [], { plainSynced: [synced] });
|
|
230
322
|
}
|
|
231
|
-
/**
|
|
232
|
-
|
|
323
|
+
/**
|
|
324
|
+
* A plain (lockless) npm bundle has no deterministic content path — unlike
|
|
325
|
+
* git/website, resolving an npm package requires a registry round-trip to
|
|
326
|
+
* pick a concrete version/tarball, which is exactly what the lock records.
|
|
327
|
+
* So a plain npm source is synced via the same registry-install pipeline as
|
|
328
|
+
* `akm bundle add <package>` and PROMOTED to a registry-managed (lock-backed)
|
|
329
|
+
* install as a side effect of its first successful sync; it is reported via
|
|
330
|
+
* `processed` like any other managed install from then on. Building a
|
|
331
|
+
* {@link ManagedInstall} view onto the plain entry lets this reuse
|
|
332
|
+
* {@link updateManagedInstall} verbatim rather than duplicating its lock/config
|
|
333
|
+
* bookkeeping.
|
|
334
|
+
*/
|
|
335
|
+
function managedInstallViewOfPlainNpm(npmSource) {
|
|
336
|
+
const spec = npmSource.path ?? "";
|
|
337
|
+
const ref = spec.startsWith("npm:") ? spec : `npm:${spec}`;
|
|
338
|
+
const id = npmSource.name ?? ref;
|
|
339
|
+
return {
|
|
340
|
+
bundleKey: id,
|
|
341
|
+
installId: id,
|
|
342
|
+
source: "npm",
|
|
343
|
+
ref,
|
|
344
|
+
localRoot: "",
|
|
345
|
+
resolvedVersion: undefined,
|
|
346
|
+
resolvedRevision: undefined,
|
|
347
|
+
writable: false,
|
|
348
|
+
requiredRoots: [],
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Sync a single registry-managed install and return the processed record.
|
|
353
|
+
*
|
|
354
|
+
* `yes` gates ONLY the destructive branch below (deleting a previous
|
|
355
|
+
* `localRoot` whose content moved) — it has no effect on the sync itself. A
|
|
356
|
+
* normal refresh that resolves the SAME content directory (the overwhelming
|
|
357
|
+
* majority of updates) never reaches that branch, so it never prompts and
|
|
358
|
+
* never requires `--yes` (F1/R-058: `update` must stay usable bare in
|
|
359
|
+
* scripts; only the branch that can `rm -rf` needs a gate, not the whole
|
|
360
|
+
* command).
|
|
361
|
+
*/
|
|
362
|
+
async function updateManagedInstall(managed, force, yes, stashDir) {
|
|
233
363
|
// No pre-cleanup of the old root, even under --force: the providers already
|
|
234
364
|
// re-materialize staging-first (git clones into a `.tmp-*` sibling and swaps
|
|
235
365
|
// only on success), so destroying `managed.localRoot` BEFORE `syncFromRef`
|
|
236
366
|
// succeeds would turn any sync failure (network down, bad ref) into losing a
|
|
237
|
-
// previously-working install. The old root is cleaned up below
|
|
238
|
-
// lock
|
|
239
|
-
const synced = await syncFromRef(managed.ref, {
|
|
367
|
+
// previously-working install. The old root is cleaned up below only after
|
|
368
|
+
// config/lock publication and reindexing both succeed.
|
|
369
|
+
const synced = await syncFromRef(managed.ref, {
|
|
370
|
+
force,
|
|
371
|
+
writable: managed.writable,
|
|
372
|
+
...(managed.writable && managed.localRoot ? { writableRoot: managed.localRoot } : {}),
|
|
373
|
+
...(managed.writable && managed.requiredRoots.length > 0 ? { writableRequiredRoots: managed.requiredRoots } : {}),
|
|
374
|
+
});
|
|
240
375
|
const installedEntry = {
|
|
241
376
|
id: managed.installId,
|
|
242
377
|
// Preserve the original source classification. syncFromRef() re-derives the
|
|
@@ -252,44 +387,100 @@ async function updateManagedInstall(managed, force) {
|
|
|
252
387
|
installedAt: synced.syncedAt,
|
|
253
388
|
writable: synced.writable ?? managed.writable,
|
|
254
389
|
};
|
|
255
|
-
const
|
|
256
|
-
await upsertLockEntry({
|
|
257
|
-
id: bundleId,
|
|
258
|
-
// Preserve the STORED install kind: a `github:`-ref entry recorded as
|
|
259
|
-
// source "git" must not be reclassified by the sync flow's re-derivation
|
|
260
|
-
// (the issue this file's update pin exists for).
|
|
261
|
-
source: managed.source,
|
|
262
|
-
ref: synced.ref,
|
|
263
|
-
resolvedVersion: synced.resolvedVersion,
|
|
264
|
-
resolvedRevision: synced.resolvedRevision,
|
|
265
|
-
integrity: synced.integrity ?? (synced.source === "local" ? "local" : undefined),
|
|
266
|
-
// §10.2 resolved lock state the sync flow has on hand.
|
|
267
|
-
localRoot: synced.contentDir,
|
|
268
|
-
installedAt: synced.syncedAt,
|
|
269
|
-
});
|
|
270
|
-
if (managed.localRoot &&
|
|
390
|
+
const movedRoot = managed.localRoot !== "" &&
|
|
271
391
|
path.resolve(managed.localRoot) !== path.resolve(synced.contentDir) &&
|
|
272
|
-
managed.source !== "local"
|
|
273
|
-
|
|
392
|
+
managed.source !== "local" &&
|
|
393
|
+
!managed.writable;
|
|
394
|
+
if (movedRoot) {
|
|
395
|
+
const { confirmDestructive } = await import("../../cli/confirm.js");
|
|
396
|
+
const confirmed = await confirmDestructive(`Update resolved a new content directory for "${managed.installId}" (${synced.contentDir}) and would delete the previous install directory at ${managed.localRoot}. This cannot be undone.`, { yes });
|
|
397
|
+
if (!confirmed)
|
|
398
|
+
throw new UsageError(`Update cancelled for "${managed.installId}"; no configuration was changed.`);
|
|
274
399
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
400
|
+
return withAssetMutationLease("source-update", async () => {
|
|
401
|
+
const config = loadConfig();
|
|
402
|
+
const oldLocks = readLockfile();
|
|
403
|
+
if (!config.bundles?.[managed.bundleKey]) {
|
|
404
|
+
throw new ConfigError(`Managed source "${managed.installId}" disappeared before update publication.`);
|
|
405
|
+
}
|
|
406
|
+
const desiredLock = {
|
|
407
|
+
id: managed.bundleKey,
|
|
408
|
+
// Preserve the STORED install kind: a `github:`-ref entry recorded as
|
|
409
|
+
// source "git" must not be reclassified by the sync flow's re-derivation.
|
|
410
|
+
source: managed.source,
|
|
411
|
+
ref: synced.ref,
|
|
412
|
+
resolvedVersion: synced.resolvedVersion,
|
|
413
|
+
resolvedRevision: synced.resolvedRevision,
|
|
414
|
+
integrity: synced.integrity ?? (synced.source === "local" ? "local" : undefined),
|
|
415
|
+
localRoot: synced.contentDir,
|
|
416
|
+
installedAt: synced.syncedAt,
|
|
417
|
+
};
|
|
418
|
+
const desiredLocks = [...oldLocks.filter((entry) => entry.id !== desiredLock.id), desiredLock];
|
|
419
|
+
const desiredLockGeneration = generationHash(desiredLocks);
|
|
420
|
+
if (!(await compareAndSwapLockfile(oldLocks, desiredLocks)))
|
|
421
|
+
throw concurrentGenerationError(managed);
|
|
422
|
+
const index = await akmIndex({ stashDir });
|
|
423
|
+
if (generationHash(readLockfile()) !== desiredLockGeneration)
|
|
424
|
+
throw concurrentGenerationError(managed);
|
|
425
|
+
if (movedRoot) {
|
|
426
|
+
const currentConfig = loadConfig();
|
|
427
|
+
const currentLocks = readLockfile();
|
|
428
|
+
if (referencesRoot(managed.localRoot, currentConfig, currentLocks)) {
|
|
429
|
+
warn(`[akm bundle update] kept previous install directory at ${managed.localRoot} because another configured or locked bundle still references it.`);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
cleanupDirectoryBestEffort(managed.localRoot, "update");
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
const versionChanged = (managed.resolvedVersion ?? "") !== (synced.resolvedVersion ?? "");
|
|
436
|
+
const revisionChanged = (managed.resolvedRevision ?? "") !== (synced.resolvedRevision ?? "");
|
|
437
|
+
return {
|
|
438
|
+
index,
|
|
439
|
+
item: {
|
|
440
|
+
id: managed.installId,
|
|
441
|
+
source: managed.source,
|
|
442
|
+
ref: managed.ref,
|
|
443
|
+
previous: {
|
|
444
|
+
resolvedVersion: managed.resolvedVersion,
|
|
445
|
+
resolvedRevision: managed.resolvedRevision,
|
|
446
|
+
cacheDir: managed.localRoot,
|
|
447
|
+
},
|
|
448
|
+
installed: { ...installedEntry, extractedDir: synced.extractedDir },
|
|
449
|
+
changed: {
|
|
450
|
+
version: versionChanged,
|
|
451
|
+
revision: revisionChanged,
|
|
452
|
+
any: versionChanged || revisionChanged,
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function generationHash(value) {
|
|
459
|
+
return sha256Hex(JSON.stringify(value));
|
|
460
|
+
}
|
|
461
|
+
function referencesRoot(target, config, locks) {
|
|
462
|
+
const resolvedTarget = path.resolve(target);
|
|
463
|
+
const locksById = new Map(locks.map((entry) => [entry.id, entry]));
|
|
464
|
+
for (const [bundleId, bundle] of Object.entries(config.bundles ?? {})) {
|
|
465
|
+
const root = bundle.path ?? locksById.get(bundleId)?.localRoot;
|
|
466
|
+
if (!root)
|
|
467
|
+
continue;
|
|
468
|
+
if (isRootAtOrBelow(root, resolvedTarget))
|
|
469
|
+
return true;
|
|
470
|
+
for (const component of Object.values(bundle.components ?? {})) {
|
|
471
|
+
if (isRootAtOrBelow(path.resolve(root, component.root ?? "."), resolvedTarget))
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return locks.some((entry) => entry.localRoot && isRootAtOrBelow(entry.localRoot, resolvedTarget));
|
|
476
|
+
}
|
|
477
|
+
function isRootAtOrBelow(candidate, root) {
|
|
478
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
479
|
+
const lexicallyWithin = relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
480
|
+
return lexicallyWithin || isWithin(candidate, root);
|
|
481
|
+
}
|
|
482
|
+
function concurrentGenerationError(managed) {
|
|
483
|
+
return new ConfigError(`Managed source "${managed.installId}" changed concurrently; retained both materialized roots and deleted nothing.`, "INVALID_CONFIG_FILE", "Inspect `akm bundle list` and retry the update after the concurrent source operation completes.");
|
|
293
484
|
}
|
|
294
485
|
// ── akmUpdate dispatcher ─────────────────────────────────────────────────────
|
|
295
486
|
export async function akmUpdate(input) {
|
|
@@ -297,13 +488,15 @@ export async function akmUpdate(input) {
|
|
|
297
488
|
const target = input?.target?.trim();
|
|
298
489
|
const all = input?.all === true;
|
|
299
490
|
const force = input?.force === true;
|
|
491
|
+
const yes = input?.yes === true;
|
|
300
492
|
const config = loadConfig();
|
|
301
493
|
const managedInstalls = listManagedInstalls(config);
|
|
302
494
|
if (target && !all) {
|
|
303
495
|
// Registry-managed install (lock-backed) — re-download from its locator.
|
|
304
496
|
const managed = resolveManagedTarget(config, target);
|
|
305
497
|
if (managed) {
|
|
306
|
-
|
|
498
|
+
const updated = await updateManagedInstall(managed, force, yes, stashDir);
|
|
499
|
+
return buildUpdateResponse(stashDir, target, all, [updated.item], { index: updated.index });
|
|
307
500
|
}
|
|
308
501
|
// Plain git / website source (bundles without a lock) — provider re-sync.
|
|
309
502
|
const stashes = getSources(config);
|
|
@@ -345,13 +538,93 @@ export async function akmUpdate(input) {
|
|
|
345
538
|
});
|
|
346
539
|
if (websiteMatch)
|
|
347
540
|
return updateWebsiteSource(stashDir, target, all, websiteMatch);
|
|
541
|
+
// Plain npm source (bundle without a lock) — sync via the registry
|
|
542
|
+
// pipeline and promote to a managed install (see
|
|
543
|
+
// managedInstallViewOfPlainNpm's doc comment for why npm can't stay
|
|
544
|
+
// plain the way git/website do).
|
|
545
|
+
const npmMatch = stashes.find((s) => {
|
|
546
|
+
if (s.type !== "npm")
|
|
547
|
+
return false;
|
|
548
|
+
if (s.name === target)
|
|
549
|
+
return true;
|
|
550
|
+
if (s.path === target)
|
|
551
|
+
return true;
|
|
552
|
+
return false;
|
|
553
|
+
});
|
|
554
|
+
if (npmMatch) {
|
|
555
|
+
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(npmMatch), force, yes, stashDir);
|
|
556
|
+
return buildUpdateResponse(stashDir, target, all, [updated.item], { index: updated.index });
|
|
557
|
+
}
|
|
348
558
|
}
|
|
349
|
-
const
|
|
559
|
+
const enabledManagedInstalls = all
|
|
560
|
+
? managedInstalls.filter((managed) => config.bundles?.[managed.bundleKey]?.enabled !== false)
|
|
561
|
+
: managedInstalls;
|
|
562
|
+
const selected = selectManagedTargets(config, enabledManagedInstalls, target, all);
|
|
350
563
|
const processed = [];
|
|
564
|
+
let latestManagedIndex;
|
|
351
565
|
for (const managed of selected) {
|
|
352
|
-
|
|
566
|
+
const updated = await updateManagedInstall(managed, force, yes, stashDir);
|
|
567
|
+
processed.push(updated.item);
|
|
568
|
+
latestManagedIndex = updated.index;
|
|
569
|
+
}
|
|
570
|
+
// `--all` must account for EVERY configured source, not only the
|
|
571
|
+
// registry-managed (lock-backed) ones (R-015) — `selectManagedTargets`
|
|
572
|
+
// above returns only `installs` for `all`, so plain sources were
|
|
573
|
+
// previously never even looked at. Git/npm plain sources are synced here
|
|
574
|
+
// too (git in place; npm promoted to managed, same as the single-target
|
|
575
|
+
// path above); website/filesystem sources have no `--all` sync path, so
|
|
576
|
+
// they are reported as skipped with the same explanatory wording
|
|
577
|
+
// `selectManagedTargets` already uses for a single unmatched target.
|
|
578
|
+
let plainSynced;
|
|
579
|
+
let skipped;
|
|
580
|
+
let sawGitSync = false;
|
|
581
|
+
if (all) {
|
|
582
|
+
const managedKeys = new Set(managedInstalls.map((m) => m.bundleKey));
|
|
583
|
+
const plainSources = getSources(config).filter((source) => source.enabled !== false && !managedKeys.has(source.name ?? ""));
|
|
584
|
+
plainSynced = [];
|
|
585
|
+
skipped = [];
|
|
586
|
+
for (const plain of plainSources) {
|
|
587
|
+
const id = plain.name ?? plain.path ?? plain.url ?? "";
|
|
588
|
+
try {
|
|
589
|
+
if (plain.type === "git") {
|
|
590
|
+
plainSynced.push(await syncGitPlainSource(plain));
|
|
591
|
+
sawGitSync = true;
|
|
592
|
+
}
|
|
593
|
+
else if (plain.type === "npm") {
|
|
594
|
+
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(plain), force, yes, stashDir);
|
|
595
|
+
processed.push(updated.item);
|
|
596
|
+
latestManagedIndex = updated.index;
|
|
597
|
+
}
|
|
598
|
+
else if (plain.type === "website") {
|
|
599
|
+
skipped.push({
|
|
600
|
+
id,
|
|
601
|
+
kind: "website",
|
|
602
|
+
reason: `website caching not yet implemented for --all; run \`akm bundle update ${id}\` to re-mirror this source individually.`,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
skipped.push({
|
|
607
|
+
id,
|
|
608
|
+
kind: plain.type,
|
|
609
|
+
reason: "reflects your files in place and has no remote to sync; run `akm index` to refresh the search index.",
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
catch (err) {
|
|
614
|
+
skipped.push({
|
|
615
|
+
id,
|
|
616
|
+
kind: plain.type,
|
|
617
|
+
reason: `sync failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
353
621
|
}
|
|
354
|
-
return buildUpdateResponse(stashDir, target, all, processed
|
|
622
|
+
return buildUpdateResponse(stashDir, target, all, processed, {
|
|
623
|
+
full: sawGitSync,
|
|
624
|
+
plainSynced,
|
|
625
|
+
skipped,
|
|
626
|
+
...(!sawGitSync && latestManagedIndex ? { index: latestManagedIndex } : {}),
|
|
627
|
+
});
|
|
355
628
|
}
|
|
356
629
|
function selectManagedTargets(config, installs, target, all) {
|
|
357
630
|
if (all && target) {
|
|
@@ -381,18 +654,29 @@ function selectManagedTargets(config, installs, target, all) {
|
|
|
381
654
|
if (stashMatch) {
|
|
382
655
|
if (stashMatch.type === "website") {
|
|
383
656
|
throw new UsageError(`"${target}" is a website source — website caching not yet implemented for --all. ` +
|
|
384
|
-
`Run \`akm update ${target}\` to re-mirror this source individually.`, "TARGET_NOT_UPDATABLE");
|
|
657
|
+
`Run \`akm bundle update ${target}\` to re-mirror this source individually.`, "TARGET_NOT_UPDATABLE");
|
|
385
658
|
}
|
|
386
659
|
throw new UsageError(`"${target}" is a local directory — it reflects your files in place. To refresh the search index, run: akm index`, "TARGET_NOT_UPDATABLE");
|
|
387
660
|
}
|
|
388
661
|
throw new NotFoundError(`No matching source for target: ${target}`, "SOURCE_NOT_FOUND");
|
|
389
662
|
}
|
|
390
|
-
|
|
663
|
+
/**
|
|
664
|
+
* Best-effort removal of a directory that is no longer referenced by config
|
|
665
|
+
* or the lockfile. `context` labels the call site (e.g. "remove", "update")
|
|
666
|
+
* in the warning so an operator can tell which command left the directory
|
|
667
|
+
* behind. Failure does not throw — callers already committed the config/lock
|
|
668
|
+
* change that made `target` orphaned — but it must not be silent either
|
|
669
|
+
* (F1/R-058): a swallowed `rmSync` error used to leave no trace anywhere the
|
|
670
|
+
* caller could inspect, so a confirmed deletion that then failed (permission
|
|
671
|
+
* error, file in use, …) looked identical to a successful one.
|
|
672
|
+
*/
|
|
673
|
+
function cleanupDirectoryBestEffort(target, context) {
|
|
391
674
|
try {
|
|
392
675
|
fs.rmSync(target, { recursive: true, force: true });
|
|
393
676
|
}
|
|
394
|
-
catch {
|
|
395
|
-
|
|
677
|
+
catch (err) {
|
|
678
|
+
warn(`[akm ${context}] failed to remove directory ${target}: ${err instanceof Error ? err.message : String(err)}. ` +
|
|
679
|
+
"Remove it manually if it is no longer needed.");
|
|
396
680
|
}
|
|
397
681
|
}
|
|
398
682
|
function directoryExists(target) {
|