akm-cli 0.9.0-beta.6 → 0.9.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +663 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -14
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-cli.js +220 -227
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +153 -147
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +76 -72
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1083
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1313 -1278
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +243 -599
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +55 -4
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +160 -7
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +32 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +510 -2537
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +115 -0
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +212 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +7 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -882
- package/dist/commands/read/curate.js +419 -103
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +29 -41
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +15 -1
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +21 -4
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +389 -58
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +21 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +142 -1091
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +356 -780
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +103 -24
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +106 -0
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +291 -310
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +9 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +39 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +7 -1
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +59 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +69 -67
- package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
- package/dist/integrations/harnesses/claude/index.js +2 -0
- package/dist/integrations/harnesses/claude/session-log.js +11 -1
- package/dist/integrations/harnesses/index.js +2 -3
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +2 -0
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
- package/dist/integrations/harnesses/types.js +1 -0
- package/dist/integrations/session-logs/index.js +16 -0
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +57 -15
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +85 -14
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +2 -1
- package/dist/output/text/helpers.js +29 -1
- package/dist/output/text/workflow.js +1 -0
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/scripts/migrate-storage.js +2718 -2354
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +891 -597
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/tar-utils.js +16 -8
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +0 -0
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/cli.js +1 -0
- package/dist/workflows/db.js +3 -4
- package/dist/workflows/runtime/runs.js +43 -118
- package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +5 -4
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import * as p from "
|
|
7
|
-
import {
|
|
8
|
-
import { output, runWithJsonErrors } from "../../cli/shared.js";
|
|
6
|
+
import * as p from "../../cli/clack.js";
|
|
7
|
+
import { defineJsonCommand, output } from "../../cli/shared.js";
|
|
9
8
|
import { UsageError } from "../../core/errors.js";
|
|
10
9
|
import { appendEvent } from "../../core/events.js";
|
|
11
10
|
import { warn } from "../../core/warn.js";
|
|
12
|
-
import { getHyphenatedBoolean } from "../../output/context.js";
|
|
13
11
|
import { akmRemove } from "./installed-stashes.js";
|
|
14
12
|
import { akmAdd } from "./source-add.js";
|
|
15
13
|
import { addStash } from "./source-manage.js";
|
|
@@ -149,7 +147,7 @@ export async function auditInstalledStashForDangerousKeys(opts) {
|
|
|
149
147
|
return { blocked: true, exitCode: 1 };
|
|
150
148
|
}
|
|
151
149
|
// ── Command definition ────────────────────────────────────────────────────────
|
|
152
|
-
export const addCommand =
|
|
150
|
+
export const addCommand = defineJsonCommand({
|
|
153
151
|
meta: {
|
|
154
152
|
name: "add",
|
|
155
153
|
description: "Add a source (local directory, website, npm package, GitHub repo, git URL, or remote provider)",
|
|
@@ -181,48 +179,11 @@ export const addCommand = defineCommand({
|
|
|
181
179
|
},
|
|
182
180
|
},
|
|
183
181
|
async run({ args }) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (args.provider) {
|
|
190
|
-
if (shouldWarnOnPlainHttp(ref)) {
|
|
191
|
-
if (!allowInsecure) {
|
|
192
|
-
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
193
|
-
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
|
|
194
|
-
}
|
|
195
|
-
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
196
|
-
}
|
|
197
|
-
let parsedOptions;
|
|
198
|
-
if (args.options) {
|
|
199
|
-
try {
|
|
200
|
-
const parsed = JSON.parse(args.options);
|
|
201
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
202
|
-
throw new UsageError("--options must be a JSON object");
|
|
203
|
-
}
|
|
204
|
-
parsedOptions = parsed;
|
|
205
|
-
}
|
|
206
|
-
catch (err) {
|
|
207
|
-
if (err instanceof UsageError)
|
|
208
|
-
throw err;
|
|
209
|
-
throw new UsageError("--options must be valid JSON");
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const result = addStash({
|
|
213
|
-
target: ref,
|
|
214
|
-
name: args.name,
|
|
215
|
-
providerType: args.provider,
|
|
216
|
-
options: parsedOptions,
|
|
217
|
-
writable: args.writable,
|
|
218
|
-
});
|
|
219
|
-
appendEvent({
|
|
220
|
-
eventType: "add",
|
|
221
|
-
metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
|
|
222
|
-
});
|
|
223
|
-
output("add", result);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
182
|
+
const ref = args.ref.trim();
|
|
183
|
+
const allowInsecure = args["allow-insecure"];
|
|
184
|
+
const allowDangerousKeys = allowInsecure;
|
|
185
|
+
// URL with --provider → stash source (remote or git provider)
|
|
186
|
+
if (args.provider) {
|
|
226
187
|
if (shouldWarnOnPlainHttp(ref)) {
|
|
227
188
|
if (!allowInsecure) {
|
|
228
189
|
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
@@ -230,64 +191,99 @@ export const addCommand = defineCommand({
|
|
|
230
191
|
}
|
|
231
192
|
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
232
193
|
}
|
|
233
|
-
|
|
234
|
-
if (args.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return;
|
|
194
|
+
let parsedOptions;
|
|
195
|
+
if (args.options) {
|
|
196
|
+
try {
|
|
197
|
+
const parsed = JSON.parse(args.options);
|
|
198
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
199
|
+
throw new UsageError("--options must be a JSON object");
|
|
200
|
+
}
|
|
201
|
+
parsedOptions = parsed;
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
if (err instanceof UsageError)
|
|
205
|
+
throw err;
|
|
206
|
+
throw new UsageError("--options must be valid JSON");
|
|
207
|
+
}
|
|
248
208
|
}
|
|
249
|
-
const result =
|
|
209
|
+
const result = addStash({
|
|
210
|
+
target: ref,
|
|
211
|
+
name: args.name,
|
|
212
|
+
providerType: args.provider,
|
|
213
|
+
options: parsedOptions,
|
|
214
|
+
writable: args.writable,
|
|
215
|
+
});
|
|
216
|
+
appendEvent({
|
|
217
|
+
eventType: "add",
|
|
218
|
+
metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
|
|
219
|
+
});
|
|
220
|
+
output("add", result);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (shouldWarnOnPlainHttp(ref)) {
|
|
224
|
+
if (!allowInsecure) {
|
|
225
|
+
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
226
|
+
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
|
|
227
|
+
}
|
|
228
|
+
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
229
|
+
}
|
|
230
|
+
const websiteOptions = buildWebsiteOptions(args);
|
|
231
|
+
if (args.type === "wiki") {
|
|
232
|
+
const { registerWikiSource } = await import("./source-add.js");
|
|
233
|
+
const result = await registerWikiSource({
|
|
250
234
|
ref,
|
|
251
235
|
name: args.name,
|
|
252
|
-
overrideType: args.type,
|
|
253
236
|
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
254
237
|
writable: args.writable,
|
|
255
238
|
});
|
|
256
239
|
appendEvent({
|
|
257
240
|
eventType: "add",
|
|
258
|
-
metadata: {
|
|
259
|
-
target: ref,
|
|
260
|
-
name: args.name ?? null,
|
|
261
|
-
overrideType: args.type ?? null,
|
|
262
|
-
writable: args.writable === true,
|
|
263
|
-
},
|
|
241
|
+
metadata: { target: ref, type: "wiki", name: args.name ?? null, writable: args.writable === true },
|
|
264
242
|
});
|
|
265
|
-
// ── Post-install env key audit ──────────────────────────────────────────
|
|
266
|
-
// Resolve the stash root from the install result and scan any env files
|
|
267
|
-
// for dangerous env var keys. When findings are present the install is
|
|
268
|
-
// gated: TTY → interactive confirmation prompt; non-TTY without
|
|
269
|
-
// --allow-insecure → hard failure (exit 1). Pass
|
|
270
|
-
// --allow-insecure to skip the prompt non-interactively.
|
|
271
|
-
const installedStashRoot = result.installed?.stashRoot ??
|
|
272
|
-
(result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
|
|
273
|
-
if (installedStashRoot) {
|
|
274
|
-
// Use the canonical installed id (most reliably resolved by akmRemove) rather
|
|
275
|
-
// than the raw user-supplied ref which may not match after URL normalisation.
|
|
276
|
-
const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
|
|
277
|
-
// The audit RETURNS its decision; we decide `process.exit` here, OUTSIDE
|
|
278
|
-
// any catch, so the abort cannot be lost to a swallowed exception (C3).
|
|
279
|
-
const decision = await auditInstalledStashForDangerousKeys({
|
|
280
|
-
installedStashRoot,
|
|
281
|
-
ref,
|
|
282
|
-
allowDangerousKeys,
|
|
283
|
-
rollbackTarget,
|
|
284
|
-
isTTY: process.stdin.isTTY === true,
|
|
285
|
-
});
|
|
286
|
-
if (decision.blocked) {
|
|
287
|
-
process.exit(decision.exitCode);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
243
|
output("add", result);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const result = await akmAdd({
|
|
247
|
+
ref,
|
|
248
|
+
name: args.name,
|
|
249
|
+
overrideType: args.type,
|
|
250
|
+
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
251
|
+
writable: args.writable,
|
|
252
|
+
});
|
|
253
|
+
appendEvent({
|
|
254
|
+
eventType: "add",
|
|
255
|
+
metadata: {
|
|
256
|
+
target: ref,
|
|
257
|
+
name: args.name ?? null,
|
|
258
|
+
overrideType: args.type ?? null,
|
|
259
|
+
writable: args.writable === true,
|
|
260
|
+
},
|
|
291
261
|
});
|
|
262
|
+
// ── Post-install env key audit ──────────────────────────────────────────
|
|
263
|
+
// Resolve the stash root from the install result and scan any env files
|
|
264
|
+
// for dangerous env var keys. When findings are present the install is
|
|
265
|
+
// gated: TTY → interactive confirmation prompt; non-TTY without
|
|
266
|
+
// --allow-insecure → hard failure (exit 1). Pass
|
|
267
|
+
// --allow-insecure to skip the prompt non-interactively.
|
|
268
|
+
const installedStashRoot = result.installed?.stashRoot ??
|
|
269
|
+
(result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
|
|
270
|
+
if (installedStashRoot) {
|
|
271
|
+
// Use the canonical installed id (most reliably resolved by akmRemove) rather
|
|
272
|
+
// than the raw user-supplied ref which may not match after URL normalisation.
|
|
273
|
+
const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
|
|
274
|
+
// The audit RETURNS its decision; we decide `process.exit` here, OUTSIDE
|
|
275
|
+
// any catch, so the abort cannot be lost to a swallowed exception (C3).
|
|
276
|
+
const decision = await auditInstalledStashForDangerousKeys({
|
|
277
|
+
installedStashRoot,
|
|
278
|
+
ref,
|
|
279
|
+
allowDangerousKeys,
|
|
280
|
+
rollbackTarget,
|
|
281
|
+
isTTY: process.stdin.isTTY === true,
|
|
282
|
+
});
|
|
283
|
+
if (decision.blocked) {
|
|
284
|
+
process.exit(decision.exitCode);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
output("add", result);
|
|
292
288
|
},
|
|
293
289
|
});
|
|
@@ -22,7 +22,7 @@ import { readEvents } from "../../core/events.js";
|
|
|
22
22
|
import { isoToSqlite, parseSinceToIso } from "../../core/time.js";
|
|
23
23
|
import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
|
|
24
24
|
import { getUsageEvents } from "../../indexer/usage/usage-events.js";
|
|
25
|
-
import { listProposals } from "../proposal/
|
|
25
|
+
import { listProposals } from "../proposal/repository.js";
|
|
26
26
|
// Proposal lifecycle event types emitted by the proposal substrate (#225).
|
|
27
27
|
const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
|
|
28
28
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -15,7 +15,7 @@ import { loadUserConfig, saveConfig } from "../../core/config/config.js";
|
|
|
15
15
|
import { ConfigError } from "../../core/errors.js";
|
|
16
16
|
import { assertSafeStashDir, getBinDir, getConfigPath, getDefaultStashDir } from "../../core/paths.js";
|
|
17
17
|
import { ensureRg } from "../../core/ripgrep/install.js";
|
|
18
|
-
import { copyStashSkeleton, scaffoldStashMeta } from "./stash-skeleton.js";
|
|
18
|
+
import { copyStashSkeleton, ensureStashGitignore, scaffoldStashMeta } from "./stash-skeleton.js";
|
|
19
19
|
/**
|
|
20
20
|
* Refuse to persist a temporary-directory stashDir to the user's config when
|
|
21
21
|
* running under a test runner AND `--dir <tempdir>` was passed explicitly.
|
|
@@ -37,7 +37,7 @@ import { copyStashSkeleton, scaffoldStashMeta } from "./stash-skeleton.js";
|
|
|
37
37
|
function assertInitSandbox(stashDir, dirExplicitlyProvided) {
|
|
38
38
|
if (!dirExplicitlyProvided)
|
|
39
39
|
return; // Only guard explicit --dir, not default HOME resolution.
|
|
40
|
-
const isUnderTest =
|
|
40
|
+
const isUnderTest = isUnderTestRunner();
|
|
41
41
|
if (!isUnderTest)
|
|
42
42
|
return;
|
|
43
43
|
if (process.env.AKM_FORCE_INIT_TMP_STASH === "1")
|
|
@@ -52,7 +52,24 @@ function assertInitSandbox(stashDir, dirExplicitlyProvided) {
|
|
|
52
52
|
return;
|
|
53
53
|
throw new ConfigError(`refusing to persist --dir stashDir to a temporary path while under test runner; set AKM_FORCE_INIT_TMP_STASH=1 if you really mean it (stashDir=${stashDir})`, "INIT_TMP_STASH_REFUSED");
|
|
54
54
|
}
|
|
55
|
+
function isUnderTestRunner() {
|
|
56
|
+
return process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
|
|
57
|
+
}
|
|
58
|
+
// ── Test seam ────────────────────────────────────────────────────────────────
|
|
59
|
+
// Swap-and-restore override. Inert in production; only tests call the setter.
|
|
60
|
+
let akmInitOverride;
|
|
61
|
+
/** TEST-ONLY. Swap the implementation of `akmInit`; pass undefined to restore. */
|
|
62
|
+
export function _setAkmInitForTests(fake) {
|
|
63
|
+
akmInitOverride = fake;
|
|
64
|
+
}
|
|
55
65
|
export async function akmInit(options) {
|
|
66
|
+
if (akmInitOverride)
|
|
67
|
+
return akmInitOverride(options);
|
|
68
|
+
return akmInitReal(options);
|
|
69
|
+
}
|
|
70
|
+
async function akmInitReal(options) {
|
|
71
|
+
const dirExplicitlyProvided = options?.dir != null;
|
|
72
|
+
const setDefault = options?.setDefault === true;
|
|
56
73
|
const stashDir = options?.dir ? path.resolve(options.dir) : getDefaultStashDir();
|
|
57
74
|
// Safety check (#473): refuse stashDir at /, $HOME, /etc, ~/.config, etc.
|
|
58
75
|
// Runs BEFORE any disk write — a fat-fingered `akm init --dir /` or
|
|
@@ -61,7 +78,7 @@ export async function akmInit(options) {
|
|
|
61
78
|
assertSafeStashDir(stashDir);
|
|
62
79
|
// Defense-in-depth: refuse to persist an explicit --dir /tmp/... stashDir
|
|
63
80
|
// to config under a test runner. Default HOME-resolved paths are exempt.
|
|
64
|
-
assertInitSandbox(stashDir,
|
|
81
|
+
assertInitSandbox(stashDir, dirExplicitlyProvided);
|
|
65
82
|
let created = false;
|
|
66
83
|
if (!fs.existsSync(stashDir)) {
|
|
67
84
|
fs.mkdirSync(stashDir, { recursive: true });
|
|
@@ -75,27 +92,58 @@ export async function akmInit(options) {
|
|
|
75
92
|
}
|
|
76
93
|
// Ensure the default stash is a local git repo (no remote required)
|
|
77
94
|
ensureGitRepo(stashDir);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
//
|
|
95
|
+
// 08-F1: scaffold a default `.gitignore` that keeps env/ + secrets/ out of git
|
|
96
|
+
// so a `git push` can never leak them. Idempotent + non-clobbering; the user
|
|
97
|
+
// opts into versioning by un-ignoring a path.
|
|
98
|
+
ensureStashGitignore(stashDir);
|
|
99
|
+
// Run seeding UNCONDITIONALLY (not just when the stash was newly created) so
|
|
100
|
+
// re-running `akm init` on an existing stash backfills any missing skeleton
|
|
101
|
+
// files — the README, the per-type SOFT convention templates under
|
|
102
|
+
// facts/conventions/assets/, and the `.meta/index.md` orientation doc. Both
|
|
103
|
+
// helpers are absent-only: they never overwrite a file a user has edited.
|
|
104
|
+
copyStashSkeleton(stashDir);
|
|
105
|
+
scaffoldStashMeta(stashDir);
|
|
106
|
+
// Persist stashDir in config.json — but ONLY when the user is actually
|
|
107
|
+
// setting up / opting into a default. A bare `akm init --dir <secondary>`
|
|
108
|
+
// must NOT silently repoint the user's real default stash (the footgun
|
|
109
|
+
// documented in memory:akm-init-persists-stashdir-warning).
|
|
110
|
+
//
|
|
111
|
+
// Decision matrix — persist when ANY of:
|
|
112
|
+
// (a) no --dir provided → default HOME-resolved setup flow
|
|
113
|
+
// (b) --dir AND no existing stashDir in config → first-time bootstrap
|
|
114
|
+
// (c) --dir AND --set-default → explicit opt-in
|
|
115
|
+
// Otherwise (--dir + existing default + no --set-default) leave the default
|
|
116
|
+
// pointer alone; the target dir is still scaffolded above.
|
|
83
117
|
const configPath = getConfigPath();
|
|
84
118
|
const existing = loadUserConfig();
|
|
85
|
-
|
|
86
|
-
|
|
119
|
+
const existingStashDir = existing.stashDir;
|
|
120
|
+
const shouldPersist = !dirExplicitlyProvided || !existingStashDir || setDefault;
|
|
121
|
+
let defaultStashUpdated = false;
|
|
122
|
+
let previousStashDir;
|
|
123
|
+
if (shouldPersist) {
|
|
124
|
+
if (!existingStashDir || existingStashDir !== stashDir) {
|
|
125
|
+
saveConfig({ ...existing, stashDir });
|
|
126
|
+
defaultStashUpdated = true;
|
|
127
|
+
}
|
|
128
|
+
// else: already pointed here — no-op, no spurious rewrite.
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Default left untouched; surface it so the CLI can inform the user.
|
|
132
|
+
previousStashDir = existingStashDir;
|
|
87
133
|
}
|
|
88
134
|
// Ensure ripgrep is available (install to cache/bin if needed)
|
|
89
135
|
let ripgrep;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
136
|
+
if (!isUnderTestRunner()) {
|
|
137
|
+
try {
|
|
138
|
+
const binDir = getBinDir();
|
|
139
|
+
const rgResult = ensureRg(binDir);
|
|
140
|
+
ripgrep = rgResult;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Non-fatal: ripgrep is optional, search works without it
|
|
144
|
+
}
|
|
97
145
|
}
|
|
98
|
-
return { stashDir, created, configPath, ripgrep };
|
|
146
|
+
return { stashDir, created, configPath, defaultStashUpdated, previousStashDir, ripgrep };
|
|
99
147
|
}
|
|
100
148
|
/** Initialise `dir` as a git repository if it is not already one. */
|
|
101
149
|
function ensureGitRepo(dir) {
|
|
@@ -17,7 +17,7 @@ import { removeLockEntry, upsertLockEntry } from "../../integrations/lockfile.js
|
|
|
17
17
|
import { parseRegistryRef } from "../../registry/resolve.js";
|
|
18
18
|
import { parseGitRepoUrl, syncMirroredRepo } from "../../sources/providers/git.js";
|
|
19
19
|
import { syncFromRef } from "../../sources/providers/sync-from-ref.js";
|
|
20
|
-
import { ensureWebsiteMirror } from "../../sources/website-ingest.js";
|
|
20
|
+
import { ensureWebsiteMirror, shouldAllowPrivateWebsiteUrlForTests } from "../../sources/website-ingest.js";
|
|
21
21
|
import { listWikis, resolveWikisRoot } from "../../wiki/wiki.js";
|
|
22
22
|
import { removeInstalledRegistryEntry, upsertInstalledRegistryEntry } from "./source-add.js";
|
|
23
23
|
import { removeStash } from "./source-manage.js";
|
|
@@ -194,7 +194,11 @@ async function updateGitSource(stashDir, target, all, gitSource) {
|
|
|
194
194
|
/** Re-crawl a website source and return an UpdateResponse. */
|
|
195
195
|
async function updateWebsiteSource(stashDir, target, all, websiteSource) {
|
|
196
196
|
// TODO: full incremental re-crawl with delta tracking (#19)
|
|
197
|
-
await ensureWebsiteMirror(websiteSource, {
|
|
197
|
+
await ensureWebsiteMirror(websiteSource, {
|
|
198
|
+
requireStashDir: true,
|
|
199
|
+
force: true,
|
|
200
|
+
...(shouldAllowPrivateWebsiteUrlForTests(websiteSource.url ?? "") ? { allowPrivateHosts: true } : {}),
|
|
201
|
+
});
|
|
198
202
|
return buildUpdateResponse(stashDir, target, all, []);
|
|
199
203
|
}
|
|
200
204
|
/** Sync a single installed registry entry and return the processed record. */
|
|
@@ -205,7 +209,11 @@ async function updateRegistryEntry(entry, force) {
|
|
|
205
209
|
const synced = await syncFromRef(entry.ref, { force });
|
|
206
210
|
const installedEntry = {
|
|
207
211
|
id: synced.id,
|
|
208
|
-
source
|
|
212
|
+
// Preserve the original source classification. syncFromRef() re-derives the
|
|
213
|
+
// source type from the ref scheme (e.g. "github:" → source: "github"), but
|
|
214
|
+
// an update should not reclassify an existing entry. A writable entry stored
|
|
215
|
+
// as source: "git" would fail config validation if rewritten to "github".
|
|
216
|
+
source: entry.source,
|
|
209
217
|
ref: synced.ref,
|
|
210
218
|
artifactUrl: synced.artifactUrl,
|
|
211
219
|
resolvedVersion: synced.resolvedVersion,
|
|
@@ -17,11 +17,13 @@ import path from "node:path";
|
|
|
17
17
|
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
18
18
|
import { assembleAsset } from "../../core/asset/asset-serialize.js";
|
|
19
19
|
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
20
|
+
import { authoringRulesForType } from "../../core/authoring-rules.js";
|
|
20
21
|
import { appendEvent, readEvents } from "../../core/events.js";
|
|
21
|
-
import {
|
|
22
|
+
import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
|
|
23
|
+
import { info } from "../../core/warn.js";
|
|
22
24
|
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
23
25
|
import { chatCompletion, parseEmbeddedJsonResponse } from "../../llm/client.js";
|
|
24
|
-
import { createProposal, isProposalSkipped } from "../proposal/
|
|
26
|
+
import { createProposal, isProposalSkipped } from "../proposal/repository.js";
|
|
25
27
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
26
28
|
/** Minimum gap between schema-repair attempts on the same asset. */
|
|
27
29
|
const SCHEMA_REPAIR_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
@@ -44,6 +46,9 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
44
46
|
const repairs = [];
|
|
45
47
|
const repairedRefs = new Set();
|
|
46
48
|
const { startMs, budgetMs, llmConfig, stashDir, findFilePath = defaultFindFilePath, isLessonCandidateFn = defaultIsLessonCandidate, chatFn = chatCompletion, } = options;
|
|
49
|
+
if (!stashDir) {
|
|
50
|
+
throw new Error("runSchemaRepairPass requires stashDir so repairs route through the proposal queue");
|
|
51
|
+
}
|
|
47
52
|
for (const failure of failures) {
|
|
48
53
|
if (Date.now() - startMs >= budgetMs)
|
|
49
54
|
break;
|
|
@@ -94,6 +99,16 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
94
99
|
const fieldList = missingFields.join(" and ");
|
|
95
100
|
info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
|
|
96
101
|
const bodyPreview = (fm.content ?? raw).slice(0, 2000);
|
|
102
|
+
// Standards "rulebook" for this target — wiki schema (wiki page) or stash
|
|
103
|
+
// convention/meta facts (non-wiki asset). `resolveStandardsContext`
|
|
104
|
+
// dispatches on the ref.
|
|
105
|
+
const standardsContext = resolveStandardsContext(failure.ref, stashDir);
|
|
106
|
+
const standardsSection = standardsContext.trim()
|
|
107
|
+
? `\n\nStandards to follow (the rulebook for this target):\n${standardsContext.trim()}`
|
|
108
|
+
: "";
|
|
109
|
+
const assetType = parseAssetRef(failure.ref).type;
|
|
110
|
+
const authoringRules = authoringRulesForType(assetType);
|
|
111
|
+
const authoringRulesSection = authoringRules ? `\n\n${authoringRules}` : "";
|
|
97
112
|
const llmResponse = await chatFn(llmConfig, [
|
|
98
113
|
{
|
|
99
114
|
role: "system",
|
|
@@ -101,7 +116,7 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
101
116
|
},
|
|
102
117
|
{
|
|
103
118
|
role: "user",
|
|
104
|
-
content: `Generate the missing frontmatter fields (${fieldList}) for this ${
|
|
119
|
+
content: `Generate the missing frontmatter fields (${fieldList}) for this ${assetType} asset. Return ONLY valid JSON like {"description": "...", "when_to_use": "..."}${standardsSection}${authoringRulesSection}\n\n${bodyPreview}`,
|
|
105
120
|
},
|
|
106
121
|
]);
|
|
107
122
|
const parsed = parseEmbeddedJsonResponse(llmResponse.trim());
|
|
@@ -127,50 +142,33 @@ export async function runSchemaRepairPass(failures, options) {
|
|
|
127
142
|
// them human-reviewable before they affect search ranking and curate hints.
|
|
128
143
|
// mem0 open gaps (arXiv:2504.19413) — any LLM write to a memory field
|
|
129
144
|
// should be human-reviewable.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
info(`[improve] schema-repair queued: ${failure.ref} (proposal id: ${proposalResult.id})`);
|
|
145
|
-
appendEvent({
|
|
146
|
-
eventType: "schema_repair_invoked",
|
|
147
|
-
ref: failure.ref,
|
|
148
|
-
metadata: { outcome: "queued", reason: failure.reason, proposalId: proposalResult.id },
|
|
149
|
-
});
|
|
150
|
-
repairs.push({
|
|
151
|
-
ref: failure.ref,
|
|
152
|
-
reason: failure.reason,
|
|
153
|
-
outcome: "queued",
|
|
154
|
-
proposalId: proposalResult.id,
|
|
155
|
-
});
|
|
156
|
-
// Mark as repaired so the caller removes it from the validation-failure set.
|
|
157
|
-
repairedRefs.add(failure.ref);
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
// Fallback: no stash dir available — write directly (legacy path).
|
|
161
|
-
// This should not occur in production; stashDir is always provided by
|
|
162
|
-
// `runSchemaRepairPass` callers in improve.ts.
|
|
163
|
-
warn(`[improve] schema-repair: no stashDir available for ${failure.ref}, falling back to direct write`);
|
|
164
|
-
fs.writeFileSync(filePath, newContent, "utf8");
|
|
165
|
-
info(`[improve] schema-repair written: ${failure.ref}`);
|
|
166
|
-
appendEvent({
|
|
167
|
-
eventType: "schema_repair_invoked",
|
|
168
|
-
ref: failure.ref,
|
|
169
|
-
metadata: { outcome: "written", reason: failure.reason },
|
|
170
|
-
});
|
|
171
|
-
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "written" });
|
|
172
|
-
repairedRefs.add(failure.ref);
|
|
145
|
+
const proposalResult = createProposal(stashDir, {
|
|
146
|
+
ref: failure.ref,
|
|
147
|
+
source: "schema-repair",
|
|
148
|
+
payload: {
|
|
149
|
+
content: newContent,
|
|
150
|
+
...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
if (isProposalSkipped(proposalResult)) {
|
|
154
|
+
info(`[improve] schema-repair proposal skipped for ${failure.ref}: ${proposalResult.message}`);
|
|
155
|
+
repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
|
|
156
|
+
continue;
|
|
173
157
|
}
|
|
158
|
+
info(`[improve] schema-repair queued: ${failure.ref} (proposal id: ${proposalResult.id})`);
|
|
159
|
+
appendEvent({
|
|
160
|
+
eventType: "schema_repair_invoked",
|
|
161
|
+
ref: failure.ref,
|
|
162
|
+
metadata: { outcome: "queued", reason: failure.reason, proposalId: proposalResult.id },
|
|
163
|
+
});
|
|
164
|
+
repairs.push({
|
|
165
|
+
ref: failure.ref,
|
|
166
|
+
reason: failure.reason,
|
|
167
|
+
outcome: "queued",
|
|
168
|
+
proposalId: proposalResult.id,
|
|
169
|
+
});
|
|
170
|
+
// Mark as repaired so the caller removes it from the validation-failure set.
|
|
171
|
+
repairedRefs.add(failure.ref);
|
|
174
172
|
}
|
|
175
173
|
catch (e) {
|
|
176
174
|
appendEvent({
|
|
@@ -306,8 +306,8 @@ export async function performUpgrade(check, opts) {
|
|
|
306
306
|
* The new binary's `akm index` does the work for us:
|
|
307
307
|
* 1. loadConfig() runs at startup — auto-migrates legacy `stashes` →
|
|
308
308
|
* `sources` if the on-disk config still uses the old key.
|
|
309
|
-
* 2. ensureSchema()
|
|
310
|
-
*
|
|
309
|
+
* 2. ensureSchema() converges index.db forward via its idempotent baseline
|
|
310
|
+
* schema + additive migrations (no destructive rebuild).
|
|
311
311
|
* 3. The full reindex repopulates entries + workflow_documents + FTS.
|
|
312
312
|
*/
|
|
313
313
|
function runPostUpgradeTasks(akmBin, opts) {
|
|
@@ -11,7 +11,7 @@ import { upsertLockEntry } from "../../integrations/lockfile.js";
|
|
|
11
11
|
import { parseRegistryRef } from "../../registry/resolve.js";
|
|
12
12
|
import { detectStashRoot } from "../../sources/providers/provider-utils.js";
|
|
13
13
|
import { syncFromRef } from "../../sources/providers/sync-from-ref.js";
|
|
14
|
-
import { ensureWebsiteMirror, validateWebsiteInputUrl } from "../../sources/website-ingest.js";
|
|
14
|
+
import { ensureWebsiteMirror, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteInputUrl, } from "../../sources/website-ingest.js";
|
|
15
15
|
import { ensureWikiNameAvailable, validateWikiName } from "../../wiki/wiki.js";
|
|
16
16
|
const VALID_OVERRIDE_TYPES = new Set(["wiki"]);
|
|
17
17
|
export async function akmAdd(input) {
|
|
@@ -126,7 +126,8 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
|
|
|
126
126
|
};
|
|
127
127
|
}
|
|
128
128
|
async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
129
|
-
const
|
|
129
|
+
const allowPrivateHosts = shouldAllowPrivateWebsiteUrlForTests(ref);
|
|
130
|
+
const normalizedUrl = validateWebsiteInputUrl(ref, { allowPrivateHosts });
|
|
130
131
|
const config = loadUserConfig();
|
|
131
132
|
const sources = [...getSources(config)];
|
|
132
133
|
let entry = sources.find((stash) => stash.type === "website" && stash.url === normalizedUrl);
|
|
@@ -154,7 +155,10 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
|
|
|
154
155
|
if (changed)
|
|
155
156
|
saveConfig({ ...config, sources });
|
|
156
157
|
}
|
|
157
|
-
const cachePaths = await ensureWebsiteMirror(entry, {
|
|
158
|
+
const cachePaths = await ensureWebsiteMirror(entry, {
|
|
159
|
+
requireStashDir: true,
|
|
160
|
+
...(allowPrivateHosts ? { allowPrivateHosts: true } : {}),
|
|
161
|
+
});
|
|
158
162
|
const index = await akmIndex({ stashDir });
|
|
159
163
|
const updatedConfig = loadConfig();
|
|
160
164
|
return {
|
|
@@ -23,7 +23,7 @@ import { loadConfig } from "../../core/config/config.js";
|
|
|
23
23
|
import { UsageError } from "../../core/errors.js";
|
|
24
24
|
import { appendEvent } from "../../core/events.js";
|
|
25
25
|
import { resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
26
|
-
import {
|
|
26
|
+
import { parseFlagValue } from "../../output/context.js";
|
|
27
27
|
import { resolveWritableOverride, saveGitStash } from "../../sources/providers/git.js";
|
|
28
28
|
import { pkgVersion } from "../../version.js";
|
|
29
29
|
import { akmHistory } from "./history.js";
|
|
@@ -125,8 +125,8 @@ export const upgradeCommand = defineJsonCommand({
|
|
|
125
125
|
output("upgrade", check);
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
|
-
const skipChecksum =
|
|
129
|
-
const skipPostUpgrade =
|
|
128
|
+
const skipChecksum = args["skip-checksum"];
|
|
129
|
+
const skipPostUpgrade = args["skip-post-upgrade"];
|
|
130
130
|
const result = await performUpgrade(check, { force: args.force, skipChecksum, skipPostUpgrade });
|
|
131
131
|
output("upgrade", result);
|
|
132
132
|
},
|