akm-cli 0.8.0-rc2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/{.github/CHANGELOG.md → CHANGELOG.md} +191 -3
  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 +93 -3
  8. package/dist/cli/shared.js +129 -0
  9. package/dist/cli.js +2141 -1268
  10. package/dist/commands/add-cli.js +279 -0
  11. package/dist/commands/agent-dispatch.js +20 -12
  12. package/dist/commands/agent-support.js +11 -5
  13. package/dist/commands/completions.js +3 -0
  14. package/dist/commands/config-cli.js +129 -517
  15. package/dist/commands/consolidate.js +1533 -144
  16. package/dist/commands/curate.js +44 -3
  17. package/dist/commands/db-cli.js +23 -0
  18. package/dist/commands/distill-promotion-policy.js +5 -3
  19. package/dist/commands/distill.js +906 -100
  20. package/dist/commands/env.js +213 -0
  21. package/dist/commands/eval-cases.js +3 -0
  22. package/dist/commands/events.js +3 -0
  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 +260 -5
  28. package/dist/commands/health.js +977 -51
  29. package/dist/commands/help/help-accept.md +6 -3
  30. package/dist/commands/help/help-improve.md +36 -8
  31. package/dist/commands/help/help-proposals.md +7 -4
  32. package/dist/commands/help/help-reject.md +5 -2
  33. package/dist/commands/history.js +51 -16
  34. package/dist/commands/improve-auto-accept.js +97 -0
  35. package/dist/commands/improve-cli.js +236 -0
  36. package/dist/commands/improve-profiles.js +184 -0
  37. package/dist/commands/improve-result-file.js +167 -0
  38. package/dist/commands/improve.js +1725 -332
  39. package/dist/commands/info.js +3 -0
  40. package/dist/commands/init.js +49 -1
  41. package/dist/commands/installed-stashes.js +6 -23
  42. package/dist/commands/knowledge.js +3 -0
  43. package/dist/commands/lint/agent-linter.js +3 -0
  44. package/dist/commands/lint/base-linter.js +199 -5
  45. package/dist/commands/lint/command-linter.js +3 -0
  46. package/dist/commands/lint/default-linter.js +3 -0
  47. package/dist/commands/lint/env-key-rules.js +154 -0
  48. package/dist/commands/lint/index.js +92 -3
  49. package/dist/commands/lint/knowledge-linter.js +3 -0
  50. package/dist/commands/lint/markdown-insertion.js +343 -0
  51. package/dist/commands/lint/memory-linter.js +3 -0
  52. package/dist/commands/lint/registry.js +3 -0
  53. package/dist/commands/lint/skill-linter.js +3 -0
  54. package/dist/commands/lint/task-linter.js +15 -12
  55. package/dist/commands/lint/types.js +3 -0
  56. package/dist/commands/lint/workflow-linter.js +3 -0
  57. package/dist/commands/lint.js +3 -0
  58. package/dist/commands/migration-help.js +5 -2
  59. package/dist/commands/proposal-drain-policies.js +128 -0
  60. package/dist/commands/proposal-drain.js +477 -0
  61. package/dist/commands/proposal.js +60 -6
  62. package/dist/commands/propose.js +24 -19
  63. package/dist/commands/reflect.js +1004 -94
  64. package/dist/commands/registry-cli.js +150 -0
  65. package/dist/commands/registry-search.js +3 -0
  66. package/dist/commands/remember-cli.js +257 -0
  67. package/dist/commands/remember.js +15 -6
  68. package/dist/commands/schema-repair.js +88 -15
  69. package/dist/commands/search.js +99 -14
  70. package/dist/commands/secret.js +173 -0
  71. package/dist/commands/self-update.js +3 -0
  72. package/dist/commands/show.js +32 -13
  73. package/dist/commands/source-add.js +7 -35
  74. package/dist/commands/source-clone.js +3 -0
  75. package/dist/commands/source-manage.js +3 -0
  76. package/dist/commands/tasks.js +161 -95
  77. package/dist/commands/url-checker.js +3 -0
  78. package/dist/core/action-contributors.js +3 -0
  79. package/dist/core/asset-ref.js +13 -2
  80. package/dist/core/asset-registry.js +9 -2
  81. package/dist/core/asset-serialize.js +88 -0
  82. package/dist/core/asset-spec.js +61 -5
  83. package/dist/core/common.js +93 -5
  84. package/dist/core/concurrent.js +3 -0
  85. package/dist/core/config-io.js +347 -0
  86. package/dist/core/config-migration.js +622 -0
  87. package/dist/core/config-schema.js +558 -0
  88. package/dist/core/config-sources.js +108 -0
  89. package/dist/core/config-types.js +4 -0
  90. package/dist/core/config-walker.js +337 -0
  91. package/dist/core/config.js +366 -1077
  92. package/dist/core/errors.js +42 -20
  93. package/dist/core/events.js +31 -25
  94. package/dist/core/file-lock.js +104 -0
  95. package/dist/core/frontmatter.js +75 -10
  96. package/dist/core/lesson-lint.js +3 -0
  97. package/dist/core/markdown.js +3 -0
  98. package/dist/core/memory-belief.js +62 -0
  99. package/dist/core/memory-contradiction-detect.js +274 -0
  100. package/dist/core/memory-improve.js +142 -14
  101. package/dist/core/parse.js +3 -0
  102. package/dist/core/paths.js +218 -50
  103. package/dist/core/proposal-quality-validators.js +380 -0
  104. package/dist/core/proposal-validators.js +11 -3
  105. package/dist/core/proposals.js +464 -5
  106. package/dist/core/state-db.js +349 -56
  107. package/dist/core/text-truncation.js +107 -0
  108. package/dist/core/time.js +3 -0
  109. package/dist/core/tty.js +59 -0
  110. package/dist/core/warn.js +7 -2
  111. package/dist/core/write-source.js +12 -0
  112. package/dist/indexer/db-backup.js +391 -0
  113. package/dist/indexer/db-search.js +136 -28
  114. package/dist/indexer/db.js +661 -166
  115. package/dist/indexer/ensure-index.js +3 -0
  116. package/dist/indexer/file-context.js +3 -0
  117. package/dist/indexer/graph-boost.js +162 -40
  118. package/dist/indexer/graph-db.js +241 -51
  119. package/dist/indexer/graph-dedup.js +3 -7
  120. package/dist/indexer/graph-extraction.js +242 -149
  121. package/dist/indexer/index-context.js +3 -9
  122. package/dist/indexer/indexer.js +84 -14
  123. package/dist/indexer/llm-cache.js +24 -19
  124. package/dist/indexer/manifest.js +3 -0
  125. package/dist/indexer/matchers.js +184 -11
  126. package/dist/indexer/memory-inference.js +94 -50
  127. package/dist/indexer/metadata-contributors.js +3 -0
  128. package/dist/indexer/metadata.js +110 -50
  129. package/dist/indexer/path-resolver.js +3 -0
  130. package/dist/indexer/project-context.js +192 -0
  131. package/dist/indexer/ranking-contributors.js +134 -7
  132. package/dist/indexer/ranking.js +8 -1
  133. package/dist/indexer/search-fields.js +5 -9
  134. package/dist/indexer/search-hit-enrichers.js +91 -2
  135. package/dist/indexer/search-source.js +20 -1
  136. package/dist/indexer/semantic-status.js +4 -1
  137. package/dist/indexer/staleness-detect.js +447 -0
  138. package/dist/indexer/usage-events.js +12 -9
  139. package/dist/indexer/walker.js +3 -0
  140. package/dist/integrations/agent/builders.js +135 -0
  141. package/dist/integrations/agent/config.js +121 -401
  142. package/dist/integrations/agent/detect.js +3 -0
  143. package/dist/integrations/agent/index.js +6 -14
  144. package/dist/integrations/agent/model-aliases.js +55 -0
  145. package/dist/integrations/agent/profiles.js +3 -0
  146. package/dist/integrations/agent/prompts.js +137 -8
  147. package/dist/integrations/agent/runner.js +208 -0
  148. package/dist/integrations/agent/sdk-runner.js +8 -2
  149. package/dist/integrations/agent/spawn.js +54 -14
  150. package/dist/integrations/github.js +3 -0
  151. package/dist/integrations/lockfile.js +22 -51
  152. package/dist/integrations/session-logs/index.js +4 -0
  153. package/dist/integrations/session-logs/inline-refs.js +35 -0
  154. package/dist/integrations/session-logs/pre-filter.js +152 -0
  155. package/dist/integrations/session-logs/providers/claude-code.js +226 -0
  156. package/dist/integrations/session-logs/providers/opencode.js +231 -25
  157. package/dist/integrations/session-logs/types.js +3 -0
  158. package/dist/llm/call-ai.js +14 -26
  159. package/dist/llm/client.js +16 -2
  160. package/dist/llm/embedder.js +20 -29
  161. package/dist/llm/embedders/cache.js +3 -7
  162. package/dist/llm/embedders/local.js +42 -1
  163. package/dist/llm/embedders/remote.js +20 -8
  164. package/dist/llm/embedders/types.js +3 -7
  165. package/dist/llm/feature-gate.js +92 -56
  166. package/dist/llm/graph-extract.js +401 -30
  167. package/dist/llm/index-passes.js +44 -29
  168. package/dist/llm/memory-infer.js +30 -2
  169. package/dist/llm/metadata-enhance.js +3 -7
  170. package/dist/llm/prompts/extract-session.md +80 -0
  171. package/dist/llm/prompts/graph-extract-user-prompt.md +24 -1
  172. package/dist/output/cli-hints-full.md +60 -32
  173. package/dist/output/cli-hints-short.md +10 -7
  174. package/dist/output/cli-hints.js +5 -2
  175. package/dist/output/context.js +60 -8
  176. package/dist/output/renderers.js +170 -194
  177. package/dist/output/shapes/curate.js +56 -0
  178. package/dist/output/shapes/distill.js +10 -0
  179. package/dist/output/shapes/env-list.js +19 -0
  180. package/dist/output/shapes/events.js +11 -0
  181. package/dist/output/shapes/helpers.js +424 -0
  182. package/dist/output/shapes/history.js +7 -0
  183. package/dist/output/shapes/passthrough.js +105 -0
  184. package/dist/output/shapes/proposal-accept.js +7 -0
  185. package/dist/output/shapes/proposal-diff.js +7 -0
  186. package/dist/output/shapes/proposal-list.js +7 -0
  187. package/dist/output/shapes/proposal-producer.js +11 -0
  188. package/dist/output/shapes/proposal-reject.js +7 -0
  189. package/dist/output/shapes/proposal-show.js +7 -0
  190. package/dist/output/shapes/registry-search.js +6 -0
  191. package/dist/output/shapes/registry.js +30 -0
  192. package/dist/output/shapes/search.js +6 -0
  193. package/dist/output/shapes/secret-list.js +19 -0
  194. package/dist/output/shapes/show.js +6 -0
  195. package/dist/output/shapes/vault-list.js +19 -0
  196. package/dist/output/shapes.js +51 -549
  197. package/dist/output/text/add.js +6 -0
  198. package/dist/output/text/clone.js +6 -0
  199. package/dist/output/text/config.js +6 -0
  200. package/dist/output/text/curate.js +6 -0
  201. package/dist/output/text/distill.js +7 -0
  202. package/dist/output/text/enable-disable.js +7 -0
  203. package/dist/output/text/events.js +10 -0
  204. package/dist/output/text/feedback.js +6 -0
  205. package/dist/output/text/helpers.js +1059 -0
  206. package/dist/output/text/history.js +7 -0
  207. package/dist/output/text/import.js +6 -0
  208. package/dist/output/text/index.js +6 -0
  209. package/dist/output/text/info.js +6 -0
  210. package/dist/output/text/init.js +6 -0
  211. package/dist/output/text/list.js +6 -0
  212. package/dist/output/text/proposal-producer.js +8 -0
  213. package/dist/output/text/proposal.js +12 -0
  214. package/dist/output/text/registry-commands.js +11 -0
  215. package/dist/output/text/registry.js +30 -0
  216. package/dist/output/text/remember.js +6 -0
  217. package/dist/output/text/remove.js +6 -0
  218. package/dist/output/text/save.js +6 -0
  219. package/dist/output/text/search.js +6 -0
  220. package/dist/output/text/show.js +6 -0
  221. package/dist/output/text/update.js +6 -0
  222. package/dist/output/text/upgrade.js +6 -0
  223. package/dist/output/text/vault.js +16 -0
  224. package/dist/output/text/wiki.js +15 -0
  225. package/dist/output/text/workflow.js +14 -0
  226. package/dist/output/text.js +44 -1329
  227. package/dist/registry/build-index.js +3 -0
  228. package/dist/registry/create-provider-registry.js +3 -0
  229. package/dist/registry/factory.js +4 -1
  230. package/dist/registry/origin-resolve.js +3 -0
  231. package/dist/registry/providers/index.js +3 -0
  232. package/dist/registry/providers/skills-sh.js +11 -2
  233. package/dist/registry/providers/static-index.js +10 -1
  234. package/dist/registry/providers/types.js +3 -24
  235. package/dist/registry/resolve.js +11 -16
  236. package/dist/registry/types.js +3 -0
  237. package/dist/scripts/migrate-storage.js +17767 -0
  238. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
  239. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  240. package/dist/setup/detect.js +3 -0
  241. package/dist/setup/ripgrep-install.js +3 -0
  242. package/dist/setup/ripgrep-resolve.js +3 -0
  243. package/dist/setup/setup.js +306 -67
  244. package/dist/setup/steps.js +3 -15
  245. package/dist/sources/include.js +3 -0
  246. package/dist/sources/provider-factory.js +3 -11
  247. package/dist/sources/provider.js +3 -20
  248. package/dist/sources/providers/filesystem.js +19 -23
  249. package/dist/sources/providers/git.js +171 -21
  250. package/dist/sources/providers/index.js +3 -0
  251. package/dist/sources/providers/install-types.js +3 -13
  252. package/dist/sources/providers/npm.js +3 -4
  253. package/dist/sources/providers/provider-utils.js +3 -0
  254. package/dist/sources/providers/sync-from-ref.js +3 -11
  255. package/dist/sources/providers/tar-utils.js +3 -0
  256. package/dist/sources/providers/website.js +18 -22
  257. package/dist/sources/resolve.js +3 -0
  258. package/dist/sources/types.js +3 -0
  259. package/dist/sources/website-ingest.js +3 -0
  260. package/dist/tasks/backends/cron.js +3 -0
  261. package/dist/tasks/backends/exec-utils.js +3 -0
  262. package/dist/tasks/backends/index.js +3 -11
  263. package/dist/tasks/backends/launchd.js +3 -0
  264. package/dist/tasks/backends/schtasks.js +3 -0
  265. package/dist/tasks/parser.js +51 -38
  266. package/dist/tasks/resolveAkmBin.js +3 -0
  267. package/dist/tasks/runner.js +35 -9
  268. package/dist/tasks/schedule.js +20 -1
  269. package/dist/tasks/schema.js +5 -3
  270. package/dist/tasks/validator.js +6 -3
  271. package/dist/version.js +3 -0
  272. package/dist/wiki/wiki-templates.js +3 -0
  273. package/dist/wiki/wiki.js +3 -0
  274. package/dist/workflows/authoring.js +3 -0
  275. package/dist/workflows/cli.js +3 -0
  276. package/dist/workflows/db.js +140 -10
  277. package/dist/workflows/document-cache.js +3 -10
  278. package/dist/workflows/parser.js +3 -0
  279. package/dist/workflows/renderer.js +3 -0
  280. package/dist/workflows/runs.js +18 -1
  281. package/dist/workflows/schema.js +3 -0
  282. package/dist/workflows/scope-key.js +3 -0
  283. package/dist/workflows/validator.js +5 -9
  284. package/docs/README.md +7 -2
  285. package/docs/data-and-telemetry.md +225 -0
  286. package/docs/migration/release-notes/0.7.5.md +2 -2
  287. package/docs/migration/release-notes/0.8.0.md +57 -5
  288. package/docs/migration/v0.7-to-v0.8.md +1378 -0
  289. package/package.json +28 -11
  290. package/.github/LICENSE +0 -374
  291. package/dist/commands/install-audit.js +0 -385
  292. package/dist/commands/vault.js +0 -310
  293. package/dist/indexer/match-contributors.js +0 -141
  294. package/dist/integrations/agent/pipeline.js +0 -39
  295. package/dist/integrations/agent/runners.js +0 -31
@@ -1,3 +1,6 @@
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/.
1
4
  /**
2
5
  * Shared argument-parsing utilities for the AKM CLI entry point.
3
6
  *
@@ -25,7 +28,7 @@ export function hasSubcommand(args, validSet) {
25
28
  /**
26
29
  * Parse a `--limit`-style flag value into a positive integer.
27
30
  *
28
- * Returns `undefined` when `raw` is `undefined` (flag not supplied).
31
+ * Returns `undefined` when `raw` is `undefined` or empty (flag not supplied).
29
32
  * Throws `UsageError` when the raw value is present but not a valid positive
30
33
  * integer so the caller gets a structured, machine-readable error response.
31
34
  *
@@ -35,9 +38,96 @@ export function hasSubcommand(args, validSet) {
35
38
  export function parsePositiveIntFlag(raw, flagName = "--limit") {
36
39
  if (raw === undefined)
37
40
  return undefined;
38
- const parsed = parseInt(raw, 10);
41
+ const trimmed = raw.trim();
42
+ if (!trimmed)
43
+ return undefined;
44
+ const parsed = parseInt(trimmed, 10);
39
45
  if (Number.isNaN(parsed) || parsed <= 0) {
40
- throw new UsageError(`Invalid ${flagName} value: "${raw}". Must be a positive integer.`);
46
+ throw new UsageError(`Invalid ${flagName} value: "${raw}". Must be a positive integer.`, "INVALID_FLAG_VALUE");
41
47
  }
42
48
  return parsed;
43
49
  }
50
+ /**
51
+ * Parse a non-negative integer flag value (0 is allowed, unlike `parsePositiveIntFlag`).
52
+ *
53
+ * Returns `undefined` when `raw` is `undefined` or empty (flag not supplied).
54
+ * Throws `UsageError` when the raw value is present but not a valid non-negative
55
+ * integer (e.g. contains decimals, letters, or is negative).
56
+ *
57
+ * @param raw The raw string value (may be undefined).
58
+ * @param flagName The flag name to include in the error message.
59
+ */
60
+ export function parseNonNegativeIntFlag(raw, flagName) {
61
+ if (raw === undefined)
62
+ return undefined;
63
+ const trimmed = raw.trim();
64
+ if (!trimmed)
65
+ return undefined;
66
+ if (!/^\d+$/.test(trimmed)) {
67
+ throw new UsageError(`Invalid ${flagName} value: "${raw}". Must be a non-negative integer.`, "INVALID_FLAG_VALUE");
68
+ }
69
+ return parseInt(trimmed, 10);
70
+ }
71
+ // ── Auto-accept flag parsing ─────────────────────────────────────────────────
72
+ /**
73
+ * Parse the value of `akm improve --auto-accept` into a confidence threshold.
74
+ *
75
+ * Semantics (see docs/migration/v0.7-to-v0.8.md):
76
+ * - `undefined` (flag absent) → `undefined` (default-OFF; pre-prod flip)
77
+ * - `""` (bare `--auto-accept`, no value) → `undefined` (treated as flag absent)
78
+ * - `"false"` (case-insensitive) → `undefined` (explicit disable)
79
+ * - `"safe"` (case-insensitive) → `90` (permanent back-compat alias)
80
+ * - integer string `"0".."100"` → that integer
81
+ * - anything else → throws `UsageError("INVALID_FLAG_VALUE")`
82
+ *
83
+ * Citty's `type: "string"` resolves bare flags to `""` and an absent flag to
84
+ * `undefined`. Both forms now disable auto-accept; users must pass an explicit
85
+ * threshold (`--auto-accept=N` or `--auto-accept=safe`) to opt in. This is a
86
+ * deliberate flip from the earlier 0.8.0-RC behaviour, which defaulted to ON
87
+ * at threshold 90 and surprised users who didn't expect Phase B operations to
88
+ * apply without confirmation.
89
+ *
90
+ * Until proposals expose per-operation confidence scores, any non-`undefined`
91
+ * threshold causes the consolidate path to auto-accept the whole batch
92
+ * (legacy "safe" behaviour). The threshold value is preserved for the eventual
93
+ * per-operation comparison; see the TODO in `consolidate.ts`.
94
+ */
95
+ export function parseAutoAcceptFlag(raw) {
96
+ if (raw === undefined)
97
+ return undefined;
98
+ const trimmed = raw.trim();
99
+ if (trimmed === "")
100
+ return undefined;
101
+ const lower = trimmed.toLowerCase();
102
+ if (lower === "false")
103
+ return undefined;
104
+ if (lower === "safe")
105
+ return 90;
106
+ if (!/^\d+$/.test(trimmed)) {
107
+ throw new UsageError(`Invalid --auto-accept value: "${raw}". Must be an integer 0-100, 'safe', or 'false'.`, "INVALID_FLAG_VALUE");
108
+ }
109
+ const parsed = parseInt(trimmed, 10);
110
+ if (parsed < 0 || parsed > 100) {
111
+ throw new UsageError(`Invalid --auto-accept value: "${raw}". Must be an integer 0-100, 'safe', or 'false'.`, "INVALID_FLAG_VALUE");
112
+ }
113
+ return parsed;
114
+ }
115
+ // ── String flag parsing ──────────────────────────────────────────────────────
116
+ /**
117
+ * Extract a string value from a parsed citty argument object by key.
118
+ *
119
+ * Returns the trimmed string when present and non-empty, or `undefined`
120
+ * otherwise. Eliminates the repeated
121
+ * `typeof args.X === "string" && args.X.trim() ? args.X.trim() : undefined`
122
+ * pattern throughout the CLI command handlers.
123
+ *
124
+ * @param args The citty argument object (typed as unknown for flexibility).
125
+ * @param key The argument key to look up.
126
+ */
127
+ export function getStringArg(args, key) {
128
+ const val = args[key];
129
+ if (typeof val !== "string")
130
+ return undefined;
131
+ const trimmed = val.trim();
132
+ return trimmed || undefined;
133
+ }
@@ -0,0 +1,129 @@
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
+ * Shared CLI utilities extracted from `src/cli.ts` so that individual
6
+ * command modules can import them without a circular dependency.
7
+ *
8
+ * Exported: output, runWithJsonErrors, parseAllFlagValues, emitJsonError
9
+ */
10
+ import { stringify as yamlStringify } from "yaml";
11
+ import { ConfigError, NotFoundError, UsageError } from "../core/errors";
12
+ import { getOutputMode } from "../output/context";
13
+ import { shapeForCommand } from "../output/shapes";
14
+ import { formatPlain, outputJsonl } from "../output/text";
15
+ // ── Exit codes ───────────────────────────────────────────────────────────────
16
+ /**
17
+ * Canonical process exit-code table for the akm CLI. Single source of truth —
18
+ * referenced by `classifyExitCode` here and re-imported by `src/cli.ts` so the
19
+ * health-warn / general-failure paths stay in sync.
20
+ *
21
+ * 0 success
22
+ * 1 general / not-found
23
+ * 2 usage error
24
+ * 4 health warn (health command only)
25
+ * 78 config error
26
+ */
27
+ export const EXIT_CODES = {
28
+ SUCCESS: 0,
29
+ GENERAL: 1,
30
+ USAGE: 2,
31
+ HEALTH_WARN: 4,
32
+ CONFIG: 78,
33
+ };
34
+ // ── Helpers ──────────────────────────────────────────────────────────────────
35
+ function classifyExitCode(error) {
36
+ if (error instanceof UsageError)
37
+ return EXIT_CODES.USAGE;
38
+ if (error instanceof ConfigError)
39
+ return EXIT_CODES.CONFIG;
40
+ if (error instanceof NotFoundError)
41
+ return EXIT_CODES.GENERAL;
42
+ return EXIT_CODES.GENERAL;
43
+ }
44
+ function extractHint(error) {
45
+ if (error instanceof Error && "hint" in error && typeof error.hint === "function") {
46
+ return error.hint();
47
+ }
48
+ return undefined;
49
+ }
50
+ /**
51
+ * Serialize an error to the standard JSON envelope and exit.
52
+ * Used in both the startup try/catch and `runWithJsonErrors`.
53
+ */
54
+ export function emitJsonError(error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ const hint = extractHint(error);
57
+ const exitCode = classifyExitCode(error);
58
+ const code = error instanceof UsageError || error instanceof ConfigError || error instanceof NotFoundError
59
+ ? error.code
60
+ : undefined;
61
+ console.error(JSON.stringify({ ok: false, error: message, ...(code ? { code } : {}), hint }, null, 2));
62
+ process.exit(exitCode);
63
+ }
64
+ /**
65
+ * Run an async function and route any thrown error through the standard JSON
66
+ * error envelope so users never see a raw stack trace.
67
+ */
68
+ export async function runWithJsonErrors(fn) {
69
+ try {
70
+ await fn();
71
+ }
72
+ catch (error) {
73
+ emitJsonError(error);
74
+ }
75
+ }
76
+ /**
77
+ * Render a command result according to the active output mode (json/jsonl/yaml/text).
78
+ */
79
+ export function output(command, result) {
80
+ const mode = getOutputMode();
81
+ const shaped = shapeForCommand(command, result, mode.detail, mode.shape);
82
+ if (mode.format === "jsonl") {
83
+ outputJsonl(command, shaped);
84
+ return;
85
+ }
86
+ switch (mode.format) {
87
+ case "json":
88
+ console.log(JSON.stringify(shaped, null, 2));
89
+ return;
90
+ case "yaml":
91
+ console.log(yamlStringify(shaped));
92
+ return;
93
+ case "text": {
94
+ const plain = formatPlain(command, shaped, mode.detail);
95
+ console.log(plain ?? JSON.stringify(shaped, null, 2));
96
+ return;
97
+ }
98
+ case "md":
99
+ // `--format md` is currently only consumed by `akm health` for the
100
+ // per-run / window-compare table renderings. Commands that don't
101
+ // implement an md renderer fall back to the JSON envelope so
102
+ // pipelines never get an empty stdout.
103
+ console.log(JSON.stringify(shaped, null, 2));
104
+ return;
105
+ }
106
+ }
107
+ /**
108
+ * Collect all occurrences of a repeatable flag from process.argv.
109
+ * Citty's StringArgDef only exposes the last value when a flag is repeated,
110
+ * so for repeatable CLI args (like `--tag foo --tag bar`) we read argv directly.
111
+ * Supports both `--flag value` and `--flag=value` forms.
112
+ */
113
+ export function parseAllFlagValues(flag) {
114
+ const values = [];
115
+ for (let i = 0; i < process.argv.length; i++) {
116
+ const arg = process.argv[i];
117
+ if (arg === flag && i + 1 < process.argv.length) {
118
+ values.push(process.argv[i + 1]);
119
+ // BUG-M4: skip the value index so `--tag --tag` (literal `--tag`
120
+ // value) does not double-count the second `--tag` as a separate
121
+ // flag occurrence.
122
+ i++;
123
+ }
124
+ else if (arg.startsWith(`${flag}=`)) {
125
+ values.push(arg.slice(flag.length + 1));
126
+ }
127
+ }
128
+ return values;
129
+ }