akm-cli 0.8.2 → 0.9.0-beta.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 +187 -0
- package/dist/assets/help/help-proposals.md +1 -2
- package/dist/assets/hints/cli-hints-full.md +34 -19
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/profiles/catchup.json +13 -0
- package/dist/assets/profiles/consolidate.json +13 -0
- package/dist/assets/profiles/frequent.json +13 -0
- package/dist/assets/tasks/core/backup.yml +4 -0
- package/dist/assets/tasks/core/extract.yml +4 -0
- package/dist/assets/tasks/core/improve.yml +4 -0
- package/dist/assets/tasks/core/index-refresh.yml +4 -0
- package/dist/assets/tasks/core/sync.yml +4 -0
- package/dist/assets/tasks/core/update-stashes.yml +4 -0
- package/dist/assets/tasks/core/version-check.yml +4 -0
- package/dist/cli/config-migrate.js +6 -6
- package/dist/cli/config-validate.js +4 -4
- package/dist/cli/confirm.js +3 -3
- package/dist/cli/parse-args.js +1 -1
- package/dist/cli/shared.js +51 -14
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +171 -3862
- package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
- package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
- package/dist/commands/agent/contribute-cli.js +200 -0
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +240 -3
- package/dist/commands/config-edit.js +344 -0
- package/dist/commands/db-cli.js +2 -2
- package/dist/commands/env/env-cli.js +529 -0
- package/dist/commands/env/env.js +410 -0
- package/dist/commands/env/secret-cli.js +259 -0
- package/dist/commands/{secret.js → env/secret.js} +6 -47
- package/dist/commands/events.js +4 -4
- package/dist/commands/feedback-cli.js +18 -34
- package/dist/commands/graph/graph-cli.js +132 -0
- package/dist/commands/{graph.js → graph/graph.js} +22 -16
- package/dist/commands/health/checks.js +279 -0
- package/dist/commands/health.js +94 -262
- package/dist/commands/{consolidate.js → improve/consolidate.js} +48 -36
- package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
- package/dist/commands/{distill.js → improve/distill.js} +39 -18
- package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
- package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
- package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
- package/dist/commands/{extract.js → improve/extract.js} +185 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +4 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
- package/dist/commands/{improve.js → improve/improve.js} +509 -245
- package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
- package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
- package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
- package/dist/commands/{reflect.js → improve/reflect.js} +33 -28
- package/dist/commands/improve/session-asset.js +248 -0
- package/dist/commands/lint/agent-linter.js +1 -1
- package/dist/commands/lint/base-linter.js +55 -37
- package/dist/commands/lint/command-linter.js +1 -1
- package/dist/commands/lint/default-linter.js +1 -1
- package/dist/commands/lint/env-key-rules.js +1 -1
- package/dist/commands/lint/index.js +19 -25
- package/dist/commands/lint/knowledge-linter.js +1 -1
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +8 -8
- package/dist/commands/lint/skill-linter.js +1 -1
- package/dist/commands/lint/task-linter.js +1 -1
- package/dist/commands/lint/workflow-linter.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/observability-cli.js +244 -0
- package/dist/commands/proposal/drain-policies.js +3 -3
- package/dist/commands/proposal/drain.js +15 -10
- package/dist/commands/proposal/proposal-cli.js +478 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +5 -5
- package/dist/commands/{propose.js → proposal/propose.js} +11 -11
- package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
- package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
- package/dist/{core → commands/proposal/validators}/proposals.js +13 -7
- package/dist/commands/{curate.js → read/curate.js} +7 -7
- package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
- package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
- package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
- package/dist/commands/read/search-cli.js +207 -0
- package/dist/commands/{search.js → read/search.js} +22 -27
- package/dist/commands/{show.js → read/show.js} +31 -45
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +8 -8
- package/dist/commands/sources/add-cli.js +293 -0
- package/dist/commands/{history.js → sources/history.js} +27 -25
- package/dist/commands/{info.js → sources/info.js} +6 -6
- package/dist/commands/{init.js → sources/init.js} +6 -6
- package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
- package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
- package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
- package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
- package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
- package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
- package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
- package/dist/commands/sources/sources-cli.js +305 -0
- package/dist/commands/sources/stash-cli.js +219 -0
- package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
- package/dist/commands/tasks/default-tasks.js +173 -0
- package/dist/commands/tasks/tasks-cli.js +210 -0
- package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
- package/dist/commands/wiki-cli.js +307 -0
- package/dist/commands/workflow-cli.js +329 -0
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/assert.js +40 -0
- package/dist/core/asset/asset-create.js +54 -0
- package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
- package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
- package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
- package/dist/core/{markdown.js → asset/markdown.js} +1 -1
- package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
- package/dist/core/best-effort.js +64 -0
- package/dist/core/common.js +32 -18
- package/dist/core/{config-io.js → config/config-io.js} +29 -19
- package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
- package/dist/core/{config-schema.js → config/config-schema.js} +45 -1
- package/dist/core/config/config-types.js +16 -0
- package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
- package/dist/core/{config.js → config/config.js} +10 -8
- package/dist/core/env-secret-ref.js +90 -0
- package/dist/core/errors.js +13 -3
- package/dist/core/events.js +27 -4
- package/dist/core/file-lock.js +1 -1
- package/dist/core/improve-types.js +48 -0
- package/dist/core/lesson-lint.js +2 -2
- package/dist/core/paths.js +2 -2
- package/dist/core/ripgrep/install.js +2 -2
- package/dist/core/ripgrep/resolve.js +2 -2
- package/dist/core/state-db.js +88 -46
- package/dist/core/text-truncation.js +148 -0
- package/dist/core/time.js +1 -1
- package/dist/core/write-source.js +98 -85
- package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
- package/dist/indexer/{db.js → db/db.js} +126 -116
- package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
- package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
- package/dist/indexer/ensure-index.js +4 -4
- package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
- package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
- package/dist/indexer/indexer.js +37 -30
- package/dist/indexer/init.js +54 -0
- package/dist/indexer/manifest.js +10 -10
- package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +92 -23
- package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
- package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
- package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
- package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
- package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
- package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
- package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
- package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
- package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
- package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
- package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
- package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
- package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
- package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
- package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
- package/dist/indexer/{walker.js → walk/walker.js} +4 -3
- package/dist/integrations/agent/builder-shared.js +39 -0
- package/dist/integrations/agent/builders.js +14 -81
- package/dist/integrations/agent/config.js +6 -4
- package/dist/integrations/agent/detect.js +1 -1
- package/dist/integrations/agent/index.js +23 -8
- package/dist/integrations/agent/prompts.js +2 -3
- package/dist/integrations/agent/runner.js +22 -3
- package/dist/integrations/agent/spawn.js +9 -10
- package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
- package/dist/integrations/harnesses/claude/config-import.js +70 -0
- package/dist/integrations/harnesses/claude/index.js +64 -0
- package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +16 -1
- package/dist/integrations/harnesses/index.js +144 -0
- package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
- package/dist/integrations/harnesses/opencode/config-import.js +82 -0
- package/dist/integrations/harnesses/opencode/index.js +59 -0
- package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
- package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
- package/dist/integrations/harnesses/types.js +43 -0
- package/dist/integrations/lockfile.js +7 -16
- package/dist/integrations/session-logs/index.js +82 -9
- package/dist/llm/call-ai.js +4 -4
- package/dist/llm/client.js +131 -6
- package/dist/llm/embedder.js +6 -6
- package/dist/llm/embedders/local.js +9 -22
- package/dist/llm/embedders/remote.js +2 -2
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +31 -12
- package/dist/llm/index-passes.js +1 -1
- package/dist/llm/memory-infer.js +12 -5
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/output/context.js +6 -44
- package/dist/output/renderers.js +88 -58
- package/dist/output/shapes/curate.js +7 -3
- package/dist/output/shapes/distill.js +7 -3
- package/dist/output/shapes/env-list.js +18 -16
- package/dist/output/shapes/events.js +5 -4
- package/dist/output/shapes/helpers.js +2 -4
- package/dist/output/shapes/history.js +7 -3
- package/dist/output/shapes/passthrough.js +8 -11
- package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
- package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
- package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
- package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
- package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
- package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
- package/dist/output/shapes/registry-search.js +7 -3
- package/dist/output/shapes/registry.js +12 -0
- package/dist/output/shapes/search.js +7 -3
- package/dist/output/shapes/secret-list.js +18 -16
- package/dist/output/shapes/show.js +7 -3
- package/dist/output/shapes.js +55 -30
- package/dist/output/text/add.js +2 -3
- package/dist/output/text/clone.js +2 -3
- package/dist/output/text/config.js +2 -3
- package/dist/output/text/curate.js +4 -3
- package/dist/output/text/distill.js +2 -3
- package/dist/output/text/enable-disable.js +5 -4
- package/dist/output/text/env.js +13 -0
- package/dist/output/text/events.js +5 -4
- package/dist/output/text/feedback.js +4 -3
- package/dist/output/text/helpers.js +54 -39
- package/dist/output/text/history.js +2 -3
- package/dist/output/text/import.js +2 -3
- package/dist/output/text/index.js +2 -3
- package/dist/output/text/info.js +2 -3
- package/dist/output/text/init.js +2 -3
- package/dist/output/text/list.js +2 -3
- package/dist/output/text/proposal/producer.js +9 -0
- package/dist/output/text/proposal/proposal.js +13 -0
- package/dist/output/text/registry-commands.js +8 -7
- package/dist/output/text/registry.js +12 -0
- package/dist/output/text/remember.js +4 -3
- package/dist/output/text/remove.js +2 -3
- package/dist/output/text/save.js +2 -3
- package/dist/output/text/search.js +4 -3
- package/dist/output/text/show.js +4 -3
- package/dist/output/text/update.js +2 -3
- package/dist/output/text/upgrade.js +2 -3
- package/dist/output/text/wiki.js +12 -11
- package/dist/output/text/workflow.js +12 -10
- package/dist/output/text.js +66 -32
- package/dist/registry/build-index.js +11 -10
- package/dist/registry/factory.js +1 -1
- package/dist/registry/origin-resolve.js +1 -1
- package/dist/registry/providers/index.js +2 -2
- package/dist/registry/providers/skills-sh.js +91 -72
- package/dist/registry/providers/static-index.js +75 -52
- package/dist/registry/resolve.js +3 -3
- package/dist/runtime.js +242 -0
- package/dist/scripts/migrate-storage.js +1594 -673
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +240 -166
- package/dist/setup/detect.js +311 -9
- package/dist/setup/harness-config-import.js +6 -120
- package/dist/setup/setup.js +454 -43
- package/dist/sources/include.js +1 -1
- package/dist/sources/provider-factory.js +2 -2
- package/dist/sources/providers/filesystem.js +3 -3
- package/dist/sources/providers/git.js +9 -9
- package/dist/sources/providers/index.js +4 -4
- package/dist/sources/providers/npm.js +6 -6
- package/dist/sources/providers/provider-utils.js +13 -20
- package/dist/sources/providers/sync-from-ref.js +5 -5
- package/dist/sources/providers/tar-utils.js +2 -2
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/website-ingest.js +5 -5
- package/dist/storage/database.js +102 -0
- package/dist/storage/engines/sqlite-migrations.js +42 -0
- package/dist/storage/locations.js +25 -0
- package/dist/storage/repositories/index-db.js +43 -0
- package/dist/storage/repositories/workflow-runs-repository.js +141 -0
- package/dist/tasks/backends/cron.js +4 -4
- package/dist/tasks/backends/exec-utils.js +32 -0
- package/dist/tasks/backends/index.js +3 -3
- package/dist/tasks/backends/launchd.js +7 -14
- package/dist/tasks/backends/schtasks.js +7 -16
- package/dist/tasks/embedded.js +71 -0
- package/dist/tasks/parser.js +2 -2
- package/dist/tasks/resolveAkmBin.js +1 -1
- package/dist/tasks/runner.js +28 -15
- package/dist/tasks/schedule.js +1 -1
- package/dist/tasks/validator.js +7 -7
- package/dist/text-import-hook.mjs +51 -0
- package/dist/version.js +2 -1
- package/dist/wiki/wiki.js +7 -7
- package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
- package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
- package/dist/workflows/cli.js +1 -1
- package/dist/workflows/db.js +50 -32
- package/dist/workflows/parser.js +4 -4
- package/dist/workflows/renderer.js +5 -5
- package/dist/workflows/runtime/agent-identity.js +56 -0
- package/dist/workflows/runtime/checkin.js +57 -0
- package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
- package/dist/workflows/validate-summary.js +82 -0
- package/docs/README.md +1 -1
- package/docs/data-and-telemetry.md +6 -6
- package/package.json +16 -8
- package/dist/commands/add-cli.js +0 -279
- package/dist/commands/env.js +0 -213
- package/dist/integrations/agent/sdk-runner.js +0 -126
- package/dist/output/shapes/vault-list.js +0 -19
- package/dist/output/text/proposal-producer.js +0 -8
- package/dist/output/text/proposal.js +0 -12
- package/dist/output/text/vault.js +0 -16
- /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
- /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
- /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
- /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
- /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
- /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
- /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
- /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Interactive `akm config edit` — a schema-driven, menu-based config editor.
|
|
6
|
+
*
|
|
7
|
+
* ## Why @clack/prompts (not a widget TUI)
|
|
8
|
+
*
|
|
9
|
+
* The issue (#513) originally proposed a `neo-blessed` BIOS-style widget TUI.
|
|
10
|
+
* After evaluation (see `docs/technical/ink-tui-evaluation.md` and the #513
|
|
11
|
+
* comments) we ship this on `@clack/prompts` — the prompt library akm already
|
|
12
|
+
* uses for `akm setup` and `confirmDestructive`. Zero new deps, the same
|
|
13
|
+
* interaction paradigm as setup, and a proven packaging path through the
|
|
14
|
+
* `bun build --compile` single binary.
|
|
15
|
+
*
|
|
16
|
+
* ## Schema-driven, single source of truth
|
|
17
|
+
*
|
|
18
|
+
* The section list, the per-section fields, and each field's input type are
|
|
19
|
+
* DERIVED from the Zod config schema (`core/config/config-schema.ts`) by
|
|
20
|
+
* {@link buildConfigEditModel}. There is no hand-maintained parallel field
|
|
21
|
+
* table — adding a field to the schema makes it appear in the editor for free.
|
|
22
|
+
*
|
|
23
|
+
* ## Reuse, don't reimplement
|
|
24
|
+
*
|
|
25
|
+
* The write path reuses the existing machinery verbatim:
|
|
26
|
+
* - {@link setConfigValue} (the config-cli walker front-end) for coercion,
|
|
27
|
+
* validation, legacy aliasing, and apiKey rejection.
|
|
28
|
+
* - {@link loadConfig} / {@link saveConfig} for read/write.
|
|
29
|
+
* - {@link backupExistingConfig} for the timestamped pre-write snapshot.
|
|
30
|
+
*
|
|
31
|
+
* ## Pure core, thin shell
|
|
32
|
+
*
|
|
33
|
+
* {@link buildConfigEditModel} and {@link applyConfigEdit} are pure and unit
|
|
34
|
+
* tested directly — no TTY required. {@link runConfigEdit} is the thin
|
|
35
|
+
* @clack interaction layer.
|
|
36
|
+
*/
|
|
37
|
+
import * as p from "@clack/prompts";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
import { loadConfig, saveConfig } from "../core/config/config.js";
|
|
40
|
+
import { backupExistingConfig } from "../core/config/config-io.js";
|
|
41
|
+
import { AkmConfigShape } from "../core/config/config-schema.js";
|
|
42
|
+
import { UsageError } from "../core/errors.js";
|
|
43
|
+
import { getConfigPath } from "../core/paths.js";
|
|
44
|
+
import { getConfigValue, setConfigValue } from "./config-cli.js";
|
|
45
|
+
/** Maximum nesting depth walked when deriving fields. Guards against records. */
|
|
46
|
+
const MAX_FIELD_DEPTH = 3;
|
|
47
|
+
/** Strip Zod wrappers (.optional/.default/.nullable/.catch/.effects). */
|
|
48
|
+
function unwrapSchema(schema) {
|
|
49
|
+
let current = schema;
|
|
50
|
+
for (;;) {
|
|
51
|
+
if (current instanceof z.ZodOptional)
|
|
52
|
+
current = current._def.innerType;
|
|
53
|
+
else if (current instanceof z.ZodDefault)
|
|
54
|
+
current = current._def.innerType;
|
|
55
|
+
else if (current instanceof z.ZodNullable)
|
|
56
|
+
current = current._def.innerType;
|
|
57
|
+
else if (current instanceof z.ZodCatch)
|
|
58
|
+
current = current._def.innerType;
|
|
59
|
+
else if (current instanceof z.ZodReadonly)
|
|
60
|
+
current = current._def.innerType;
|
|
61
|
+
else if (current instanceof z.ZodEffects)
|
|
62
|
+
current = current._def.schema;
|
|
63
|
+
else
|
|
64
|
+
return current;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Classify an unwrapped leaf schema into a {@link ConfigFieldKind}. */
|
|
68
|
+
function classifyLeaf(schema, isSecret) {
|
|
69
|
+
if (isSecret)
|
|
70
|
+
return "secret";
|
|
71
|
+
if (schema instanceof z.ZodString)
|
|
72
|
+
return "text";
|
|
73
|
+
if (schema instanceof z.ZodNumber)
|
|
74
|
+
return "number";
|
|
75
|
+
if (schema instanceof z.ZodBoolean)
|
|
76
|
+
return "boolean";
|
|
77
|
+
if (schema instanceof z.ZodEnum)
|
|
78
|
+
return "select";
|
|
79
|
+
// ZodNativeEnum / ZodLiteral are treated as select/text fallbacks.
|
|
80
|
+
if (schema instanceof z.ZodLiteral)
|
|
81
|
+
return "text";
|
|
82
|
+
// Unions of primitives (e.g. configVersion: string|number) → text.
|
|
83
|
+
if (schema instanceof z.ZodUnion) {
|
|
84
|
+
const opts = schema._def.options.map(unwrapSchema);
|
|
85
|
+
if (opts.some((o) => o instanceof z.ZodString || o instanceof z.ZodNumber))
|
|
86
|
+
return "text";
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Recursively collect editable leaf fields from an object schema. Descends
|
|
92
|
+
* into nested `z.object(...)` shapes (building dotted paths); records, arrays,
|
|
93
|
+
* and unknown composites are surfaced as a single `json` field so the user can
|
|
94
|
+
* still edit them as raw JSON via the walker's JSON coercion path.
|
|
95
|
+
*/
|
|
96
|
+
function collectFields(schema, prefix, depth) {
|
|
97
|
+
const unwrapped = unwrapSchema(schema);
|
|
98
|
+
const fields = [];
|
|
99
|
+
if (unwrapped instanceof z.ZodObject && depth < MAX_FIELD_DEPTH) {
|
|
100
|
+
const shape = unwrapped.shape;
|
|
101
|
+
for (const [key, child] of Object.entries(shape)) {
|
|
102
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
103
|
+
const childUnwrapped = unwrapSchema(child);
|
|
104
|
+
const isSecret = key === "apiKey";
|
|
105
|
+
if (childUnwrapped instanceof z.ZodObject && depth + 1 < MAX_FIELD_DEPTH) {
|
|
106
|
+
fields.push(...collectFields(childUnwrapped, path, depth + 1));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const kind = classifyLeaf(childUnwrapped, isSecret);
|
|
110
|
+
if (kind === null) {
|
|
111
|
+
// Records / arrays / nested composites at the depth limit: editable as JSON.
|
|
112
|
+
if (childUnwrapped instanceof z.ZodRecord ||
|
|
113
|
+
childUnwrapped instanceof z.ZodArray ||
|
|
114
|
+
childUnwrapped instanceof z.ZodObject) {
|
|
115
|
+
fields.push({ path, label: key, kind: "json", secret: false });
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const field = {
|
|
120
|
+
path,
|
|
121
|
+
label: key,
|
|
122
|
+
kind,
|
|
123
|
+
secret: kind === "secret",
|
|
124
|
+
};
|
|
125
|
+
if (kind === "select") {
|
|
126
|
+
field.options = [...childUnwrapped._def.values];
|
|
127
|
+
}
|
|
128
|
+
fields.push(field);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return fields;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build the schema-driven edit model: one section per top-level config key,
|
|
135
|
+
* each with its editable leaf fields and input kinds. Pure — depends only on
|
|
136
|
+
* the schema (the `config` argument is reserved for future value-aware
|
|
137
|
+
* shaping; current callers pass it through unchanged for symmetry with
|
|
138
|
+
* {@link applyConfigEdit}).
|
|
139
|
+
*
|
|
140
|
+
* Sections that yield no editable fields (pure records/arrays like `sources`,
|
|
141
|
+
* `installed`, `registries`, `index`, `profiles`) are still surfaced with a
|
|
142
|
+
* single `json` field so they remain reachable in the menu.
|
|
143
|
+
*/
|
|
144
|
+
export function buildConfigEditModel(shape = AkmConfigShape, _config) {
|
|
145
|
+
const sections = [];
|
|
146
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
147
|
+
const unwrapped = unwrapSchema(schema);
|
|
148
|
+
let fields;
|
|
149
|
+
if (unwrapped instanceof z.ZodObject) {
|
|
150
|
+
fields = collectFields(unwrapped, key, 1);
|
|
151
|
+
if (fields.length === 0) {
|
|
152
|
+
fields = [{ path: key, label: key, kind: "json", secret: false }];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const kind = classifyLeaf(unwrapped, false);
|
|
157
|
+
if (kind) {
|
|
158
|
+
const field = { path: key, label: key, kind, secret: false };
|
|
159
|
+
if (kind === "select")
|
|
160
|
+
field.options = [...unwrapped._def.values];
|
|
161
|
+
fields = [field];
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Arrays / records / unknown top-level shapes → editable as JSON.
|
|
165
|
+
fields = [{ path: key, label: key, kind: "json", secret: false }];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
sections.push({ key, fields });
|
|
169
|
+
}
|
|
170
|
+
return sections.length > 0 ? { sections } : { sections: [] };
|
|
171
|
+
}
|
|
172
|
+
// ── Apply (pure write delegation) ────────────────────────────────────────────
|
|
173
|
+
/**
|
|
174
|
+
* Apply a single edit to a config object, returning the next config. Pure —
|
|
175
|
+
* delegates to {@link setConfigValue} (the existing walker front-end), so it
|
|
176
|
+
* inherits coercion, schema validation, legacy aliasing, AND the apiKey
|
|
177
|
+
* rejection guard (#454). Callers must NOT pass apiKey paths; the editor shell
|
|
178
|
+
* routes secrets to env-var guidance and never reaches here for them.
|
|
179
|
+
*
|
|
180
|
+
* @throws UsageError on apiKey paths, unknown keys, or invalid values.
|
|
181
|
+
*/
|
|
182
|
+
export function applyConfigEdit(config, path, value) {
|
|
183
|
+
return setConfigValue(config, path, value);
|
|
184
|
+
}
|
|
185
|
+
/** Environment variable a secret field steers the user toward (#454). */
|
|
186
|
+
export function envVarForSecret(path) {
|
|
187
|
+
if (path === "embedding.apiKey")
|
|
188
|
+
return "AKM_EMBED_API_KEY";
|
|
189
|
+
if (path === "llm.apiKey")
|
|
190
|
+
return "AKM_LLM_API_KEY";
|
|
191
|
+
if (path.startsWith("profiles.llm."))
|
|
192
|
+
return "AKM_LLM_API_KEY";
|
|
193
|
+
return "AKM_LLM_API_KEY / AKM_EMBED_API_KEY";
|
|
194
|
+
}
|
|
195
|
+
// ── Interactive shell (thin @clack layer) ────────────────────────────────────
|
|
196
|
+
/**
|
|
197
|
+
* Determine whether the current process can run an interactive editor.
|
|
198
|
+
* Requires a real TTY on both stdin and stdout and a non-CI environment.
|
|
199
|
+
*/
|
|
200
|
+
export function isInteractiveTerminal(env = process.env) {
|
|
201
|
+
const ci = env.CI;
|
|
202
|
+
const isCi = ci !== undefined && ci !== null && !["", "0", "false"].includes(String(ci).trim().toLowerCase());
|
|
203
|
+
if (isCi)
|
|
204
|
+
return false;
|
|
205
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
206
|
+
}
|
|
207
|
+
const NON_INTERACTIVE_MESSAGE = "`akm config edit` is interactive and requires a TTY. " +
|
|
208
|
+
"Use `akm config set <key> <value>` for scripted or CI edits.";
|
|
209
|
+
function formatValue(value) {
|
|
210
|
+
if (value === null || value === undefined)
|
|
211
|
+
return "(unset)";
|
|
212
|
+
if (typeof value === "object")
|
|
213
|
+
return JSON.stringify(value);
|
|
214
|
+
return String(value);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Run the interactive config editor. Throws {@link UsageError} when no TTY is
|
|
218
|
+
* available (CI / piped). Otherwise drives a menu loop:
|
|
219
|
+
* section select → field select → typed value prompt → confirm → backup+save.
|
|
220
|
+
*/
|
|
221
|
+
export async function runConfigEdit() {
|
|
222
|
+
if (!isInteractiveTerminal()) {
|
|
223
|
+
throw new UsageError(NON_INTERACTIVE_MESSAGE, "NON_INTERACTIVE_REQUIRES_YES");
|
|
224
|
+
}
|
|
225
|
+
let config = loadConfig();
|
|
226
|
+
const model = buildConfigEditModel(AkmConfigShape, config);
|
|
227
|
+
let dirty = false;
|
|
228
|
+
p.intro("akm config edit");
|
|
229
|
+
for (;;) {
|
|
230
|
+
const sectionKey = await p.select({
|
|
231
|
+
message: "Select a config section to edit:",
|
|
232
|
+
options: [
|
|
233
|
+
...model.sections.map((s) => ({ value: s.key, label: s.key })),
|
|
234
|
+
{ value: "__exit__", label: dirty ? "Save and exit" : "Exit" },
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
if (p.isCancel(sectionKey) || sectionKey === "__exit__")
|
|
238
|
+
break;
|
|
239
|
+
const section = model.sections.find((s) => s.key === sectionKey);
|
|
240
|
+
if (!section)
|
|
241
|
+
continue;
|
|
242
|
+
const fieldPath = await p.select({
|
|
243
|
+
message: `Select a field in "${section.key}":`,
|
|
244
|
+
options: [
|
|
245
|
+
...section.fields.map((f) => ({
|
|
246
|
+
value: f.path,
|
|
247
|
+
label: f.label,
|
|
248
|
+
hint: `${f.kind} — ${formatValue(safeGet(config, f.path))}`,
|
|
249
|
+
})),
|
|
250
|
+
{ value: "__back__", label: "← Back" },
|
|
251
|
+
],
|
|
252
|
+
});
|
|
253
|
+
if (p.isCancel(fieldPath) || fieldPath === "__back__")
|
|
254
|
+
continue;
|
|
255
|
+
const field = section.fields.find((f) => f.path === fieldPath);
|
|
256
|
+
if (!field)
|
|
257
|
+
continue;
|
|
258
|
+
// #454: never persist secrets. Show env-var guidance and skip the write.
|
|
259
|
+
if (field.secret) {
|
|
260
|
+
p.note(`API keys are never stored in config (they leak through backups, logs, and version control).\n` +
|
|
261
|
+
`Export the environment variable instead:\n\n export ${envVarForSecret(field.path)}=…\n\n` +
|
|
262
|
+
`AKM reads it at request time.`, "apiKey is not persisted");
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const newValue = await promptForField(field, safeGet(config, field.path));
|
|
266
|
+
if (newValue === undefined)
|
|
267
|
+
continue; // cancelled / back
|
|
268
|
+
try {
|
|
269
|
+
config = applyConfigEdit(config, field.path, newValue);
|
|
270
|
+
dirty = true;
|
|
271
|
+
p.log.success(`Set ${field.path} = ${newValue}`);
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
275
|
+
p.log.error(msg);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!dirty) {
|
|
279
|
+
p.outro("No changes made.");
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const confirmed = await p.confirm({ message: "Save changes to config?", initialValue: true });
|
|
283
|
+
if (p.isCancel(confirmed) || confirmed !== true) {
|
|
284
|
+
p.outro("Discarded changes.");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const backup = backupExistingConfig(getConfigPath());
|
|
288
|
+
saveConfig(config);
|
|
289
|
+
if (backup) {
|
|
290
|
+
p.outro(`Saved. Backup written to ${backup.timestamped}`);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
p.outro("Saved.");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Read a value via the existing walker front-end, swallowing unknown-key errors. */
|
|
297
|
+
function safeGet(config, path) {
|
|
298
|
+
try {
|
|
299
|
+
return getConfigValue(config, path);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Prompt for a single field's new value, typed by its schema-derived kind.
|
|
307
|
+
* Returns the raw string to pass to {@link applyConfigEdit}, or `undefined`
|
|
308
|
+
* when the user cancels.
|
|
309
|
+
*/
|
|
310
|
+
async function promptForField(field, current) {
|
|
311
|
+
if (field.kind === "boolean") {
|
|
312
|
+
const v = await p.confirm({
|
|
313
|
+
message: `${field.label}:`,
|
|
314
|
+
initialValue: current === true,
|
|
315
|
+
});
|
|
316
|
+
if (p.isCancel(v))
|
|
317
|
+
return undefined;
|
|
318
|
+
return v ? "true" : "false";
|
|
319
|
+
}
|
|
320
|
+
if (field.kind === "select" && field.options) {
|
|
321
|
+
const v = await p.select({
|
|
322
|
+
message: `${field.label}:`,
|
|
323
|
+
options: field.options.map((o) => ({ value: o, label: o })),
|
|
324
|
+
initialValue: typeof current === "string" ? current : undefined,
|
|
325
|
+
});
|
|
326
|
+
if (p.isCancel(v))
|
|
327
|
+
return undefined;
|
|
328
|
+
return v;
|
|
329
|
+
}
|
|
330
|
+
const placeholder = field.kind === "json" ? "JSON value (or empty to clear)" : "";
|
|
331
|
+
const initial = current === null || current === undefined
|
|
332
|
+
? ""
|
|
333
|
+
: typeof current === "object"
|
|
334
|
+
? JSON.stringify(current)
|
|
335
|
+
: String(current);
|
|
336
|
+
const v = await p.text({
|
|
337
|
+
message: `${field.label}${field.kind === "number" ? " (number)" : ""}:`,
|
|
338
|
+
placeholder,
|
|
339
|
+
initialValue: initial,
|
|
340
|
+
});
|
|
341
|
+
if (p.isCancel(v))
|
|
342
|
+
return undefined;
|
|
343
|
+
return v;
|
|
344
|
+
}
|
package/dist/commands/db-cli.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* plain `mv`/`cp`) to recover. Keeping the surface narrow lets us evolve the
|
|
13
13
|
* backup format under the hood without locking in an API.
|
|
14
14
|
*/
|
|
15
|
-
import { getDataDir } from "../core/paths";
|
|
16
|
-
import { listBackups } from "../indexer/db-backup";
|
|
15
|
+
import { getDataDir } from "../core/paths.js";
|
|
16
|
+
import { listBackups } from "../indexer/db/db-backup.js";
|
|
17
17
|
export function akmDbBackups() {
|
|
18
18
|
const dataDir = getDataDir();
|
|
19
19
|
return {
|