akm-cli 0.7.4 → 0.8.0-rc.10

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 (300) hide show
  1. package/CHANGELOG.md +224 -1
  2. package/README.md +22 -6
  3. package/SECURITY.md +93 -0
  4. package/dist/cli/config-migrate.js +144 -0
  5. package/dist/cli/config-validate.js +39 -0
  6. package/dist/cli/confirm.js +73 -0
  7. package/dist/cli/parse-args.js +133 -0
  8. package/dist/cli/shared.js +129 -0
  9. package/dist/cli.js +2631 -1440
  10. package/dist/commands/add-cli.js +279 -0
  11. package/dist/commands/agent-dispatch.js +110 -0
  12. package/dist/commands/agent-support.js +68 -0
  13. package/dist/commands/completions.js +3 -0
  14. package/dist/commands/config-cli.js +130 -534
  15. package/dist/commands/consolidate.js +2122 -0
  16. package/dist/commands/curate.js +45 -3
  17. package/dist/commands/db-cli.js +23 -0
  18. package/dist/commands/distill-promotion-policy.js +660 -0
  19. package/dist/commands/distill.js +1081 -73
  20. package/dist/commands/env.js +213 -0
  21. package/dist/commands/eval-cases.js +43 -0
  22. package/dist/commands/events.js +15 -24
  23. package/dist/commands/extract-cli.js +127 -0
  24. package/dist/commands/extract-prompt.js +204 -0
  25. package/dist/commands/extract.js +477 -0
  26. package/dist/commands/feedback-cli.js +331 -0
  27. package/dist/commands/graph.js +477 -0
  28. package/dist/commands/health.js +1302 -0
  29. package/dist/commands/help/help-accept.md +12 -0
  30. package/dist/commands/help/help-improve.md +69 -0
  31. package/dist/commands/help/help-proposals.md +18 -0
  32. package/dist/commands/help/help-propose.md +17 -0
  33. package/dist/commands/help/help-reject.md +11 -0
  34. package/dist/commands/history.js +54 -46
  35. package/dist/commands/improve-auto-accept.js +97 -0
  36. package/dist/commands/improve-cli.js +217 -0
  37. package/dist/commands/improve-profiles.js +166 -0
  38. package/dist/commands/improve-result-file.js +167 -0
  39. package/dist/commands/improve.js +2373 -0
  40. package/dist/commands/info.js +5 -2
  41. package/dist/commands/init.js +50 -2
  42. package/dist/commands/installed-stashes.js +102 -139
  43. package/dist/commands/knowledge.js +136 -0
  44. package/dist/commands/lint/agent-linter.js +49 -0
  45. package/dist/commands/lint/base-linter.js +479 -0
  46. package/dist/commands/lint/command-linter.js +49 -0
  47. package/dist/commands/lint/default-linter.js +16 -0
  48. package/dist/commands/lint/env-key-rules.js +154 -0
  49. package/dist/commands/lint/index.js +196 -0
  50. package/dist/commands/lint/knowledge-linter.js +16 -0
  51. package/dist/commands/lint/markdown-insertion.js +343 -0
  52. package/dist/commands/lint/memory-linter.js +61 -0
  53. package/dist/commands/lint/registry.js +36 -0
  54. package/dist/commands/lint/skill-linter.js +45 -0
  55. package/dist/commands/lint/task-linter.js +50 -0
  56. package/dist/commands/lint/types.js +4 -0
  57. package/dist/commands/lint/workflow-linter.js +56 -0
  58. package/dist/commands/lint.js +4 -0
  59. package/dist/commands/migration-help.js +3 -0
  60. package/dist/commands/proposal.js +67 -12
  61. package/dist/commands/propose.js +120 -45
  62. package/dist/commands/reflect.js +1104 -60
  63. package/dist/commands/registry-cli.js +150 -0
  64. package/dist/commands/registry-search.js +5 -2
  65. package/dist/commands/remember-cli.js +257 -0
  66. package/dist/commands/remember.js +70 -7
  67. package/dist/commands/schema-repair.js +203 -0
  68. package/dist/commands/search.js +115 -14
  69. package/dist/commands/secret.js +173 -0
  70. package/dist/commands/self-update.js +3 -0
  71. package/dist/commands/show.js +158 -60
  72. package/dist/commands/source-add.js +17 -45
  73. package/dist/commands/source-clone.js +3 -0
  74. package/dist/commands/source-manage.js +14 -19
  75. package/dist/commands/tasks.js +437 -0
  76. package/dist/commands/url-checker.js +42 -0
  77. package/dist/core/action-contributors.js +28 -0
  78. package/dist/core/asset-ref.js +17 -2
  79. package/dist/core/asset-registry.js +12 -17
  80. package/dist/core/asset-serialize.js +88 -0
  81. package/dist/core/asset-spec.js +67 -1
  82. package/dist/core/common.js +182 -0
  83. package/dist/core/concurrent.js +25 -0
  84. package/dist/core/config-io.js +347 -0
  85. package/dist/core/config-migration.js +622 -0
  86. package/dist/core/config-schema.js +534 -0
  87. package/dist/core/config-sources.js +108 -0
  88. package/dist/core/config-types.js +4 -0
  89. package/dist/core/config-walker.js +337 -0
  90. package/dist/core/config.js +364 -968
  91. package/dist/core/errors.js +42 -20
  92. package/dist/core/events.js +105 -135
  93. package/dist/core/file-lock.js +104 -0
  94. package/dist/core/frontmatter.js +75 -8
  95. package/dist/core/lesson-lint.js +3 -0
  96. package/dist/core/markdown.js +20 -0
  97. package/dist/core/memory-belief.js +62 -0
  98. package/dist/core/memory-contradiction-detect.js +274 -0
  99. package/dist/core/memory-improve.js +806 -0
  100. package/dist/core/parse.js +158 -0
  101. package/dist/core/paths.js +280 -14
  102. package/dist/core/proposal-quality-validators.js +380 -0
  103. package/dist/core/proposal-validators.js +69 -0
  104. package/dist/core/proposals.js +512 -42
  105. package/dist/core/state-db.js +1068 -0
  106. package/dist/core/text-truncation.js +107 -0
  107. package/dist/core/time.js +54 -0
  108. package/dist/core/tty.js +59 -0
  109. package/dist/core/warn.js +64 -1
  110. package/dist/core/write-source.js +3 -0
  111. package/dist/indexer/db-backup.js +391 -0
  112. package/dist/indexer/db-search.js +198 -489
  113. package/dist/indexer/db.js +990 -108
  114. package/dist/indexer/ensure-index.js +136 -0
  115. package/dist/indexer/file-context.js +3 -0
  116. package/dist/indexer/graph-boost.js +376 -101
  117. package/dist/indexer/graph-db.js +391 -0
  118. package/dist/indexer/graph-dedup.js +95 -0
  119. package/dist/indexer/graph-extraction.js +550 -114
  120. package/dist/indexer/index-context.js +4 -0
  121. package/dist/indexer/indexer.js +547 -309
  122. package/dist/indexer/llm-cache.js +52 -0
  123. package/dist/indexer/manifest.js +3 -0
  124. package/dist/indexer/matchers.js +167 -160
  125. package/dist/indexer/memory-inference.js +152 -74
  126. package/dist/indexer/metadata-contributors.js +29 -0
  127. package/dist/indexer/metadata.js +275 -196
  128. package/dist/indexer/path-resolver.js +92 -0
  129. package/dist/indexer/project-context.js +192 -0
  130. package/dist/indexer/ranking-contributors.js +331 -0
  131. package/dist/indexer/ranking.js +81 -0
  132. package/dist/indexer/search-fields.js +5 -9
  133. package/dist/indexer/search-hit-enrichers.js +111 -0
  134. package/dist/indexer/search-source.js +44 -10
  135. package/dist/indexer/semantic-status.js +6 -17
  136. package/dist/indexer/staleness-detect.js +447 -0
  137. package/dist/indexer/usage-events.js +12 -9
  138. package/dist/indexer/walker.js +28 -0
  139. package/dist/integrations/agent/builders.js +135 -0
  140. package/dist/integrations/agent/config.js +122 -230
  141. package/dist/integrations/agent/detect.js +3 -0
  142. package/dist/integrations/agent/index.js +7 -13
  143. package/dist/integrations/agent/model-aliases.js +55 -0
  144. package/dist/integrations/agent/profiles.js +70 -5
  145. package/dist/integrations/agent/prompts.js +250 -36
  146. package/dist/integrations/agent/runner.js +151 -0
  147. package/dist/integrations/agent/sdk-runner.js +126 -0
  148. package/dist/integrations/agent/spawn.js +183 -35
  149. package/dist/integrations/github.js +3 -0
  150. package/dist/integrations/lockfile.js +32 -69
  151. package/dist/integrations/session-logs/index.js +69 -0
  152. package/dist/integrations/session-logs/inline-refs.js +35 -0
  153. package/dist/integrations/session-logs/pre-filter.js +152 -0
  154. package/dist/integrations/session-logs/providers/claude-code.js +282 -0
  155. package/dist/integrations/session-logs/providers/opencode.js +258 -0
  156. package/dist/integrations/session-logs/types.js +4 -0
  157. package/dist/llm/call-ai.js +62 -0
  158. package/dist/llm/client.js +79 -88
  159. package/dist/llm/embedder.js +20 -29
  160. package/dist/llm/embedders/cache.js +3 -7
  161. package/dist/llm/embedders/local.js +42 -1
  162. package/dist/llm/embedders/remote.js +20 -8
  163. package/dist/llm/embedders/types.js +3 -7
  164. package/dist/llm/feature-gate.js +95 -48
  165. package/dist/llm/graph-extract.js +676 -72
  166. package/dist/llm/index-passes.js +44 -29
  167. package/dist/llm/memory-infer.js +80 -71
  168. package/dist/llm/metadata-enhance.js +42 -29
  169. package/dist/llm/prompts/extract-session.md +80 -0
  170. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  171. package/dist/output/cli-hints-full.md +292 -0
  172. package/dist/output/cli-hints-short.md +66 -0
  173. package/dist/output/cli-hints.js +7 -311
  174. package/dist/output/context.js +60 -8
  175. package/dist/output/renderers.js +306 -258
  176. package/dist/output/shapes/curate.js +56 -0
  177. package/dist/output/shapes/distill.js +10 -0
  178. package/dist/output/shapes/env-list.js +19 -0
  179. package/dist/output/shapes/events.js +11 -0
  180. package/dist/output/shapes/helpers.js +424 -0
  181. package/dist/output/shapes/history.js +7 -0
  182. package/dist/output/shapes/passthrough.js +102 -0
  183. package/dist/output/shapes/proposal-accept.js +7 -0
  184. package/dist/output/shapes/proposal-diff.js +7 -0
  185. package/dist/output/shapes/proposal-list.js +7 -0
  186. package/dist/output/shapes/proposal-producer.js +11 -0
  187. package/dist/output/shapes/proposal-reject.js +7 -0
  188. package/dist/output/shapes/proposal-show.js +7 -0
  189. package/dist/output/shapes/registry-search.js +6 -0
  190. package/dist/output/shapes/registry.js +30 -0
  191. package/dist/output/shapes/search.js +6 -0
  192. package/dist/output/shapes/secret-list.js +19 -0
  193. package/dist/output/shapes/show.js +6 -0
  194. package/dist/output/shapes/vault-list.js +19 -0
  195. package/dist/output/shapes.js +51 -511
  196. package/dist/output/text/add.js +6 -0
  197. package/dist/output/text/clone.js +6 -0
  198. package/dist/output/text/config.js +6 -0
  199. package/dist/output/text/curate.js +6 -0
  200. package/dist/output/text/distill.js +7 -0
  201. package/dist/output/text/enable-disable.js +7 -0
  202. package/dist/output/text/events.js +10 -0
  203. package/dist/output/text/feedback.js +6 -0
  204. package/dist/output/text/helpers.js +1039 -0
  205. package/dist/output/text/history.js +7 -0
  206. package/dist/output/text/import.js +6 -0
  207. package/dist/output/text/index.js +6 -0
  208. package/dist/output/text/info.js +6 -0
  209. package/dist/output/text/init.js +6 -0
  210. package/dist/output/text/list.js +6 -0
  211. package/dist/output/text/proposal-producer.js +8 -0
  212. package/dist/output/text/proposal.js +11 -0
  213. package/dist/output/text/registry-commands.js +11 -0
  214. package/dist/output/text/registry.js +30 -0
  215. package/dist/output/text/remember.js +6 -0
  216. package/dist/output/text/remove.js +6 -0
  217. package/dist/output/text/save.js +6 -0
  218. package/dist/output/text/search.js +6 -0
  219. package/dist/output/text/show.js +6 -0
  220. package/dist/output/text/update.js +6 -0
  221. package/dist/output/text/upgrade.js +6 -0
  222. package/dist/output/text/vault.js +16 -0
  223. package/dist/output/text/wiki.js +15 -0
  224. package/dist/output/text/workflow.js +14 -0
  225. package/dist/output/text.js +44 -1093
  226. package/dist/registry/build-index.js +3 -0
  227. package/dist/registry/create-provider-registry.js +3 -0
  228. package/dist/registry/factory.js +4 -1
  229. package/dist/registry/origin-resolve.js +3 -0
  230. package/dist/registry/providers/index.js +3 -0
  231. package/dist/registry/providers/skills-sh.js +71 -50
  232. package/dist/registry/providers/static-index.js +53 -48
  233. package/dist/registry/providers/types.js +3 -24
  234. package/dist/registry/resolve.js +11 -16
  235. package/dist/registry/types.js +3 -0
  236. package/dist/scripts/migrate-storage.js +17750 -0
  237. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  238. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  239. package/dist/setup/detect.js +3 -0
  240. package/dist/setup/ripgrep-install.js +3 -0
  241. package/dist/setup/ripgrep-resolve.js +3 -0
  242. package/dist/setup/setup.js +775 -37
  243. package/dist/setup/steps.js +3 -15
  244. package/dist/sources/include.js +3 -0
  245. package/dist/sources/provider-factory.js +5 -12
  246. package/dist/sources/provider.js +3 -20
  247. package/dist/sources/providers/filesystem.js +19 -23
  248. package/dist/sources/providers/git.js +179 -20
  249. package/dist/sources/providers/index.js +3 -0
  250. package/dist/sources/providers/install-types.js +3 -13
  251. package/dist/sources/providers/npm.js +3 -4
  252. package/dist/sources/providers/provider-utils.js +3 -0
  253. package/dist/sources/providers/sync-from-ref.js +3 -11
  254. package/dist/sources/providers/tar-utils.js +3 -0
  255. package/dist/sources/providers/website.js +18 -22
  256. package/dist/sources/resolve.js +3 -0
  257. package/dist/sources/types.js +3 -0
  258. package/dist/sources/website-ingest.js +7 -0
  259. package/dist/tasks/backends/cron.js +203 -0
  260. package/dist/tasks/backends/exec-utils.js +28 -0
  261. package/dist/tasks/backends/index.js +24 -0
  262. package/dist/tasks/backends/launchd-template.xml +19 -0
  263. package/dist/tasks/backends/launchd.js +187 -0
  264. package/dist/tasks/backends/schtasks-template.xml +29 -0
  265. package/dist/tasks/backends/schtasks.js +215 -0
  266. package/dist/tasks/parser.js +211 -0
  267. package/dist/tasks/resolveAkmBin.js +87 -0
  268. package/dist/tasks/runner.js +458 -0
  269. package/dist/tasks/schedule.js +227 -0
  270. package/dist/tasks/schema.js +15 -0
  271. package/dist/tasks/validator.js +62 -0
  272. package/dist/version.js +3 -0
  273. package/dist/wiki/index-template.md +12 -0
  274. package/dist/wiki/ingest-workflow-template.md +54 -0
  275. package/dist/wiki/log-template.md +8 -0
  276. package/dist/wiki/schema-template.md +61 -0
  277. package/dist/wiki/wiki-templates.js +15 -0
  278. package/dist/wiki/wiki.js +13 -61
  279. package/dist/workflows/authoring.js +8 -25
  280. package/dist/workflows/cli.js +3 -0
  281. package/dist/workflows/db.js +141 -2
  282. package/dist/workflows/document-cache.js +3 -10
  283. package/dist/workflows/parser.js +3 -0
  284. package/dist/workflows/renderer.js +11 -3
  285. package/dist/workflows/runs.js +91 -89
  286. package/dist/workflows/schema.js +3 -0
  287. package/dist/workflows/scope-key.js +79 -0
  288. package/dist/workflows/validator.js +4 -8
  289. package/dist/workflows/workflow-template.md +24 -0
  290. package/docs/README.md +10 -2
  291. package/docs/data-and-telemetry.md +225 -0
  292. package/docs/migration/release-notes/0.7.0.md +1 -1
  293. package/docs/migration/release-notes/0.7.4.md +1 -1
  294. package/docs/migration/release-notes/0.7.5.md +20 -0
  295. package/docs/migration/release-notes/0.8.0.md +48 -0
  296. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  297. package/package.json +29 -11
  298. package/dist/commands/install-audit.js +0 -381
  299. package/dist/commands/vault.js +0 -333
  300. package/dist/templates/wiki-templates.js +0 -100
@@ -0,0 +1,154 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Vault security lint rules — flags known-dangerous environment variable names.
6
+ *
7
+ * These env var names, when present as vault keys, indicate the vault can be
8
+ * used to hijack process execution via loader injection, path override, or
9
+ * shell/runtime startup hooks. The lint pass emits a warning-level finding;
10
+ * it does NOT block vault load or `akm vault setKey`.
11
+ *
12
+ * Enforcement scope:
13
+ * - `akm lint` reports findings as `dangerous-vault-key` (non-blocking warn).
14
+ * - `akm add` BLOCKS install unless `--allow-insecure` is set (or, on TTY,
15
+ * the user explicitly confirms at the prompt).
16
+ * - `akm vault setKey` does NOT consult this list — by design, the operator
17
+ * owns their own vault and may legitimately store any key locally. The
18
+ * gate exists only for third-party stash installation.
19
+ *
20
+ * False-positive tradeoff:
21
+ * A handful of keys (EDITOR, VISUAL, PAGER) are included because they are
22
+ * invoked by many interactive tools and are a documented RCE vector when
23
+ * sourced from untrusted vaults. They will also flag on benign vaults
24
+ * where the operator legitimately wants to set their editor — accept the
25
+ * FP and bypass with `--allow-insecure` after review.
26
+ */
27
+ import { listKeys } from "../env";
28
+ // ── Dangerous key set ─────────────────────────────────────────────────────────
29
+ export const DANGEROUS_VAULT_KEYS = new Set([
30
+ // Dynamic linker hijacking (Linux glibc ld.so)
31
+ "LD_PRELOAD", // forces shared library injection
32
+ "LD_LIBRARY_PATH", // overrides library search path
33
+ "LD_AUDIT", // loads auditing libs (CVE-class injection vector)
34
+ "LD_DEBUG", // info disclosure / loader behaviour leak
35
+ "LD_BIND_NOW", // eager symbol resolution — can trigger malicious libs
36
+ "LD_PROFILE", // writes profile data — abusable for info disclosure
37
+ "LD_ASSUME_KERNEL", // kernel-version spoofing affecting loader behaviour
38
+ "LD_TRACE_LOADED_OBJECTS", // info disclosure (lists linked libs)
39
+ // Dynamic linker hijacking (macOS dyld)
40
+ "DYLD_INSERT_LIBRARIES", // macOS analogue of LD_PRELOAD
41
+ "DYLD_LIBRARY_PATH", // overrides dyld library search path
42
+ "DYLD_FRAMEWORK_PATH", // overrides framework search path
43
+ // Shell and command resolution
44
+ "PATH", // command lookup hijack
45
+ "BASH_ENV", // sourced on non-interactive bash startup (RCE)
46
+ "ENV", // sourced on POSIX sh startup (RCE)
47
+ "PROMPT_COMMAND", // command run before each bash prompt (RCE)
48
+ "PS1", // prompt — command substitution arbitrary code
49
+ "PS2", // continuation prompt — command substitution
50
+ "IFS", // Internal Field Separator — classic word-splitting attack
51
+ // Shell startup hijack
52
+ "ZDOTDIR", // zsh startup file lookup directory hijack
53
+ // Language runtime hijacking — Node.js
54
+ "NODE_OPTIONS", // injects flags incl. --require module-load RCE
55
+ "NODE_PATH", // module resolution hijack
56
+ "NODE_TLS_REJECT_UNAUTHORIZED", // silently disables TLS verification — MITM enabler
57
+ // Language runtime hijacking — Python
58
+ "PYTHONSTARTUP", // sourced by interactive python (RCE)
59
+ "PYTHONPATH", // module resolution hijack
60
+ "PYTHONINSPECT", // drops into REPL after script — sandbox escape vector
61
+ "PYTHONHOME", // python install prefix hijack
62
+ "PYTHONNOUSERSITE", // disables user-site isolation — sandbox weakening
63
+ // Language runtime hijacking — Ruby
64
+ "RUBYLIB", // ruby load path hijack
65
+ "RUBYOPT", // injects ruby command-line opts
66
+ // Language runtime hijacking — Perl
67
+ "PERL5LIB", // perl @INC hijack
68
+ "PERL5OPT", // injects perl command-line opts
69
+ // Language runtime hijacking — Java
70
+ "JAVA_TOOL_OPTIONS", // honoured by every JVM — flag injection / agent load
71
+ "JDK_JAVA_OPTIONS", // JDK launcher options injection
72
+ "_JAVA_OPTIONS", // legacy JVM options injection
73
+ // Git (RCE via git invocations)
74
+ "GIT_SSH_COMMAND", // replaces ssh with arbitrary command (RCE)
75
+ "GIT_EXTERNAL_DIFF", // runs arbitrary command during diff (RCE)
76
+ "GIT_PAGER", // runs arbitrary command for paging (RCE)
77
+ "GIT_EDITOR", // runs arbitrary command for editor (RCE)
78
+ // Interactive-tool invocation hijack — high FP rate but documented RCE vectors
79
+ "EDITOR", // invoked by git, crontab, sudoedit, etc. (RCE)
80
+ "VISUAL", // EDITOR fallback used by many tools (RCE)
81
+ "PAGER", // invoked by git, man, systemctl, etc. (RCE)
82
+ ]);
83
+ /**
84
+ * Pattern-based dangerous key matchers.
85
+ *
86
+ * Some attack vectors target a family of variable names rather than a single
87
+ * literal — most famously Shellshock (CVE-2014-6271), which exploits keys
88
+ * prefixed with `BASH_FUNC_`. Listing every concrete name is impossible; we
89
+ * test against this pattern set in addition to the literal `Set`.
90
+ */
91
+ export const DANGEROUS_VAULT_KEY_PATTERNS = [
92
+ {
93
+ // CVE-2014-6271 (Shellshock) — bash imports exported functions named
94
+ // `BASH_FUNC_<name>%%` and parses their bodies, enabling RCE.
95
+ pattern: /^BASH_FUNC_/,
96
+ reason: "Shellshock-class bash function injection (CVE-2014-6271)",
97
+ },
98
+ ];
99
+ /**
100
+ * Returns `true` if the given key name is dangerous — either by literal match
101
+ * against `DANGEROUS_VAULT_KEYS` or by matching any entry in
102
+ * `DANGEROUS_VAULT_KEY_PATTERNS`.
103
+ */
104
+ export function isDangerousVaultKey(key) {
105
+ if (DANGEROUS_VAULT_KEYS.has(key))
106
+ return true;
107
+ for (const { pattern } of DANGEROUS_VAULT_KEY_PATTERNS) {
108
+ if (pattern.test(key))
109
+ return true;
110
+ }
111
+ return false;
112
+ }
113
+ // ── Checker ───────────────────────────────────────────────────────────────────
114
+ /**
115
+ * Inspect a vault `.env` file and return a lint finding for every key whose
116
+ * name appears in `DANGEROUS_VAULT_KEYS` or matches a pattern in
117
+ * `DANGEROUS_VAULT_KEY_PATTERNS`.
118
+ *
119
+ * @param vaultPath Absolute path to the `.env` file.
120
+ * @param relPath Stash-relative path used as the `file` field in findings
121
+ * (e.g. `"vaults/prod.env"`).
122
+ * @param vaultRef Human-readable vault ref (e.g. `"vault:prod"`) shown in
123
+ * the finding message.
124
+ */
125
+ export function checkVaultForDangerousKeys(vaultPath, relPath, vaultRef) {
126
+ const { keys } = listKeys(vaultPath);
127
+ const issues = [];
128
+ for (const key of keys) {
129
+ if (!isDangerousVaultKey(key))
130
+ continue;
131
+ issues.push({
132
+ file: relPath,
133
+ issue: "dangerous-vault-key",
134
+ detail: `Env key \`${key}\` can be used to hijack process execution when injected via \`akm env run\`. Ref: ${vaultRef}. Review this file before running \`akm env run\` commands against untrusted stashes.`,
135
+ fixed: false,
136
+ });
137
+ }
138
+ return issues;
139
+ }
140
+ // ── Env-neutral aliases ───────────────────────────────────────────────────────
141
+ //
142
+ // These primitives guard *environment variable injection*, which is shared by
143
+ // the `env` asset type, whole-file `secret` injection, and the `akm add`
144
+ // supply-chain gate. The original `*Vault*` names are retained above for
145
+ // backward compatibility (and stable lint output) through the 0.8.x
146
+ // deprecation window; new call sites should prefer the env-neutral names.
147
+ /** Env-neutral alias of {@link DANGEROUS_VAULT_KEYS}. */
148
+ export const DANGEROUS_ENV_KEYS = DANGEROUS_VAULT_KEYS;
149
+ /** Env-neutral alias of {@link DANGEROUS_VAULT_KEY_PATTERNS}. */
150
+ export const DANGEROUS_ENV_KEY_PATTERNS = DANGEROUS_VAULT_KEY_PATTERNS;
151
+ /** Env-neutral alias of {@link isDangerousVaultKey}. */
152
+ export const isDangerousEnvKey = isDangerousVaultKey;
153
+ /** Env-neutral alias of {@link checkVaultForDangerousKeys}. */
154
+ export const checkEnvForDangerousKeys = checkVaultForDangerousKeys;
@@ -0,0 +1,196 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ import { parse as parseYaml } from "yaml";
7
+ import { resolveStashDir } from "../../core/common";
8
+ import { loadConfig } from "../../core/config";
9
+ import { parseFrontmatter } from "../../core/frontmatter";
10
+ import { resolveSourceEntries } from "../../indexer/search-source";
11
+ import { checkVaultForDangerousKeys } from "./env-key-rules";
12
+ import { getLinterForType } from "./registry";
13
+ // ── Constants ─────────────────────────────────────────────────────────────────
14
+ const STASH_SUBDIRS = [
15
+ "agents",
16
+ "commands",
17
+ "memories",
18
+ "skills",
19
+ "workflows",
20
+ "lessons",
21
+ "tasks",
22
+ "knowledge",
23
+ ];
24
+ // ── Helpers ───────────────────────────────────────────────────────────────────
25
+ function collectYamlFiles(dir) {
26
+ if (!fs.existsSync(dir))
27
+ return [];
28
+ const results = [];
29
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
30
+ const full = path.join(dir, entry.name);
31
+ if (entry.isDirectory()) {
32
+ results.push(...collectYamlFiles(full));
33
+ }
34
+ else if (entry.isFile() && entry.name.endsWith(".yml")) {
35
+ results.push(full);
36
+ }
37
+ }
38
+ return results;
39
+ }
40
+ function collectMarkdownFiles(dir) {
41
+ if (!fs.existsSync(dir))
42
+ return [];
43
+ const results = [];
44
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
45
+ const full = path.join(dir, entry.name);
46
+ if (entry.isDirectory()) {
47
+ results.push(...collectMarkdownFiles(full));
48
+ }
49
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
50
+ results.push(full);
51
+ }
52
+ }
53
+ return results;
54
+ }
55
+ function collectEnvFiles(dir) {
56
+ const results = [];
57
+ try {
58
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
59
+ const full = path.join(dir, entry.name);
60
+ if (entry.isDirectory())
61
+ results.push(...collectEnvFiles(full));
62
+ else if (entry.isFile() && entry.name.endsWith(".env"))
63
+ results.push(full);
64
+ }
65
+ }
66
+ catch {
67
+ /* dir may not exist */
68
+ }
69
+ return results;
70
+ }
71
+ /** True when the issue represents a file deletion that was successfully applied. */
72
+ function isFileDeletion(issue) {
73
+ return issue.fixed === true && (issue.issue === "orphaned-stub" || issue.issue === "placeholder-stub");
74
+ }
75
+ // ── Main ──────────────────────────────────────────────────────────────────────
76
+ export function akmLint(options = {}) {
77
+ const stashRoot = options.dir ?? options.config?.stashDir ?? resolveStashDir();
78
+ // Collect secondary stash roots from configured filesystem sources so that
79
+ // cross-stash refs (e.g. referencing assets in dimm-city/agent-stash) are
80
+ // not falsely flagged as missing-ref.
81
+ const cfg = options.config ?? loadConfig();
82
+ const extraStashRoots = resolveSourceEntries(stashRoot, cfg)
83
+ .map((s) => s.path)
84
+ .filter((p) => p !== stashRoot && fs.existsSync(p));
85
+ const fix = options.fix ?? false;
86
+ const fixed = [];
87
+ const flagged = [];
88
+ for (const subdir of STASH_SUBDIRS) {
89
+ const dirPath = path.join(stashRoot, subdir);
90
+ // Tasks are .yml files; everything else is .md
91
+ const files = subdir === "tasks" ? collectYamlFiles(dirPath) : collectMarkdownFiles(dirPath);
92
+ const linter = getLinterForType(subdir);
93
+ // If the linter supports directory-level checks, run them for each direct
94
+ // subdirectory once before the per-file loop.
95
+ if (typeof linter.lintDirectory === "function" && fs.existsSync(dirPath)) {
96
+ for (const entry of fs.readdirSync(dirPath, { withFileTypes: true })) {
97
+ if (entry.isDirectory()) {
98
+ const subdirIssues = linter.lintDirectory(path.join(dirPath, entry.name), stashRoot);
99
+ for (const issue of subdirIssues) {
100
+ if (issue.fixed) {
101
+ fixed.push(issue);
102
+ }
103
+ else {
104
+ flagged.push(issue);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ for (const filePath of files) {
111
+ const relPath = path.relative(stashRoot, filePath);
112
+ let raw;
113
+ try {
114
+ raw = fs.readFileSync(filePath, "utf8");
115
+ }
116
+ catch {
117
+ continue;
118
+ }
119
+ let data;
120
+ let body;
121
+ let frontmatter;
122
+ if (subdir === "tasks") {
123
+ // Task files are pure YAML — parseFrontmatter returns empty data for them.
124
+ try {
125
+ const parsed = parseYaml(raw);
126
+ data =
127
+ parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
128
+ }
129
+ catch {
130
+ data = {};
131
+ }
132
+ body = raw;
133
+ frontmatter = null;
134
+ }
135
+ else {
136
+ ({ data, content: body, frontmatter } = parseFrontmatter(raw));
137
+ }
138
+ const issues = linter.lint({ filePath, relPath, raw, data, body, frontmatter, fix, stashRoot, extraStashRoots });
139
+ let fileDeleted = false;
140
+ for (const issue of issues) {
141
+ if (isFileDeletion(issue)) {
142
+ fileDeleted = true;
143
+ fixed.push(issue);
144
+ }
145
+ else if (issue.fixed) {
146
+ fixed.push(issue);
147
+ }
148
+ else {
149
+ flagged.push(issue);
150
+ }
151
+ }
152
+ if (fileDeleted)
153
+ continue; // file is gone — skip any remaining checks
154
+ }
155
+ }
156
+ // ── Env dangerous-key pass ─────────────────────────────────────────────────
157
+ // Scan every `.env` file under <stashRoot>/env/ (and the deprecated
158
+ // <stashRoot>/vaults/) across all stash roots for keys that are known to
159
+ // enable process-execution hijacking. Warn-only — findings go into `flagged`,
160
+ // never `fixed`.
161
+ const envRoots = [stashRoot, ...extraStashRoots];
162
+ for (const root of envRoots) {
163
+ for (const [subdir, prefix] of [
164
+ ["env", "env"],
165
+ ["vaults", "vault"],
166
+ ]) {
167
+ const dir = path.join(root, subdir);
168
+ if (!fs.existsSync(dir))
169
+ continue;
170
+ for (const envPath of collectEnvFiles(dir)) {
171
+ const baseName = path.basename(envPath, ".env");
172
+ // "default" (or empty) maps to ".env" → <prefix>:default
173
+ const ref = baseName === "" ? `${prefix}:default` : `${prefix}:${baseName}`;
174
+ const relPath = path.relative(root, envPath);
175
+ for (const issue of checkVaultForDangerousKeys(envPath, relPath, ref)) {
176
+ flagged.push(issue);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ // `ok` reflects whether the lint run completed successfully — NOT whether
182
+ // it found anything. Findings are surfaced via `summary.flagged`; the CLI
183
+ // gates its exit code on `--fail-on-flagged`. Conflating "issues exist"
184
+ // with "command failed" caused two downstream problems:
185
+ // 1. `akm lint --json | jq …` saw stdout-flush races on Bun's non-zero
186
+ // exit, intermittently truncating the JSON the consumer read.
187
+ // 2. `ok` is the shared `{ok, error, code}` failure indicator across the
188
+ // whole CLI; reusing it for "found stuff" forced callers to disambiguate
189
+ // a successful-but-flagged run from a hard error by inspecting fields.
190
+ return {
191
+ ok: true,
192
+ fixed,
193
+ flagged,
194
+ summary: { fixed: fixed.length, flagged: flagged.length },
195
+ };
196
+ }
@@ -0,0 +1,16 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { BaseLinter } from "./base-linter";
5
+ /**
6
+ * Linter for `knowledge/` assets.
7
+ *
8
+ * All checks are inherited from BaseLinter (`unquoted-colon`, `missing-updated`,
9
+ * `stale-path`, `missing-ref`). No type-specific rules needed.
10
+ */
11
+ export class KnowledgeLinter extends BaseLinter {
12
+ types = ["knowledge"];
13
+ lint(ctx) {
14
+ return this.runBaseChecks(ctx);
15
+ }
16
+ }