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
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Source provider factory map.
|
|
6
|
+
*
|
|
7
|
+
* Maps source kind identifiers (e.g. "filesystem", "git", "website", "npm")
|
|
8
|
+
* to factory functions that build {@link SourceProvider} instances from a
|
|
9
|
+
* {@link SourceConfigEntry}.
|
|
10
|
+
*
|
|
11
|
+
* Distinct from the registry-discovery factory (`registry/factory.ts`).
|
|
12
|
+
* Both share `create-provider-registry.ts` for the underlying string→factory
|
|
13
|
+
* map.
|
|
14
|
+
*/
|
|
5
15
|
import { createProviderRegistry } from "../registry/create-provider-registry.js";
|
|
6
16
|
// ── Factory map ─────────────────────────────────────────────────────────────
|
|
7
17
|
const registry = createProviderRegistry();
|
|
@@ -11,19 +21,3 @@ export function registerSourceProvider(type, factory) {
|
|
|
11
21
|
export function resolveSourceProviderFactory(type) {
|
|
12
22
|
return registry.resolve(type);
|
|
13
23
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Build a {@link SourceProvider} for every enabled source in the config that
|
|
16
|
-
* has a registered factory.
|
|
17
|
-
*/
|
|
18
|
-
export function resolveSourceProviders(config) {
|
|
19
|
-
const providers = [];
|
|
20
|
-
for (const entry of getSources(config)) {
|
|
21
|
-
if (entry.enabled === false)
|
|
22
|
-
continue;
|
|
23
|
-
const factory = registry.resolve(entry.type);
|
|
24
|
-
if (factory) {
|
|
25
|
-
providers.push(factory(entry));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return providers;
|
|
29
|
-
}
|
|
@@ -7,9 +7,8 @@ import { registerSourceProvider } from "../provider-factory.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Filesystem source — points at a directory the user already manages.
|
|
9
9
|
*
|
|
10
|
-
* Implements
|
|
11
|
-
*
|
|
12
|
-
* own directory, never refreshed by akm.
|
|
10
|
+
* Implements {@link SourceProvider} with `{ name, kind, path }`. No `sync()`:
|
|
11
|
+
* content is the user's own directory, never refreshed by akm.
|
|
13
12
|
*/
|
|
14
13
|
registerSourceProvider("filesystem", (entry) => {
|
|
15
14
|
if (entry.type !== "filesystem") {
|
|
@@ -5,10 +5,11 @@ import { spawnSync } from "node:child_process";
|
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
|
+
import { isWithin } from "../../core/common.js";
|
|
8
9
|
import { UsageError } from "../../core/errors.js";
|
|
9
10
|
import { getRegistryCacheDir } from "../../core/paths.js";
|
|
10
11
|
import { parseRegistryRef, resolveRegistryArtifact, validateGitRef, validateGitUrl } from "../../registry/resolve.js";
|
|
11
|
-
import { applyAkmIncludeConfig, buildInstallCacheDir,
|
|
12
|
+
import { applyAkmIncludeConfig, buildInstallCacheDir, detectStashRoot, isDirectory } from "./provider-utils.js";
|
|
12
13
|
/**
|
|
13
14
|
* Shared subprocess wrapper for `git` invocations. Disables git's interactive
|
|
14
15
|
* terminal prompt so a missing credential never hangs the process.
|
|
@@ -20,12 +21,109 @@ export function runGit(args, options) {
|
|
|
20
21
|
env: { ...process.env, ...options?.env, GIT_TERMINAL_PROMPT: "0" },
|
|
21
22
|
});
|
|
22
23
|
}
|
|
24
|
+
/** Fetch and classify the current branch against its upstream without changing the worktree. */
|
|
25
|
+
export function inspectGitUpstream(repoDir) {
|
|
26
|
+
const remotes = runGit(["-C", repoDir, "remote"]);
|
|
27
|
+
if (remotes.status !== 0)
|
|
28
|
+
throw new UsageError(`Cannot inspect Git remotes at ${repoDir}: ${remotes.stderr.trim()}`);
|
|
29
|
+
if (!remotes.stdout.trim())
|
|
30
|
+
return { hasRemote: false, ahead: 0, behind: 0 };
|
|
31
|
+
const fetch = runGit(["-C", repoDir, "fetch", "--prune"], { timeout: 120_000 });
|
|
32
|
+
if (fetch.status !== 0)
|
|
33
|
+
throw new UsageError(`Cannot refresh Git target at ${repoDir}: ${fetch.stderr.trim()}`);
|
|
34
|
+
const upstream = runGit(["-C", repoDir, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
|
|
35
|
+
if (upstream.status !== 0 || !upstream.stdout.trim()) {
|
|
36
|
+
throw new UsageError(`Git target at ${repoDir} has a remote but no upstream branch; configure one before writing.`);
|
|
37
|
+
}
|
|
38
|
+
const relation = gitRelation(repoDir, upstream.stdout.trim());
|
|
39
|
+
return { hasRemote: true, upstream: upstream.stdout.trim(), ...relation };
|
|
40
|
+
}
|
|
23
41
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
42
|
+
* Verify the actually-cloned HEAD matches the revision resolved before the
|
|
43
|
+
* clone ran (R-011). This is a presence/identity check only — full
|
|
44
|
+
* content-digest verification across install/update is separate, larger
|
|
45
|
+
* scope work. `expectedRevision` may be an annotated tag's OBJECT id (which
|
|
46
|
+
* `git ls-remote` reports, distinct from the commit it points to), so the
|
|
47
|
+
* comparison peels it (`^{commit}`) before comparing against HEAD; a plain
|
|
48
|
+
* commit SHA peels to itself.
|
|
27
49
|
*/
|
|
28
|
-
export
|
|
50
|
+
export function verifyClonedRevision(cloneDir, url, expectedRevision) {
|
|
51
|
+
if (!expectedRevision)
|
|
52
|
+
return;
|
|
53
|
+
const head = runGit(["-C", cloneDir, "rev-parse", "HEAD"]);
|
|
54
|
+
if (head.status !== 0 || !head.stdout.trim()) {
|
|
55
|
+
throw new UsageError(`Failed to read cloned HEAD at ${cloneDir}: ${head.stderr.trim() || "rev-parse failed"}`);
|
|
56
|
+
}
|
|
57
|
+
const actual = head.stdout.trim();
|
|
58
|
+
const peeled = runGit(["-C", cloneDir, "rev-parse", `${expectedRevision}^{commit}`]);
|
|
59
|
+
const expectedCommit = peeled.status === 0 ? peeled.stdout.trim() : expectedRevision;
|
|
60
|
+
if (actual !== expectedCommit) {
|
|
61
|
+
throw new UsageError(`Cloned HEAD ${actual} at ${cloneDir} does not match the revision resolved from ${url} (${expectedRevision}); refusing to install a mismatched checkout.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function gitRelation(repoDir, target) {
|
|
65
|
+
const result = runGit(["-C", repoDir, "rev-list", "--left-right", "--count", `HEAD...${target}`]);
|
|
66
|
+
const match = result.stdout.trim().match(/^(\d+)\s+(\d+)$/);
|
|
67
|
+
if (result.status !== 0 || !match) {
|
|
68
|
+
throw new UsageError(`Cannot compare Git history at ${repoDir}: ${result.stderr.trim() || "unknown relation"}`);
|
|
69
|
+
}
|
|
70
|
+
return { ahead: Number(match[1]), behind: Number(match[2]) };
|
|
71
|
+
}
|
|
72
|
+
/** Reject a fast-forward that would replace an ignored, untracked local path. */
|
|
73
|
+
export function assertNoIgnoredPathOverwrite(repoDir, targetRevision) {
|
|
74
|
+
const ignored = runGit(["-C", repoDir, "ls-files", "--others", "--ignored", "--exclude-standard", "-z"]);
|
|
75
|
+
if (ignored.status !== 0) {
|
|
76
|
+
throw new UsageError(`Cannot inspect ignored files at ${repoDir}: ${ignored.stderr.trim()}`);
|
|
77
|
+
}
|
|
78
|
+
const ignoredPaths = ignored.stdout.split("\0").filter(Boolean);
|
|
79
|
+
if (ignoredPaths.length === 0)
|
|
80
|
+
return;
|
|
81
|
+
const changed = runGit(["-C", repoDir, "diff", "--name-only", "--no-renames", "-z", "HEAD", targetRevision]);
|
|
82
|
+
if (changed.status !== 0) {
|
|
83
|
+
throw new UsageError(`Cannot inspect incoming Git paths at ${repoDir}: ${changed.stderr.trim()}`);
|
|
84
|
+
}
|
|
85
|
+
const changedPaths = changed.stdout.split("\0").filter(Boolean);
|
|
86
|
+
const conflict = ignoredPaths.find((ignoredPath) => changedPaths.some((changedPath) => changedPath === ignoredPath ||
|
|
87
|
+
changedPath.startsWith(`${ignoredPath}/`) ||
|
|
88
|
+
ignoredPath.startsWith(`${changedPath}/`)));
|
|
89
|
+
if (conflict) {
|
|
90
|
+
throw new UsageError(`Git update would overwrite ignored local path ${path.join(repoDir, conflict)}; move or remove it before update.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function normalizeRemoteUrl(value) {
|
|
94
|
+
return value
|
|
95
|
+
.trim()
|
|
96
|
+
.replace(/\/+$/, "")
|
|
97
|
+
.replace(/\.git$/i, "");
|
|
98
|
+
}
|
|
99
|
+
function replaceDirectory(stagedDir, destination) {
|
|
100
|
+
const backup = `${destination}.backup-${randomBytes(4).toString("hex")}`;
|
|
101
|
+
const hadDestination = fs.existsSync(destination);
|
|
102
|
+
if (hadDestination)
|
|
103
|
+
fs.renameSync(destination, backup);
|
|
104
|
+
try {
|
|
105
|
+
fs.renameSync(stagedDir, destination);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (hadDestination && fs.existsSync(backup))
|
|
109
|
+
fs.renameSync(backup, destination);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
if (hadDestination) {
|
|
113
|
+
try {
|
|
114
|
+
fs.rmSync(backup, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// The new destination is live; a stale backup is safer than reporting a
|
|
118
|
+
// failed replacement after the swap already succeeded.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Materialize a Git install ref (`akm bundle add github:owner/repo` or
|
|
124
|
+
* `akm bundle add git:url`) through the clone, strip, and include-filter pipeline.
|
|
125
|
+
*/
|
|
126
|
+
export async function syncGitRef(ref, options) {
|
|
29
127
|
const parsed = parseRegistryRef(ref);
|
|
30
128
|
if (parsed.source === "github") {
|
|
31
129
|
const githubRef = {
|
|
@@ -39,22 +137,40 @@ export async function syncRegistryGitRef(ref, options) {
|
|
|
39
137
|
return { ...result, source: "github" };
|
|
40
138
|
}
|
|
41
139
|
if (parsed.source !== "git") {
|
|
42
|
-
throw new UsageError(`
|
|
140
|
+
throw new UsageError(`syncGitRef requires a git: or github: ref, got "${ref}"`);
|
|
43
141
|
}
|
|
44
142
|
return doSyncGit(parsed, options);
|
|
45
143
|
}
|
|
46
144
|
async function doSyncGit(parsed, options) {
|
|
145
|
+
validateGitUrl(parsed.url);
|
|
146
|
+
if (parsed.requestedRef)
|
|
147
|
+
validateGitRef(parsed.requestedRef);
|
|
47
148
|
const resolved = await resolveRegistryArtifact(parsed);
|
|
48
149
|
const syncedAt = (options?.now ?? new Date()).toISOString();
|
|
150
|
+
if (options?.writable && options.writableRoot) {
|
|
151
|
+
return syncExistingWritableCheckout(parsed, resolved, options.writableRoot, syncedAt, options.writableRequiredRoots);
|
|
152
|
+
}
|
|
49
153
|
const cacheRootDir = options?.cacheRootDir ?? getRegistryCacheDir();
|
|
50
|
-
const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, resolved.resolvedRevision);
|
|
154
|
+
const cacheDir = buildInstallCacheDir(cacheRootDir, parsed.source, parsed.id, options?.writable ? "writable" : resolved.resolvedRevision);
|
|
51
155
|
const cloneDir = path.join(cacheDir, "clone");
|
|
52
156
|
const extractedDir = path.join(cacheDir, "extracted");
|
|
53
|
-
// Cache hit
|
|
54
|
-
|
|
157
|
+
// Cache hit. Writable installs must remain real checkouts so every mutation
|
|
158
|
+
// can be committed; an older extracted snapshot is not eligible.
|
|
159
|
+
if (isDirectory(extractedDir) && (!options?.writable || isDirectory(path.join(extractedDir, ".git")))) {
|
|
160
|
+
if (options?.writable) {
|
|
161
|
+
const provisionalBundleRoot = detectStashRoot(extractedDir);
|
|
162
|
+
const installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
|
|
163
|
+
if (installRoot !== provisionalBundleRoot) {
|
|
164
|
+
throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
|
|
165
|
+
}
|
|
166
|
+
return syncExistingWritableCheckout(parsed, resolved, detectStashRoot(installRoot), syncedAt, options.writableRequiredRoots);
|
|
167
|
+
}
|
|
55
168
|
try {
|
|
56
|
-
|
|
57
|
-
|
|
169
|
+
if (options?.force) {
|
|
170
|
+
throw new Error("refresh read-only snapshot");
|
|
171
|
+
}
|
|
172
|
+
const provisionalBundleRoot = detectStashRoot(extractedDir);
|
|
173
|
+
const installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
|
|
58
174
|
const stashRoot = detectStashRoot(installRoot);
|
|
59
175
|
if (stashRoot) {
|
|
60
176
|
return {
|
|
@@ -72,20 +188,20 @@ async function doSyncGit(parsed, options) {
|
|
|
72
188
|
};
|
|
73
189
|
}
|
|
74
190
|
}
|
|
75
|
-
catch {
|
|
191
|
+
catch (error) {
|
|
76
192
|
// Cache invalid, re-clone
|
|
77
193
|
}
|
|
78
194
|
}
|
|
195
|
+
const cacheExisted = fs.existsSync(cacheDir);
|
|
79
196
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (parsed.requestedRef)
|
|
83
|
-
validateGitRef(parsed.requestedRef);
|
|
84
|
-
let provisionalKitRoot;
|
|
197
|
+
fs.rmSync(cloneDir, { recursive: true, force: true });
|
|
198
|
+
let provisionalBundleRoot;
|
|
85
199
|
let installRoot;
|
|
86
200
|
let stashRoot;
|
|
87
201
|
try {
|
|
88
|
-
const cloneArgs = ["clone"
|
|
202
|
+
const cloneArgs = ["clone"];
|
|
203
|
+
if (!options?.writable)
|
|
204
|
+
cloneArgs.push("--depth", "1");
|
|
89
205
|
if (parsed.requestedRef) {
|
|
90
206
|
cloneArgs.push("--branch", parsed.requestedRef);
|
|
91
207
|
}
|
|
@@ -94,20 +210,40 @@ async function doSyncGit(parsed, options) {
|
|
|
94
210
|
if (cloneResult.status !== 0) {
|
|
95
211
|
throw new Error(classifyCloneFailure(parsed.url, cloneResult.stderr, cloneResult.error));
|
|
96
212
|
}
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
213
|
+
// R-011: `resolved.resolvedRevision` was resolved via a SEPARATE
|
|
214
|
+
// `git ls-remote` round-trip before this clone ran (resolveGitArtifact /
|
|
215
|
+
// resolveGithubArtifact in registry/resolve.ts) and was never checked
|
|
216
|
+
// against what actually got cloned. Verify it now, while `.git` still
|
|
217
|
+
// exists (the read-only branch below strips it).
|
|
218
|
+
verifyClonedRevision(cloneDir, parsed.url, resolved.resolvedRevision);
|
|
219
|
+
if (options?.writable) {
|
|
220
|
+
const branch = runGit(["-C", cloneDir, "branch", "--show-current"]);
|
|
221
|
+
if (branch.status !== 0 || !branch.stdout.trim()) {
|
|
222
|
+
throw new UsageError("Writable Git installs require a branch ref; tags and detached revisions are read-only.");
|
|
223
|
+
}
|
|
224
|
+
const stagedRoot = detectStashRoot(cloneDir);
|
|
225
|
+
if (applyAkmIncludeConfig(stagedRoot, cacheDir, cloneDir)) {
|
|
226
|
+
throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
|
|
227
|
+
}
|
|
228
|
+
replaceDirectory(cloneDir, extractedDir);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Read-only installs are immutable snapshots and do not retain Git metadata.
|
|
232
|
+
fs.rmSync(path.join(cloneDir, ".git"), { recursive: true, force: true });
|
|
233
|
+
replaceDirectory(cloneDir, extractedDir);
|
|
234
|
+
}
|
|
235
|
+
provisionalBundleRoot = detectStashRoot(extractedDir);
|
|
236
|
+
installRoot = applyAkmIncludeConfig(provisionalBundleRoot, cacheDir, extractedDir) ?? provisionalBundleRoot;
|
|
237
|
+
if (options?.writable && installRoot !== provisionalBundleRoot) {
|
|
238
|
+
throw new UsageError("Writable Git installs do not support akm.include (package.json) filtered snapshots.");
|
|
239
|
+
}
|
|
104
240
|
stashRoot = detectStashRoot(installRoot);
|
|
105
241
|
}
|
|
106
242
|
catch (err) {
|
|
107
|
-
|
|
108
|
-
// don't cause false cache hits on the next install attempt.
|
|
243
|
+
fs.rmSync(cloneDir, { recursive: true, force: true });
|
|
109
244
|
try {
|
|
110
|
-
|
|
245
|
+
if (!cacheExisted)
|
|
246
|
+
fs.rmSync(cacheDir, { recursive: true, force: true });
|
|
111
247
|
}
|
|
112
248
|
catch {
|
|
113
249
|
/* best-effort */
|
|
@@ -128,12 +264,123 @@ async function doSyncGit(parsed, options) {
|
|
|
128
264
|
syncedAt,
|
|
129
265
|
};
|
|
130
266
|
}
|
|
267
|
+
export function syncExistingWritableCheckout(parsed, resolved, contentRoot, syncedAt, requiredRoots = []) {
|
|
268
|
+
const root = path.resolve(contentRoot);
|
|
269
|
+
const repoResult = runGit(["-C", root, "rev-parse", "--show-toplevel"]);
|
|
270
|
+
if (repoResult.status !== 0 || !repoResult.stdout.trim()) {
|
|
271
|
+
throw new UsageError(`Writable Git install at ${root} is not a checkout; refusing to replace it because it may contain local work.`);
|
|
272
|
+
}
|
|
273
|
+
const repoDir = path.resolve(repoResult.stdout.trim());
|
|
274
|
+
if (!isWithin(root, repoDir)) {
|
|
275
|
+
throw new UsageError(`Writable Git content root ${root} resolves outside its checkout at ${repoDir}.`);
|
|
276
|
+
}
|
|
277
|
+
const remote = runGit(["-C", repoDir, "remote", "get-url", "origin"]);
|
|
278
|
+
if (remote.status !== 0 || normalizeRemoteUrl(remote.stdout) !== normalizeRemoteUrl(parsed.url)) {
|
|
279
|
+
throw new UsageError(`Writable Git install at ${root} points at a different origin; refusing to update or replace the checkout.`);
|
|
280
|
+
}
|
|
281
|
+
const status = runGit(["-C", repoDir, "status", "--porcelain"]);
|
|
282
|
+
if (status.status !== 0 || status.stdout.trim()) {
|
|
283
|
+
throw new UsageError(`Writable Git install at ${root} has uncommitted changes; commit or discard them before update.`);
|
|
284
|
+
}
|
|
285
|
+
if (parsed.requestedRef) {
|
|
286
|
+
const branch = runGit(["-C", repoDir, "branch", "--show-current"]);
|
|
287
|
+
const expectedBranch = parsed.requestedRef.replace(/^refs\/heads\//, "");
|
|
288
|
+
if (branch.status !== 0 || !branch.stdout.trim() || branch.stdout.trim() !== expectedBranch) {
|
|
289
|
+
throw new UsageError(`Writable Git install at ${root} is checked out on a different branch than requested ref "${parsed.requestedRef}".`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const shallow = runGit(["-C", repoDir, "rev-parse", "--is-shallow-repository"]);
|
|
293
|
+
const fetchArgs = ["-C", repoDir, "fetch", "--prune", "--tags"];
|
|
294
|
+
if (shallow.status === 0 && shallow.stdout.trim() === "true")
|
|
295
|
+
fetchArgs.push("--unshallow");
|
|
296
|
+
fetchArgs.push("origin");
|
|
297
|
+
if (parsed.requestedRef)
|
|
298
|
+
fetchArgs.push(parsed.requestedRef);
|
|
299
|
+
const fetch = runGit(fetchArgs, { timeout: 120_000 });
|
|
300
|
+
if (fetch.status !== 0) {
|
|
301
|
+
throw new UsageError(`Writable Git install at ${root} could not fetch its expected origin; local work was preserved. ${fetch.stderr.trim()}`);
|
|
302
|
+
}
|
|
303
|
+
let targetRevision = resolved.resolvedRevision;
|
|
304
|
+
if (targetRevision) {
|
|
305
|
+
const resolvedTarget = runGit(["-C", repoDir, "rev-parse", "--verify", `${targetRevision}^{commit}`]);
|
|
306
|
+
if (resolvedTarget.status === 0)
|
|
307
|
+
targetRevision = resolvedTarget.stdout.trim();
|
|
308
|
+
else
|
|
309
|
+
targetRevision = undefined;
|
|
310
|
+
}
|
|
311
|
+
if (!targetRevision) {
|
|
312
|
+
const fallbackTarget = runGit([
|
|
313
|
+
"-C",
|
|
314
|
+
repoDir,
|
|
315
|
+
"rev-parse",
|
|
316
|
+
"--verify",
|
|
317
|
+
parsed.requestedRef ? "FETCH_HEAD" : "@{u}",
|
|
318
|
+
]);
|
|
319
|
+
if (fallbackTarget.status !== 0 || !fallbackTarget.stdout.trim()) {
|
|
320
|
+
throw new UsageError(`Writable Git install at ${root} has no verifiable upstream revision.`);
|
|
321
|
+
}
|
|
322
|
+
targetRevision = fallbackTarget.stdout.trim();
|
|
323
|
+
}
|
|
324
|
+
const relation = gitRelation(repoDir, targetRevision);
|
|
325
|
+
if (relation.ahead > 0) {
|
|
326
|
+
throw new UsageError(`Writable Git install at ${root} has local commits that are not in the requested upstream revision; push or reconcile them before update.`);
|
|
327
|
+
}
|
|
328
|
+
assertNoIgnoredPathOverwrite(repoDir, targetRevision);
|
|
329
|
+
const rootsToPreserve = [...new Set([root, ...requiredRoots.map((candidate) => path.resolve(candidate))])];
|
|
330
|
+
for (const requiredRoot of rootsToPreserve) {
|
|
331
|
+
if (!isWithin(requiredRoot, repoDir)) {
|
|
332
|
+
throw new UsageError(`Configured Git component root ${requiredRoot} resolves outside ${repoDir}.`);
|
|
333
|
+
}
|
|
334
|
+
const relative = path.relative(repoDir, requiredRoot).replaceAll(path.sep, "/");
|
|
335
|
+
if (!relative)
|
|
336
|
+
continue;
|
|
337
|
+
const tree = runGit(["-C", repoDir, "ls-tree", "-d", "-z", "--name-only", targetRevision, "--", relative]);
|
|
338
|
+
const names = tree.stdout.split("\0").filter(Boolean);
|
|
339
|
+
if (tree.status !== 0 || !names.includes(relative)) {
|
|
340
|
+
throw new UsageError(`Writable Git update would remove configured content root ${requiredRoot}; the existing checkout was left unchanged.`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (relation.behind > 0) {
|
|
344
|
+
const statusBeforeMerge = runGit(["-C", repoDir, "status", "--porcelain"]);
|
|
345
|
+
if (statusBeforeMerge.status !== 0 || statusBeforeMerge.stdout.trim()) {
|
|
346
|
+
throw new UsageError(`Writable Git install at ${root} changed while its update was prepared; local work was preserved.`);
|
|
347
|
+
}
|
|
348
|
+
assertNoIgnoredPathOverwrite(repoDir, targetRevision);
|
|
349
|
+
const merge = runGit(["-C", repoDir, "merge", "--ff-only", "--no-overwrite-ignore", targetRevision], {
|
|
350
|
+
timeout: 120_000,
|
|
351
|
+
});
|
|
352
|
+
if (merge.status !== 0) {
|
|
353
|
+
throw new UsageError(`Writable Git install at ${root} cannot fast-forward; local commits were preserved. ${merge.stderr.trim()}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
for (const requiredRoot of rootsToPreserve) {
|
|
357
|
+
if (!isDirectory(requiredRoot)) {
|
|
358
|
+
throw new UsageError(`Writable Git update did not preserve configured content root ${requiredRoot}.`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const head = runGit(["-C", repoDir, "rev-parse", "HEAD"]);
|
|
362
|
+
return {
|
|
363
|
+
id: resolved.id,
|
|
364
|
+
source: resolved.source,
|
|
365
|
+
ref: resolved.ref,
|
|
366
|
+
artifactUrl: resolved.artifactUrl,
|
|
367
|
+
resolvedVersion: resolved.resolvedVersion,
|
|
368
|
+
resolvedRevision: head.status === 0 ? head.stdout.trim() : targetRevision,
|
|
369
|
+
contentDir: root,
|
|
370
|
+
cacheDir: repoDir,
|
|
371
|
+
extractedDir: repoDir,
|
|
372
|
+
writable: true,
|
|
373
|
+
syncedAt,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
131
376
|
export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
|
|
132
377
|
// Stage the clone into a sibling temp dir so that a failed clone never
|
|
133
378
|
// destroys a previously-valid destDir (e.g. when the remote is temporarily
|
|
134
379
|
// unreachable and we have a valid cached copy).
|
|
135
380
|
const tmpDir = `${destDir}.tmp-${randomBytes(4).toString("hex")}`;
|
|
136
|
-
const args = ["clone"
|
|
381
|
+
const args = ["clone"];
|
|
382
|
+
if (!writable)
|
|
383
|
+
args.push("--depth", "1");
|
|
137
384
|
if (ref)
|
|
138
385
|
args.push("--branch", ref);
|
|
139
386
|
args.push(cloneUrl, tmpDir);
|
|
@@ -150,10 +397,7 @@ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
|
|
|
150
397
|
if (fs.existsSync(gitDir))
|
|
151
398
|
fs.rmSync(gitDir, { recursive: true, force: true });
|
|
152
399
|
}
|
|
153
|
-
|
|
154
|
-
if (fs.existsSync(destDir))
|
|
155
|
-
fs.rmSync(destDir, { recursive: true, force: true });
|
|
156
|
-
fs.renameSync(tmpDir, destDir);
|
|
400
|
+
replaceDirectory(tmpDir, destDir);
|
|
157
401
|
}
|
|
158
402
|
catch (err) {
|
|
159
403
|
// Post-clone steps failed — clean up the temp dir to avoid orphaned dirs.
|
|
@@ -164,7 +408,7 @@ export function cloneRepo(cloneUrl, ref, destDir, writable = false) {
|
|
|
164
408
|
// ── Clone-failure classification (#487) ─────────────────────────────────────
|
|
165
409
|
/**
|
|
166
410
|
* Translate git's stderr into an actionable message. Without this, a user
|
|
167
|
-
* who passes a nonexistent or private repo to `akm add` sees:
|
|
411
|
+
* who passes a nonexistent or private repo to `akm bundle add` sees:
|
|
168
412
|
*
|
|
169
413
|
* "could not read Username for 'https://github.com': No such device or
|
|
170
414
|
* address"
|
|
@@ -11,7 +11,7 @@ import { getRegistryIndexCacheDir } from "../../core/paths.js";
|
|
|
11
11
|
import { validateGitUrl } from "../../registry/resolve.js";
|
|
12
12
|
import { withFreshnessCache } from "../freshness.js";
|
|
13
13
|
import { registerSourceProvider } from "../provider-factory.js";
|
|
14
|
-
import { cloneRepo,
|
|
14
|
+
import { assertNoIgnoredPathOverwrite, cloneRepo, inspectGitUpstream, runGit } from "./git-install.js";
|
|
15
15
|
import { sanitizeString } from "./provider-utils.js";
|
|
16
16
|
/** Cache TTL before refreshing the mirrored repo (12 hours). */
|
|
17
17
|
const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
@@ -19,12 +19,11 @@ const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
|
19
19
|
const CACHE_STALE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
20
20
|
/**
|
|
21
21
|
* Git source provider — clones (and re-pulls) a remote repo into a local
|
|
22
|
-
* cache directory. Implements the
|
|
23
|
-
* §2.1, §2.5): `{ name, kind, init, path, sync }`.
|
|
22
|
+
* cache directory. Implements the {@link SourceProvider} interface.
|
|
24
23
|
*
|
|
25
24
|
* Reading is the indexer's job — this class doesn't implement `search` or
|
|
26
|
-
* `show`.
|
|
27
|
-
*
|
|
25
|
+
* `show`. Install refs are materialized by `syncFromRef`; this provider only
|
|
26
|
+
* refreshes configured Git sources.
|
|
28
27
|
*/
|
|
29
28
|
export class GitSourceProvider {
|
|
30
29
|
kind = "git";
|
|
@@ -37,23 +36,11 @@ export class GitSourceProvider {
|
|
|
37
36
|
}
|
|
38
37
|
path() {
|
|
39
38
|
if (this.#path == null) {
|
|
40
|
-
// Lazy resolution: providers are sometimes constructed without an
|
|
41
|
-
// explicit init() call (e.g. by legacy callers that just want the
|
|
42
|
-
// path). Resolve on demand and cache.
|
|
43
39
|
this.#path = resolveGitContentDir(this.#config);
|
|
44
40
|
}
|
|
45
41
|
return this.#path;
|
|
46
42
|
}
|
|
47
43
|
async sync(options) {
|
|
48
|
-
// Two execution modes:
|
|
49
|
-
// 1. Long-lived configured source (config.url) — mirror into the
|
|
50
|
-
// registry-index cache and serve as a read-only working tree.
|
|
51
|
-
// 2. One-shot install ref (options.ref like "git:..." / "github:...") —
|
|
52
|
-
// delegate to the install-time pipeline.
|
|
53
|
-
if (typeof this.#config.options?.ref === "string" && this.#config.options.ref) {
|
|
54
|
-
await syncRegistryGitRef(String(this.#config.options.ref), { force: options?.force });
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
44
|
await syncMirroredRepo(this.#config, { force: options?.force });
|
|
58
45
|
}
|
|
59
46
|
}
|
|
@@ -88,6 +75,7 @@ export async function ensureGitMirror(repo, cachePaths, options) {
|
|
|
88
75
|
ttlMs: CACHE_TTL_MS,
|
|
89
76
|
staleMs: CACHE_STALE_MS,
|
|
90
77
|
force: options?.force === true,
|
|
78
|
+
allowStaleOnRefreshFailure: !(options?.force === true && writable),
|
|
91
79
|
isUsable: () => !requireRepoDir || hasExtractedRepo(cachePaths.repoDir),
|
|
92
80
|
refresh: async () => {
|
|
93
81
|
fs.mkdirSync(cachePaths.rootDir, { recursive: true });
|
|
@@ -134,12 +122,26 @@ export async function syncMirroredRepo(config, options) {
|
|
|
134
122
|
};
|
|
135
123
|
}
|
|
136
124
|
function pullRepo(repoDir) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
125
|
+
const status = runGit(["-C", repoDir, "status", "--porcelain"]);
|
|
126
|
+
if (status.status !== 0 || status.stdout.trim()) {
|
|
127
|
+
throw new UsageError(`Writable Git source at ${repoDir} has uncommitted changes; refusing to update it.`);
|
|
128
|
+
}
|
|
129
|
+
const relation = inspectGitUpstream(repoDir);
|
|
130
|
+
if (relation.ahead > 0) {
|
|
131
|
+
throw new UsageError(`Writable Git source at ${repoDir} has unpushed commits; refusing to update it.`);
|
|
132
|
+
}
|
|
133
|
+
if (relation.behind > 0 && relation.upstream) {
|
|
134
|
+
const statusBeforeMerge = runGit(["-C", repoDir, "status", "--porcelain"]);
|
|
135
|
+
if (statusBeforeMerge.status !== 0 || statusBeforeMerge.stdout.trim()) {
|
|
136
|
+
throw new UsageError(`Writable Git source at ${repoDir} changed while its update was prepared; refusing to merge.`);
|
|
137
|
+
}
|
|
138
|
+
assertNoIgnoredPathOverwrite(repoDir, relation.upstream);
|
|
139
|
+
const merge = runGit(["-C", repoDir, "merge", "--ff-only", "--no-overwrite-ignore", relation.upstream], {
|
|
140
|
+
timeout: 120_000,
|
|
141
|
+
});
|
|
142
|
+
if (merge.status !== 0) {
|
|
143
|
+
throw new Error(`Failed to fast-forward ${repoDir}: ${merge.stderr?.trim() || "unknown error"}`);
|
|
144
|
+
}
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
function hasExtractedRepo(repoDir) {
|