akm-cli 0.9.1 → 0.9.2-alpha.2
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 +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* call per asset. Results are recorded as `schema_repair_invoked` events.
|
|
10
10
|
*
|
|
11
11
|
* This module is extracted from `improve.ts` to make the repair logic
|
|
12
|
-
* independently testable and to use the
|
|
13
|
-
* `chatCompletion`.
|
|
12
|
+
* independently testable and to use the shared structured execution seam.
|
|
14
13
|
*/
|
|
15
14
|
import fs from "node:fs";
|
|
16
15
|
import path from "node:path";
|
|
@@ -18,12 +17,14 @@ import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
|
18
17
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
19
18
|
import { parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
20
19
|
import { authoringRulesForType } from "../../core/authoring-rules.js";
|
|
20
|
+
import { ConfigError } from "../../core/errors.js";
|
|
21
21
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
22
22
|
import { parseEmbeddedJsonResponse } from "../../core/parse.js";
|
|
23
23
|
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
24
24
|
import { info } from "../../core/warn.js";
|
|
25
25
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
26
|
-
import {
|
|
26
|
+
import { disposeLoweredExecutionDispatchLease } from "../../integrations/agent/execution-lowering.js";
|
|
27
|
+
import { callStructured, preflightStructuredLlmRunner } from "../../llm/structured-call.js";
|
|
27
28
|
import { createProposal, isProposalSkipped } from "../proposal/repository.js";
|
|
28
29
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
29
30
|
/** Minimum gap between schema-repair attempts on the same asset. */
|
|
@@ -37,36 +38,23 @@ const SCHEMA_REPAIR_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
|
37
38
|
*/
|
|
38
39
|
const SCHEMA_REPAIR_MAX_ATTEMPTS = 3;
|
|
39
40
|
const SCHEMA_REPAIR_WINDOW_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* files were repaired. Queued proposals never count as live repairs.
|
|
45
|
-
*/
|
|
46
|
-
export async function runSchemaRepairPass(failures, options) {
|
|
47
|
-
const repairs = [];
|
|
48
|
-
const repairedRefs = new Set();
|
|
49
|
-
const { startMs, budgetMs, llmConfig, stashDir, findFilePath = defaultFindFilePath, isLessonCandidateFn = defaultIsLessonCandidate, chatFn = chatCompletion, } = options;
|
|
50
|
-
if (!stashDir) {
|
|
51
|
-
throw new Error("runSchemaRepairPass requires stashDir so repairs route through the proposal queue");
|
|
52
|
-
}
|
|
41
|
+
async function classifySchemaRepairs(args) {
|
|
42
|
+
const { failures, startMs, budgetMs, stashDir, findFilePath, isLessonCandidateFn, repairs } = args;
|
|
43
|
+
const eligibleRepairs = [];
|
|
44
|
+
const pendingErrors = [];
|
|
53
45
|
for (const failure of failures) {
|
|
54
46
|
if (Date.now() - startMs >= budgetMs)
|
|
55
47
|
break;
|
|
56
|
-
// Cooldown: skip repair if we ran it successfully recently.
|
|
57
48
|
const recentRepairs = readEvents({ type: "schema_repair_invoked", ref: failure.ref });
|
|
58
49
|
const lastRepair = recentRepairs.events
|
|
59
|
-
.filter((
|
|
50
|
+
.filter((event) => event.metadata?.outcome === "queued")
|
|
60
51
|
.sort((a, b) => new Date(b.ts ?? 0).getTime() - new Date(a.ts ?? 0).getTime())[0];
|
|
61
52
|
if (lastRepair?.ts && Date.now() - new Date(lastRepair.ts).getTime() < SCHEMA_REPAIR_COOLDOWN_MS) {
|
|
62
53
|
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
|
|
63
54
|
continue;
|
|
64
55
|
}
|
|
65
|
-
// O-6 / #379: Cap total attempts at SCHEMA_REPAIR_MAX_ATTEMPTS per SCHEMA_REPAIR_WINDOW_MS.
|
|
66
|
-
// Prevents indefinite nightly re-repair of assets whose source is genuinely ambiguous.
|
|
67
|
-
// After the cap is reached, the asset is skipped until the window rolls over.
|
|
68
56
|
const windowStart = Date.now() - SCHEMA_REPAIR_WINDOW_MS;
|
|
69
|
-
const attemptsInWindow = recentRepairs.events.filter((
|
|
57
|
+
const attemptsInWindow = recentRepairs.events.filter((event) => event.ts !== undefined && new Date(event.ts).getTime() >= windowStart).length;
|
|
70
58
|
if (attemptsInWindow >= SCHEMA_REPAIR_MAX_ATTEMPTS) {
|
|
71
59
|
repairs.push({
|
|
72
60
|
ref: failure.ref,
|
|
@@ -77,32 +65,23 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
77
65
|
continue;
|
|
78
66
|
}
|
|
79
67
|
const filePath = await findFilePath(failure.ref, stashDir);
|
|
80
|
-
if (!filePath) {
|
|
81
|
-
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (path.extname(filePath).toLowerCase() !== ".md") {
|
|
68
|
+
if (!filePath || path.extname(filePath).toLowerCase() !== ".md") {
|
|
85
69
|
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
|
|
86
70
|
continue;
|
|
87
71
|
}
|
|
88
72
|
try {
|
|
89
73
|
const raw = fs.readFileSync(filePath, "utf8");
|
|
90
|
-
const
|
|
74
|
+
const frontmatter = parseFrontmatter(raw);
|
|
91
75
|
const missingFields = [];
|
|
92
|
-
if (!
|
|
76
|
+
if (!frontmatter.data.description)
|
|
93
77
|
missingFields.push("description");
|
|
94
|
-
if (isLessonCandidateFn(failure.ref) && !
|
|
78
|
+
if (isLessonCandidateFn(failure.ref) && !frontmatter.data.when_to_use)
|
|
95
79
|
missingFields.push("when_to_use");
|
|
96
80
|
if (missingFields.length === 0) {
|
|
97
81
|
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
|
|
98
82
|
continue;
|
|
99
83
|
}
|
|
100
84
|
const fieldList = missingFields.join(" and ");
|
|
101
|
-
info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
|
|
102
|
-
const bodyPreview = (fm.content ?? raw).slice(0, 2000);
|
|
103
|
-
// Standards "rulebook" for this target — wiki schema (wiki page) or stash
|
|
104
|
-
// convention/meta facts (non-wiki asset). `resolveStandardsContext`
|
|
105
|
-
// dispatches on the ref.
|
|
106
85
|
const standardsContext = resolveStandardsContext(failure.ref, stashDir);
|
|
107
86
|
const standardsSection = standardsContext.trim()
|
|
108
87
|
? `\n\nStandards to follow (the rulebook for this target):\n${standardsContext.trim()}`
|
|
@@ -110,75 +89,175 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
110
89
|
const assetType = parseRefInput(failure.ref).type;
|
|
111
90
|
const authoringRules = authoringRulesForType(assetType);
|
|
112
91
|
const authoringRulesSection = authoringRules ? `\n\n${authoringRules}` : "";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
reason: failure.reason,
|
|
128
|
-
outcome: "error",
|
|
129
|
-
error: "LLM returned unparseable JSON for schema repair",
|
|
130
|
-
});
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
const newFm = { ...fm.data };
|
|
134
|
-
if (parsed.description)
|
|
135
|
-
newFm.description = parsed.description;
|
|
136
|
-
if (parsed.when_to_use)
|
|
137
|
-
newFm.when_to_use = parsed.when_to_use;
|
|
138
|
-
const newContent = assembleAsset(newFm, fm.content);
|
|
139
|
-
// M-3 / #387: Route through proposal queue instead of writing directly to
|
|
140
|
-
// disk. This restores akm's safety invariant — the proposal queue is the
|
|
141
|
-
// only path to a committed asset write. LLM-generated `description` /
|
|
142
|
-
// `when_to_use` fields can be incorrect; routing through the queue makes
|
|
143
|
-
// them human-reviewable before they affect search ranking and curate hints.
|
|
144
|
-
// mem0 open gaps (arXiv:2504.19413) — any LLM write to a memory field
|
|
145
|
-
// should be human-reviewable.
|
|
146
|
-
const proposalResult = createProposal(stashDir, {
|
|
147
|
-
ref: failure.ref,
|
|
148
|
-
source: "schema-repair",
|
|
149
|
-
// §23.6 fingerprint model-id term (WI-6.4).
|
|
150
|
-
modelId: llmConfig.model,
|
|
151
|
-
payload: {
|
|
152
|
-
content: newContent,
|
|
153
|
-
...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
|
|
154
|
-
},
|
|
92
|
+
eligibleRepairs.push({
|
|
93
|
+
failure,
|
|
94
|
+
frontmatter,
|
|
95
|
+
fieldList,
|
|
96
|
+
messages: [
|
|
97
|
+
{
|
|
98
|
+
role: "system",
|
|
99
|
+
content: "You generate concise asset frontmatter fields. Respond with a JSON object containing only the missing fields. No prose, no markdown fences.",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
role: "user",
|
|
103
|
+
content: `Generate the missing frontmatter fields (${fieldList}) for this ${assetType} asset. Return ONLY valid JSON like {"description": "...", "when_to_use": "..."}${standardsSection}${authoringRulesSection}\n\n${(frontmatter.content ?? raw).slice(0, 2000)}`,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
155
106
|
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
pendingErrors.push({ ref: failure.ref, reason: failure.reason, outcome: "error", error: String(error) });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { eligibleRepairs, pendingErrors };
|
|
113
|
+
}
|
|
114
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* Run the schema-repair loop for a batch of validation failures.
|
|
117
|
+
* Returns a list of per-asset outcome records and the set of refs whose live
|
|
118
|
+
* files were repaired. Queued proposals never count as live repairs. Invalid
|
|
119
|
+
* symbolic credentials reject with {@link ConfigError} before any repair
|
|
120
|
+
* proposal or per-ref event is written.
|
|
121
|
+
*/
|
|
122
|
+
export async function runSchemaRepairPass(failures, options) {
|
|
123
|
+
const repairs = [];
|
|
124
|
+
const repairedRefs = new Set();
|
|
125
|
+
const { startMs, budgetMs, stashDir, findFilePath = defaultFindFilePath, isLessonCandidateFn = defaultIsLessonCandidate, chatFn, } = options;
|
|
126
|
+
const llmRunner = options.llmRunner ?? null;
|
|
127
|
+
if (!llmRunner)
|
|
128
|
+
throw new Error("runSchemaRepairPass requires a resolved LLM runner");
|
|
129
|
+
if (!stashDir) {
|
|
130
|
+
throw new Error("runSchemaRepairPass requires stashDir so repairs route through the proposal queue");
|
|
131
|
+
}
|
|
132
|
+
// Classify the entire batch using read-only work before credential lookup or
|
|
133
|
+
// proposal/event persistence. A missing required credential therefore aborts
|
|
134
|
+
// the eligible batch atomically instead of leaving an earlier proposal behind.
|
|
135
|
+
const { eligibleRepairs, pendingErrors } = await classifySchemaRepairs({
|
|
136
|
+
failures,
|
|
137
|
+
startMs,
|
|
138
|
+
budgetMs,
|
|
139
|
+
stashDir,
|
|
140
|
+
findFilePath,
|
|
141
|
+
isLessonCandidateFn,
|
|
142
|
+
repairs,
|
|
143
|
+
});
|
|
144
|
+
if (eligibleRepairs.length === 0) {
|
|
145
|
+
for (const repair of pendingErrors) {
|
|
162
146
|
appendEvent({
|
|
163
147
|
eventType: "schema_repair_invoked",
|
|
164
|
-
ref:
|
|
165
|
-
metadata: { outcome: "
|
|
166
|
-
});
|
|
167
|
-
repairs.push({
|
|
168
|
-
ref: failure.ref,
|
|
169
|
-
reason: failure.reason,
|
|
170
|
-
outcome: "queued",
|
|
171
|
-
proposalId: proposalResult.id,
|
|
148
|
+
ref: repair.ref,
|
|
149
|
+
metadata: { outcome: "error", reason: repair.reason, error: repair.error },
|
|
172
150
|
});
|
|
151
|
+
repairs.push(repair);
|
|
173
152
|
}
|
|
174
|
-
|
|
153
|
+
return { repairs, repairedRefs };
|
|
154
|
+
}
|
|
155
|
+
const dispatchLease = await preflightStructuredLlmRunner(llmRunner);
|
|
156
|
+
try {
|
|
157
|
+
for (const repair of pendingErrors) {
|
|
175
158
|
appendEvent({
|
|
176
159
|
eventType: "schema_repair_invoked",
|
|
177
|
-
ref:
|
|
178
|
-
metadata: { outcome: "error", reason:
|
|
160
|
+
ref: repair.ref,
|
|
161
|
+
metadata: { outcome: "error", reason: repair.reason, error: repair.error },
|
|
179
162
|
});
|
|
180
|
-
repairs.push(
|
|
163
|
+
repairs.push(repair);
|
|
181
164
|
}
|
|
165
|
+
for (const { failure, frontmatter: fm, fieldList, messages } of eligibleRepairs) {
|
|
166
|
+
let loweringNotices = [];
|
|
167
|
+
const noticeFields = () => loweringNotices.length > 0 ? { notices: loweringNotices } : {};
|
|
168
|
+
try {
|
|
169
|
+
info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
|
|
170
|
+
const llmResponse = await callStructured({
|
|
171
|
+
feature: "schema_repair",
|
|
172
|
+
runner: llmRunner,
|
|
173
|
+
lease: dispatchLease,
|
|
174
|
+
messages,
|
|
175
|
+
...(chatFn ? { request: { chat: chatFn } } : {}),
|
|
176
|
+
onNotices: (value) => {
|
|
177
|
+
loweringNotices = value;
|
|
178
|
+
},
|
|
179
|
+
parse: (rawResponse) => rawResponse ?? "",
|
|
180
|
+
onError: () => "",
|
|
181
|
+
fallback: "",
|
|
182
|
+
});
|
|
183
|
+
const parsed = parseEmbeddedJsonResponse(llmResponse.trim());
|
|
184
|
+
if (!parsed) {
|
|
185
|
+
repairs.push({
|
|
186
|
+
ref: failure.ref,
|
|
187
|
+
reason: failure.reason,
|
|
188
|
+
outcome: "error",
|
|
189
|
+
error: "LLM returned unparseable JSON for schema repair",
|
|
190
|
+
...noticeFields(),
|
|
191
|
+
});
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const newFm = { ...fm.data };
|
|
195
|
+
if (parsed.description)
|
|
196
|
+
newFm.description = parsed.description;
|
|
197
|
+
if (parsed.when_to_use)
|
|
198
|
+
newFm.when_to_use = parsed.when_to_use;
|
|
199
|
+
const newContent = assembleAsset(newFm, fm.content);
|
|
200
|
+
// M-3 / #387: Route through proposal queue instead of writing directly to
|
|
201
|
+
// disk. This restores akm's safety invariant — the proposal queue is the
|
|
202
|
+
// only path to a committed asset write. LLM-generated `description` /
|
|
203
|
+
// `when_to_use` fields can be incorrect; routing through the queue makes
|
|
204
|
+
// them human-reviewable before they affect search ranking and curate hints.
|
|
205
|
+
// mem0 open gaps (arXiv:2504.19413) — any LLM write to a memory field
|
|
206
|
+
// should be human-reviewable.
|
|
207
|
+
const proposalResult = createProposal(stashDir, {
|
|
208
|
+
ref: failure.ref,
|
|
209
|
+
source: "schema-repair",
|
|
210
|
+
// §23.6 fingerprint model-id term (WI-6.4).
|
|
211
|
+
modelId: llmRunner.connection.model,
|
|
212
|
+
payload: {
|
|
213
|
+
content: newContent,
|
|
214
|
+
...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
if (isProposalSkipped(proposalResult)) {
|
|
218
|
+
info(`[improve] schema-repair proposal skipped for ${failure.ref}: ${proposalResult.message}`);
|
|
219
|
+
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped", ...noticeFields() });
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
info(`[improve] schema-repair queued: ${failure.ref} (proposal id: ${proposalResult.id})`);
|
|
223
|
+
appendEvent({
|
|
224
|
+
eventType: "schema_repair_invoked",
|
|
225
|
+
ref: failure.ref,
|
|
226
|
+
metadata: {
|
|
227
|
+
outcome: "queued",
|
|
228
|
+
reason: failure.reason,
|
|
229
|
+
proposalId: proposalResult.id,
|
|
230
|
+
...noticeFields(),
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
repairs.push({
|
|
234
|
+
ref: failure.ref,
|
|
235
|
+
reason: failure.reason,
|
|
236
|
+
outcome: "queued",
|
|
237
|
+
proposalId: proposalResult.id,
|
|
238
|
+
...noticeFields(),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
if (e instanceof ConfigError)
|
|
243
|
+
throw e;
|
|
244
|
+
appendEvent({
|
|
245
|
+
eventType: "schema_repair_invoked",
|
|
246
|
+
ref: failure.ref,
|
|
247
|
+
metadata: { outcome: "error", reason: failure.reason, error: String(e), ...noticeFields() },
|
|
248
|
+
});
|
|
249
|
+
repairs.push({
|
|
250
|
+
ref: failure.ref,
|
|
251
|
+
reason: failure.reason,
|
|
252
|
+
outcome: "error",
|
|
253
|
+
error: String(e),
|
|
254
|
+
...noticeFields(),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
finally {
|
|
260
|
+
disposeLoweredExecutionDispatchLease(dispatchLease);
|
|
182
261
|
}
|
|
183
262
|
return { repairs, repairedRefs };
|
|
184
263
|
}
|
|
@@ -7,29 +7,17 @@ import fs from "node:fs";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fetchWithRetry, IS_WINDOWS, ResponseTooLargeError, readBodyWithByteCap, readChunkWithDeadline, } from "../../core/common.js";
|
|
9
9
|
import { ConfigError } from "../../core/errors.js";
|
|
10
|
+
import { upgradeHistoricalStateDatabase } from "../../core/state-db.js";
|
|
10
11
|
import { warn } from "../../core/warn.js";
|
|
11
12
|
import { githubHeaders } from "../../integrations/github.js";
|
|
12
13
|
import { getDirname, mainPath, semverOrder } from "../../runtime.js";
|
|
13
|
-
import { resolveAkmInvocation } from "../../tasks/resolve-akm-bin.js";
|
|
14
14
|
const REPO = "itlackey/akm";
|
|
15
15
|
const DEFAULT_PACKAGE_NAME = "akm-cli";
|
|
16
16
|
const NODE_MODULES_SEGMENT = "/node_modules/";
|
|
17
17
|
const BUN_GLOBAL_INSTALL_PATTERN = /(^|\/)\.bun\/(?:[^/]+\/)+node_modules\//;
|
|
18
18
|
const PNPM_GLOBAL_INSTALL_PATTERN = /(^|\/)(?:pnpm\/global|\.pnpm-global)(?:\/\d+)?\/node_modules\//;
|
|
19
|
-
const MIGRATION_CONTRACT_VERSION = "0.9.0-rc.0";
|
|
20
|
-
const MIGRATION_CONTRACT_BLOCKED_MESSAGE = "AKM 0.8 does not implement the migrate command or the --migration-config upgrade contract, so self-update cannot safely cross into 0.9. " +
|
|
21
|
-
"Prepare the 0.9 config and an independent backup, install or stage the 0.9 binary manually, then use the new binary to run: " +
|
|
22
|
-
"akm migrate apply --config <prepared-0.9.json>. See docs/migration/v0.8-to-v0.9.md.";
|
|
23
19
|
const MAX_BINARY_DOWNLOAD_BYTES = 256 * 1024 * 1024;
|
|
24
20
|
const MAX_CHECKSUM_METADATA_BYTES = 1024 * 1024;
|
|
25
|
-
export function defaultMigrationCommand(preparedConfigPath) {
|
|
26
|
-
const configArgs = preparedConfigPath ? ["--config", preparedConfigPath] : [];
|
|
27
|
-
return {
|
|
28
|
-
preflight: (akmBin) => runRequiredCommand(akmBin, ["migrate", "status"], "Migration preflight"),
|
|
29
|
-
stagedPreflight: (akmBin) => runRequiredCommand(akmBin, ["migrate", "status", ...configArgs], "Staged migration preflight"),
|
|
30
|
-
apply: (akmBin) => runRequiredCommand(akmBin, ["migrate", "apply", ...configArgs], "Migration apply"),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
21
|
/**
|
|
34
22
|
* Bounds on the binary body read. `fetchWithTimeout`'s timer only covers
|
|
35
23
|
* time-to-HEADERS — it is cleared the moment `fetch` resolves — so without
|
|
@@ -184,7 +172,6 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
184
172
|
const { currentVersion, latestVersion, installMethod } = check;
|
|
185
173
|
const force = opts?.force === true;
|
|
186
174
|
const skipPostUpgrade = opts?.skipPostUpgrade === true;
|
|
187
|
-
const migration = dependencies?.migration ?? defaultMigrationCommand(opts?.migrationConfig);
|
|
188
175
|
// All install methods can short-circuit here unless the user explicitly forces an upgrade.
|
|
189
176
|
if (!check.updateAvailable && !force) {
|
|
190
177
|
return {
|
|
@@ -195,11 +182,6 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
195
182
|
message: `akm v${currentVersion} is already the latest version`,
|
|
196
183
|
};
|
|
197
184
|
}
|
|
198
|
-
if (latestVersion &&
|
|
199
|
-
semverOrder(currentVersion, MIGRATION_CONTRACT_VERSION) < 0 &&
|
|
200
|
-
semverOrder(latestVersion, MIGRATION_CONTRACT_VERSION) >= 0) {
|
|
201
|
-
throw new ConfigError(MIGRATION_CONTRACT_BLOCKED_MESSAGE, "UPGRADE_BLOCKED");
|
|
202
|
-
}
|
|
203
185
|
const packageManagerCommand = getPackageManagerUpgradeCommand(installMethod);
|
|
204
186
|
if (packageManagerCommand) {
|
|
205
187
|
return runPackageManagerUpgrade({
|
|
@@ -207,8 +189,8 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
207
189
|
currentVersion,
|
|
208
190
|
latestVersion,
|
|
209
191
|
installMethod,
|
|
210
|
-
migration,
|
|
211
192
|
skipPostUpgrade,
|
|
193
|
+
upgradeState: dependencies?.upgradeHistoricalStateDatabase ?? upgradeHistoricalStateDatabase,
|
|
212
194
|
});
|
|
213
195
|
}
|
|
214
196
|
if (installMethod === "unknown") {
|
|
@@ -307,14 +289,6 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
307
289
|
`Set AKM_UPGRADE_SKIP_CHECKSUM=1 to bypass (not recommended).`);
|
|
308
290
|
}
|
|
309
291
|
}
|
|
310
|
-
try {
|
|
311
|
-
migration.preflight(execPath);
|
|
312
|
-
migration.stagedPreflight(stagedPath);
|
|
313
|
-
}
|
|
314
|
-
catch (err) {
|
|
315
|
-
removeFileBestEffort(stagedPath);
|
|
316
|
-
throw err;
|
|
317
|
-
}
|
|
318
292
|
if (fs.existsSync(backupPath)) {
|
|
319
293
|
removeFileBestEffort(stagedPath);
|
|
320
294
|
throw new ConfigError(`Refusing to overwrite retained previous binary at ${backupPath}.`, "UPGRADE_BLOCKED");
|
|
@@ -333,14 +307,7 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
333
307
|
}
|
|
334
308
|
throw err;
|
|
335
309
|
}
|
|
336
|
-
|
|
337
|
-
migration.apply(execPath);
|
|
338
|
-
}
|
|
339
|
-
catch (err) {
|
|
340
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
341
|
-
throw new Error(`Migration apply failed; the new binary remains installed and the previous binary retained at ${backupPath}: ${detail}`);
|
|
342
|
-
}
|
|
343
|
-
// Keep the previous binary available until migration apply has completed.
|
|
310
|
+
// The replacement completed; the temporary rollback copy is no longer needed.
|
|
344
311
|
removeFileBestEffort(backupPath);
|
|
345
312
|
return {
|
|
346
313
|
currentVersion,
|
|
@@ -349,19 +316,32 @@ export async function performUpgrade(check, opts, dependencies) {
|
|
|
349
316
|
installMethod,
|
|
350
317
|
binaryPath: execPath,
|
|
351
318
|
checksumVerified,
|
|
352
|
-
postUpgrade: runPostUpgradeTasks(execPath, { skip: skipPostUpgrade }),
|
|
319
|
+
postUpgrade: runPostUpgradeTasks(execPath, { skip: skipPostUpgrade }, dependencies?.upgradeHistoricalStateDatabase ?? upgradeHistoricalStateDatabase),
|
|
353
320
|
};
|
|
354
321
|
}
|
|
355
322
|
/**
|
|
356
|
-
* Rebuild the derived index after
|
|
357
|
-
* Migration and indexing are intentionally separate lifecycle steps.
|
|
323
|
+
* Rebuild the derived index after a successful upgrade.
|
|
358
324
|
*/
|
|
359
|
-
function runPostUpgradeTasks(akmBin, opts) {
|
|
325
|
+
function runPostUpgradeTasks(akmBin, opts, upgradeState) {
|
|
326
|
+
let stateUpgrade;
|
|
327
|
+
try {
|
|
328
|
+
stateUpgrade = upgradeState();
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
skipped: opts.skip,
|
|
335
|
+
message: `Upgrade completed, but the state schema was not prepared (${detail}). ` +
|
|
336
|
+
"Preserve state.db and run `akm upgrade --force` before other AKM commands.",
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
const stateNote = stateUpgrade.safetyCopyPath ? ` Historical state safety copy: ${stateUpgrade.safetyCopyPath}.` : "";
|
|
360
340
|
if (opts.skip) {
|
|
361
341
|
return {
|
|
362
342
|
ok: true,
|
|
363
343
|
skipped: true,
|
|
364
|
-
message:
|
|
344
|
+
message: `Upgrade completed.${stateNote} Skipped the index rebuild. Run \`akm index\` manually to rebuild the index.`,
|
|
365
345
|
};
|
|
366
346
|
}
|
|
367
347
|
try {
|
|
@@ -374,7 +354,7 @@ function runPostUpgradeTasks(akmBin, opts) {
|
|
|
374
354
|
return {
|
|
375
355
|
ok: false,
|
|
376
356
|
skipped: false,
|
|
377
|
-
message: `
|
|
357
|
+
message: `Upgrade completed.${stateNote} The index rebuild could not start: ${result.error.message}. Run \`akm index\` manually.`,
|
|
378
358
|
};
|
|
379
359
|
}
|
|
380
360
|
if (result.status !== 0) {
|
|
@@ -383,14 +363,14 @@ function runPostUpgradeTasks(akmBin, opts) {
|
|
|
383
363
|
ok: false,
|
|
384
364
|
skipped: false,
|
|
385
365
|
exitCode: result.status,
|
|
386
|
-
message: `
|
|
366
|
+
message: `Upgrade completed.${stateNote} Post-upgrade \`akm index\` failed (${detail}). Run \`akm index\` manually.`,
|
|
387
367
|
};
|
|
388
368
|
}
|
|
389
369
|
return {
|
|
390
370
|
ok: true,
|
|
391
371
|
skipped: false,
|
|
392
372
|
exitCode: 0,
|
|
393
|
-
message:
|
|
373
|
+
message: `Upgrade completed and the index was rebuilt against the new binary.${stateNote}`,
|
|
394
374
|
};
|
|
395
375
|
}
|
|
396
376
|
catch (err) {
|
|
@@ -398,21 +378,20 @@ function runPostUpgradeTasks(akmBin, opts) {
|
|
|
398
378
|
return {
|
|
399
379
|
ok: false,
|
|
400
380
|
skipped: false,
|
|
401
|
-
message: `
|
|
381
|
+
message: `Upgrade completed.${stateNote} The index rebuild failed: ${detail}. Run \`akm index\` manually.`,
|
|
402
382
|
};
|
|
403
383
|
}
|
|
404
384
|
}
|
|
405
385
|
/**
|
|
406
|
-
* The package-manager arm of {@link performUpgrade}:
|
|
407
|
-
*
|
|
386
|
+
* The package-manager arm of {@link performUpgrade}: install → post-install
|
|
387
|
+
* version verification → post-upgrade tasks.
|
|
408
388
|
* Extracted whole so performUpgrade stays under its fn-size baseline.
|
|
409
389
|
*/
|
|
410
390
|
function runPackageManagerUpgrade(input) {
|
|
411
|
-
const { packageManagerCommand, currentVersion, latestVersion, installMethod,
|
|
391
|
+
const { packageManagerCommand, currentVersion, latestVersion, installMethod, skipPostUpgrade, upgradeState } = input;
|
|
412
392
|
if (!latestVersion) {
|
|
413
393
|
throw new Error("Unable to determine latest version from GitHub releases. Check https://github.com/itlackey/akm/releases");
|
|
414
394
|
}
|
|
415
|
-
migration.preflight("akm");
|
|
416
395
|
const result = childProcess.spawnSync(packageManagerCommand.command, packageManagerCommand.args, {
|
|
417
396
|
encoding: "utf8",
|
|
418
397
|
env: process.env,
|
|
@@ -429,8 +408,7 @@ function runPackageManagerUpgrade(input) {
|
|
|
429
408
|
// `latestVersion`: a lagging `@latest` dist-tag (partial publish,
|
|
430
409
|
// registry mirror lag) "succeeds" while leaving the old version on PATH.
|
|
431
410
|
// Re-read the version the shim actually reports before claiming an
|
|
432
|
-
// upgrade
|
|
433
|
-
// against the OLD binary, so stop before it.
|
|
411
|
+
// upgrade, so stop before claiming success.
|
|
434
412
|
const installedVersion = readInstalledCliVersion("akm");
|
|
435
413
|
if (installedVersion !== undefined && installedVersion !== latestVersion) {
|
|
436
414
|
return {
|
|
@@ -444,7 +422,6 @@ function runPackageManagerUpgrade(input) {
|
|
|
444
422
|
`${packageManagerCommand.displayCommand.replace(/@latest\b/, `@${latestVersion}`)}`,
|
|
445
423
|
};
|
|
446
424
|
}
|
|
447
|
-
migration.apply("akm");
|
|
448
425
|
return {
|
|
449
426
|
currentVersion,
|
|
450
427
|
newVersion: latestVersion,
|
|
@@ -453,7 +430,7 @@ function runPackageManagerUpgrade(input) {
|
|
|
453
430
|
message: installedVersion === latestVersion
|
|
454
431
|
? `akm upgraded via ${installMethod} (verified: akm --version reports v${installedVersion})`
|
|
455
432
|
: `akm upgraded via ${installMethod} (installed version could not be verified)`,
|
|
456
|
-
postUpgrade: runPostUpgradeTasks("akm", { skip: skipPostUpgrade }),
|
|
433
|
+
postUpgrade: runPostUpgradeTasks("akm", { skip: skipPostUpgrade }, upgradeState),
|
|
457
434
|
};
|
|
458
435
|
}
|
|
459
436
|
/**
|
|
@@ -474,27 +451,6 @@ function readInstalledCliVersion(akmBin) {
|
|
|
474
451
|
const match = (result.stdout ?? "").match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
|
|
475
452
|
return match?.[0];
|
|
476
453
|
}
|
|
477
|
-
function runRequiredCommand(akmBin, args, label) {
|
|
478
|
-
// A bare "akm" is not spawnable on Windows: npm/pnpm/yarn install a global CLI
|
|
479
|
-
// as akm.cmd / akm.ps1 shims, and spawnSync without a shell does not apply
|
|
480
|
-
// PATHEXT — so the package-manager upgrade arm died with ENOENT before it ever
|
|
481
|
-
// ran. resolveAkmInvocation returns a concrete argv (launcher, runtime + main
|
|
482
|
-
// script, or a standalone binary) for however this install actually runs.
|
|
483
|
-
// An explicit path (the standalone arm passes one) is used as given.
|
|
484
|
-
const [command, ...prefixArgs] = path.isAbsolute(akmBin) ? [akmBin] : resolveAkmInvocation().argv;
|
|
485
|
-
const result = childProcess.spawnSync(command ?? akmBin, [...prefixArgs, ...args], {
|
|
486
|
-
encoding: "utf8",
|
|
487
|
-
env: process.env,
|
|
488
|
-
stdio: "pipe",
|
|
489
|
-
});
|
|
490
|
-
if (result.error) {
|
|
491
|
-
throw new Error(`${label} could not start: ${result.error.message}`);
|
|
492
|
-
}
|
|
493
|
-
if (result.status !== 0) {
|
|
494
|
-
const detail = (result.stderr ?? "").trim() || (result.stdout ?? "").trim() || `exit code ${result.status}`;
|
|
495
|
-
throw new Error(`${label} failed (${detail}).`);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
454
|
function removeFileBestEffort(filePath) {
|
|
499
455
|
try {
|
|
500
456
|
fs.unlinkSync(filePath);
|
|
@@ -26,7 +26,8 @@ export async function akmAdd(input) {
|
|
|
26
26
|
if (shouldAddAsWebsiteUrl(ref)) {
|
|
27
27
|
return addWebsiteSource(ref, stashDir, input.name, input.options);
|
|
28
28
|
}
|
|
29
|
-
//
|
|
29
|
+
// Local directories become filesystem bundles; registry refs use the
|
|
30
|
+
// registry-backed bundle installer below.
|
|
30
31
|
try {
|
|
31
32
|
const parsed = parseRegistryRef(ref);
|
|
32
33
|
if (parsed.source === "local") {
|
|
@@ -38,10 +39,7 @@ export async function akmAdd(input) {
|
|
|
38
39
|
}
|
|
39
40
|
return addRegistryStash(ref, stashDir, input.writable);
|
|
40
41
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Add a local directory as a filesystem bundle (spec §10.1) — replaces the
|
|
43
|
-
* retired `sources[]` filesystem entry.
|
|
44
|
-
*/
|
|
42
|
+
/** Add a local directory as a filesystem bundle. */
|
|
45
43
|
async function addLocalSource(ref, sourcePath, stashDir, explicitName) {
|
|
46
44
|
const stashRoot = detectStashRoot(sourcePath);
|
|
47
45
|
const resolvedPath = path.resolve(stashRoot);
|
|
@@ -42,13 +42,9 @@ export const upgradeCommand = defineJsonCommand({
|
|
|
42
42
|
force: { type: "boolean", description: "Force upgrade even if on latest", default: false },
|
|
43
43
|
"skip-post-upgrade": {
|
|
44
44
|
type: "boolean",
|
|
45
|
-
description: "Skip the post-upgrade index rebuild
|
|
45
|
+
description: "Skip the post-upgrade index rebuild",
|
|
46
46
|
default: false,
|
|
47
47
|
},
|
|
48
|
-
"migration-config": {
|
|
49
|
-
type: "string",
|
|
50
|
-
description: "For 0.9+ upgrades, pass an operator-prepared config only to the new binary's migration apply",
|
|
51
|
-
},
|
|
52
48
|
},
|
|
53
49
|
async run({ args }) {
|
|
54
50
|
const check = await checkForUpdate(pkgVersion);
|
|
@@ -57,8 +53,7 @@ export const upgradeCommand = defineJsonCommand({
|
|
|
57
53
|
return;
|
|
58
54
|
}
|
|
59
55
|
const skipPostUpgrade = args["skip-post-upgrade"];
|
|
60
|
-
const
|
|
61
|
-
const result = await performUpgrade(check, { force: args.force, skipPostUpgrade, migrationConfig });
|
|
56
|
+
const result = await performUpgrade(check, { force: args.force, skipPostUpgrade });
|
|
62
57
|
output("upgrade", result);
|
|
63
58
|
},
|
|
64
59
|
});
|
|
@@ -75,14 +70,6 @@ async function runSyncBody(args) {
|
|
|
75
70
|
writable = resolveWritableOverride(loadConfig());
|
|
76
71
|
}
|
|
77
72
|
const result = saveGitStash(effectiveName, args.message, writable, { push: args.push !== false });
|
|
78
|
-
// 0.9.0 breaking change: both "save" holdovers from the command's
|
|
79
|
-
// pre-rename name are now "sync" — the persisted eventType below and the
|
|
80
|
-
// envelope shape emitted at the end of this function. Historical state.db
|
|
81
|
-
// rows still carry "save" — `readEvents`/`tailEvents` (src/core/events.ts)
|
|
82
|
-
// treat "save" and "sync" as synonyms on READ so `akm log --type save`
|
|
83
|
-
// keeps returning both old and new rows. Only the WRITE side changes here.
|
|
84
|
-
// The envelope shape needs no such synonym: it is per-invocation, never
|
|
85
|
-
// persisted, so nothing can be holding an old value.
|
|
86
73
|
appendEvent({
|
|
87
74
|
eventType: "sync",
|
|
88
75
|
metadata: {
|