akm-cli 0.8.0-rc2 → 0.8.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/{.github/CHANGELOG.md → CHANGELOG.md} +191 -3
- package/README.md +22 -6
- 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 +93 -3
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2141 -1268
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +20 -12
- 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 +1533 -144
- 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 +906 -100
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +3 -0
- package/dist/commands/events.js +3 -0
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +260 -5
- package/dist/commands/health.js +977 -51
- package/dist/commands/help/help-accept.md +6 -3
- package/dist/commands/help/help-improve.md +36 -8
- package/dist/commands/help/help-proposals.md +7 -4
- package/dist/commands/help/help-reject.md +5 -2
- package/dist/commands/history.js +51 -16
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +236 -0
- package/dist/commands/improve-profiles.js +184 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +1725 -332
- 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 +199 -5
- package/dist/commands/lint/command-linter.js +3 -0
- package/dist/commands/lint/default-linter.js +3 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +92 -3
- 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/workflow-linter.js +3 -0
- package/dist/commands/lint.js +3 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal-drain-policies.js +128 -0
- package/dist/commands/proposal-drain.js +477 -0
- package/dist/commands/proposal.js +60 -6
- package/dist/commands/propose.js +24 -19
- package/dist/commands/reflect.js +1004 -94
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +3 -0
- package/dist/commands/remember-cli.js +257 -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/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +32 -13
- 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 +161 -95
- package/dist/commands/url-checker.js +3 -0
- package/dist/core/action-contributors.js +3 -0
- package/dist/core/asset-ref.js +13 -2
- package/dist/core/asset-registry.js +9 -2
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +61 -5
- package/dist/core/common.js +93 -5
- package/dist/core/concurrent.js +3 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +558 -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 +366 -1077
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +31 -25
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -10
- 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 +218 -50
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +11 -3
- package/dist/core/proposals.js +464 -5
- package/dist/core/state-db.js +349 -56
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +3 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +7 -2
- package/dist/core/write-source.js +12 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +136 -28
- package/dist/indexer/db.js +661 -166
- 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 +84 -14
- package/dist/indexer/llm-cache.js +24 -19
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +184 -11
- package/dist/indexer/memory-inference.js +94 -50
- package/dist/indexer/metadata-contributors.js +3 -0
- package/dist/indexer/metadata.js +110 -50
- 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 +4 -1
- 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 +135 -0
- package/dist/integrations/agent/config.js +121 -401
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +6 -14
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +3 -0
- package/dist/integrations/agent/prompts.js +137 -8
- package/dist/integrations/agent/runner.js +208 -0
- package/dist/integrations/agent/sdk-runner.js +8 -2
- package/dist/integrations/agent/spawn.js +54 -14
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +22 -51
- package/dist/integrations/session-logs/index.js +4 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +226 -0
- package/dist/integrations/session-logs/providers/opencode.js +231 -25
- package/dist/integrations/session-logs/types.js +3 -0
- package/dist/llm/call-ai.js +14 -26
- package/dist/llm/client.js +16 -2
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +92 -56
- package/dist/llm/graph-extract.js +401 -30
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +30 -2
- package/dist/llm/metadata-enhance.js +3 -7
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +24 -1
- package/dist/output/cli-hints-full.md +60 -32
- package/dist/output/cli-hints-short.md +10 -7
- package/dist/output/cli-hints.js +5 -2
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +170 -194
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +105 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -549
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1059 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +12 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1329
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +11 -2
- package/dist/registry/providers/static-index.js +10 -1
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17767 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -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 +19 -23
- package/dist/sources/providers/git.js +171 -21
- 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 +18 -22
- 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 +20 -1
- 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 +18 -1
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +5 -9
- package/docs/README.md +7 -2
- 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 +57 -5
- package/docs/migration/v0.7-to-v0.8.md +1378 -0
- package/package.json +28 -11
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -385
- package/dist/commands/vault.js +0 -310
- package/dist/indexer/match-contributors.js +0 -141
- package/dist/integrations/agent/pipeline.js +0 -39
- package/dist/integrations/agent/runners.js +0 -31
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
Usage:
|
|
2
|
-
akm accept <id>
|
|
2
|
+
akm proposal accept <id>
|
|
3
3
|
|
|
4
4
|
Description:
|
|
5
5
|
Accept a proposal and promote it into the stash.
|
|
6
6
|
|
|
7
|
+
(`akm accept` is a deprecated alias for `akm proposal accept`; it warns on
|
|
8
|
+
stderr and is removed in 0.9.0.)
|
|
9
|
+
|
|
7
10
|
Examples:
|
|
8
|
-
akm accept proposal_123
|
|
9
|
-
akm accept proposal_123 --target team-stash
|
|
11
|
+
akm proposal accept proposal_123
|
|
12
|
+
akm proposal accept proposal_123 --target team-stash
|
|
@@ -30,20 +30,44 @@ Options:
|
|
|
30
30
|
--task <text> Add extra guidance for this improvement pass
|
|
31
31
|
--dry-run Show planned actions without generating proposals
|
|
32
32
|
--target <source> Override the write target for accepted proposals
|
|
33
|
-
--auto-accept
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
--auto-accept[=<value>]
|
|
34
|
+
Confidence threshold (0-100) for auto-accepting proposals.
|
|
35
|
+
Default when flag is absent: ON at threshold 90 (all sub-processes).
|
|
36
|
+
--auto-accept same as --auto-accept=90
|
|
37
|
+
--auto-accept=<N> integer 0-100; accept proposals at or above N
|
|
38
|
+
--auto-accept=safe alias for 90 (back-compat, not deprecated)
|
|
39
|
+
--auto-accept=false disable auto-accept for all sub-processes;
|
|
40
|
+
reflect/distill proposals go to the queue and
|
|
41
|
+
consolidation will prompt interactively on HTTP paths
|
|
42
|
+
Note: until proposals carry real confidence scores, any non-`false`
|
|
43
|
+
value behaves like the legacy "safe" mode (whole-batch auto-accept).
|
|
44
|
+
--profile <name> Improve profile to apply. Built-ins: default, quick,
|
|
45
|
+
thorough, memory-focus. User-defined profiles under
|
|
46
|
+
`profiles.improve.<name>` in config are also accepted.
|
|
47
|
+
Profiles bundle process gating, type filters, and
|
|
48
|
+
run-level autoAccept/limit defaults. Falls back to
|
|
49
|
+
`defaults.improve` in config, then to "default".
|
|
50
|
+
Unknown names fall back to default with a warning.
|
|
51
|
+
Sync behavior by profile: default and thorough enable
|
|
52
|
+
auto-commit + push; quick and memory-focus skip sync.
|
|
53
|
+
--sync Commit (and optionally push) the git-backed primary
|
|
54
|
+
stash when the run finishes. Use --no-sync to disable.
|
|
55
|
+
Default: per profile config (enabled for default and
|
|
56
|
+
thorough, disabled for quick and memory-focus).
|
|
57
|
+
--push Push after the end-of-run sync commit when the stash
|
|
58
|
+
is writable and has a remote configured. Use --no-push
|
|
59
|
+
to commit only. Default: per profile config (true when
|
|
60
|
+
sync is enabled).
|
|
41
61
|
--consolidate-recovery <mode>
|
|
42
62
|
Recovery mode for stale consolidate journals: abort (default) or clean
|
|
43
63
|
--require-feedback-signal
|
|
44
64
|
Only process refs with recent feedback signal events
|
|
45
65
|
--min-retrieval-count <n>
|
|
46
66
|
Retrieval fallback threshold when no recent feedback exists (default: 5)
|
|
67
|
+
--json-to-stdout Emit the full JSON result on stdout (legacy behaviour).
|
|
68
|
+
(0.8.0+: full result is recorded in the improve_runs table of
|
|
69
|
+
state.db and stdout is empty; use --json-to-stdout for the prior
|
|
70
|
+
behaviour, e.g. `akm improve --json-to-stdout | jq`.)
|
|
47
71
|
|
|
48
72
|
Examples:
|
|
49
73
|
akm improve
|
|
@@ -51,3 +75,7 @@ Examples:
|
|
|
51
75
|
akm improve skill
|
|
52
76
|
akm improve skill:code-review
|
|
53
77
|
akm improve workflow:incident-response --task "reduce duplication"
|
|
78
|
+
akm improve --profile quick
|
|
79
|
+
akm improve --profile memory-focus
|
|
80
|
+
akm improve --no-sync
|
|
81
|
+
akm improve --no-push
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
Usage:
|
|
2
|
-
akm
|
|
2
|
+
akm proposal list
|
|
3
3
|
|
|
4
4
|
Description:
|
|
5
5
|
List proposal queue entries.
|
|
6
6
|
|
|
7
|
+
(`akm proposals` is a deprecated alias for `akm proposal list`; it warns on
|
|
8
|
+
stderr and is removed in 0.9.0.)
|
|
9
|
+
|
|
7
10
|
Options:
|
|
8
11
|
--status <status> Filter by pending, accepted, or rejected
|
|
9
12
|
--type <type> Filter by asset type
|
|
10
13
|
--ref <ref> Filter by exact asset ref
|
|
11
14
|
|
|
12
15
|
Examples:
|
|
13
|
-
akm
|
|
14
|
-
akm
|
|
15
|
-
akm
|
|
16
|
+
akm proposal list
|
|
17
|
+
akm proposal list --status pending
|
|
18
|
+
akm proposal list --type skill
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
Usage:
|
|
2
|
-
akm reject <id> --reason "..."
|
|
2
|
+
akm proposal reject <id> --reason "..."
|
|
3
3
|
|
|
4
4
|
Description:
|
|
5
5
|
Reject a proposal and record the reason.
|
|
6
6
|
|
|
7
|
+
(`akm reject` is a deprecated alias for `akm proposal reject`; it warns on
|
|
8
|
+
stderr and is removed in 0.9.0.)
|
|
9
|
+
|
|
7
10
|
Examples:
|
|
8
|
-
akm reject proposal_123 --reason "duplicates existing workflow"
|
|
11
|
+
akm proposal reject proposal_123 --reason "duplicates existing workflow"
|
package/dist/commands/history.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Event sources:
|
|
6
|
-
* - `usage_events` SQLite table: search, show, and feedback events recorded
|
|
7
|
-
* by the local indexer during normal CLI use.
|
|
8
|
-
* - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
|
|
9
|
-
* proposal lifecycle events (`promoted`, `rejected`) emitted by
|
|
10
|
-
* `akm proposal accept` / `akm proposal reject`. Use this flag to see
|
|
11
|
-
* the full proposal review trail alongside usage events.
|
|
12
|
-
*
|
|
13
|
-
* The two sources are merged and sorted chronologically (oldest first) so
|
|
14
|
-
* consumers see a coherent lifecycle trail in a single output.
|
|
15
|
-
*/
|
|
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/.
|
|
16
4
|
import { parseAssetRef } from "../core/asset-ref";
|
|
17
5
|
import { UsageError } from "../core/errors";
|
|
18
6
|
import { readEvents } from "../core/events";
|
|
7
|
+
import { listProposals } from "../core/proposals";
|
|
19
8
|
import { isoToSqlite, parseSinceToIso } from "../core/time";
|
|
20
9
|
import { closeDatabase, openExistingDatabase } from "../indexer/db";
|
|
21
10
|
// Proposal lifecycle event types emitted by the proposal substrate (#225).
|
|
@@ -39,6 +28,7 @@ function toEntry(row) {
|
|
|
39
28
|
entryId: row.entry_id,
|
|
40
29
|
query: row.query,
|
|
41
30
|
signal: row.signal,
|
|
31
|
+
source: row.source,
|
|
42
32
|
metadata: parseMetadata(row.metadata),
|
|
43
33
|
createdAt: row.created_at,
|
|
44
34
|
};
|
|
@@ -91,8 +81,12 @@ export async function akmHistory(options = {}) {
|
|
|
91
81
|
conditions.push("created_at >= ?");
|
|
92
82
|
params.push(sinceNormalized);
|
|
93
83
|
}
|
|
84
|
+
if (options.source !== undefined) {
|
|
85
|
+
conditions.push("source = ?");
|
|
86
|
+
params.push(options.source);
|
|
87
|
+
}
|
|
94
88
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
95
|
-
const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, created_at
|
|
89
|
+
const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
|
|
96
90
|
FROM usage_events ${where}
|
|
97
91
|
ORDER BY id ASC`;
|
|
98
92
|
const rows = db.prepare(sql).all(...params);
|
|
@@ -127,6 +121,7 @@ export async function akmHistory(options = {}) {
|
|
|
127
121
|
entryId: null,
|
|
128
122
|
query: null,
|
|
129
123
|
signal: null,
|
|
124
|
+
source: null,
|
|
130
125
|
metadata: event.metadata ?? null,
|
|
131
126
|
createdAt,
|
|
132
127
|
});
|
|
@@ -142,6 +137,45 @@ export async function akmHistory(options = {}) {
|
|
|
142
137
|
return 1;
|
|
143
138
|
return a.id - b.id;
|
|
144
139
|
});
|
|
140
|
+
// ── Accept-rate-per-source (F-4 / #385) ─────────────────────────────────
|
|
141
|
+
let acceptRateBySource;
|
|
142
|
+
if (options.acceptRateBySource) {
|
|
143
|
+
const stashDir = options.stashDir;
|
|
144
|
+
if (stashDir) {
|
|
145
|
+
const bySource = new Map();
|
|
146
|
+
const countProposals = (statuses, includeArchive) => {
|
|
147
|
+
for (const status of statuses) {
|
|
148
|
+
const proposals = listProposals(stashDir, { status, includeArchive });
|
|
149
|
+
for (const p of proposals) {
|
|
150
|
+
const src = p.source || "(unknown)";
|
|
151
|
+
const entry = bySource.get(src) ?? { accepted: 0, rejected: 0, pending: 0 };
|
|
152
|
+
if (status === "accepted")
|
|
153
|
+
entry.accepted++;
|
|
154
|
+
else if (status === "rejected")
|
|
155
|
+
entry.rejected++;
|
|
156
|
+
else
|
|
157
|
+
entry.pending++;
|
|
158
|
+
bySource.set(src, entry);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
countProposals(["pending"], false);
|
|
163
|
+
countProposals(["accepted", "rejected"], true);
|
|
164
|
+
acceptRateBySource = Array.from(bySource.entries())
|
|
165
|
+
.map(([source, counts]) => {
|
|
166
|
+
const decided = counts.accepted + counts.rejected;
|
|
167
|
+
return {
|
|
168
|
+
source,
|
|
169
|
+
total: decided + counts.pending,
|
|
170
|
+
accepted: counts.accepted,
|
|
171
|
+
rejected: counts.rejected,
|
|
172
|
+
pending: counts.pending,
|
|
173
|
+
acceptRate: decided > 0 ? counts.accepted / decided : null,
|
|
174
|
+
};
|
|
175
|
+
})
|
|
176
|
+
.sort((a, b) => b.total - a.total); // Most active source first
|
|
177
|
+
}
|
|
178
|
+
}
|
|
145
179
|
const response = {
|
|
146
180
|
schemaVersion: 1,
|
|
147
181
|
...(normalizedRef !== undefined ? { ref: normalizedRef } : {}),
|
|
@@ -149,6 +183,7 @@ export async function akmHistory(options = {}) {
|
|
|
149
183
|
totalCount: entries.length,
|
|
150
184
|
entries,
|
|
151
185
|
sources,
|
|
186
|
+
...(acceptRateBySource !== undefined ? { acceptRateBySource } : {}),
|
|
152
187
|
};
|
|
153
188
|
return response;
|
|
154
189
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
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 { loadConfig } from "../core/config";
|
|
5
|
+
import { appendEvent } from "../core/events";
|
|
6
|
+
import { promoteProposal } from "../core/proposals";
|
|
7
|
+
import { info, warn } from "../core/warn";
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Gate implementation
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
/**
|
|
12
|
+
* Attempt to auto-accept each candidate proposal whose confidence meets the
|
|
13
|
+
* effective threshold. Safe to call unconditionally — returns all-empty when
|
|
14
|
+
* the gate is disabled or the run is a dry-run.
|
|
15
|
+
*
|
|
16
|
+
* @param candidates Proposals to evaluate, each with an optional confidence.
|
|
17
|
+
* @param cfg Gate configuration (phase label, thresholds, context).
|
|
18
|
+
* @param promoteFn Injectable override for `promoteProposal` (test seam).
|
|
19
|
+
*/
|
|
20
|
+
export async function runAutoAcceptGate(candidates, cfg, promoteFn = promoteProposal) {
|
|
21
|
+
const result = { promoted: [], skipped: [], failed: [] };
|
|
22
|
+
// --- Guard: gate is disabled or context is incomplete ---
|
|
23
|
+
if (cfg.dryRun || cfg.globalThreshold === undefined || !cfg.stashDir) {
|
|
24
|
+
result.skipped = candidates.map((c) => c.proposalId);
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
const effectiveThreshold = Math.max(cfg.globalThreshold, cfg.minimumThreshold ?? 0) / 100;
|
|
28
|
+
const resolvedConfig = typeof cfg.config === "function" ? cfg.config() : cfg.config;
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const { proposalId, confidence } = candidate;
|
|
31
|
+
if (confidence === undefined || confidence < effectiveThreshold) {
|
|
32
|
+
result.skipped.push(proposalId);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const promotion = await promoteFn(cfg.stashDir, resolvedConfig, proposalId, {}, undefined);
|
|
37
|
+
appendEvent({
|
|
38
|
+
eventType: "promoted",
|
|
39
|
+
ref: promotion.ref,
|
|
40
|
+
metadata: {
|
|
41
|
+
proposalId: promotion.proposal.id,
|
|
42
|
+
source: promotion.proposal.source,
|
|
43
|
+
...(promotion.proposal.sourceRun !== undefined ? { sourceRun: promotion.proposal.sourceRun } : {}),
|
|
44
|
+
assetPath: promotion.assetPath,
|
|
45
|
+
autoAccept: true,
|
|
46
|
+
confidence,
|
|
47
|
+
threshold: effectiveThreshold,
|
|
48
|
+
phase: cfg.phase,
|
|
49
|
+
},
|
|
50
|
+
}, cfg.eventsCtx ?? {});
|
|
51
|
+
info(`[improve] auto-accepted ${promotion.ref} (${cfg.phase}; confidence=${confidence.toFixed(2)} >= threshold=${effectiveThreshold.toFixed(2)})`);
|
|
52
|
+
result.promoted.push(proposalId);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
warn(`[improve] ${cfg.phase} auto-accept failed for ${proposalId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
56
|
+
result.failed.push(proposalId);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Confidence resolvers
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Read the confidence value for an extract proposal.
|
|
66
|
+
* Extract stores confidence at `payload.frontmatter.confidence` (set by
|
|
67
|
+
* extract.ts when the LLM response is parsed), not at the top-level field.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveExtractConfidence(proposal) {
|
|
70
|
+
const fm = proposal.payload.frontmatter;
|
|
71
|
+
const fmConf = fm?.confidence;
|
|
72
|
+
if (typeof fmConf === "number")
|
|
73
|
+
return fmConf;
|
|
74
|
+
// Fall back to top-level in case a future extract version normalises the path
|
|
75
|
+
if (typeof proposal.confidence === "number")
|
|
76
|
+
return proposal.confidence;
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Config builder helpers
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
/**
|
|
83
|
+
* Build a gate config for a phase, inheriting global settings from the
|
|
84
|
+
* improve options. Callers supply only the phase-specific overrides.
|
|
85
|
+
*/
|
|
86
|
+
export function makeGateConfig(phase, shared, overrides = {}) {
|
|
87
|
+
return {
|
|
88
|
+
phase,
|
|
89
|
+
globalThreshold: shared.globalThreshold,
|
|
90
|
+
dryRun: shared.dryRun,
|
|
91
|
+
stashDir: shared.stashDir,
|
|
92
|
+
config: shared.config,
|
|
93
|
+
eventsCtx: shared.eventsCtx,
|
|
94
|
+
...overrides,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export { loadConfig };
|
|
@@ -0,0 +1,236 @@
|
|
|
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 path from "node:path";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import { getStringArg, parseAutoAcceptFlag, parseNonNegativeIntFlag, parsePositiveIntFlag } from "../cli/parse-args";
|
|
7
|
+
import { output, runWithJsonErrors } from "../cli/shared";
|
|
8
|
+
import { loadConfig } from "../core/config";
|
|
9
|
+
import { UsageError } from "../core/errors";
|
|
10
|
+
import { getCacheDir } from "../core/paths";
|
|
11
|
+
import { clearLogFile, setLogFile } from "../core/warn";
|
|
12
|
+
import { resolveSourceEntries } from "../indexer/search-source";
|
|
13
|
+
import { getHyphenatedArg, getHyphenatedBoolean, parseFlagValue } from "../output/context";
|
|
14
|
+
import { akmImprove } from "./improve";
|
|
15
|
+
import { buildImproveRunId, recordTerminatedImproveRun, relativeImproveResultPath, writeImproveResultFile, } from "./improve-result-file";
|
|
16
|
+
export const improveCommand = defineCommand({
|
|
17
|
+
meta: {
|
|
18
|
+
name: "improve",
|
|
19
|
+
description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when profiles.improve.default.processes.consolidate.enabled is true",
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
scope: {
|
|
23
|
+
type: "positional",
|
|
24
|
+
description: "Optional asset type or asset ref to improve",
|
|
25
|
+
required: false,
|
|
26
|
+
},
|
|
27
|
+
task: { type: "string", description: "Add extra guidance for this improvement pass" },
|
|
28
|
+
"dry-run": { type: "boolean", description: "Show planned actions without writing", default: false },
|
|
29
|
+
target: { type: "string", description: "Override the write target for accepted proposals" },
|
|
30
|
+
"auto-accept": {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Auto-accept proposals at or above this confidence threshold (0-100). Default: disabled. Pass a value 0-100 to enable. 'safe' is an alias for 90. Pass 'false' to be explicit.",
|
|
33
|
+
},
|
|
34
|
+
limit: { type: "string", description: "Maximum number of assets to process (highest utility first)" },
|
|
35
|
+
"timeout-ms": {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Wall-clock budget for the entire run in milliseconds (default: 7200000 = 2 hours)",
|
|
38
|
+
},
|
|
39
|
+
"consolidate-recovery": {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "How to handle stale/incomplete consolidation journals: abort (default) or clean (remove stale journal artifacts)",
|
|
42
|
+
},
|
|
43
|
+
"require-feedback-signal": {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
description: "Only process assets with recent feedback signals (disables retrieval fallback)",
|
|
46
|
+
default: false,
|
|
47
|
+
},
|
|
48
|
+
"min-retrieval-count": {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Minimum retrieval count for zero-feedback fallback eligibility (default: 1, set 0 to include all assets regardless of retrieval history)",
|
|
51
|
+
},
|
|
52
|
+
"json-to-stdout": {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
description: "Emit the full JSON result on stdout (legacy behaviour). (0.8.0+: full result is recorded in the improve_runs table of state.db and stdout is empty; use this flag for the prior behaviour, e.g. `akm improve --json-to-stdout | jq`.)",
|
|
55
|
+
default: false,
|
|
56
|
+
},
|
|
57
|
+
profile: {
|
|
58
|
+
type: "string",
|
|
59
|
+
description: "Named improve profile from profiles.improve or built-in profiles (default, quick, thorough, memory-focus). Controls which sub-processes run and which asset types are processed.",
|
|
60
|
+
},
|
|
61
|
+
sync: {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
description: "Commit (and optionally push) the git-backed primary stash when the run finishes. Use --no-sync to disable. Default: on for git-backed stashes (per profile config).",
|
|
64
|
+
},
|
|
65
|
+
push: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Push after the end-of-run sync commit when writable + remote configured. Use --no-push to commit only. Default: per profile config (true).",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
async run({ args }) {
|
|
71
|
+
await runWithJsonErrors(async () => {
|
|
72
|
+
const formatFlagValue = parseFlagValue(process.argv, "--format");
|
|
73
|
+
if (formatFlagValue !== undefined) {
|
|
74
|
+
throw new UsageError(`akm improve does not accept --format. That flag controls output formatting for other commands (search, show, etc.).\n` +
|
|
75
|
+
`Did you mean: akm improve (no --format flag)?`, "INVALID_FLAG_VALUE");
|
|
76
|
+
}
|
|
77
|
+
const jsonToStdout = getHyphenatedBoolean(args, "json-to-stdout");
|
|
78
|
+
const autoAcceptRaw = getHyphenatedArg(args, "auto-accept");
|
|
79
|
+
const autoAccept = parseAutoAcceptFlag(autoAcceptRaw);
|
|
80
|
+
const targetArg = getStringArg(args, "target");
|
|
81
|
+
const taskArg = getStringArg(args, "task");
|
|
82
|
+
const dryRun = getHyphenatedBoolean(args, "dry-run");
|
|
83
|
+
const limitRaw = parsePositiveIntFlag(args.limit ?? undefined);
|
|
84
|
+
const timeoutMs = parsePositiveIntFlag(getHyphenatedArg(args, "timeout-ms"), "--timeout-ms");
|
|
85
|
+
const consolidateRecoveryRaw = getHyphenatedArg(args, "consolidate-recovery");
|
|
86
|
+
const consolidateRecovery = consolidateRecoveryRaw === undefined
|
|
87
|
+
? undefined
|
|
88
|
+
: consolidateRecoveryRaw.trim().toLowerCase();
|
|
89
|
+
if (consolidateRecovery !== undefined && consolidateRecovery !== "abort" && consolidateRecovery !== "clean") {
|
|
90
|
+
throw new UsageError(`Invalid --consolidate-recovery value: "${consolidateRecoveryRaw}". Must be one of: abort, clean.`, "INVALID_FLAG_VALUE");
|
|
91
|
+
}
|
|
92
|
+
const minRetrievalCountRaw = getHyphenatedArg(args, "min-retrieval-count");
|
|
93
|
+
const minRetrievalCount = parseNonNegativeIntFlag(minRetrievalCountRaw, "--min-retrieval-count");
|
|
94
|
+
const requireFeedbackSignal = getHyphenatedBoolean(args, "require-feedback-signal");
|
|
95
|
+
const profileArg = getStringArg(args, "profile");
|
|
96
|
+
// Only set the keys the user actually passed (citty leaves the flag
|
|
97
|
+
// undefined unless `--sync`/`--no-sync` / `--push`/`--no-push` appears),
|
|
98
|
+
// so the resolved profile `sync` block wins by default.
|
|
99
|
+
const syncFlag = getHyphenatedArg(args, "sync");
|
|
100
|
+
const pushFlag = getHyphenatedArg(args, "push");
|
|
101
|
+
const syncOverride = {};
|
|
102
|
+
if (syncFlag !== undefined)
|
|
103
|
+
syncOverride.enabled = syncFlag;
|
|
104
|
+
if (pushFlag !== undefined)
|
|
105
|
+
syncOverride.push = pushFlag;
|
|
106
|
+
const improveLogFile = path.join(getCacheDir(), "logs", "improve", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
|
|
107
|
+
setLogFile(improveLogFile);
|
|
108
|
+
const startedAtMs = Date.now();
|
|
109
|
+
const startedAtIso = new Date(startedAtMs).toISOString();
|
|
110
|
+
// Mint the run-id up front so signal handlers can persist a partial
|
|
111
|
+
// record if the process is killed mid-run. Pre-2026-05-26 the runId
|
|
112
|
+
// was minted at end-of-run, so SIGTERM'd runs (cron timeout) left no
|
|
113
|
+
// row in improve_runs and effectively disappeared from `akm health`.
|
|
114
|
+
const runId = buildImproveRunId();
|
|
115
|
+
const primaryStashDir = resolveSourceEntries(undefined, loadConfig())[0]?.path;
|
|
116
|
+
const scopeArg = getStringArg(args, "scope");
|
|
117
|
+
const inferredScopeMode = (scopeArg ?? "").includes(":") ? "ref" : scopeArg ? "type" : "all";
|
|
118
|
+
// Signal handler + exception path both flow through this helper so
|
|
119
|
+
// every abnormal termination produces a row with ok:false and a
|
|
120
|
+
// reason in metadata.terminated.
|
|
121
|
+
let runRecorded = false;
|
|
122
|
+
const persistTerminated = (reason, errorMessage) => {
|
|
123
|
+
if (runRecorded)
|
|
124
|
+
return;
|
|
125
|
+
if (!primaryStashDir)
|
|
126
|
+
return;
|
|
127
|
+
runRecorded = true;
|
|
128
|
+
try {
|
|
129
|
+
recordTerminatedImproveRun(primaryStashDir, runId, startedAtIso, reason, {
|
|
130
|
+
scopeMode: inferredScopeMode,
|
|
131
|
+
scopeValue: scopeArg ?? null,
|
|
132
|
+
dryRun: Boolean(dryRun),
|
|
133
|
+
profile: profileArg ?? null,
|
|
134
|
+
...(errorMessage ? { errorMessage } : {}),
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
process.stderr.write(`warning: failed to persist terminated improve run ${runId}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const sigtermHandler = () => {
|
|
142
|
+
persistTerminated("SIGTERM");
|
|
143
|
+
process.stderr.write(`[improve] received SIGTERM; recorded terminated run ${runId}\n`);
|
|
144
|
+
process.exit(143);
|
|
145
|
+
};
|
|
146
|
+
const sigintHandler = () => {
|
|
147
|
+
persistTerminated("SIGINT");
|
|
148
|
+
process.stderr.write(`[improve] received SIGINT; recorded terminated run ${runId}\n`);
|
|
149
|
+
process.exit(130);
|
|
150
|
+
};
|
|
151
|
+
const sighupHandler = () => {
|
|
152
|
+
persistTerminated("SIGHUP");
|
|
153
|
+
process.exit(129);
|
|
154
|
+
};
|
|
155
|
+
process.once("SIGTERM", sigtermHandler);
|
|
156
|
+
process.once("SIGINT", sigintHandler);
|
|
157
|
+
process.once("SIGHUP", sighupHandler);
|
|
158
|
+
let improveResult;
|
|
159
|
+
try {
|
|
160
|
+
improveResult = await akmImprove({
|
|
161
|
+
scope: scopeArg,
|
|
162
|
+
task: taskArg,
|
|
163
|
+
dryRun,
|
|
164
|
+
target: targetArg,
|
|
165
|
+
autoAccept,
|
|
166
|
+
...(limitRaw !== undefined ? { limit: limitRaw } : {}),
|
|
167
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
168
|
+
...(minRetrievalCount !== undefined ? { minRetrievalCount } : {}),
|
|
169
|
+
...(requireFeedbackSignal ? { requireFeedbackSignal } : {}),
|
|
170
|
+
...(profileArg !== undefined ? { profile: profileArg } : {}),
|
|
171
|
+
...(Object.keys(syncOverride).length > 0 ? { sync: syncOverride } : {}),
|
|
172
|
+
consolidateOptions: {
|
|
173
|
+
target: targetArg,
|
|
174
|
+
dryRun,
|
|
175
|
+
autoAccept,
|
|
176
|
+
task: taskArg,
|
|
177
|
+
...(consolidateRecovery !== undefined ? { recoveryMode: consolidateRecovery } : {}),
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
// akmImprove threw — record the failure before letting runWithJsonErrors
|
|
183
|
+
// emit the standard JSON error envelope. Without this, exceptions in
|
|
184
|
+
// the main loop (LLM provider crash, OOM, etc.) leave no improve_runs
|
|
185
|
+
// row, matching the SIGTERM gap.
|
|
186
|
+
persistTerminated("exception", err instanceof Error ? err.message : String(err));
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
process.removeListener("SIGTERM", sigtermHandler);
|
|
191
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
192
|
+
process.removeListener("SIGHUP", sighupHandler);
|
|
193
|
+
clearLogFile();
|
|
194
|
+
}
|
|
195
|
+
const durationMs = Date.now() - startedAtMs;
|
|
196
|
+
if (jsonToStdout) {
|
|
197
|
+
// Legacy / escape-hatch mode: full JSON on stdout, no file write.
|
|
198
|
+
// Kept for scripts/agents that already pipe to jq.
|
|
199
|
+
output("improve", improveResult);
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
// Default mode (0.8.0+): persist the full result as a row in the
|
|
203
|
+
// `improve_runs` table of state.db (migration 003) and emit NOTHING
|
|
204
|
+
// on stdout. The verbose JSON would otherwise scroll earlier progress
|
|
205
|
+
// logs out of the terminal buffer. The existing `[improve] ...`
|
|
206
|
+
// progress log lines on stderr remain the canonical console UX —
|
|
207
|
+
// do NOT add any new console output here.
|
|
208
|
+
//
|
|
209
|
+
// Pre-0.8.0 wrote `<stash>/.akm/runs/<run-id>/improve-result.json`;
|
|
210
|
+
// those files are no longer authored. Query recent runs with:
|
|
211
|
+
// sqlite3 "$AKM_DATA_DIR/state.db" \
|
|
212
|
+
// "SELECT id, started_at, ok, dry_run FROM improve_runs \
|
|
213
|
+
// ORDER BY started_at DESC LIMIT 10"
|
|
214
|
+
// runId + primaryStashDir minted up-top so signal handlers can record
|
|
215
|
+
// partial runs; reuse them here for the success path.
|
|
216
|
+
const resultRef = relativeImproveResultPath(runId);
|
|
217
|
+
runRecorded = true; // Suppress any late signal-handler write — the success path owns the row now.
|
|
218
|
+
if (primaryStashDir) {
|
|
219
|
+
try {
|
|
220
|
+
writeImproveResultFile(primaryStashDir, runId, improveResult);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
// Stderr warning on the failure path is preferable to crashing
|
|
224
|
+
// the run after all the work has completed.
|
|
225
|
+
process.stderr.write(`warning: failed to record improve run ${resultRef}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
process.stderr.write(`warning: no writable stash directory resolved; improve result not persisted to state.db (use --json-to-stdout to capture)\n`);
|
|
230
|
+
}
|
|
231
|
+
// durationMs reserved for future use (no console emission today).
|
|
232
|
+
void durationMs;
|
|
233
|
+
process.exit(0);
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
});
|