akm-cli 0.8.0-rc.3 → 0.8.0-rc.6
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/{.github/CHANGELOG.md → CHANGELOG.md} +112 -1
- package/README.md +20 -4
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +47 -0
- package/dist/cli.js +1011 -69
- package/dist/commands/agent-dispatch.js +3 -0
- package/dist/commands/agent-support.js +11 -5
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +129 -517
- package/dist/commands/consolidate.js +781 -62
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +5 -3
- package/dist/commands/distill.js +803 -63
- package/dist/commands/eval-cases.js +3 -0
- package/dist/commands/events.js +3 -0
- package/dist/commands/graph.js +260 -5
- package/dist/commands/health.js +32 -8
- package/dist/commands/help/help-improve.md +25 -1
- package/dist/commands/history.js +51 -16
- package/dist/commands/improve-profiles.js +146 -0
- package/dist/commands/improve-result-file.js +103 -0
- package/dist/commands/improve.js +1267 -253
- package/dist/commands/info.js +3 -0
- package/dist/commands/init.js +49 -1
- package/dist/commands/installed-stashes.js +6 -23
- package/dist/commands/knowledge.js +3 -0
- package/dist/commands/lint/agent-linter.js +3 -0
- package/dist/commands/lint/base-linter.js +192 -4
- package/dist/commands/lint/command-linter.js +3 -0
- package/dist/commands/lint/default-linter.js +3 -0
- package/dist/commands/lint/index.js +40 -2
- package/dist/commands/lint/knowledge-linter.js +3 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +3 -0
- package/dist/commands/lint/registry.js +3 -0
- package/dist/commands/lint/skill-linter.js +3 -0
- package/dist/commands/lint/task-linter.js +15 -12
- package/dist/commands/lint/types.js +3 -0
- package/dist/commands/lint/vault-key-rules.js +103 -31
- package/dist/commands/lint/workflow-linter.js +3 -0
- package/dist/commands/lint.js +3 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +59 -6
- package/dist/commands/propose.js +15 -3
- package/dist/commands/reflect.js +1032 -86
- package/dist/commands/registry-search.js +3 -0
- package/dist/commands/remember.js +15 -6
- package/dist/commands/schema-repair.js +88 -15
- package/dist/commands/search.js +99 -14
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +21 -7
- package/dist/commands/source-add.js +7 -35
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +3 -0
- package/dist/commands/tasks.js +144 -91
- package/dist/commands/url-checker.js +3 -0
- package/dist/commands/vault.js +27 -65
- package/dist/core/action-contributors.js +3 -0
- package/dist/core/asset-ref.js +3 -0
- package/dist/core/asset-registry.js +4 -1
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +16 -4
- package/dist/core/common.js +57 -0
- package/dist/core/concurrent.js +3 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +625 -0
- package/dist/core/config-schema.js +501 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +323 -1083
- package/dist/core/errors.js +40 -19
- package/dist/core/events.js +31 -25
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +3 -0
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +3 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +142 -14
- package/dist/core/parse.js +3 -0
- package/dist/core/paths.js +225 -11
- package/dist/core/proposal-quality-validators.js +364 -0
- package/dist/core/proposal-validators.js +11 -3
- package/dist/core/proposals.js +450 -5
- package/dist/core/state-db.js +207 -11
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +3 -0
- package/dist/core/warn.js +3 -0
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +110 -25
- package/dist/indexer/db.js +594 -113
- package/dist/indexer/ensure-index.js +3 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +162 -40
- package/dist/indexer/graph-db.js +241 -51
- package/dist/indexer/graph-dedup.js +3 -7
- package/dist/indexer/graph-extraction.js +242 -149
- package/dist/indexer/index-context.js +3 -9
- package/dist/indexer/indexer.js +52 -3
- package/dist/indexer/llm-cache.js +18 -18
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +24 -0
- package/dist/indexer/memory-inference.js +36 -45
- package/dist/indexer/metadata-contributors.js +3 -0
- package/dist/indexer/metadata.js +60 -0
- package/dist/indexer/path-resolver.js +3 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +134 -7
- package/dist/indexer/ranking.js +8 -1
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +91 -2
- package/dist/indexer/search-source.js +20 -1
- package/dist/indexer/semantic-status.js +3 -0
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +3 -0
- package/dist/integrations/agent/builders.js +26 -0
- package/dist/integrations/agent/config.js +121 -429
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +3 -12
- package/dist/integrations/agent/model-aliases.js +3 -11
- package/dist/integrations/agent/profiles.js +3 -0
- package/dist/integrations/agent/prompts.js +73 -2
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +8 -2
- package/dist/integrations/agent/spawn.js +32 -8
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +22 -51
- package/dist/integrations/session-logs/index.js +3 -0
- package/dist/integrations/session-logs/providers/claude-code.js +3 -0
- package/dist/integrations/session-logs/providers/opencode.js +3 -0
- package/dist/integrations/session-logs/types.js +3 -0
- package/dist/llm/call-ai.js +14 -26
- package/dist/llm/client.js +11 -2
- package/dist/llm/embedder.js +3 -19
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +3 -0
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +86 -56
- package/dist/llm/graph-extract.js +401 -30
- package/dist/llm/index-passes.js +9 -23
- package/dist/llm/memory-infer.js +5 -2
- package/dist/llm/metadata-enhance.js +3 -7
- package/dist/llm/prompts/graph-extract-user-prompt.md +24 -1
- package/dist/output/cli-hints-full.md +8 -4
- package/dist/output/cli-hints-short.md +2 -2
- package/dist/output/cli-hints.js +3 -0
- package/dist/output/context.js +3 -0
- package/dist/output/renderers.js +6 -3
- package/dist/output/shapes.js +49 -12
- package/dist/output/text.js +64 -15
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +3 -0
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +10 -1
- package/dist/registry/providers/static-index.js +10 -1
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +3 -0
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17307 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +306 -67
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +3 -11
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +3 -0
- package/dist/sources/providers/git.js +3 -0
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +3 -0
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +3 -0
- package/dist/tasks/backends/cron.js +3 -0
- package/dist/tasks/backends/exec-utils.js +3 -0
- package/dist/tasks/backends/index.js +3 -11
- package/dist/tasks/backends/launchd.js +3 -0
- package/dist/tasks/backends/schtasks.js +3 -0
- package/dist/tasks/parser.js +51 -38
- package/dist/tasks/resolveAkmBin.js +3 -0
- package/dist/tasks/runner.js +35 -9
- package/dist/tasks/schedule.js +3 -0
- package/dist/tasks/schema.js +5 -3
- package/dist/tasks/validator.js +6 -3
- package/dist/version.js +3 -0
- package/dist/wiki/wiki-templates.js +3 -0
- package/dist/wiki/wiki.js +3 -0
- package/dist/workflows/authoring.js +3 -0
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +3 -0
- package/dist/workflows/runs.js +3 -0
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +3 -7
- package/docs/README.md +4 -0
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +5 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +18 -7
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -385
|
@@ -6,6 +6,117 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
> **CI / Docker users:** the 0.8.0 storage split moved `akm.lock`, the event
|
|
10
|
+
> database, and the registry cache out of `$XDG_CONFIG_HOME/akm/` into
|
|
11
|
+
> `$XDG_DATA_HOME`, `$XDG_STATE_HOME`, and `$XDG_CACHE_HOME` respectively. If
|
|
12
|
+
> you override any of `AKM_CONFIG_DIR`, `AKM_DATA_DIR`, `AKM_STATE_DIR`,
|
|
13
|
+
> `AKM_CACHE_DIR` in CI to isolate per-job state, set **all four** (or none,
|
|
14
|
+
> and rely on XDG defaults). Overriding only `AKM_CONFIG_DIR` will leave the
|
|
15
|
+
> lock file / event DB pointing at the host's default `$XDG_DATA_HOME`,
|
|
16
|
+
> causing lock contention and bleed between jobs.
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- **Install-time security audit (`security.installAudit`) and the `--trust`
|
|
21
|
+
flag**. The audit scanned incoming stash assets for risky patterns (e.g.
|
|
22
|
+
`curl ... | bash`, "ignore previous instructions") and blocked installs on
|
|
23
|
+
critical findings. In practice it produced too many false positives on
|
|
24
|
+
benign documentation strings and forced first-time users to pass `--trust`
|
|
25
|
+
or twiddle config just to install the official stash. The whole feature is
|
|
26
|
+
gone:
|
|
27
|
+
- `akm add` and `akm update` no longer scan synced content.
|
|
28
|
+
- The `--trust` flag is removed from `akm add` and `akm wiki register`.
|
|
29
|
+
- The `security.installAudit.*` config keys (`enabled`, `blockOnCritical`,
|
|
30
|
+
`registryAllowlist`, `registryWhitelist`, `blockUnlistedRegistries`,
|
|
31
|
+
`allowedFindings`) are no longer recognised; the entire `security` block
|
|
32
|
+
is removed from the config schema.
|
|
33
|
+
- The `akm config set security.installAudit.*` keys now error as unknown.
|
|
34
|
+
- `audit` fields are removed from `AddResponse.installed` and
|
|
35
|
+
`SourceInstallStatus`.
|
|
36
|
+
|
|
37
|
+
### Breaking Changes
|
|
38
|
+
|
|
39
|
+
- **Project-level `.akm/config.json` files are no longer merged**. The
|
|
40
|
+
multi-layer config discovery introduced in the 0.7 line was deprecated
|
|
41
|
+
in late-0.8.x with a warning; that warning is now backed by removal.
|
|
42
|
+
`loadConfig` walks cwd-ancestors only to emit a one-time deprecation
|
|
43
|
+
warning per discovered file. Move any needed settings to
|
|
44
|
+
`~/.config/akm/config.json`. `stashInheritance` (a multi-layer-only
|
|
45
|
+
field) is removed from the schema.
|
|
46
|
+
|
|
47
|
+
- **`${VAR}` env-var expansion only resolves at the apiKey consumption
|
|
48
|
+
sites**. The recursive expansion walker that ran on the load path is
|
|
49
|
+
gone. Other config string values now round-trip verbatim: a literal
|
|
50
|
+
`${HOME}` in (say) `stashDir` is preserved as the literal `${HOME}`
|
|
51
|
+
on read. The new exported `resolveSecret(value)` helper is applied
|
|
52
|
+
only where authorization headers are built (`src/llm/client.ts`,
|
|
53
|
+
`src/llm/embedders/remote.ts`, `src/integrations/agent/sdk-runner.ts`).
|
|
54
|
+
Documented `${OPENAI_API_KEY}` recipes in `docs/configuration.md`
|
|
55
|
+
continue to work because expansion still happens at request time for
|
|
56
|
+
apiKey fields.
|
|
57
|
+
|
|
58
|
+
- **`AKM_FORCE_DOWNGRADE_CONFIG` env var removed**. The newer-than-binary
|
|
59
|
+
read-only guard (`configReadOnlyReason`, `markConfigReadOnlyIfNewer`,
|
|
60
|
+
`getConfigReadOnlyReason`) is gone. Configs declaring a `configVersion`
|
|
61
|
+
newer than the running binary now save through silently — unknown
|
|
62
|
+
fields are stripped on save by `sanitizeConfigForWrite` plus the
|
|
63
|
+
strict-walled Zod schema. Users on 0.9.x configs should not open them
|
|
64
|
+
with a 0.8.x binary in writable workflows.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **Config layer rewrite** — single-source-of-truth Zod schema in
|
|
69
|
+
`src/core/config-schema.ts` replaces the per-field parse switch AND
|
|
70
|
+
the per-shape load-time parser. Adding a new config field is now one
|
|
71
|
+
line of schema + zero lines of CLI code. `loadConfig` now consists of
|
|
72
|
+
parse-text → migrate (pure JSON transforms) → Zod safeParse → overlay
|
|
73
|
+
defaults — a ~30-line pipeline that absorbs ~900 LOC of legacy
|
|
74
|
+
per-shape parsers (`parseLlmConfig`, `parseEmbeddingConfig`,
|
|
75
|
+
`parseIndexConfig`, `parseSourceConfigEntry`, and ~20 more).
|
|
76
|
+
- **#454**: `akm config set llm.apiKey` / `embedding.apiKey` /
|
|
77
|
+
`profiles.llm.<name>.apiKey` now throws `UsageError` pointing at the
|
|
78
|
+
corresponding env var (`AKM_LLM_API_KEY`, `AKM_EMBED_API_KEY`,
|
|
79
|
+
`AKM_PROFILE_<NAME>_API_KEY`). Was previously a silent strip.
|
|
80
|
+
- **#455**: every schema-leaf key is now reachable via `akm config set`.
|
|
81
|
+
Includes previously hand-listed gaps: `defaults.agent`, `search.minScore`,
|
|
82
|
+
`improve.eventRetentionDays`, `embedding.provider`, `llm.temperature`,
|
|
83
|
+
`profiles.llm.<name>.*`, `profiles.agent.<name>.*`, etc.
|
|
84
|
+
- **#456**: `akm config validate` and `akm config migrate` are now real
|
|
85
|
+
registered subcommands. The orphan implementations in `config-validate.ts`
|
|
86
|
+
have been removed; the new entry points live in `src/cli/`.
|
|
87
|
+
- **#457**: project-level `.akm/config.json` files are now flagged with a
|
|
88
|
+
deprecation warning ("will be ignored in 0.9.0+"). The merge still
|
|
89
|
+
happens in 0.8.x — one release of grace.
|
|
90
|
+
- **#458**: malformed JSON or non-object root in the config file now raises
|
|
91
|
+
`ConfigError("INVALID_CONFIG_FILE")` with the underlying parse error.
|
|
92
|
+
Was previously a silent fallback to `DEFAULT_CONFIG`, which masked
|
|
93
|
+
corruption. File-not-existing remains the legitimate cold-start case.
|
|
94
|
+
- **#459**: `~/.cache/akm/config-backups/` is now bounded to the 5 most
|
|
95
|
+
recent timestamped backups. Pruning runs on each `saveConfig`.
|
|
96
|
+
`config.latest.json` is preserved separately.
|
|
97
|
+
- **#460**: `UNKNOWN_CONFIG_KEY_HINT` is now auto-generated from the
|
|
98
|
+
schema via `listTopLevelConfigKeys()`. No more stale hand-maintained string.
|
|
99
|
+
- **#461**: if the auto-migration disk-write fails, `loadConfig` now throws
|
|
100
|
+
a hard error instead of returning the in-memory migrated shape. Eliminates
|
|
101
|
+
the silent infinite re-migrate loop on every `akm` command.
|
|
102
|
+
- **#462**: nested registries[], sources[], profiles.* objects are
|
|
103
|
+
`.strict()` — unknown keys are rejected with a path-pointing error at
|
|
104
|
+
both set time and saveConfig time.
|
|
105
|
+
- **#463**: `schemas/akm-config.json` is now auto-generated from the Zod
|
|
106
|
+
source via `bun scripts/gen-config-schema.ts`. A drift test fails CI if
|
|
107
|
+
the committed file disagrees with the regeneration output.
|
|
108
|
+
- **#464.a**: `defaultWriteTarget` is validated via Zod `.refine()` against
|
|
109
|
+
`sources[].name`. With no sources configured, save-time validation
|
|
110
|
+
rejects instead of silently accepting (no implicit "first writable" fallback).
|
|
111
|
+
- **#464.b**: generic unset works on `semanticSearchMode` and every other
|
|
112
|
+
key via the dotted-path walker.
|
|
113
|
+
- **#464.c**: all write paths route through `writeFileAtomic`.
|
|
114
|
+
- **#464.d**: duplicate `mergeSecurityConfig` / `mergeInstallAuditConfig`
|
|
115
|
+
in `config-cli.ts` are deleted; merging happens via re-parse through the
|
|
116
|
+
Zod schema.
|
|
117
|
+
|
|
118
|
+
See `docs/migration/v0.7-to-v0.8.md` for the user-facing migration guide.
|
|
119
|
+
|
|
9
120
|
## [0.7.5] - 2026-05-08
|
|
10
121
|
|
|
11
122
|
### Added
|
|
@@ -19,7 +130,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
19
130
|
- **Workflow runs are now scoped to the current workspace** — ref-based workflow commands (`workflow next/status/list`) now resolve runs within the current project, worktree, or non-repo directory instead of sharing active-run state globally across the whole cache. Direct run-id commands still target the exact run.
|
|
20
131
|
- **Help, hints, and workflow docs now explain run scoping** — CLI descriptions, embedded hints, operator docs, and workflow guides now describe the current-scope semantics so users understand how ref-based run resolution behaves across repos and local sandboxes.
|
|
21
132
|
- **`akm show` auto-indexes stale state instead of falling back to raw filesystem reads** — show/search parity is tighter because stale index state now triggers refresh rather than silently drifting to a separate fallback path.
|
|
22
|
-
- **Release metadata lookup follows the published
|
|
133
|
+
- **Release metadata lookup follows the published `CHANGELOG.md` layout** — migration-help, package publish metadata, and related docs now consistently reference the shipped changelog location at the package root.
|
|
23
134
|
- **Documentation refresh across README and posts** — README positioning, command-tour docs, workflow examples, and dev.to post organization were refreshed to better match the current CLI surface.
|
|
24
135
|
|
|
25
136
|
### Fixed
|
package/README.md
CHANGED
|
@@ -7,17 +7,32 @@
|
|
|
7
7
|
[](https://github.com/itlackey/akm/blob/main/LICENSE)
|
|
8
8
|
|
|
9
9
|
`akm` is a package manager for AI agent capabilities -- scripts, skills, commands,
|
|
10
|
-
agents, knowledge,
|
|
11
|
-
run shell commands,
|
|
10
|
+
agents, knowledge, memories, workflows, wikis, vaults, lessons, and scheduled
|
|
11
|
+
tasks. It works with any AI coding assistant that can run shell commands,
|
|
12
|
+
including [Claude Code](https://claude.ai/code),
|
|
12
13
|
[OpenCode](https://opencode.ai), [Cursor](https://cursor.com), and more.
|
|
13
14
|
|
|
14
15
|
## Install
|
|
15
16
|
|
|
17
|
+
**Option 1 — Prebuilt binary (recommended, no runtime required):**
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# Linux / macOS
|
|
21
|
+
curl -fsSL https://github.com/itlackey/akm/releases/latest/download/install.sh | bash
|
|
22
|
+
|
|
23
|
+
# Windows (PowerShell)
|
|
24
|
+
irm https://github.com/itlackey/akm/releases/latest/download/install.ps1 | iex
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Option 2 — Bun (requires [Bun](https://bun.sh) >= 1.0):**
|
|
28
|
+
|
|
16
29
|
```sh
|
|
17
30
|
bun install -g akm-cli
|
|
18
31
|
```
|
|
19
32
|
|
|
20
|
-
|
|
33
|
+
Upgrade in place with `akm upgrade`.
|
|
34
|
+
|
|
35
|
+
> **AKM 0.8 requires the prebuilt binary or the Bun runtime. Node.js / npm / pnpm are not supported in 0.8.0** — running `npm install -g akm-cli` on a Node.js-only machine will print an error from the preinstall hook and exit without installing. Cross-runtime support (Node, npm, pnpm) is planned for 0.9.0.
|
|
21
36
|
|
|
22
37
|
## Quick Start
|
|
23
38
|
|
|
@@ -44,7 +59,8 @@ Add this to your `AGENTS.md`, `CLAUDE.md`, or system prompt:
|
|
|
44
59
|
## Resources & Capabilities
|
|
45
60
|
|
|
46
61
|
You have access to a searchable library of scripts, skills, commands, agents,
|
|
47
|
-
knowledge,
|
|
62
|
+
knowledge, memories, workflows, wikis, vaults, lessons, and scheduled tasks
|
|
63
|
+
via the `akm` CLI. Use `akm -h` for details.
|
|
48
64
|
```
|
|
49
65
|
|
|
50
66
|
## Install Stashes from Anywhere
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are made on the latest minor release line of `akm-cli`. The
|
|
6
|
+
0.x line is pre-1.0 — please upgrade promptly when a fix lands.
|
|
7
|
+
|
|
8
|
+
| Version | Supported |
|
|
9
|
+
| --- | --- |
|
|
10
|
+
| 0.8.x | ✅ active |
|
|
11
|
+
| 0.7.x | ❌ no longer maintained |
|
|
12
|
+
| < 0.7 | ❌ no longer maintained |
|
|
13
|
+
|
|
14
|
+
## Reporting a vulnerability
|
|
15
|
+
|
|
16
|
+
Please report security issues **privately** via GitHub Security Advisories:
|
|
17
|
+
|
|
18
|
+
- https://github.com/itlackey/akm/security/advisories/new
|
|
19
|
+
|
|
20
|
+
If GitHub Security Advisories is unavailable, email `itlackey@gmail.com`
|
|
21
|
+
with the word `SECURITY` in the subject. Please include reproduction steps,
|
|
22
|
+
the impacted akm version, and your operating environment.
|
|
23
|
+
|
|
24
|
+
We will acknowledge receipt within 72 hours and aim to ship a fix or a
|
|
25
|
+
mitigation guidance within two weeks, depending on severity.
|
|
26
|
+
|
|
27
|
+
## Threat model
|
|
28
|
+
|
|
29
|
+
`akm` is a local CLI that reads and writes user files, executes user-authored
|
|
30
|
+
shell commands (via scripts, workflows, and agent dispatch), and talks to
|
|
31
|
+
explicitly configured external services (LLM endpoints, git remotes, npm,
|
|
32
|
+
HTTP sources). It does **not** ship telemetry, send data to anyone by
|
|
33
|
+
default, or open network listeners. See
|
|
34
|
+
[`docs/data-and-telemetry.md`](docs/data-and-telemetry.md) for the on-disk
|
|
35
|
+
inventory.
|
|
36
|
+
|
|
37
|
+
Several akm surfaces execute user-controlled code or data with the full
|
|
38
|
+
permissions of the akm process. These are documented design decisions, not
|
|
39
|
+
bugs, but you should be aware of them:
|
|
40
|
+
|
|
41
|
+
### Workflows execute shell commands with full environment access
|
|
42
|
+
|
|
43
|
+
Workflow steps run in your shell with your PATH and your environment
|
|
44
|
+
variables — including any secrets you have exported or loaded via
|
|
45
|
+
`akm vault load`. **Only add workflow sources you trust.** See
|
|
46
|
+
[`docs/features/workflows.md` — "Security: workflow sources are executed
|
|
47
|
+
code"](docs/features/workflows.md#security-workflow-sources-are-executed-code)
|
|
48
|
+
for the full discussion.
|
|
49
|
+
|
|
50
|
+
### Scripts execute shell commands
|
|
51
|
+
|
|
52
|
+
`akm show script:<name>` returns a `run:` command line the user (or an
|
|
53
|
+
integrating agent) then executes. The same trust model applies: scripts you
|
|
54
|
+
install from third-party stashes are third-party code.
|
|
55
|
+
|
|
56
|
+
### Agents and commands embed user-authored prompts
|
|
57
|
+
|
|
58
|
+
`akm show agent:<name>` and `akm show command:<name>` return prompt
|
|
59
|
+
templates and system prompts that an LLM will execute. A malicious stash
|
|
60
|
+
maintainer could write a system prompt that instructs the LLM to read
|
|
61
|
+
sensitive files in your working tree and exfiltrate them via the LLM
|
|
62
|
+
response. Audit the prompt body the same way you'd audit a script.
|
|
63
|
+
|
|
64
|
+
### Vaults are plaintext on disk
|
|
65
|
+
|
|
66
|
+
`akm vault` files are `0o600`-permissioned plaintext at
|
|
67
|
+
`<stash>/vaults/<name>.env`. They are protected against other local users
|
|
68
|
+
by filesystem permissions but not encrypted at rest. Do not commit vault
|
|
69
|
+
files to source control — they are `.gitignore`d in the default stash
|
|
70
|
+
layout for that reason. The `akm vault show` / `akm vault list` commands
|
|
71
|
+
never echo values; `akm vault load` produces shell-eval output meant to be
|
|
72
|
+
piped to `eval`, never displayed.
|
|
73
|
+
|
|
74
|
+
### Improve / propose / distill send asset content to the configured LLM
|
|
75
|
+
|
|
76
|
+
`akm improve`, `akm propose`, `akm distill`, `akm reflect`, and `akm
|
|
77
|
+
consolidate` send asset frontmatter and body to whatever LLM endpoint is
|
|
78
|
+
configured in `~/.config/akm/config.json` (under `llm.endpoint`). If you
|
|
79
|
+
have configured a third-party LLM, your asset content goes to that
|
|
80
|
+
third-party. Use a local model (`http://localhost:11434` via Ollama, etc.)
|
|
81
|
+
for assets containing secrets or private notes.
|
|
82
|
+
|
|
83
|
+
## Known non-issues
|
|
84
|
+
|
|
85
|
+
- **`akm` requires Bun or the prebuilt binary** — Node.js is not supported
|
|
86
|
+
in 0.8.0 because Bun-specific APIs are used in hot paths. This is a
|
|
87
|
+
compatibility limitation, not a security risk; the prebuilt binary is a
|
|
88
|
+
Bun-compiled standalone executable.
|
|
89
|
+
- **Workflows can read any file the akm process can read.** This is not a
|
|
90
|
+
bug — see "Threat model" above.
|
|
91
|
+
- **`bun install -g akm-cli` runs the preinstall hook.** The hook only
|
|
92
|
+
emits an error message and exits non-zero on Node.js; it does not phone
|
|
93
|
+
home or write outside the install directory.
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { stripJsonComments } from "../core/config";
|
|
7
|
+
import { unifiedDiff, withConfigLock, writeConfigAtomic } from "../core/config-io";
|
|
8
|
+
import { migrateConfigShape } from "../core/config-migration";
|
|
9
|
+
import { getCacheDir, getConfigPath } from "../core/paths";
|
|
10
|
+
import { warn } from "../core/warn";
|
|
11
|
+
export { migrateConfigShape } from "../core/config-migration";
|
|
12
|
+
const PROJECT_CONFIG_RELATIVE_PATH = path.join(".akm", "config.json");
|
|
13
|
+
function backupConfigFile(configPath) {
|
|
14
|
+
if (!fs.existsSync(configPath))
|
|
15
|
+
return;
|
|
16
|
+
const backupDir = path.join(getCacheDir(), "config-backups");
|
|
17
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
18
|
+
const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
|
|
19
|
+
const backupPath = path.join(backupDir, `config-${timestamp}.json`);
|
|
20
|
+
fs.copyFileSync(configPath, backupPath);
|
|
21
|
+
const latestPath = path.join(backupDir, "config.latest.json");
|
|
22
|
+
fs.copyFileSync(configPath, latestPath);
|
|
23
|
+
}
|
|
24
|
+
function acquireMigrateLock(lockPath, noWait) {
|
|
25
|
+
const lockDir = path.dirname(lockPath);
|
|
26
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
27
|
+
const maxAttempts = noWait ? 1 : 20;
|
|
28
|
+
const delayMs = 200;
|
|
29
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
30
|
+
try {
|
|
31
|
+
fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
|
|
32
|
+
return () => {
|
|
33
|
+
try {
|
|
34
|
+
fs.unlinkSync(lockPath);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// ignore
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
if (noWait) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
// Simple busy-wait — synchronous since this is a one-shot CLI action
|
|
46
|
+
const deadline = Date.now() + delayMs;
|
|
47
|
+
while (Date.now() < deadline) {
|
|
48
|
+
// spin
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
export async function migrateConfigFile(filePath, opts) {
|
|
55
|
+
if (!fs.existsSync(filePath)) {
|
|
56
|
+
return { changed: false, result: {} };
|
|
57
|
+
}
|
|
58
|
+
const text = fs.readFileSync(filePath, "utf8");
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
const stripped = stripJsonComments(text);
|
|
62
|
+
const parsed = JSON.parse(stripped);
|
|
63
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
64
|
+
warn(`[akm] config-migrate: ${filePath} is not a valid JSON object, skipping.`);
|
|
65
|
+
return { changed: false, result: {} };
|
|
66
|
+
}
|
|
67
|
+
raw = parsed;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
warn(`[akm] config-migrate: failed to parse ${filePath}, skipping.`);
|
|
71
|
+
return { changed: false, result: {} };
|
|
72
|
+
}
|
|
73
|
+
const { changed, result } = migrateConfigShape(raw);
|
|
74
|
+
if (!changed) {
|
|
75
|
+
return { changed: false, result };
|
|
76
|
+
}
|
|
77
|
+
const migratedText = `${JSON.stringify(result, null, 2)}\n`;
|
|
78
|
+
// WS-2: compute a diff when requested (always computed for --print-diff;
|
|
79
|
+
// never requires a write so safe in --dry-run).
|
|
80
|
+
const diff = opts.printDiff ? unifiedDiff(text, migratedText, filePath) : undefined;
|
|
81
|
+
if (opts.dryRun) {
|
|
82
|
+
return { changed: true, result, diff };
|
|
83
|
+
}
|
|
84
|
+
// WS-3: acquire config write lock + use atomic write (tmp → rename).
|
|
85
|
+
withConfigLock(() => {
|
|
86
|
+
backupConfigFile(filePath);
|
|
87
|
+
writeConfigAtomic(filePath, result);
|
|
88
|
+
});
|
|
89
|
+
return { changed: true, result, diff };
|
|
90
|
+
}
|
|
91
|
+
function discoverProjectConfigPaths(startDir) {
|
|
92
|
+
const paths = [];
|
|
93
|
+
let currentDir = path.resolve(startDir);
|
|
94
|
+
while (true) {
|
|
95
|
+
const configPath = path.join(currentDir, PROJECT_CONFIG_RELATIVE_PATH);
|
|
96
|
+
if (fs.existsSync(configPath) && fs.statSync(configPath).isFile()) {
|
|
97
|
+
paths.unshift(configPath);
|
|
98
|
+
}
|
|
99
|
+
const parentDir = path.dirname(currentDir);
|
|
100
|
+
if (parentDir === currentDir)
|
|
101
|
+
break;
|
|
102
|
+
currentDir = parentDir;
|
|
103
|
+
}
|
|
104
|
+
return paths;
|
|
105
|
+
}
|
|
106
|
+
export async function runConfigMigrate(opts) {
|
|
107
|
+
const userConfigPath = getConfigPath();
|
|
108
|
+
const projectPaths = discoverProjectConfigPaths(process.cwd());
|
|
109
|
+
const allPaths = [userConfigPath, ...projectPaths].filter((p, i, arr) => arr.indexOf(p) === i && fs.existsSync(p));
|
|
110
|
+
if (allPaths.length === 0) {
|
|
111
|
+
console.log("No config files found to migrate.");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const lockPath = path.join(getCacheDir(), "config-migrate.lock");
|
|
115
|
+
const releaseLock = acquireMigrateLock(lockPath, opts.noWait ?? false);
|
|
116
|
+
if (!releaseLock) {
|
|
117
|
+
warn("[akm] config-migrate: another migration is already in progress, skipping.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
let anyChanged = false;
|
|
122
|
+
for (const filePath of allPaths) {
|
|
123
|
+
const { changed, diff } = await migrateConfigFile(filePath, { dryRun: opts.dryRun, printDiff: opts.printDiff });
|
|
124
|
+
if (changed) {
|
|
125
|
+
const action = opts.dryRun ? "would migrate" : "migrated";
|
|
126
|
+
console.log(`[akm] ${action}: ${filePath}`);
|
|
127
|
+
// WS-2: print the unified diff to stdout when --print-diff is set.
|
|
128
|
+
if (diff) {
|
|
129
|
+
console.log(diff);
|
|
130
|
+
}
|
|
131
|
+
anyChanged = true;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.log(`[akm] already up to date: ${filePath}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!anyChanged) {
|
|
138
|
+
console.log("All config files are already at the current version.");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
releaseLock();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
* `akm config validate` — verify the on-disk config matches the schema.
|
|
6
|
+
*
|
|
7
|
+
* Reads the user config file, validates against AkmConfigSchema, and either
|
|
8
|
+
* prints "All checks passed." or a list of structured errors (path + message).
|
|
9
|
+
* Exits non-zero on errors so it composes well in CI hooks.
|
|
10
|
+
*/
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import { parseConfigText } from "../core/config-io";
|
|
13
|
+
import { validateConfigShape } from "../core/config-schema";
|
|
14
|
+
import { ConfigError } from "../core/errors";
|
|
15
|
+
import { getConfigPath } from "../core/paths";
|
|
16
|
+
export async function runConfigValidate() {
|
|
17
|
+
const configPath = getConfigPath();
|
|
18
|
+
if (!fs.existsSync(configPath)) {
|
|
19
|
+
console.log(`No config file at ${configPath} — nothing to validate.`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let text;
|
|
23
|
+
try {
|
|
24
|
+
text = fs.readFileSync(configPath, "utf8");
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
28
|
+
throw new ConfigError(`Could not read config at ${configPath}: ${detail}`, "INVALID_CONFIG_FILE");
|
|
29
|
+
}
|
|
30
|
+
// parseConfigText throws ConfigError on malformed JSON (#458). Surface as-is.
|
|
31
|
+
const raw = parseConfigText(text, configPath);
|
|
32
|
+
const result = validateConfigShape(raw);
|
|
33
|
+
if (result.ok) {
|
|
34
|
+
console.log(`All checks passed. (${configPath})`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const lines = result.errors.map((e) => ` - ${e.path || "(root)"}: ${e.message}`).join("\n");
|
|
38
|
+
throw new ConfigError(`Config at ${configPath} has ${result.errors.length} validation error${result.errors.length === 1 ? "" : "s"}:\n${lines}`, "INVALID_CONFIG_FILE", "Fix the listed fields, or run `akm config migrate` if the errors look like legacy-shape leftovers.");
|
|
39
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
* Confirmation helper for destructive CLI commands.
|
|
6
|
+
*
|
|
7
|
+
* ## Usage
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { confirmDestructive } from "../cli/confirm";
|
|
11
|
+
*
|
|
12
|
+
* async function run({ args }) {
|
|
13
|
+
* const yes = await confirmDestructive(
|
|
14
|
+
* `Remove source "${args.target}"? This cannot be undone.`,
|
|
15
|
+
* { yes: args.yes === true }
|
|
16
|
+
* );
|
|
17
|
+
* if (!yes) { console.error("Aborted."); return; }
|
|
18
|
+
* // proceed...
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ## Non-TTY policy
|
|
23
|
+
*
|
|
24
|
+
* In non-interactive contexts (stdin is not a TTY), destructive commands
|
|
25
|
+
* **fail by default** and require explicit `--yes` to proceed. This prevents
|
|
26
|
+
* accidental destruction in scripts that forget to pass `-y`.
|
|
27
|
+
*
|
|
28
|
+
* This is intentionally stricter than many CLIs that silently proceed in
|
|
29
|
+
* non-TTY mode. The rationale: vault keys, sources, and proposals cannot be
|
|
30
|
+
* recovered after deletion, so the cost of requiring `--yes` in scripts is
|
|
31
|
+
* low and the cost of accidental deletion is high.
|
|
32
|
+
*
|
|
33
|
+
* ## Safety exemptions
|
|
34
|
+
*
|
|
35
|
+
* `--quiet` NEVER suppresses the confirmation prompt — it is safety-critical
|
|
36
|
+
* output. The auto-migration banner is similarly exempt from `--quiet`.
|
|
37
|
+
*/
|
|
38
|
+
import * as p from "@clack/prompts";
|
|
39
|
+
import { UsageError } from "../core/errors";
|
|
40
|
+
/**
|
|
41
|
+
* Prompt the user to confirm a destructive action.
|
|
42
|
+
*
|
|
43
|
+
* Returns `true` if the user confirmed (or `--yes` was passed).
|
|
44
|
+
* Returns `false` if the user declined.
|
|
45
|
+
* Throws `UsageError("NON_INTERACTIVE_REQUIRES_YES")` when stdin is not a
|
|
46
|
+
* TTY and `--yes` was not passed — callers should propagate this error.
|
|
47
|
+
*
|
|
48
|
+
* The prompt defaults to NO so accidental Enter presses do not proceed.
|
|
49
|
+
*
|
|
50
|
+
* @param message Human-readable description of what will be destroyed.
|
|
51
|
+
* @param opts Options controlling confirmation behaviour.
|
|
52
|
+
*/
|
|
53
|
+
export async function confirmDestructive(message, opts) {
|
|
54
|
+
// --yes always skips the prompt
|
|
55
|
+
if (opts.yes)
|
|
56
|
+
return true;
|
|
57
|
+
// Non-TTY: fail loudly — require explicit --yes in scripts
|
|
58
|
+
const isInteractive = process.stdin.isTTY === true;
|
|
59
|
+
if (!isInteractive) {
|
|
60
|
+
throw new UsageError(`This command requires confirmation in non-interactive mode. Pass --yes / -y to proceed without prompting.\n\nAction: ${message}`, "NON_INTERACTIVE_REQUIRES_YES");
|
|
61
|
+
}
|
|
62
|
+
// Interactive: prompt with default = NO (false)
|
|
63
|
+
const confirmed = await p.confirm({
|
|
64
|
+
message,
|
|
65
|
+
initialValue: false,
|
|
66
|
+
});
|
|
67
|
+
// p.confirm returns a symbol when the user cancels (Ctrl+C)
|
|
68
|
+
if (p.isCancel(confirmed)) {
|
|
69
|
+
p.outro("Cancelled.");
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
return confirmed === true;
|
|
73
|
+
}
|
package/dist/cli/parse-args.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* Shared argument-parsing utilities for the AKM CLI entry point.
|
|
3
6
|
*
|
|
@@ -65,6 +68,50 @@ export function parseNonNegativeIntFlag(raw, flagName) {
|
|
|
65
68
|
}
|
|
66
69
|
return parseInt(trimmed, 10);
|
|
67
70
|
}
|
|
71
|
+
// ── Auto-accept flag parsing ─────────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Parse the value of `akm improve --auto-accept` into a confidence threshold.
|
|
74
|
+
*
|
|
75
|
+
* Semantics (see docs/migration/v0.7-to-v0.8.md):
|
|
76
|
+
* - `undefined` (flag absent) → `undefined` (default-OFF; pre-prod flip)
|
|
77
|
+
* - `""` (bare `--auto-accept`, no value) → `undefined` (treated as flag absent)
|
|
78
|
+
* - `"false"` (case-insensitive) → `undefined` (explicit disable)
|
|
79
|
+
* - `"safe"` (case-insensitive) → `90` (permanent back-compat alias)
|
|
80
|
+
* - integer string `"0".."100"` → that integer
|
|
81
|
+
* - anything else → throws `UsageError("INVALID_FLAG_VALUE")`
|
|
82
|
+
*
|
|
83
|
+
* Citty's `type: "string"` resolves bare flags to `""` and an absent flag to
|
|
84
|
+
* `undefined`. Both forms now disable auto-accept; users must pass an explicit
|
|
85
|
+
* threshold (`--auto-accept=N` or `--auto-accept=safe`) to opt in. This is a
|
|
86
|
+
* deliberate flip from the earlier 0.8.0-RC behaviour, which defaulted to ON
|
|
87
|
+
* at threshold 90 and surprised users who didn't expect Phase B operations to
|
|
88
|
+
* apply without confirmation.
|
|
89
|
+
*
|
|
90
|
+
* Until proposals expose per-operation confidence scores, any non-`undefined`
|
|
91
|
+
* threshold causes the consolidate path to auto-accept the whole batch
|
|
92
|
+
* (legacy "safe" behaviour). The threshold value is preserved for the eventual
|
|
93
|
+
* per-operation comparison; see the TODO in `consolidate.ts`.
|
|
94
|
+
*/
|
|
95
|
+
export function parseAutoAcceptFlag(raw) {
|
|
96
|
+
if (raw === undefined)
|
|
97
|
+
return undefined;
|
|
98
|
+
const trimmed = raw.trim();
|
|
99
|
+
if (trimmed === "")
|
|
100
|
+
return undefined;
|
|
101
|
+
const lower = trimmed.toLowerCase();
|
|
102
|
+
if (lower === "false")
|
|
103
|
+
return undefined;
|
|
104
|
+
if (lower === "safe")
|
|
105
|
+
return 90;
|
|
106
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
107
|
+
throw new UsageError(`Invalid --auto-accept value: "${raw}". Must be an integer 0-100, 'safe', or 'false'.`, "INVALID_FLAG_VALUE");
|
|
108
|
+
}
|
|
109
|
+
const parsed = parseInt(trimmed, 10);
|
|
110
|
+
if (parsed < 0 || parsed > 100) {
|
|
111
|
+
throw new UsageError(`Invalid --auto-accept value: "${raw}". Must be an integer 0-100, 'safe', or 'false'.`, "INVALID_FLAG_VALUE");
|
|
112
|
+
}
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
68
115
|
// ── String flag parsing ──────────────────────────────────────────────────────
|
|
69
116
|
/**
|
|
70
117
|
* Extract a string value from a parsed citty argument object by key.
|