akm-cli 0.9.0-rc.1 → 0.9.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1190 -52
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +13 -9
- package/dist/commands/env/env-cli.js +76 -159
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +75 -228
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -855
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -73
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -58
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +8 -3
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +78 -13
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +48 -279
- package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
- package/dist/integrations/harnesses/aider/index.js +2 -12
- package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
- package/dist/integrations/harnesses/amazonq/index.js +3 -17
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +1 -14
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
- package/dist/integrations/harnesses/codex/index.js +2 -13
- package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
- package/dist/integrations/harnesses/copilot/index.js +1 -13
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
- package/dist/integrations/harnesses/gemini/index.js +1 -13
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +31 -33
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +1 -18
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
- package/dist/integrations/harnesses/openhands/index.js +2 -12
- package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
- package/dist/integrations/harnesses/pi/index.js +3 -16
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +38 -34
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +24 -40
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +15 -281
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +8 -40
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1375
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +1 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +68 -35
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/workflows/authoring/authoring.js +96 -148
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -20
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +25 -59
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +157 -94
- package/dist/workflows/exec/report.js +365 -200
- package/dist/workflows/exec/run-workflow.js +46 -40
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +239 -205
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/ir/compile.js +141 -283
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +537 -1
- package/dist/workflows/parser.js +878 -306
- package/dist/workflows/program/expressions.js +20 -208
- package/dist/workflows/program/schema.js +7 -10
- package/dist/workflows/renderer.js +99 -121
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +200 -113
- package/dist/workflows/runtime/unit-checkin.js +1 -1
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +4 -26
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/{dist/schemas → schemas}/akm-workflow.json +127 -82
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -316
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1414
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/schemas/akm-config.json +0 -14225
- package/dist/scripts/migrate-storage.js +0 -13169
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
- package/dist/workflows/db.js +0 -350
- package/dist/workflows/exec/watch.js +0 -116
- package/dist/workflows/program/parser.js +0 -760
- package/dist/workflows/program/project.js +0 -105
- package/docs/data-and-telemetry.md +0 -227
- package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
package/dist/setup/setup.js
CHANGED
|
@@ -9,19 +9,22 @@
|
|
|
9
9
|
* Collects all choices and writes config once at the end.
|
|
10
10
|
*
|
|
11
11
|
* This module holds the wizard orchestration; the individual wizard steps,
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* engine writers, prompt shims, provider table, and semantic-asset preparation
|
|
13
|
+
* live in sibling modules (`steps/*`, `engine-config`, `prompt`,
|
|
14
14
|
* `providers`, `semantic-assets`).
|
|
15
15
|
*/
|
|
16
16
|
import { promises as dnsPromises } from "node:dns";
|
|
17
17
|
import fs from "node:fs";
|
|
18
18
|
import os from "node:os";
|
|
19
19
|
import path from "node:path";
|
|
20
|
+
import { isDeepStrictEqual } from "node:util";
|
|
20
21
|
import * as p from "../cli/clack.js";
|
|
21
22
|
import { akmInit } from "../commands/sources/init.js";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { deriveBundleIds } from "../core/bundle-id.js";
|
|
24
|
+
import { bundleEntryToSourceEntry, DEFAULT_CONFIG, loadUserConfig, mutateConfigWithPrecommit, parseAndValidateConfigText, primaryBundlePath, validateCompleteConfig, } from "../core/config/config.js";
|
|
25
|
+
import { readConfigText } from "../core/config/config-io.js";
|
|
26
|
+
import { listTopLevelConfigKeys } from "../core/config/config-schema.js";
|
|
27
|
+
import { deepMergeConfig } from "../core/config/deep-merge.js";
|
|
25
28
|
import { ConfigError, UsageError } from "../core/errors.js";
|
|
26
29
|
import { getConfigPath, getDefaultStashDir, isTransientStashPath } from "../core/paths.js";
|
|
27
30
|
import { warn } from "../core/warn.js";
|
|
@@ -29,11 +32,12 @@ import { akmIndex } from "../indexer/indexer.js";
|
|
|
29
32
|
import { clearSemanticStatus, deriveSemanticProviderFingerprint, writeSemanticStatus, } from "../indexer/search/semantic-status.js";
|
|
30
33
|
import { detectAgentCliProfiles, pickDefaultAgentProfile } from "../integrations/agent/index.js";
|
|
31
34
|
import { defaultProfileName } from "../integrations/harnesses/index.js";
|
|
35
|
+
import { readLockfile } from "../integrations/lockfile.js";
|
|
32
36
|
import { probeLlmCapabilities } from "../llm/client.js";
|
|
33
|
-
import { getOutputMode } from "../output/context.js";
|
|
34
37
|
import { detectEnvironment, detectLMStudio, renderDetectionSummary, } from "./detect.js";
|
|
38
|
+
import { upsertDetectedAgentEngine, upsertDetectedLlmEngine, verifyOpenAiCompatibleEndpoint } from "./detected-engines.js";
|
|
39
|
+
import { readCurrentLlmEngine, writeAgentEngines, writeLlmEngine } from "./engine-config.js";
|
|
35
40
|
import { detectHarnessConfigs } from "./harness-config-import.js";
|
|
36
|
-
import { applyLegacyAgent, applyLegacyLlm } from "./legacy-config.js";
|
|
37
41
|
import { bail, prompt } from "./prompt.js";
|
|
38
42
|
import { PROVIDER_DEFAULTS } from "./providers.js";
|
|
39
43
|
import { prepareSemanticSearchAssets } from "./semantic-assets.js";
|
|
@@ -44,21 +48,21 @@ import { printCapabilitySummary, stepAgentCliDetection, stepAgentPlatforms, step
|
|
|
44
48
|
import { stepSemanticSearch } from "./steps/semantic.js";
|
|
45
49
|
import { stepAdditionalSources, stepAddSources, stepRegistries } from "./steps/sources.js";
|
|
46
50
|
import { stepStashDir } from "./steps/stashdir.js";
|
|
47
|
-
import {
|
|
51
|
+
import { stepScheduledTasks } from "./steps/tasks.js";
|
|
48
52
|
// ── Setup sandbox guard ─────────────────────────────────────────────────────
|
|
49
53
|
/**
|
|
50
|
-
* Refuse to persist an explicit `--dir /tmp/...`
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
+
* Refuse to persist an explicit `--dir /tmp/...` as the default bundle path.
|
|
55
|
+
* The OS may reap the directory at any time, leaving the next run with a
|
|
56
|
+
* default bundle that points at a deleted path. Mirrors the
|
|
57
|
+
* `assertInitSandbox` check in commands/init.ts, but
|
|
54
58
|
* fires under all runtimes (not just `bun test`) because `akm setup --dir
|
|
55
59
|
* /tmp/X` is a documented isolation pattern that has been observed to
|
|
56
|
-
* silently clobber the host config
|
|
57
|
-
*
|
|
60
|
+
* silently clobber the host config (the 2026-05-23 setup-clobbers-user-config
|
|
61
|
+
* incident).
|
|
58
62
|
*
|
|
59
63
|
* Escape hatch: set `AKM_FORCE_SETUP_TMP_STASH=1` to override. When the
|
|
60
64
|
* escape hatch is on, `applyStashIsolationToEnv` below also pre-sets
|
|
61
|
-
* `
|
|
65
|
+
* `AKM_BUNDLE_DIR` so that the `getConfigDir` / `getCacheDir` isolation
|
|
62
66
|
* rules fire and config + cache writes route into `$stashDir/.akm/`
|
|
63
67
|
* instead of the user's host `~/.config/akm`.
|
|
64
68
|
*/
|
|
@@ -70,7 +74,7 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
|
|
|
70
74
|
if (!isTransientStashPath(stashDir))
|
|
71
75
|
return;
|
|
72
76
|
throw new ConfigError(`refusing to run \`akm setup --dir ${stashDir}\`: the path is in a transient/sandbox directory family the OS may reap. ` +
|
|
73
|
-
"Persisting it as the
|
|
77
|
+
"Persisting it as the default bundle would leave the next run pointing at a deleted path. " +
|
|
74
78
|
"Use a persistent directory, OR set AKM_FORCE_SETUP_TMP_STASH=1 if you intentionally want a sandbox setup " +
|
|
75
79
|
"(setup will also auto-isolate config + cache writes into $stashDir/.akm/ so the host config is preserved).", "SETUP_TMP_STASH_REFUSED");
|
|
76
80
|
}
|
|
@@ -78,11 +82,11 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
|
|
|
78
82
|
* Propagate the explicit `--dir <stashDir>` choice to the env so that the
|
|
79
83
|
* `getConfigDir` / `getCacheDir` isolation rules in `src/core/paths.ts`
|
|
80
84
|
* actually fire for the duration of this setup run. Without this, a CLI
|
|
81
|
-
* caller who passes `--dir /tmp/X` but doesn't pre-export `
|
|
85
|
+
* caller who passes `--dir /tmp/X` but doesn't pre-export `AKM_BUNDLE_DIR`
|
|
82
86
|
* would still write config to the host `~/.config/akm/config.json`. We
|
|
83
87
|
* only set the env var when:
|
|
84
88
|
* - `--dir` was explicitly provided (we have an operator-stated stash), AND
|
|
85
|
-
* - `
|
|
89
|
+
* - `AKM_BUNDLE_DIR` is not already set (caller's explicit env wins).
|
|
86
90
|
* The set is process-wide; for the CLI that's the right scope (the process
|
|
87
91
|
* is about to do all its work against this stash). For tests, each test
|
|
88
92
|
* already isolates env via beforeEach/afterEach so there is no leak.
|
|
@@ -90,11 +94,215 @@ function assertSetupSandbox(stashDir, dirExplicitlyProvided) {
|
|
|
90
94
|
function applyStashIsolationToEnv(stashDir, dirExplicitlyProvided) {
|
|
91
95
|
if (!dirExplicitlyProvided)
|
|
92
96
|
return;
|
|
93
|
-
if (process.env.
|
|
97
|
+
if (process.env.AKM_BUNDLE_DIR?.trim())
|
|
94
98
|
return;
|
|
95
|
-
process.env.
|
|
99
|
+
process.env.AKM_BUNDLE_DIR = stashDir;
|
|
96
100
|
}
|
|
97
101
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
102
|
+
/** Raw preflight used before setup performs prompts, initialization, or writes. */
|
|
103
|
+
export function assertSetupConfigPreflight() {
|
|
104
|
+
const configPath = getConfigPath();
|
|
105
|
+
let text;
|
|
106
|
+
try {
|
|
107
|
+
text = readConfigText(configPath);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
throw new ConfigError(`Could not read config at ${configPath}: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
|
|
111
|
+
}
|
|
112
|
+
if (text !== undefined)
|
|
113
|
+
parseAndValidateConfigText(text, configPath);
|
|
114
|
+
}
|
|
115
|
+
function isPlainRecord(value) {
|
|
116
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
117
|
+
return false;
|
|
118
|
+
const prototype = Object.getPrototypeOf(value);
|
|
119
|
+
return prototype === Object.prototype || prototype === null;
|
|
120
|
+
}
|
|
121
|
+
function sameConfigValue(left, right) {
|
|
122
|
+
return isDeepStrictEqual(left, right);
|
|
123
|
+
}
|
|
124
|
+
function configArrayItemKey(value) {
|
|
125
|
+
if (!isPlainRecord(value))
|
|
126
|
+
return `value:${JSON.stringify(value)}`;
|
|
127
|
+
if (typeof value.id === "string")
|
|
128
|
+
return `id:${value.id}`;
|
|
129
|
+
if (typeof value.type === "string" && typeof value.url === "string")
|
|
130
|
+
return `source:${value.type}:url:${value.url}`;
|
|
131
|
+
if (typeof value.type === "string" && typeof value.path === "string")
|
|
132
|
+
return `source:${value.type}:path:${path.resolve(value.path)}`;
|
|
133
|
+
if (typeof value.url === "string")
|
|
134
|
+
return `url:${value.url}`;
|
|
135
|
+
if (typeof value.name === "string")
|
|
136
|
+
return `name:${value.name}`;
|
|
137
|
+
return `value:${JSON.stringify(value)}`;
|
|
138
|
+
}
|
|
139
|
+
function setupConflict(pathParts) {
|
|
140
|
+
const field = pathParts.length > 0 ? pathParts.join(".") : "(root)";
|
|
141
|
+
throw new ConfigError(`Setup config conflict at ${field}: another process changed the same field after setup started. Rerun setup against the latest config.`, "INVALID_CONFIG_FILE");
|
|
142
|
+
}
|
|
143
|
+
/** Reapply setup's changes while rejecting concurrent edits to the same field. */
|
|
144
|
+
export function rebaseSetupChanges(original, desired, latest, pathParts = []) {
|
|
145
|
+
if (sameConfigValue(original, desired))
|
|
146
|
+
return latest;
|
|
147
|
+
if (Array.isArray(original) && Array.isArray(desired)) {
|
|
148
|
+
if (latest !== undefined && !Array.isArray(latest))
|
|
149
|
+
setupConflict(pathParts);
|
|
150
|
+
const latestItems = Array.isArray(latest) ? latest : [];
|
|
151
|
+
const originalByKey = new Map(original.map((item) => [configArrayItemKey(item), item]));
|
|
152
|
+
const desiredByKey = new Map(desired.map((item) => [configArrayItemKey(item), item]));
|
|
153
|
+
const latestByKey = new Map(latestItems.map((item) => [configArrayItemKey(item), item]));
|
|
154
|
+
const result = [];
|
|
155
|
+
for (const [key, desiredItem] of desiredByKey) {
|
|
156
|
+
const originalItem = originalByKey.get(key);
|
|
157
|
+
const latestItem = latestByKey.get(key);
|
|
158
|
+
if (originalByKey.has(key)) {
|
|
159
|
+
if (!latestByKey.has(key))
|
|
160
|
+
setupConflict([...pathParts, key]);
|
|
161
|
+
result.push(rebaseSetupChanges(originalItem, desiredItem, latestItem, [...pathParts, key]));
|
|
162
|
+
}
|
|
163
|
+
else if (!latestByKey.has(key) || sameConfigValue(latestItem, desiredItem)) {
|
|
164
|
+
result.push(desiredItem);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
setupConflict([...pathParts, key]);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const [key, originalItem] of originalByKey) {
|
|
171
|
+
if (desiredByKey.has(key) || !latestByKey.has(key))
|
|
172
|
+
continue;
|
|
173
|
+
if (!sameConfigValue(latestByKey.get(key), originalItem))
|
|
174
|
+
setupConflict([...pathParts, key]);
|
|
175
|
+
}
|
|
176
|
+
for (const item of latestItems) {
|
|
177
|
+
const key = configArrayItemKey(item);
|
|
178
|
+
if (!originalByKey.has(key) && !desiredByKey.has(key))
|
|
179
|
+
result.push(item);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
if (!isPlainRecord(original) || !isPlainRecord(desired)) {
|
|
184
|
+
if (!sameConfigValue(latest, original) && !sameConfigValue(latest, desired))
|
|
185
|
+
setupConflict(pathParts);
|
|
186
|
+
return desired;
|
|
187
|
+
}
|
|
188
|
+
if (latest !== undefined && !isPlainRecord(latest))
|
|
189
|
+
setupConflict(pathParts);
|
|
190
|
+
const result = isPlainRecord(latest) ? { ...latest } : {};
|
|
191
|
+
for (const key of new Set([...Object.keys(original), ...Object.keys(desired)])) {
|
|
192
|
+
if (!Object.hasOwn(desired, key)) {
|
|
193
|
+
if (Object.hasOwn(result, key) && !sameConfigValue(result[key], original[key]))
|
|
194
|
+
setupConflict([...pathParts, key]);
|
|
195
|
+
delete result[key];
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
result[key] = rebaseSetupChanges(original[key], desired[key], result[key], [...pathParts, key]);
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
async function saveSetupConfig(original, desired, precommit) {
|
|
203
|
+
const result = await mutateConfigWithPrecommit((latest) => rebaseSetupChanges(original, desired, latest), precommit);
|
|
204
|
+
return { config: result.config, precommit: result.precommit };
|
|
205
|
+
}
|
|
206
|
+
/** Registry-managed bundles are not editable through setup's source picker. */
|
|
207
|
+
function managedBundleIds() {
|
|
208
|
+
return new Set(readLockfile().map((entry) => entry.id));
|
|
209
|
+
}
|
|
210
|
+
function setupSourceDescriptor(source) {
|
|
211
|
+
switch (source.type) {
|
|
212
|
+
case "filesystem":
|
|
213
|
+
return source.path ? { path: source.path } : undefined;
|
|
214
|
+
case "git":
|
|
215
|
+
return source.url ? { git: source.url } : undefined;
|
|
216
|
+
case "website":
|
|
217
|
+
return source.url ? { website: { url: source.url, ...(source.options ?? {}) } } : undefined;
|
|
218
|
+
case "npm":
|
|
219
|
+
return source.path ? { npm: source.path } : undefined;
|
|
220
|
+
default:
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function selectsExistingBundle(source, id, bundle) {
|
|
225
|
+
const configured = bundleEntryToSourceEntry(id, bundle);
|
|
226
|
+
return (configured !== undefined &&
|
|
227
|
+
configured.type === source.type &&
|
|
228
|
+
configured.path === source.path &&
|
|
229
|
+
configured.url === source.url);
|
|
230
|
+
}
|
|
231
|
+
/** Convert only the wizard's private drafts, leaving current bundle entries intact. */
|
|
232
|
+
function finalizeSetupDraft(draft) {
|
|
233
|
+
const { primaryPath, additionalSources, ...canonical } = draft;
|
|
234
|
+
const finalized = canonical;
|
|
235
|
+
if (primaryPath === undefined && additionalSources === undefined)
|
|
236
|
+
return finalized;
|
|
237
|
+
const bundles = { ...(draft.bundles ?? {}) };
|
|
238
|
+
const managedIds = managedBundleIds();
|
|
239
|
+
const newBundles = [];
|
|
240
|
+
if (additionalSources !== undefined) {
|
|
241
|
+
const selectedExistingIds = new Set();
|
|
242
|
+
for (const source of additionalSources) {
|
|
243
|
+
const existing = source.name ? bundles[source.name] : undefined;
|
|
244
|
+
if (source.name &&
|
|
245
|
+
source.name !== draft.defaultBundle &&
|
|
246
|
+
existing &&
|
|
247
|
+
selectsExistingBundle(source, source.name, existing)) {
|
|
248
|
+
selectedExistingIds.add(source.name);
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const descriptor = setupSourceDescriptor(source);
|
|
252
|
+
if (!descriptor)
|
|
253
|
+
continue;
|
|
254
|
+
newBundles.push({
|
|
255
|
+
descriptor,
|
|
256
|
+
derivationPath: source.path ? path.resolve(source.path) : (source.url ?? ""),
|
|
257
|
+
preferredId: source.name,
|
|
258
|
+
primary: false,
|
|
259
|
+
writable: source.writable,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
for (const id of Object.keys(bundles)) {
|
|
263
|
+
if (id === draft.defaultBundle || managedIds.has(id) || selectedExistingIds.has(id))
|
|
264
|
+
continue;
|
|
265
|
+
delete bundles[id];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
let defaultBundle = draft.defaultBundle;
|
|
269
|
+
const configuredPrimaryPath = primaryBundlePath(draft);
|
|
270
|
+
if (primaryPath !== undefined &&
|
|
271
|
+
(configuredPrimaryPath === undefined || path.resolve(configuredPrimaryPath) !== path.resolve(primaryPath))) {
|
|
272
|
+
if (defaultBundle && !managedIds.has(defaultBundle))
|
|
273
|
+
delete bundles[defaultBundle];
|
|
274
|
+
defaultBundle = undefined;
|
|
275
|
+
newBundles.unshift({
|
|
276
|
+
descriptor: { path: primaryPath },
|
|
277
|
+
derivationPath: path.resolve(primaryPath),
|
|
278
|
+
primary: true,
|
|
279
|
+
writable: true,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
const reserved = Object.keys(bundles).map((id) => ({ path: id, registryId: id }));
|
|
283
|
+
const ids = deriveBundleIds([
|
|
284
|
+
...reserved,
|
|
285
|
+
...newBundles.map((bundle) => ({ path: bundle.derivationPath, registryId: bundle.preferredId })),
|
|
286
|
+
]).slice(reserved.length);
|
|
287
|
+
for (const [index, bundle] of newBundles.entries()) {
|
|
288
|
+
const id = ids[index];
|
|
289
|
+
bundles[id] = {
|
|
290
|
+
...bundle.descriptor,
|
|
291
|
+
...(bundle.writable !== undefined ? { writable: bundle.writable } : {}),
|
|
292
|
+
};
|
|
293
|
+
if (bundle.primary)
|
|
294
|
+
defaultBundle = id;
|
|
295
|
+
}
|
|
296
|
+
if (Object.keys(bundles).length > 0)
|
|
297
|
+
finalized.bundles = bundles;
|
|
298
|
+
else
|
|
299
|
+
delete finalized.bundles;
|
|
300
|
+
if (defaultBundle !== undefined)
|
|
301
|
+
finalized.defaultBundle = defaultBundle;
|
|
302
|
+
else
|
|
303
|
+
delete finalized.defaultBundle;
|
|
304
|
+
return finalized;
|
|
305
|
+
}
|
|
98
306
|
/**
|
|
99
307
|
* Quick connectivity check. Returns true if we can resolve a hostname
|
|
100
308
|
* the user has already implicitly trusted within 3 seconds, false
|
|
@@ -145,14 +353,14 @@ export function buildSetupSteps(options) {
|
|
|
145
353
|
const steps = [
|
|
146
354
|
{
|
|
147
355
|
id: "stash-dir",
|
|
148
|
-
label: "
|
|
356
|
+
label: "Bundle Directory",
|
|
149
357
|
nonInteractive: true,
|
|
150
358
|
async run(ctx) {
|
|
151
359
|
const stashDir = await stepStashDir(ctx.config, {
|
|
152
360
|
nonInteractive: ctx.nonInteractive,
|
|
153
361
|
preferredDir: options.preferredStashDir,
|
|
154
362
|
});
|
|
155
|
-
ctx.apply({ stashDir });
|
|
363
|
+
ctx.apply({ primaryPath: stashDir });
|
|
156
364
|
},
|
|
157
365
|
},
|
|
158
366
|
{
|
|
@@ -178,7 +386,7 @@ export function buildSetupSteps(options) {
|
|
|
178
386
|
return;
|
|
179
387
|
}
|
|
180
388
|
const llm = await stepLlm(ctx.config, ollamaEndpoint, ollamaChatModels, lmStudioResult, harnessConfigs);
|
|
181
|
-
ctx.apply(
|
|
389
|
+
ctx.apply(writeLlmEngine(ctx.config, llm));
|
|
182
390
|
},
|
|
183
391
|
},
|
|
184
392
|
{
|
|
@@ -200,17 +408,17 @@ export function buildSetupSteps(options) {
|
|
|
200
408
|
},
|
|
201
409
|
{
|
|
202
410
|
id: "stash-sources",
|
|
203
|
-
label: "
|
|
411
|
+
label: "Bundle Sources",
|
|
204
412
|
async run(ctx) {
|
|
205
413
|
const stashes = await stepAddSources(ctx.config, { promptForAdditional: false });
|
|
206
|
-
const platforms = await stepAgentPlatforms({ ...ctx.config,
|
|
414
|
+
const platforms = await stepAgentPlatforms({ ...ctx.config, additionalSources: stashes });
|
|
207
415
|
const merged = [...stashes];
|
|
208
416
|
for (const ps of platforms) {
|
|
209
417
|
if (!merged.some((s) => s.path === ps.path))
|
|
210
418
|
merged.push(ps);
|
|
211
419
|
}
|
|
212
420
|
const withAdditional = await stepAdditionalSources(merged);
|
|
213
|
-
ctx.apply({
|
|
421
|
+
ctx.apply({ additionalSources: withAdditional });
|
|
214
422
|
},
|
|
215
423
|
},
|
|
216
424
|
{
|
|
@@ -226,11 +434,10 @@ export function buildSetupSteps(options) {
|
|
|
226
434
|
else {
|
|
227
435
|
p.log.info("No agent CLIs detected on PATH. Agent commands will be disabled until one is installed and `akm setup` is re-run.");
|
|
228
436
|
}
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
const synthConfig = { ...ctx.config, ...applyLegacyAgent(ctx.config, result.agent) };
|
|
437
|
+
// Apply detected engines to a synthetic config for the selection UI.
|
|
438
|
+
const synthConfig = deepMergeConfig(ctx.config, writeAgentEngines(ctx.config, result.agent));
|
|
232
439
|
const agent = await stepAgentSelection(synthConfig, result.detections);
|
|
233
|
-
ctx.apply(
|
|
440
|
+
ctx.apply(writeAgentEngines(ctx.config, agent));
|
|
234
441
|
},
|
|
235
442
|
},
|
|
236
443
|
{
|
|
@@ -241,34 +448,27 @@ export function buildSetupSteps(options) {
|
|
|
241
448
|
ctx.apply({ output });
|
|
242
449
|
},
|
|
243
450
|
},
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
await stepScheduledTasks();
|
|
252
|
-
},
|
|
253
|
-
},
|
|
451
|
+
// NOTE: the "Scheduled Tasks" step is deliberately NOT part of this list.
|
|
452
|
+
// It is the only wizard step with externally-visible side effects (task
|
|
453
|
+
// files + OS scheduler entries), so `runSetupWizard` runs it by hand AFTER
|
|
454
|
+
// the config is confirmed and persisted — see the call there. Keeping it
|
|
455
|
+
// out of the step list also preserves the issue #512 guard: the
|
|
456
|
+
// non-interactive entry points (`akm init` / `--yes`) run this list but
|
|
457
|
+
// never that step, so headless runs never enable a scheduled task.
|
|
254
458
|
];
|
|
255
459
|
return { steps, outcome };
|
|
256
460
|
}
|
|
257
461
|
export async function runSetupWizard(opts) {
|
|
462
|
+
assertSetupConfigPreflight();
|
|
258
463
|
p.intro("akm setup");
|
|
259
464
|
const current = loadUserConfig();
|
|
260
465
|
const configPath = getConfigPath();
|
|
261
466
|
// Resolve stash directory early so akmInit can run before any prompts
|
|
262
|
-
const resolvedStashDir = opts?.dir ? path.resolve(opts.dir) : (current
|
|
467
|
+
const resolvedStashDir = opts?.dir ? path.resolve(opts.dir) : (primaryBundlePath(current) ?? getDefaultStashDir());
|
|
263
468
|
// Refuse explicit --dir /tmp/... before doing any work — protects the host
|
|
264
469
|
// config from being clobbered with a stashDir that the OS may reap.
|
|
265
470
|
assertSetupSandbox(resolvedStashDir, opts?.dir != null);
|
|
266
471
|
applyStashIsolationToEnv(resolvedStashDir, opts?.dir != null);
|
|
267
|
-
// Bootstrap directory structure before any prompts so the stash exists
|
|
268
|
-
// even if the wizard is interrupted after this point.
|
|
269
|
-
if (!opts?.noInit) {
|
|
270
|
-
await akmInit({ dir: resolvedStashDir, setDefault: true });
|
|
271
|
-
}
|
|
272
472
|
// Quick connectivity check — skip network-dependent steps when offline
|
|
273
473
|
const online = await isOnline();
|
|
274
474
|
if (!online) {
|
|
@@ -278,9 +478,9 @@ export async function runSetupWizard(opts) {
|
|
|
278
478
|
// Aggregate environment detection — run once before any prompt and surface
|
|
279
479
|
// a summary so the user sees what was auto-detected. NAMES only, never
|
|
280
480
|
// API key values.
|
|
281
|
-
const detection = await detectEnvironment({ existingStashDir: current
|
|
481
|
+
const detection = await detectEnvironment({ existingStashDir: primaryBundlePath(current) });
|
|
282
482
|
p.note(renderDetectionSummary(detection), "Detected environment");
|
|
283
|
-
// Interactive entry point for
|
|
483
|
+
// Interactive entry point for `runResetRecommended`: offer to apply the
|
|
284
484
|
// opinionated, detection-derived defaults and skip the step-by-step wizard.
|
|
285
485
|
const useRecommended = await prompt(() => p.confirm({
|
|
286
486
|
message: "Apply recommended defaults from the detected environment (merged into your existing config)?",
|
|
@@ -313,35 +513,31 @@ export async function runSetupWizard(opts) {
|
|
|
313
513
|
// Step 1/2: Small model connection (for enrichment features)
|
|
314
514
|
const smallModelResult = await stepSmallModelConnection(ctx.config);
|
|
315
515
|
if (!smallModelResult.skipped) {
|
|
316
|
-
ctx.apply(
|
|
516
|
+
ctx.apply(writeLlmEngine(ctx.config, smallModelResult.llm));
|
|
317
517
|
}
|
|
318
518
|
// Step 2/2: Agent connection (for agentic features)
|
|
319
519
|
const agentConfig = await stepAgentConnection(ctx.config, smallModelResult);
|
|
320
|
-
ctx.apply(
|
|
321
|
-
const newConfig = {
|
|
322
|
-
...ctx.config,
|
|
323
|
-
// Preserve fields the steps don't manage explicitly.
|
|
324
|
-
installed: current.installed,
|
|
325
|
-
};
|
|
520
|
+
ctx.apply(writeAgentEngines(ctx.config, agentConfig));
|
|
521
|
+
const newConfig = { ...ctx.config };
|
|
326
522
|
const semanticSearchMode = outcome.semantic;
|
|
327
|
-
const stashDir = newConfig.
|
|
523
|
+
const stashDir = newConfig.primaryPath ?? primaryBundlePath(current) ?? getDefaultStashDir();
|
|
328
524
|
const embedding = newConfig.embedding;
|
|
329
|
-
const llm =
|
|
525
|
+
const llm = readCurrentLlmEngine(newConfig);
|
|
330
526
|
const registries = newConfig.registries;
|
|
331
|
-
const allStashes = newConfig.
|
|
527
|
+
const allStashes = newConfig.additionalSources ?? [];
|
|
332
528
|
// Feature capability summary
|
|
333
|
-
const agentConfigured = Boolean(agentConfig);
|
|
529
|
+
const agentConfigured = Boolean(agentConfig && !agentConfig.disabled);
|
|
334
530
|
printCapabilitySummary(smallModelResult.skipped, agentConfigured);
|
|
335
531
|
// Confirm before saving
|
|
336
532
|
const effectiveRegistries = registries ?? DEFAULT_CONFIG.registries ?? [];
|
|
337
533
|
p.note([
|
|
338
|
-
`
|
|
534
|
+
`Bundle directory: ${stashDir}`,
|
|
339
535
|
`Embedding: ${embedding ? `${embedding.provider ?? "remote"} / ${embedding.model}` : "built-in local"}`,
|
|
340
536
|
`LLM: ${llm ? `${llm.provider ?? "remote"} / ${llm.model}` : "disabled"}`,
|
|
341
537
|
`Semantic search: ${semanticSearchMode.mode}`,
|
|
342
538
|
`Registries: ${effectiveRegistries.filter((r) => r.enabled !== false).length} enabled`,
|
|
343
|
-
`
|
|
344
|
-
`Agent default: ${newConfig.defaults?.
|
|
539
|
+
`Bundle sources: ${allStashes.length}`,
|
|
540
|
+
`Agent default: ${newConfig.defaults?.engine ?? "disabled"}`,
|
|
345
541
|
`Output: ${newConfig.output?.format ?? "json"} / ${newConfig.output?.detail ?? "brief"}`,
|
|
346
542
|
].join("\n"), "Configuration Summary");
|
|
347
543
|
const shouldSave = await prompt(() => p.confirm({
|
|
@@ -350,22 +546,29 @@ export async function runSetupWizard(opts) {
|
|
|
350
546
|
}));
|
|
351
547
|
if (!shouldSave)
|
|
352
548
|
bail();
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
549
|
+
const finalConfig = finalizeSetupDraft(newConfig);
|
|
550
|
+
validateCompleteConfig(finalConfig);
|
|
551
|
+
const { config: savedConfig } = await saveSetupConfig(current, finalConfig, async () => {
|
|
552
|
+
if (!opts?.noInit)
|
|
553
|
+
await akmInit({ dir: stashDir, setDefault: true, persistConfig: false });
|
|
554
|
+
});
|
|
555
|
+
// After config persistence, the task step reviews the plan and asks one
|
|
556
|
+
// explicit confirmation before changing task files or scheduler state.
|
|
557
|
+
// Non-interactive setup paths never reach this interactive-only step.
|
|
558
|
+
p.log.step("Scheduled Tasks");
|
|
559
|
+
await stepScheduledTasks();
|
|
357
560
|
if (semanticSearchMode.mode === "off") {
|
|
358
561
|
clearSemanticStatus();
|
|
359
562
|
}
|
|
360
563
|
if (semanticSearchMode.mode === "auto") {
|
|
361
564
|
if (semanticSearchMode.prepareAssets) {
|
|
362
|
-
const ready = await prepareSemanticSearchAssets(
|
|
565
|
+
const ready = await prepareSemanticSearchAssets(savedConfig);
|
|
363
566
|
if (!ready.ok) {
|
|
364
567
|
writeSemanticStatus({
|
|
365
568
|
status: "blocked",
|
|
366
569
|
reason: ready.reason,
|
|
367
570
|
message: ready.message,
|
|
368
|
-
providerFingerprint: deriveSemanticProviderFingerprint(
|
|
571
|
+
providerFingerprint: deriveSemanticProviderFingerprint(savedConfig.embedding),
|
|
369
572
|
lastCheckedAt: new Date().toISOString(),
|
|
370
573
|
});
|
|
371
574
|
p.log.warn("Semantic search remains set to auto, but is currently blocked. Re-run `akm index --full --verbose` once the issue is resolved.");
|
|
@@ -374,7 +577,7 @@ export async function runSetupWizard(opts) {
|
|
|
374
577
|
writeSemanticStatus({
|
|
375
578
|
status: "pending",
|
|
376
579
|
message: "Semantic prerequisites verified. Building the index to finish activation.",
|
|
377
|
-
providerFingerprint: deriveSemanticProviderFingerprint(
|
|
580
|
+
providerFingerprint: deriveSemanticProviderFingerprint(savedConfig.embedding),
|
|
378
581
|
lastCheckedAt: new Date().toISOString(),
|
|
379
582
|
});
|
|
380
583
|
}
|
|
@@ -383,7 +586,7 @@ export async function runSetupWizard(opts) {
|
|
|
383
586
|
writeSemanticStatus({
|
|
384
587
|
status: "pending",
|
|
385
588
|
message: "Semantic search is enabled, but asset preparation was skipped.",
|
|
386
|
-
providerFingerprint: deriveSemanticProviderFingerprint(
|
|
589
|
+
providerFingerprint: deriveSemanticProviderFingerprint(savedConfig.embedding),
|
|
387
590
|
lastCheckedAt: new Date().toISOString(),
|
|
388
591
|
});
|
|
389
592
|
p.log.info("Semantic search is set to auto, but asset preparation was skipped. Run `akm index --full --verbose` later to verify it.");
|
|
@@ -396,7 +599,7 @@ export async function runSetupWizard(opts) {
|
|
|
396
599
|
try {
|
|
397
600
|
const indexResult = await akmIndex({ stashDir });
|
|
398
601
|
spin.stop(`Indexed ${indexResult.totalEntries} assets.`);
|
|
399
|
-
if (
|
|
602
|
+
if (savedConfig.semanticSearchMode === "auto") {
|
|
400
603
|
if (indexResult.verification.ok) {
|
|
401
604
|
p.log.success(indexResult.verification.message);
|
|
402
605
|
}
|
|
@@ -411,12 +614,12 @@ export async function runSetupWizard(opts) {
|
|
|
411
614
|
catch (err) {
|
|
412
615
|
spin.stop("Indexing failed — you can run `akm index` manually later.");
|
|
413
616
|
p.log.warn(String(err));
|
|
414
|
-
if (
|
|
617
|
+
if (savedConfig.semanticSearchMode === "auto") {
|
|
415
618
|
writeSemanticStatus({
|
|
416
619
|
status: "blocked",
|
|
417
620
|
reason: "index-failed",
|
|
418
621
|
message: String(err),
|
|
419
|
-
providerFingerprint: deriveSemanticProviderFingerprint(
|
|
622
|
+
providerFingerprint: deriveSemanticProviderFingerprint(savedConfig.embedding),
|
|
420
623
|
lastCheckedAt: new Date().toISOString(),
|
|
421
624
|
});
|
|
422
625
|
}
|
|
@@ -433,57 +636,32 @@ export async function runSetupWizard(opts) {
|
|
|
433
636
|
p.log.info("Reminder: Set your LLM API key via the AKM_LLM_API_KEY environment variable.");
|
|
434
637
|
}
|
|
435
638
|
}
|
|
436
|
-
p.outro(`Configuration saved to ${configPath}`
|
|
639
|
+
p.outro(`Configuration saved to ${configPath}\n` +
|
|
640
|
+
'Next: `akm bundle add <source>`, `akm index`, `akm search "<query>"`, `akm help agents`');
|
|
437
641
|
}
|
|
438
642
|
// ── Non-interactive / scripting entry points ─────────────────────────────────
|
|
439
|
-
/**
|
|
440
|
-
* Back up an existing config file and print the real, timestamped backup
|
|
441
|
-
* location (not a generic display string). On a fresh install where there is
|
|
442
|
-
* nothing to back up, print a "nothing to back up" notice instead.
|
|
443
|
-
*/
|
|
444
|
-
function backupAndAnnounce(configPath) {
|
|
445
|
-
const result = backupExistingConfig(configPath);
|
|
446
|
-
const message = result ? `Config backed up to ${result.timestamped}` : "No existing config to back up.";
|
|
447
|
-
// In JSON output mode the structured envelope (which already carries
|
|
448
|
-
// `configPath`) MUST be the only thing on stdout — `setup --yes | jq` is a
|
|
449
|
-
// supported scripting contract. @clack's `p.log.info` writes to stdout, which
|
|
450
|
-
// would corrupt that envelope, so route this human-progress notice to stderr
|
|
451
|
-
// when emitting JSON. Interactive/text runs keep the inline clack banner.
|
|
452
|
-
if (isJsonOutputMode()) {
|
|
453
|
-
process.stderr.write(`${message}\n`);
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
p.log.info(message);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
/**
|
|
460
|
-
* True when the process-level output mode is JSON (the default machine format).
|
|
461
|
-
* Defensive: setup is also invoked programmatically (tests) where the output
|
|
462
|
-
* mode singleton may not be initialized — treat that as "not JSON" so the
|
|
463
|
-
* human-readable clack banner is used.
|
|
464
|
-
*/
|
|
465
|
-
function isJsonOutputMode() {
|
|
466
|
-
try {
|
|
467
|
-
return getOutputMode().format === "json";
|
|
468
|
-
}
|
|
469
|
-
catch {
|
|
470
|
-
return false;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
643
|
/**
|
|
474
644
|
* Run setup in non-interactive mode, applying all defaults.
|
|
475
645
|
* Safe to call from CI or scripts. Idempotent — re-running produces the same result.
|
|
476
646
|
*/
|
|
477
647
|
export async function runSetupWithDefaults(opts) {
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
assertSetupSandbox(stashDir,
|
|
481
|
-
applyStashIsolationToEnv(stashDir,
|
|
482
|
-
//
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
648
|
+
assertSetupConfigPreflight();
|
|
649
|
+
const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : undefined;
|
|
650
|
+
// R-066 #4: a second `assertSetupSandbox(stashDir, explicitStashDir != null)`
|
|
651
|
+
// + `applyStashIsolationToEnv(stashDir, explicitStashDir != null)` pair used
|
|
652
|
+
// to follow `stashDir`'s resolution below. It was strictly redundant: unlike
|
|
653
|
+
// `runSetupFromConfig` (which can also derive an explicit stash dir from an
|
|
654
|
+
// incoming config file's bundle path, a case the early block below can't
|
|
655
|
+
// see yet), `runSetupWithDefaults` has only one source of an explicit dir —
|
|
656
|
+
// `opts.dir` — so `stashDir` always equals `explicitStashDir` whenever the
|
|
657
|
+
// `if` below ran, and always fails `dirExplicitlyProvided` (an immediate
|
|
658
|
+
// no-op in both helpers) whenever it didn't. Removed rather than duplicated.
|
|
659
|
+
if (explicitStashDir) {
|
|
660
|
+
assertSetupSandbox(explicitStashDir, true);
|
|
661
|
+
applyStashIsolationToEnv(explicitStashDir, true);
|
|
486
662
|
}
|
|
663
|
+
const current = loadUserConfig();
|
|
664
|
+
const stashDir = explicitStashDir ?? primaryBundlePath(current) ?? getDefaultStashDir();
|
|
487
665
|
// Run steps in non-interactive mode (applies defaults, skips prompts)
|
|
488
666
|
const ctx = createSetupContext(current, { nonInteractive: true });
|
|
489
667
|
const { steps } = buildSetupSteps({
|
|
@@ -492,18 +670,17 @@ export async function runSetupWithDefaults(opts) {
|
|
|
492
670
|
preferredStashDir: stashDir,
|
|
493
671
|
});
|
|
494
672
|
await runSetupSteps(steps, ctx);
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
ctx.apply({ stashDir });
|
|
673
|
+
if (!ctx.config.primaryPath)
|
|
674
|
+
ctx.apply({ primaryPath: stashDir });
|
|
498
675
|
// Aggregate environment detection — apply detected values directly.
|
|
499
|
-
const env = await detectEnvironment({ existingStashDir: ctx.config.
|
|
676
|
+
const env = await detectEnvironment({ existingStashDir: ctx.config.primaryPath });
|
|
500
677
|
// Apply a detected LLM (live local server) when the config has none yet.
|
|
501
|
-
if (!
|
|
678
|
+
if (!readCurrentLlmEngine(ctx.config) && opts.probe) {
|
|
502
679
|
const liveLocal = env.localServers.find((s) => s.available && s.defaultModel);
|
|
503
680
|
if (liveLocal?.defaultModel) {
|
|
504
681
|
const llm = {
|
|
505
682
|
provider: "local",
|
|
506
|
-
endpoint: `${liveLocal.baseUrl.replace(/\/$/, "")}/v1`,
|
|
683
|
+
endpoint: `${liveLocal.baseUrl.replace(/\/$/, "")}/v1/chat/completions`,
|
|
507
684
|
model: liveLocal.defaultModel,
|
|
508
685
|
};
|
|
509
686
|
// A required field being unresolvable must fail loudly rather than write
|
|
@@ -511,11 +688,22 @@ export async function runSetupWithDefaults(opts) {
|
|
|
511
688
|
if (!llm.endpoint?.trim() || !llm.model?.trim()) {
|
|
512
689
|
throw new UsageError("Detected a local LLM server but could not resolve a required field (endpoint/model). Re-run `akm setup` interactively.", "MISSING_REQUIRED_ARGUMENT");
|
|
513
690
|
}
|
|
514
|
-
|
|
691
|
+
const verified = await verifyOpenAiCompatibleEndpoint(llm);
|
|
692
|
+
if (verified.ok) {
|
|
693
|
+
const applied = upsertDetectedLlmEngine(ctx.config, {
|
|
694
|
+
provider: llm.provider ?? "local",
|
|
695
|
+
endpoint: verified.endpoint,
|
|
696
|
+
model: llm.model,
|
|
697
|
+
});
|
|
698
|
+
ctx.apply(applied.config);
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
warn(`[akm setup] Skipping detected local LLM: ${verified.reason}. Verify it and rerun setup.`);
|
|
702
|
+
}
|
|
515
703
|
}
|
|
516
704
|
}
|
|
517
705
|
// Auto-detect agent CLI if not already configured
|
|
518
|
-
if (!ctx.config.defaults?.
|
|
706
|
+
if (!ctx.config.defaults?.engine) {
|
|
519
707
|
let defaultProfile;
|
|
520
708
|
if (env.harness !== "none") {
|
|
521
709
|
defaultProfile = env.harness;
|
|
@@ -525,31 +713,33 @@ export async function runSetupWithDefaults(opts) {
|
|
|
525
713
|
defaultProfile = pickDefaultAgentProfile(detected, undefined);
|
|
526
714
|
}
|
|
527
715
|
if (defaultProfile) {
|
|
528
|
-
ctx.apply(
|
|
716
|
+
ctx.apply(upsertDetectedAgentEngine(ctx.config, defaultProfile).config);
|
|
529
717
|
}
|
|
530
718
|
}
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
719
|
+
const finalConfig = finalizeSetupDraft(ctx.config);
|
|
720
|
+
validateCompleteConfig(finalConfig);
|
|
721
|
+
const { precommit: initResult } = await saveSetupConfig(current, finalConfig, async () => {
|
|
722
|
+
if (opts.noInit)
|
|
723
|
+
return undefined;
|
|
724
|
+
return akmInit({ dir: stashDir, setDefault: true, persistConfig: false });
|
|
725
|
+
});
|
|
534
726
|
return {
|
|
535
727
|
configPath: getConfigPath(),
|
|
536
|
-
stashDir,
|
|
728
|
+
bundleDir: stashDir,
|
|
537
729
|
stashCreated: initResult?.created ?? false,
|
|
538
730
|
written: true,
|
|
539
|
-
fields: Object.keys(
|
|
540
|
-
ripgrep: initResult?.ripgrep,
|
|
731
|
+
fields: Object.keys(finalConfig).filter((k) => finalConfig[k] !== undefined),
|
|
541
732
|
};
|
|
542
733
|
}
|
|
543
734
|
/**
|
|
544
735
|
* Run ONLY environment detection and return the typed result. Performs no
|
|
545
|
-
* config writes and shows no prompts.
|
|
736
|
+
* config writes and shows no prompts.
|
|
546
737
|
*
|
|
547
738
|
* SAFETY: The returned object carries env var NAMES only — never any API key
|
|
548
739
|
* value.
|
|
549
740
|
*/
|
|
550
741
|
export async function runDetectOnly() {
|
|
551
|
-
|
|
552
|
-
return detectEnvironment({ existingStashDir: current.stashDir });
|
|
742
|
+
return detectEnvironment();
|
|
553
743
|
}
|
|
554
744
|
/**
|
|
555
745
|
* Derive opinionated defaults from a detection result.
|
|
@@ -557,9 +747,8 @@ export async function runDetectOnly() {
|
|
|
557
747
|
* - Best harness → agent default (when a profile maps to it).
|
|
558
748
|
* - Fastest live local model, else the first detected cloud key's provider.
|
|
559
749
|
* - `nomic-embed-text` embeddings when a local LLM is live.
|
|
560
|
-
* - improve task `0 2 * * *`, index task `0 4 * * *`.
|
|
561
750
|
*
|
|
562
|
-
* Returns a partial `AkmConfig`-shaped object plus
|
|
751
|
+
* Returns a partial `AkmConfig`-shaped object plus an LLM connection block, ready
|
|
563
752
|
* to merge. Never includes an API key value.
|
|
564
753
|
*/
|
|
565
754
|
export function deriveRecommendedConfig(env) {
|
|
@@ -590,31 +779,59 @@ export function deriveRecommendedConfig(env) {
|
|
|
590
779
|
const defaults = PROVIDER_DEFAULTS[cloud.provider];
|
|
591
780
|
if (defaults) {
|
|
592
781
|
result.llm = { provider: cloud.provider, endpoint: defaults.endpoint, model: defaults.model };
|
|
782
|
+
result.llmApiKeyEnvVar = cloud.envVar;
|
|
593
783
|
}
|
|
594
784
|
}
|
|
595
785
|
}
|
|
596
|
-
result.taskSchedules = { improve: "0 2 * * *", index: "0 4 * * *" };
|
|
597
786
|
return result;
|
|
598
787
|
}
|
|
599
788
|
/**
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
* Uses the same merge path as
|
|
603
|
-
* (follows #511 semantics).
|
|
789
|
+
* Merge opinionated, detection-derived defaults into the existing config
|
|
790
|
+
* WITHOUT removing pre-existing custom keys. Backs the interactive wizard's
|
|
791
|
+
* "apply recommended defaults" prompt. Uses the same merge path as
|
|
792
|
+
* {@link runSetupFromConfig} so custom keys survive (follows #511 semantics).
|
|
604
793
|
*/
|
|
605
794
|
export async function runResetRecommended(opts) {
|
|
795
|
+
assertSetupConfigPreflight();
|
|
796
|
+
const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : undefined;
|
|
797
|
+
if (explicitStashDir) {
|
|
798
|
+
assertSetupSandbox(explicitStashDir, true);
|
|
799
|
+
applyStashIsolationToEnv(explicitStashDir, true);
|
|
800
|
+
}
|
|
606
801
|
const current = loadUserConfig();
|
|
607
|
-
const env = await detectEnvironment({ existingStashDir: current
|
|
802
|
+
const env = await detectEnvironment({ existingStashDir: primaryBundlePath(current) });
|
|
608
803
|
const recommended = deriveRecommendedConfig(env);
|
|
609
|
-
|
|
610
|
-
if (recommended.llm)
|
|
611
|
-
|
|
804
|
+
let incoming = {};
|
|
805
|
+
if (recommended.llm && recommended.llm.provider !== "anthropic") {
|
|
806
|
+
let endpoint = recommended.llm.endpoint;
|
|
807
|
+
let accepted = true;
|
|
808
|
+
if (opts.probe) {
|
|
809
|
+
const verified = await verifyOpenAiCompatibleEndpoint({
|
|
810
|
+
endpoint,
|
|
811
|
+
model: recommended.llm.model,
|
|
812
|
+
apiKeyEnvVar: recommended.llmApiKeyEnvVar,
|
|
813
|
+
});
|
|
814
|
+
if (verified.ok) {
|
|
815
|
+
endpoint = verified.endpoint;
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
accepted = false;
|
|
819
|
+
warn(`[akm setup] Skipping detected LLM: ${verified.reason}. Verify it and rerun setup.`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (accepted) {
|
|
823
|
+
incoming = upsertDetectedLlmEngine(incoming, {
|
|
824
|
+
provider: recommended.llm.provider ?? "local",
|
|
825
|
+
endpoint,
|
|
826
|
+
model: recommended.llm.model,
|
|
827
|
+
apiKeyEnvVar: recommended.llmApiKeyEnvVar,
|
|
828
|
+
}).config;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
612
831
|
if (recommended.embedding)
|
|
613
832
|
incoming.embedding = recommended.embedding;
|
|
614
|
-
if (recommended.agentDefault)
|
|
615
|
-
incoming
|
|
616
|
-
if (recommended.taskSchedules) {
|
|
617
|
-
incoming.setup = { taskSchedules: recommended.taskSchedules };
|
|
833
|
+
if (recommended.agentDefault) {
|
|
834
|
+
incoming = deepMergeConfig(incoming, writeAgentEngines(incoming, { default: recommended.agentDefault }));
|
|
618
835
|
}
|
|
619
836
|
return runSetupFromConfig({
|
|
620
837
|
configJson: JSON.stringify(incoming),
|
|
@@ -628,25 +845,38 @@ export async function runResetRecommended(opts) {
|
|
|
628
845
|
* Validates required sub-fields and strips unknown/restricted keys.
|
|
629
846
|
*/
|
|
630
847
|
export async function runSetupFromConfig(opts) {
|
|
631
|
-
|
|
848
|
+
assertSetupConfigPreflight();
|
|
849
|
+
// Phase 1: Parse JSON
|
|
850
|
+
let parsed;
|
|
632
851
|
try {
|
|
633
|
-
|
|
852
|
+
parsed = JSON.parse(opts.configJson);
|
|
634
853
|
}
|
|
635
854
|
catch (e) {
|
|
636
855
|
throw new Error(`Invalid JSON in --config: ${e.message}`);
|
|
637
856
|
}
|
|
857
|
+
if (!isPlainRecord(parsed)) {
|
|
858
|
+
throw new ConfigError("Setup config must contain a top-level object.", "INVALID_CONFIG_FILE");
|
|
859
|
+
}
|
|
860
|
+
const incoming = parsed;
|
|
638
861
|
// Phase 2: Validate — only allow safe top-level keys
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
862
|
+
for (const key of ["stashDir", "sources", "installed"]) {
|
|
863
|
+
if (key in incoming) {
|
|
864
|
+
throw new ConfigError(`${key} is not supported by the current config schema; run the standalone akm-migrate tool first.`, "INVALID_CONFIG_FILE");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
// Derived from AkmConfigShape (via `listTopLevelConfigKeys`) rather than a
|
|
868
|
+
// hand-copied set: a hand-copied allowlist silently fell out of sync as the
|
|
869
|
+
// schema grew (R-017 — `index`, `search`, `feedback`, `archiveRetentionDays`,
|
|
870
|
+
// `workflow`, and `experimental` were all valid schema keys that this list
|
|
871
|
+
// forgot, each dropped with only a warning while the command exited 0).
|
|
872
|
+
// Every key the schema recognizes is a legitimate thing to set via
|
|
873
|
+
// `--config`/`--from`; there is no key a user can set here that they
|
|
874
|
+
// couldn't set by hand-editing config.json and letting `akm config set`
|
|
875
|
+
// validate it. Keys that remain genuinely retired (`profiles`, `llm`,
|
|
876
|
+
// `agent`, `features`, `stashes`, `bindings`, `writable`) are not part of
|
|
877
|
+
// the schema shape, so they still fall into the warn-and-drop branch below;
|
|
878
|
+
// `stashDir`/`sources`/`installed` get the more specific migration error above.
|
|
879
|
+
const ALLOWED_KEYS = new Set(listTopLevelConfigKeys());
|
|
650
880
|
for (const key of Object.keys(incoming)) {
|
|
651
881
|
if (!ALLOWED_KEYS.has(key)) {
|
|
652
882
|
warn(`[akm setup] Ignoring unknown or restricted config key: "${key}"`);
|
|
@@ -654,12 +884,6 @@ export async function runSetupFromConfig(opts) {
|
|
|
654
884
|
}
|
|
655
885
|
}
|
|
656
886
|
// Validate required sub-fields
|
|
657
|
-
if (incoming.llm) {
|
|
658
|
-
if (!incoming.llm.endpoint?.trim())
|
|
659
|
-
throw new Error("llm.endpoint is required when llm is provided");
|
|
660
|
-
if (!incoming.llm.model?.trim())
|
|
661
|
-
throw new Error("llm.model is required when llm is provided");
|
|
662
|
-
}
|
|
663
887
|
if (incoming.embedding) {
|
|
664
888
|
if (!incoming.embedding.endpoint?.trim())
|
|
665
889
|
throw new Error("embedding.endpoint is required when embedding is provided");
|
|
@@ -667,34 +891,45 @@ export async function runSetupFromConfig(opts) {
|
|
|
667
891
|
throw new Error("embedding.model is required when embedding is provided");
|
|
668
892
|
}
|
|
669
893
|
// Phase 3: Merge with existing config
|
|
894
|
+
//
|
|
895
|
+
// R-066 #4 (verified, NOT reduced — unlike the `runSetupWithDefaults` pair
|
|
896
|
+
// above, this one is NOT strictly redundant): the `assertSetupSandbox` +
|
|
897
|
+
// `applyStashIsolationToEnv` pair below looks like a duplicate of the pair
|
|
898
|
+
// here, but each guards a case the other cannot:
|
|
899
|
+
// - THIS early pair must run before `loadUserConfig()` so an explicit
|
|
900
|
+
// `--dir` pointed at a force-escaped transient sandbox isolates the
|
|
901
|
+
// config READ too, not just the later write (the isolation env var has
|
|
902
|
+
// to be set before `loadUserConfig()` executes, or `current` below is
|
|
903
|
+
// read from the host config instead of the sandboxed one — the exact
|
|
904
|
+
// failure class the "2026-05-23 setup-clobbers-user-config incident"
|
|
905
|
+
// comment on `assertSetupSandbox` describes).
|
|
906
|
+
// - The LATER pair below additionally covers a case this one can't see
|
|
907
|
+
// yet: an incoming `--config`/`--file` JSON blob that names its own
|
|
908
|
+
// bundle path (`incomingPrimaryPath`) with no `--dir` at all — that
|
|
909
|
+
// path is only known after the merge, so it cannot be checked early.
|
|
910
|
+
// When `explicitStashDir` is set both pairs do end up asserting the same
|
|
911
|
+
// (stashDir, true), but removing either one changes behavior in the case
|
|
912
|
+
// it uniquely covers, so both stay.
|
|
913
|
+
const explicitStashDir = opts.dir != null ? path.resolve(opts.dir) : undefined;
|
|
914
|
+
if (explicitStashDir) {
|
|
915
|
+
assertSetupSandbox(explicitStashDir, true);
|
|
916
|
+
applyStashIsolationToEnv(explicitStashDir, true);
|
|
917
|
+
}
|
|
670
918
|
const current = loadUserConfig();
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const stashDirExplicit = opts.dir != null || incoming.stashDir != null;
|
|
919
|
+
let merged = deepMergeConfig(current, incoming);
|
|
920
|
+
const incomingPrimaryPath = primaryBundlePath(incoming);
|
|
921
|
+
const configuredPrimaryPath = primaryBundlePath(merged);
|
|
922
|
+
const stashDir = explicitStashDir ?? configuredPrimaryPath ?? getDefaultStashDir();
|
|
923
|
+
const stashDirExplicit = explicitStashDir != null || incomingPrimaryPath !== undefined;
|
|
677
924
|
assertSetupSandbox(stashDir, stashDirExplicit);
|
|
678
925
|
applyStashIsolationToEnv(stashDir, stashDirExplicit);
|
|
679
|
-
|
|
680
|
-
|
|
926
|
+
if (explicitStashDir !== undefined || configuredPrimaryPath === undefined) {
|
|
927
|
+
merged = deepMergeConfig(merged, { primaryPath: stashDir });
|
|
928
|
+
}
|
|
929
|
+
// Deep-merge canonical keys: nested objects merge key-by-key so a
|
|
681
930
|
// partial `--file` only updates the keys it carries and never drops sibling
|
|
682
931
|
// subkeys (e.g. output.detail survives an output.format-only file). Arrays
|
|
683
932
|
// and scalars replace wholesale.
|
|
684
|
-
for (const key of Object.keys(incoming)) {
|
|
685
|
-
if (key === "llm" || key === "agent")
|
|
686
|
-
continue;
|
|
687
|
-
const incomingVal = incoming[key];
|
|
688
|
-
const mergedRec = merged;
|
|
689
|
-
mergedRec[key] = deepMergeConfig(mergedRec[key], incomingVal);
|
|
690
|
-
}
|
|
691
|
-
// Translate legacy llm/agent inputs into the new shape.
|
|
692
|
-
if (incoming.llm) {
|
|
693
|
-
merged = { ...merged, ...applyLegacyLlm(merged, incoming.llm) };
|
|
694
|
-
}
|
|
695
|
-
if (incoming.agent) {
|
|
696
|
-
merged = { ...merged, ...applyLegacyAgent(merged, incoming.agent) };
|
|
697
|
-
}
|
|
698
933
|
// With `--yes`, fill keys still missing after the merge with non-interactive
|
|
699
934
|
// defaults. Steps start from `merged` and their nonInteractive path only
|
|
700
935
|
// populates absent values, so nothing the file or existing config supplied
|
|
@@ -707,31 +942,30 @@ export async function runSetupFromConfig(opts) {
|
|
|
707
942
|
preferredStashDir: stashDir,
|
|
708
943
|
});
|
|
709
944
|
await runSetupSteps(steps, ctx);
|
|
710
|
-
if (!ctx.config.
|
|
711
|
-
ctx.apply({ stashDir });
|
|
712
|
-
if (!ctx.config.defaults?.
|
|
945
|
+
if (!ctx.config.primaryPath)
|
|
946
|
+
ctx.apply({ primaryPath: stashDir });
|
|
947
|
+
if (!ctx.config.defaults?.engine) {
|
|
713
948
|
const detected = detectAgentCliProfiles(undefined);
|
|
714
949
|
const defaultProfile = pickDefaultAgentProfile(detected, undefined);
|
|
715
950
|
if (defaultProfile) {
|
|
716
|
-
ctx.apply(
|
|
951
|
+
ctx.apply(upsertDetectedAgentEngine(ctx.config, defaultProfile).config);
|
|
717
952
|
}
|
|
718
953
|
}
|
|
719
954
|
merged = ctx.config;
|
|
720
955
|
}
|
|
721
|
-
//
|
|
722
|
-
let
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
956
|
+
// Convert the private setup draft to the persisted bundle shape.
|
|
957
|
+
let finalizedMerged = finalizeSetupDraft(merged);
|
|
958
|
+
// Reject an invalid merged engine graph before probing or touching the stash.
|
|
959
|
+
validateCompleteConfig(finalizedMerged);
|
|
726
960
|
// Optional probe
|
|
727
|
-
const mergedLlm =
|
|
961
|
+
const mergedLlm = readCurrentLlmEngine(finalizedMerged);
|
|
728
962
|
if (opts.probe && mergedLlm) {
|
|
729
963
|
try {
|
|
730
964
|
const caps = await probeLlmCapabilities(mergedLlm);
|
|
731
965
|
if (caps.reachable) {
|
|
732
|
-
|
|
733
|
-
...
|
|
734
|
-
...
|
|
966
|
+
finalizedMerged = {
|
|
967
|
+
...finalizedMerged,
|
|
968
|
+
...writeLlmEngine(finalizedMerged, {
|
|
735
969
|
...mergedLlm,
|
|
736
970
|
capabilities: { structuredOutput: caps.structuredOutput ?? false },
|
|
737
971
|
}),
|
|
@@ -742,16 +976,18 @@ export async function runSetupFromConfig(opts) {
|
|
|
742
976
|
// Non-fatal: probe failure is informational only
|
|
743
977
|
}
|
|
744
978
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
979
|
+
validateCompleteConfig(finalizedMerged);
|
|
980
|
+
const { precommit: initResult } = await saveSetupConfig(current, finalizedMerged, async () => {
|
|
981
|
+
if (opts.noInit)
|
|
982
|
+
return undefined;
|
|
983
|
+
return akmInit({ dir: stashDir, setDefault: true, persistConfig: false });
|
|
984
|
+
});
|
|
748
985
|
return {
|
|
749
986
|
configPath: getConfigPath(),
|
|
750
|
-
stashDir,
|
|
987
|
+
bundleDir: stashDir,
|
|
751
988
|
stashCreated: initResult?.created ?? false,
|
|
752
989
|
written: true,
|
|
753
990
|
fields: Object.keys(incoming).filter((k) => incoming[k] !== undefined),
|
|
754
|
-
ripgrep: initResult?.ripgrep,
|
|
755
991
|
};
|
|
756
992
|
}
|
|
757
993
|
// ── Setup --from <file> bootstrap helper ────────────────────────────────────
|