akm-cli 0.9.0-beta.9 → 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.
Files changed (325) hide show
  1. package/CHANGELOG.md +592 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  39. package/dist/assets/templates/html/health.html +281 -111
  40. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  41. package/dist/assets/wiki/schema-template.md +4 -4
  42. package/dist/cli/clack.js +56 -0
  43. package/dist/cli/config-migrate.js +7 -1
  44. package/dist/cli/confirm.js +1 -1
  45. package/dist/cli/parse-args.js +46 -1
  46. package/dist/cli/shared.js +28 -0
  47. package/dist/cli.js +25 -21
  48. package/dist/commands/agent/agent-dispatch.js +3 -2
  49. package/dist/commands/agent/agent-support.js +0 -7
  50. package/dist/commands/agent/contribute-cli.js +26 -7
  51. package/dist/commands/config-cli.js +26 -13
  52. package/dist/commands/env/child-env.js +47 -0
  53. package/dist/commands/env/env-cli.js +220 -227
  54. package/dist/commands/env/env.js +14 -67
  55. package/dist/commands/env/secret-cli.js +140 -138
  56. package/dist/commands/feedback-cli.js +156 -155
  57. package/dist/commands/graph/graph-cli.js +5 -13
  58. package/dist/commands/graph/graph.js +3 -3
  59. package/dist/commands/health/advisories.js +151 -0
  60. package/dist/commands/health/checks.js +103 -16
  61. package/dist/commands/health/html-report.js +447 -81
  62. package/dist/commands/health/improve-metrics.js +771 -0
  63. package/dist/commands/health/llm-usage.js +65 -0
  64. package/dist/commands/health/md-report.js +103 -0
  65. package/dist/commands/health/metrics.js +278 -0
  66. package/dist/commands/health/stash-exposure.js +46 -0
  67. package/dist/commands/health/surfaces.js +216 -0
  68. package/dist/commands/health/task-runs.js +135 -0
  69. package/dist/commands/health/types.js +26 -0
  70. package/dist/commands/health/windows.js +195 -0
  71. package/dist/commands/health.js +91 -1091
  72. package/dist/commands/improve/anti-collapse.js +170 -0
  73. package/dist/commands/improve/calibration.js +161 -0
  74. package/dist/commands/improve/collapse-detector.js +421 -0
  75. package/dist/commands/improve/consolidate/chunking.js +141 -0
  76. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  77. package/dist/commands/improve/consolidate/merge.js +145 -0
  78. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  79. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  80. package/dist/commands/improve/consolidate.js +1295 -1277
  81. package/dist/commands/improve/dedup.js +482 -0
  82. package/dist/commands/improve/distill/content-repair.js +202 -0
  83. package/dist/commands/improve/distill/promote-memory.js +229 -0
  84. package/dist/commands/improve/distill/quality-gate.js +236 -0
  85. package/dist/commands/improve/distill-guards.js +127 -0
  86. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  87. package/dist/commands/improve/distill.js +228 -605
  88. package/dist/commands/improve/eligibility.js +434 -0
  89. package/dist/commands/improve/encoding-salience.js +205 -0
  90. package/dist/commands/improve/extract-cli.js +179 -59
  91. package/dist/commands/improve/extract-prompt.js +54 -3
  92. package/dist/commands/improve/extract-watch.js +140 -0
  93. package/dist/commands/improve/extract.js +409 -43
  94. package/dist/commands/improve/feedback-valence.js +54 -0
  95. package/dist/commands/improve/hot-probation.js +45 -0
  96. package/dist/commands/improve/improve-auto-accept.js +157 -10
  97. package/dist/commands/improve/improve-cli.js +115 -73
  98. package/dist/commands/improve/improve-profiles.js +28 -8
  99. package/dist/commands/improve/improve-result-file.js +15 -25
  100. package/dist/commands/improve/improve-session.js +58 -0
  101. package/dist/commands/improve/improve.js +485 -2764
  102. package/dist/commands/improve/locks.js +154 -0
  103. package/dist/commands/improve/loop-stages.js +1100 -0
  104. package/dist/commands/improve/memory/memory-belief.js +14 -15
  105. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  106. package/dist/commands/improve/memory/memory-improve.js +27 -27
  107. package/dist/commands/improve/outcome-loop.js +270 -0
  108. package/dist/commands/improve/preparation.js +2002 -0
  109. package/dist/commands/improve/proactive-maintenance.js +37 -35
  110. package/dist/commands/improve/procedural.js +398 -0
  111. package/dist/commands/improve/recombine.js +818 -0
  112. package/dist/commands/improve/reflect-noise.js +0 -0
  113. package/dist/commands/improve/reflect.js +206 -45
  114. package/dist/commands/improve/salience.js +455 -0
  115. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  116. package/dist/commands/improve/shared.js +51 -0
  117. package/dist/commands/improve/triage.js +93 -0
  118. package/dist/commands/lint/agent-linter.js +19 -24
  119. package/dist/commands/lint/base-linter.js +173 -60
  120. package/dist/commands/lint/command-linter.js +19 -24
  121. package/dist/commands/lint/env-key-rules.js +38 -1
  122. package/dist/commands/lint/fact-linter.js +39 -0
  123. package/dist/commands/lint/index.js +31 -13
  124. package/dist/commands/lint/memory-linter.js +1 -1
  125. package/dist/commands/lint/registry.js +7 -2
  126. package/dist/commands/lint/task-linter.js +3 -3
  127. package/dist/commands/lint/workflow-linter.js +26 -1
  128. package/dist/commands/observability-cli.js +4 -4
  129. package/dist/commands/proposal/drain-policies.js +13 -4
  130. package/dist/commands/proposal/drain.js +45 -51
  131. package/dist/commands/proposal/legacy-import.js +115 -0
  132. package/dist/commands/proposal/proposal-cli.js +24 -34
  133. package/dist/commands/proposal/proposal.js +2 -1
  134. package/dist/commands/proposal/propose.js +8 -3
  135. package/dist/commands/proposal/repository.js +829 -0
  136. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  137. package/dist/commands/proposal/validators/proposals.js +93 -895
  138. package/dist/commands/read/curate.js +410 -111
  139. package/dist/commands/read/knowledge.js +10 -3
  140. package/dist/commands/read/remember-cli.js +133 -138
  141. package/dist/commands/read/search-cli.js +15 -8
  142. package/dist/commands/read/search.js +22 -11
  143. package/dist/commands/read/show.js +106 -14
  144. package/dist/commands/registry-cli.js +76 -87
  145. package/dist/commands/remember.js +11 -12
  146. package/dist/commands/sources/add-cli.js +91 -95
  147. package/dist/commands/sources/history.js +1 -1
  148. package/dist/commands/sources/init.js +66 -18
  149. package/dist/commands/sources/installed-stashes.js +11 -3
  150. package/dist/commands/sources/schema-repair.js +44 -46
  151. package/dist/commands/sources/self-update.js +2 -2
  152. package/dist/commands/sources/source-add.js +7 -3
  153. package/dist/commands/sources/sources-cli.js +3 -3
  154. package/dist/commands/sources/stash-cli.js +19 -39
  155. package/dist/commands/sources/stash-skeleton.js +57 -8
  156. package/dist/commands/tasks/default-tasks.js +15 -2
  157. package/dist/commands/tasks/tasks-cli.js +20 -29
  158. package/dist/commands/tasks/tasks.js +39 -11
  159. package/dist/commands/wiki-cli.js +23 -38
  160. package/dist/commands/workflow-cli.js +15 -1
  161. package/dist/core/asset/asset-registry.js +3 -1
  162. package/dist/core/asset/asset-spec.js +21 -4
  163. package/dist/core/asset/frontmatter.js +188 -167
  164. package/dist/core/asset/markdown.js +8 -0
  165. package/dist/core/authoring-rules.js +92 -0
  166. package/dist/core/common.js +4 -23
  167. package/dist/core/concurrent.js +10 -1
  168. package/dist/core/config/config-io.js +10 -1
  169. package/dist/core/config/config-migration.js +18 -40
  170. package/dist/core/config/config-schema.js +382 -62
  171. package/dist/core/config/config-types.js +3 -3
  172. package/dist/core/config/config.js +67 -22
  173. package/dist/core/deep-merge.js +38 -0
  174. package/dist/core/errors.js +1 -0
  175. package/dist/core/eval/rank-metrics.js +113 -0
  176. package/dist/core/events.js +4 -7
  177. package/dist/core/improve-types.js +47 -8
  178. package/dist/core/logs-db.js +14 -75
  179. package/dist/core/parse.js +36 -16
  180. package/dist/core/paths.js +18 -18
  181. package/dist/core/standards/resolve-standards-context.js +87 -0
  182. package/dist/core/standards/resolve-stash-standards.js +99 -0
  183. package/dist/core/standards/resolve-type-conventions.js +66 -0
  184. package/dist/core/state/migrations.js +770 -0
  185. package/dist/core/state-db.js +132 -1126
  186. package/dist/core/structured.js +69 -0
  187. package/dist/core/time.js +53 -0
  188. package/dist/core/warn.js +21 -0
  189. package/dist/core/write-source.js +37 -0
  190. package/dist/indexer/db/db.js +259 -769
  191. package/dist/indexer/db/entry-mapper.js +41 -0
  192. package/dist/indexer/db/graph-db.js +129 -86
  193. package/dist/indexer/db/llm-cache.js +2 -2
  194. package/dist/indexer/db/schema.js +516 -0
  195. package/dist/indexer/ensure-index.js +36 -92
  196. package/dist/indexer/feedback/utility-policy.js +75 -0
  197. package/dist/indexer/graph/graph-boost.js +51 -41
  198. package/dist/indexer/graph/graph-extraction.js +207 -4
  199. package/dist/indexer/index-writer-lock.js +18 -11
  200. package/dist/indexer/index-written-assets.js +105 -0
  201. package/dist/indexer/indexer.js +182 -204
  202. package/dist/indexer/passes/dir-staleness.js +114 -0
  203. package/dist/indexer/passes/memory-inference.js +13 -5
  204. package/dist/indexer/passes/metadata.js +20 -0
  205. package/dist/indexer/read-preflight.js +23 -0
  206. package/dist/indexer/search/db-search.js +89 -13
  207. package/dist/indexer/search/fts-query.js +51 -0
  208. package/dist/indexer/search/ranking-contributors.js +95 -9
  209. package/dist/indexer/search/ranking.js +79 -3
  210. package/dist/indexer/search/search-fields.js +6 -0
  211. package/dist/indexer/search/search-source.js +32 -21
  212. package/dist/indexer/search/semantic-status.js +4 -0
  213. package/dist/indexer/walk/matchers.js +9 -0
  214. package/dist/indexer/walk/walker.js +21 -13
  215. package/dist/integrations/agent/builders.js +39 -13
  216. package/dist/integrations/agent/config.js +20 -59
  217. package/dist/integrations/agent/detect.js +9 -0
  218. package/dist/integrations/agent/index.js +3 -19
  219. package/dist/integrations/agent/model-aliases.js +7 -2
  220. package/dist/integrations/agent/profiles.js +7 -1
  221. package/dist/integrations/agent/prompts.js +75 -9
  222. package/dist/integrations/agent/runner-dispatch.js +59 -0
  223. package/dist/integrations/agent/runner.js +13 -9
  224. package/dist/integrations/agent/spawn.js +69 -67
  225. package/dist/integrations/harnesses/claude/agent-builder.js +1 -1
  226. package/dist/integrations/harnesses/claude/index.js +2 -0
  227. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  228. package/dist/integrations/harnesses/index.js +2 -3
  229. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  230. package/dist/integrations/harnesses/opencode/index.js +2 -0
  231. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  232. package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
  233. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +98 -17
  234. package/dist/integrations/harnesses/types.js +1 -0
  235. package/dist/integrations/session-logs/index.js +16 -0
  236. package/dist/llm/call-ai.js +2 -2
  237. package/dist/llm/client.js +34 -11
  238. package/dist/llm/embedder.js +67 -4
  239. package/dist/llm/embedders/cache.js +3 -1
  240. package/dist/llm/embedders/deterministic.js +66 -0
  241. package/dist/llm/embedders/local.js +73 -3
  242. package/dist/llm/feature-gate.js +16 -15
  243. package/dist/llm/graph-extract.js +67 -44
  244. package/dist/llm/memory-infer-impl.js +138 -0
  245. package/dist/llm/memory-infer.js +1 -127
  246. package/dist/llm/metadata-enhance.js +44 -31
  247. package/dist/llm/structured-call.js +49 -0
  248. package/dist/migrate-storage-node.mjs +8 -0
  249. package/dist/output/context.js +5 -5
  250. package/dist/output/renderers.js +85 -14
  251. package/dist/output/shapes/curate.js +14 -2
  252. package/dist/output/shapes/helpers.js +0 -3
  253. package/dist/output/shapes/passthrough.js +2 -1
  254. package/dist/output/text/helpers.js +29 -1
  255. package/dist/output/text/workflow.js +1 -0
  256. package/dist/registry/providers/skills-sh.js +21 -147
  257. package/dist/registry/providers/static-index.js +15 -157
  258. package/dist/registry/resolve.js +27 -9
  259. package/dist/runtime.js +25 -1
  260. package/dist/scripts/migrate-storage.js +2661 -2369
  261. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +883 -596
  262. package/dist/setup/detect.js +9 -0
  263. package/dist/setup/legacy-config.js +106 -0
  264. package/dist/setup/prompt.js +57 -0
  265. package/dist/setup/providers.js +14 -0
  266. package/dist/setup/registry-stash-loader.js +12 -0
  267. package/dist/setup/semantic-assets.js +124 -0
  268. package/dist/setup/setup.js +52 -1614
  269. package/dist/setup/steps/connection.js +734 -0
  270. package/dist/setup/steps/output.js +31 -0
  271. package/dist/setup/steps/platforms.js +124 -0
  272. package/dist/setup/steps/semantic.js +27 -0
  273. package/dist/setup/steps/sources.js +222 -0
  274. package/dist/setup/steps/stashdir.js +42 -0
  275. package/dist/setup/steps/tasks.js +152 -0
  276. package/dist/sources/include.js +6 -2
  277. package/dist/sources/providers/filesystem.js +0 -1
  278. package/dist/sources/providers/git-install.js +210 -0
  279. package/dist/sources/providers/git-provider.js +234 -0
  280. package/dist/sources/providers/git-stash.js +248 -0
  281. package/dist/sources/providers/git.js +10 -661
  282. package/dist/sources/providers/npm.js +2 -6
  283. package/dist/sources/providers/provider-utils.js +13 -7
  284. package/dist/sources/providers/sync-from-ref.js +9 -1
  285. package/dist/sources/providers/website.js +9 -5
  286. package/dist/sources/website-ingest.js +187 -29
  287. package/dist/sources/wiki-fetchers/registry.js +53 -0
  288. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  289. package/dist/storage/database.js +45 -10
  290. package/dist/storage/managed-db.js +82 -0
  291. package/dist/storage/repositories/canaries-repository.js +107 -0
  292. package/dist/storage/repositories/consolidation-repository.js +38 -0
  293. package/dist/storage/repositories/embeddings-repository.js +72 -0
  294. package/dist/storage/repositories/events-repository.js +187 -0
  295. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  296. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  297. package/dist/storage/repositories/index-db.js +14 -8
  298. package/dist/storage/repositories/proposals-repository.js +220 -0
  299. package/dist/storage/repositories/recombine-repository.js +213 -0
  300. package/dist/storage/repositories/registry-cache.js +93 -0
  301. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  302. package/dist/storage/repositories/task-history-repository.js +93 -0
  303. package/dist/storage/sqlite-pragmas.js +146 -0
  304. package/dist/tasks/backends/cron.js +1 -1
  305. package/dist/tasks/backends/index.js +9 -0
  306. package/dist/tasks/backends/launchd.js +1 -1
  307. package/dist/tasks/backends/schtasks.js +1 -1
  308. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  309. package/dist/tasks/runner.js +15 -13
  310. package/dist/text-import-hook.mjs +0 -0
  311. package/dist/wiki/wiki.js +52 -11
  312. package/dist/workflows/cli.js +1 -0
  313. package/dist/workflows/db.js +3 -4
  314. package/dist/workflows/runtime/runs.js +43 -118
  315. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  316. package/dist/workflows/validate-summary.js +2 -7
  317. package/docs/README.md +69 -18
  318. package/docs/data-and-telemetry.md +5 -4
  319. package/docs/migration/release-notes/0.7.0.md +1 -1
  320. package/docs/migration/release-notes/0.9.0.md +39 -0
  321. package/package.json +10 -10
  322. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  323. package/dist/commands/db-cli.js +0 -23
  324. package/dist/indexer/db/db-backup.js +0 -376
  325. 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 "@clack/prompts";
7
- import { defineCommand } from "citty";
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 = defineCommand({
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
- await runWithJsonErrors(async () => {
185
- const ref = args.ref.trim();
186
- const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
187
- const allowDangerousKeys = allowInsecure;
188
- // URL with --provider → stash source (remote or git provider)
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
- const websiteOptions = buildWebsiteOptions(args);
234
- if (args.type === "wiki") {
235
- const { registerWikiSource } = await import("./source-add.js");
236
- const result = await registerWikiSource({
237
- ref,
238
- name: args.name,
239
- options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
240
- writable: args.writable,
241
- });
242
- appendEvent({
243
- eventType: "add",
244
- metadata: { target: ref, type: "wiki", name: args.name ?? null, writable: args.writable === true },
245
- });
246
- output("add", result);
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 = await akmAdd({
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/validators/proposals.js";
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 = process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
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, options?.dir != null);
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
- if (created) {
79
- copyStashSkeleton(stashDir);
80
- scaffoldStashMeta(stashDir);
81
- }
82
- // Persist stashDir in config.json
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
- if (!existing.stashDir || existing.stashDir !== stashDir) {
86
- saveConfig({ ...existing, stashDir });
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
- try {
91
- const binDir = getBinDir();
92
- const rgResult = ensureRg(binDir);
93
- ripgrep = rgResult;
94
- }
95
- catch {
96
- // Non-fatal: ripgrep is optional, search works without it
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, { requireStashDir: true, force: true });
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: synced.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 { info, warn } from "../../core/warn.js";
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/validators/proposals.js";
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 ${parseAssetRef(failure.ref).type} asset. Return ONLY valid JSON like {"description": "...", "when_to_use": "..."}\n\n${bodyPreview}`,
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
- if (stashDir) {
131
- const proposalResult = createProposal(stashDir, {
132
- ref: failure.ref,
133
- source: "schema-repair",
134
- payload: {
135
- content: newContent,
136
- ...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
137
- },
138
- });
139
- if (isProposalSkipped(proposalResult)) {
140
- info(`[improve] schema-repair proposal skipped for ${failure.ref}: ${proposalResult.message}`);
141
- repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
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() detects DB_VERSION mismatch and rebuilds index.db
310
- * tables (preserving usage_events).
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 normalizedUrl = validateWebsiteInputUrl(ref);
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, { requireStashDir: true });
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 { getHyphenatedBoolean, parseFlagValue } from "../../output/context.js";
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 = getHyphenatedBoolean(args, "skip-checksum");
129
- const skipPostUpgrade = getHyphenatedBoolean(args, "skip-post-upgrade");
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
  },