akm-cli 0.9.31 → 0.10.26101002-alpha

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 (236) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/STABILITY.md +79 -53
  3. package/dist/api-node.mjs +20 -0
  4. package/dist/api.d.ts +23 -0
  5. package/dist/api.js +45 -0
  6. package/dist/assets/hints/cli-hints-full.md +13 -27
  7. package/dist/assets/hints/cli-hints-short.md +1 -1
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/cli/shared.js +31 -48
  11. package/dist/cli/unknown-flags.js +6 -43
  12. package/dist/cli.js +8 -34
  13. package/dist/commands/agent/contribute-cli.js +1 -1
  14. package/dist/commands/completions.js +1 -2
  15. package/dist/commands/config-cli.js +5 -28
  16. package/dist/commands/env/env-cli.js +5 -5
  17. package/dist/commands/env/secret-cli.js +2 -2
  18. package/dist/commands/health/checks.js +41 -16
  19. package/dist/commands/health/plugin-staleness.js +52 -22
  20. package/dist/commands/improve/autonomy-gate.js +5 -3
  21. package/dist/commands/improve/consolidate/pair-pass.js +7 -1
  22. package/dist/commands/improve/consolidate.js +6 -35
  23. package/dist/commands/improve/distill/lesson-update.js +91 -0
  24. package/dist/commands/improve/distill-guards.js +1 -43
  25. package/dist/commands/improve/distill.js +140 -20
  26. package/dist/commands/improve/eligibility.js +14 -1
  27. package/dist/commands/improve/extract-cli.js +1 -1
  28. package/dist/commands/improve/improve-cli.js +87 -102
  29. package/dist/commands/improve/improve-strategies.js +8 -12
  30. package/dist/commands/improve/improve.js +2 -2
  31. package/dist/commands/improve/loop-stages.js +0 -1
  32. package/dist/commands/improve/planner.js +0 -1
  33. package/dist/commands/improve/preparation.js +40 -99
  34. package/dist/commands/improve/proactive-maintenance.js +2 -2
  35. package/dist/commands/improve/reflect-noise.js +3 -55
  36. package/dist/commands/improve/reflect.js +3 -8
  37. package/dist/commands/improve/retrieval-scope.js +2 -2
  38. package/dist/commands/improve/salience.js +2 -9
  39. package/dist/commands/improve/stage.js +14 -9
  40. package/dist/commands/lint/index.js +5 -20
  41. package/dist/commands/observability-cli.js +3 -9
  42. package/dist/commands/proposal/drain.js +39 -6
  43. package/dist/commands/proposal/proposal-cli.js +11 -31
  44. package/dist/commands/proposal/repository.js +3 -3
  45. package/dist/commands/read/knowledge.js +3 -3
  46. package/dist/commands/read/remember-cli.js +0 -13
  47. package/dist/commands/read/search-cli.js +29 -97
  48. package/dist/commands/read/search.js +0 -11
  49. package/dist/commands/read/show.js +0 -19
  50. package/dist/commands/sources/plugin-upgrade.js +164 -101
  51. package/dist/commands/sources/sources-cli.js +1 -15
  52. package/dist/commands/sources/stash-cli.js +4 -12
  53. package/dist/commands/tasks/tasks-cli.js +4 -115
  54. package/dist/commands/workflow/plan.js +11 -33
  55. package/dist/commands/workflow-cli.js +2 -7
  56. package/dist/core/adapter/adapter-ids.js +1 -2
  57. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  58. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  59. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  60. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  61. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  62. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  63. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  64. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  65. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  66. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  67. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  68. package/dist/core/adapter/recognize-match.js +3 -4
  69. package/dist/core/common.js +18 -2
  70. package/dist/core/config/config-schema.js +12 -15
  71. package/dist/core/config/config-types.js +6 -15
  72. package/dist/core/config/config-walker.js +6 -23
  73. package/dist/core/config/config.js +17 -80
  74. package/dist/core/config/engine-semantics.js +7 -3
  75. package/dist/core/config/schema/engines.js +7 -7
  76. package/dist/core/config/schema/improve-processes.js +8 -61
  77. package/dist/core/config/schema/improve.js +2 -21
  78. package/dist/core/config/schema/index-config.js +6 -39
  79. package/dist/core/env-secret-ref.js +2 -45
  80. package/dist/core/errors.js +1 -2
  81. package/dist/core/extra-params.js +2 -86
  82. package/dist/core/improve-result.js +11 -1
  83. package/dist/core/mutation-target.js +2 -2
  84. package/dist/core/recognition-util.js +4 -5
  85. package/dist/core/warn.js +21 -4
  86. package/dist/core/write-source.js +3 -3
  87. package/dist/execution/target-ref.js +1 -2
  88. package/dist/indexer/search/db-search.js +4 -19
  89. package/dist/indexer/search/fts-query.js +0 -19
  90. package/dist/integrations/agent/engine-resolution.js +5 -2
  91. package/dist/integrations/agent/index.js +1 -1
  92. package/dist/integrations/agent/profiles.js +41 -98
  93. package/dist/integrations/agent/request-lowering.js +3 -3
  94. package/dist/integrations/harnesses/aider/index.js +5 -0
  95. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  96. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  97. package/dist/integrations/harnesses/claude/index.js +5 -0
  98. package/dist/integrations/harnesses/codex/index.js +1 -0
  99. package/dist/integrations/harnesses/copilot/index.js +1 -0
  100. package/dist/integrations/harnesses/gemini/index.js +1 -0
  101. package/dist/integrations/harnesses/index.js +18 -39
  102. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  103. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  104. package/dist/integrations/harnesses/opencode/index.js +22 -0
  105. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  106. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  107. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  108. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  109. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  110. package/dist/integrations/harnesses/opencode/version.js +98 -0
  111. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  112. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  113. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  114. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +228 -0
  115. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  116. package/dist/integrations/harnesses/openhands/index.js +1 -0
  117. package/dist/integrations/harnesses/pi/index.js +1 -0
  118. package/dist/integrations/harnesses/shared.js +2 -0
  119. package/dist/integrations/harnesses/types.js +2 -0
  120. package/dist/llm/client.js +41 -11
  121. package/dist/output/cli-hints.js +1 -1
  122. package/dist/output/context.js +10 -19
  123. package/dist/output/format-exempt.js +5 -8
  124. package/dist/output/shapes/helpers.js +1 -22
  125. package/dist/output/shapes/passthrough.js +0 -1
  126. package/dist/output/shapes.js +3 -31
  127. package/dist/output/stdout.js +5 -1
  128. package/dist/output/text/command-format.js +6 -1
  129. package/dist/output/text/workflow-format.js +4 -22
  130. package/dist/scripts/akm-migrate-node.js +13049 -14013
  131. package/dist/scripts/akm-migrate.js +16316 -17276
  132. package/dist/setup/detect.js +10 -4
  133. package/dist/setup/harness-config-import.js +5 -14
  134. package/dist/setup/setup.js +1 -1
  135. package/dist/setup/steps/tasks.js +12 -13
  136. package/dist/storage/repositories/task-history-repository.js +0 -5
  137. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  138. package/dist/tasks/embedded.js +2 -1
  139. package/dist/tasks/run/task-history.js +0 -2
  140. package/dist/tasks/schedule-gates.js +39 -0
  141. package/dist/tasks/scheduler-binding.js +0 -16
  142. package/dist/tasks/scheduler-sync.js +3 -97
  143. package/dist/tasks/source/bounded-document.js +1 -1
  144. package/dist/tasks/source/task-source-v4.js +2 -12
  145. package/dist/tasks/task-run-reserved-flags.js +2 -31
  146. package/dist/workflows/authoring/authoring.js +6 -6
  147. package/dist/workflows/compile.js +8 -65
  148. package/dist/workflows/exec/child-workflow.js +22 -3
  149. package/dist/workflows/exec/native-executor.js +12 -147
  150. package/dist/workflows/exec/run-workflow.js +31 -103
  151. package/dist/workflows/exec/step-work.js +9 -178
  152. package/dist/workflows/exec/unit-dispatch.js +0 -10
  153. package/dist/workflows/freeze/environment.js +3 -44
  154. package/dist/workflows/freeze/freeze.js +8 -41
  155. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  156. package/dist/workflows/freeze/targets/command.js +2 -16
  157. package/dist/workflows/freeze/targets/shell.js +0 -2
  158. package/dist/workflows/freeze/task-bindings.js +2 -28
  159. package/dist/workflows/ir/params.js +12 -2
  160. package/dist/workflows/parser.js +82 -232
  161. package/dist/workflows/plan.js +1 -1
  162. package/dist/workflows/renderer.js +10 -19
  163. package/dist/workflows/runtime/run-plan.js +11 -53
  164. package/dist/workflows/runtime/runs.js +4 -27
  165. package/dist/workflows/source-files.js +12 -16
  166. package/dist/workflows/source-semantics.js +1 -93
  167. package/docs/README.md +1 -1
  168. package/docs/agents/README.md +3 -3
  169. package/docs/agents/agent-install.md +1 -1
  170. package/docs/architecture/adapters.md +17 -31
  171. package/docs/architecture/architecture.md +4 -5
  172. package/docs/architecture/improvement.md +52 -21
  173. package/docs/architecture/internals/improve-workflow.md +15 -8
  174. package/docs/architecture/internals/indexing.md +1 -1
  175. package/docs/architecture/internals/search.md +1 -2
  176. package/docs/architecture/internals/storage-locations.md +5 -5
  177. package/docs/architecture/persisted-data-compat.md +13 -4
  178. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  179. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  180. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  181. package/docs/architecture/testing/manual-testing-checklist.md +20 -29
  182. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  183. package/docs/architecture/testing/testing-workflow.md +1 -1
  184. package/docs/architecture/workflow-engine.md +5 -56
  185. package/docs/guides/author-workflows.md +39 -69
  186. package/docs/guides/capture-knowledge.md +1 -1
  187. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  188. package/docs/guides/discover-and-load.md +4 -4
  189. package/docs/guides/getting-started.md +1 -1
  190. package/docs/guides/improve-the-library.md +7 -7
  191. package/docs/guides/recipes/headless-install.md +2 -2
  192. package/docs/guides/run-workflows.md +1 -1
  193. package/docs/guides/use-with-any-agent.md +49 -4
  194. package/docs/integration/bundling-akm.md +7 -6
  195. package/docs/maintainers/curate-workmap.md +1 -2
  196. package/docs/maintainers/release-checklist.md +30 -1
  197. package/docs/posts/akm-command-tour.md +3 -3
  198. package/docs/product-surface.md +1 -1
  199. package/docs/reference/README.md +1 -0
  200. package/docs/reference/api.md +65 -0
  201. package/docs/reference/asset-types.md +3 -5
  202. package/docs/reference/bundle-types.md +2 -4
  203. package/docs/reference/cli.md +129 -171
  204. package/docs/reference/configuration.md +84 -19
  205. package/docs/reference/data-and-telemetry.md +1 -1
  206. package/docs/reference/env-and-secrets.md +2 -2
  207. package/docs/reference/supported-formats.md +3 -4
  208. package/docs/reference/tasks.md +12 -19
  209. package/docs/reference/website-sources.md +1 -1
  210. package/docs/reference/workflow-schema.md +89 -317
  211. package/docs/reference/workflows.md +20 -48
  212. package/package.json +12 -2
  213. package/schemas/akm-config.json +2 -243
  214. package/schemas/akm-task.json +2 -3
  215. package/schemas/akm-workflow.json +47 -84
  216. package/dist/assets/improve-strategies/catchup.json +0 -41
  217. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  218. package/dist/assets/improve-strategies/quick.json +0 -17
  219. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  220. package/dist/assets/improve-strategies/thorough.json +0 -49
  221. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  222. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  223. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  224. package/dist/cli/retired-commands.js +0 -118
  225. package/dist/commands/tasks/validate.js +0 -176
  226. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  227. package/dist/integrations/harnesses/ids.js +0 -28
  228. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  229. package/dist/tasks/source/task-to-v4.js +0 -863
  230. package/dist/workflows/exec/worktree.js +0 -372
  231. package/dist/workflows/freeze/child-output-references.js +0 -83
  232. package/dist/workflows/freeze/identity.js +0 -22
  233. package/dist/workflows/freeze/targets/script.js +0 -50
  234. package/dist/workflows/freeze/targets/task.js +0 -124
  235. package/dist/workflows/github-yaml.js +0 -506
  236. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -17,15 +17,14 @@
17
17
  * ranking's `lessonStrength` contributor is untouched, only the CLI read
18
18
  * surface for it goes away.
19
19
  *
20
- * The embedded agent-guide surfaces (`akm hints` and `akm help agents`) live
21
- * in src/cli.ts, so their loading code is intentionally outside this module.
20
+ * The embedded agent guide (`akm hints`) lives in src/cli.ts, so its
21
+ * loading code is intentionally outside this module.
22
22
  *
23
23
  * The leaf handler's body is a plain `runWithJsonErrors(...) + output(...)`,
24
24
  * migrated onto `defineJsonCommand`, which emits the same JSON envelope
25
25
  * (stdout/stderr/exit-code) as the inline form.
26
26
  */
27
27
  import { parsePositiveIntFlag } from "../cli/parse-args.js";
28
- import { retiredCommandHint } from "../cli/retired-commands.js";
29
28
  import { defineJsonCommand, output, parseAllFlagValues } from "../cli/shared.js";
30
29
  import { UsageError } from "../core/errors.js";
31
30
  import { akmEventsList } from "./log.js";
@@ -42,12 +41,7 @@ function rejectExtraLogPositionals(positionals) {
42
41
  const extra = Array.isArray(positionals) ? positionals.map(String) : [];
43
42
  if (extra.length === 0)
44
43
  return;
45
- // A retired subcommand spelling gets its replacement hint (same table the
46
- // unknown-command path uses), so `akm log tail` teaches the durable-cursor
47
- // polling pattern instead of a bare "takes no positional arguments".
48
- const retired = extra[0] === undefined ? undefined : retiredCommandHint(["log"], extra[0]);
49
- throw new UsageError(`akm log takes no positional arguments, but got ${extra.map((token) => `"${token}"`).join(" ")}. ` +
50
- '"log list"/"log tail" were removed in 0.9.0 — `akm log` alone is today\'s `log list` surface.', "INVALID_FLAG_VALUE", retired);
44
+ throw new UsageError(`akm log takes no positional arguments, but got ${extra.map((token) => `"${token}"`).join(" ")}.`, "INVALID_FLAG_VALUE");
51
45
  }
52
46
  // ── `akm log` ────────────────────────────────────────────────────────────────
53
47
  // Append-only events stream surface (#204). Reads state.db events with
@@ -9,9 +9,18 @@
9
9
  * gate decision carrying its content hash) is accepted, unless its target
10
10
  * changed since mint — then it is auto-rejected as `stale-target`, never
11
11
  * overwritten;
12
+ * - a proposal planned by the proactive lane (`eligibilitySource: "proactive"`,
13
+ * no feedback behind it) is never accepted, by a gate or by the judgment tier,
14
+ * and a staged accept does not carry it: it is left for a person with reason
15
+ * `proactive-needs-review` (#1147);
12
16
  * - everything else needs a judge: the judgment tier decides it when a runner
13
17
  * is configured, and whatever stays undecided is left for review
14
18
  * (`review_needed` in the improve ledger).
19
+ * The judgment tier may accept only a consolidate promotion (a memory proposed
20
+ * as a new knowledge note). Judged over every kind, it accepted unsafe
21
+ * retirements, applied exact fixes unreviewed and changed notes no one had
22
+ * touched (6 to 14 harmed items a night on two models, #1132); a promotion it
23
+ * judges well. Any other kind it would accept is left for a person.
15
24
  * `maxAccepts` caps promotions across both tiers; `applyMode: "queue"` never
16
25
  * promotes; `excludeIds` keeps this run's fresh proposals out; a proposal that
17
26
  * a generating stage routed to a person is left for that person.
@@ -35,6 +44,10 @@ import { isRetireProposal, PAIR_PASS_GATE, STALE_TARGET_GATE_REASON } from "./pr
35
44
  import { listProposals, listProposalsReadOnly, preflightProposalPromotion, proposalContent, proposalContentHash, readFreshProposalTarget, recordGateDecision, } from "./repository.js";
36
45
  /** The gate label on every decision the drain records. */
37
46
  const DRAIN_GATE = "triage";
47
+ /** A consolidate promotion: a memory proposed as a new knowledge note. The only kind the judgment tier may accept. */
48
+ export function isPromotionProposal(proposal) {
49
+ return proposal.source === "consolidate" && proposal.promotionSource !== undefined;
50
+ }
38
51
  /** An empty diff: no non-blank body line outside the frontmatter. */
39
52
  export function isEmptyDiff(proposal) {
40
53
  const content = proposalContent(proposal);
@@ -246,7 +259,7 @@ async function runJudgmentTier(opts, result, pending, acceptBudget, promoteFn, r
246
259
  const cappedBefore = result.skippedByCap.length;
247
260
  for (const item of result.deferred) {
248
261
  const proposal = byId.get(item.id);
249
- if (!proposal) {
262
+ if (!proposal || item.reason === "proactive-needs-review") {
250
263
  stillDeferred.push(item);
251
264
  continue;
252
265
  }
@@ -288,6 +301,15 @@ async function runJudgmentTier(opts, result, pending, acceptBudget, promoteFn, r
288
301
  }
289
302
  continue;
290
303
  }
304
+ // Only a promotion may be accepted on a judgment (#1132): anything else waits for a person.
305
+ if (!isPromotionProposal(proposal)) {
306
+ deferNotes.set(item.id, {
307
+ reason: "judgment-not-promotion",
308
+ ...(verdict.reason ? { judgeReason: verdict.reason } : {}),
309
+ });
310
+ stillDeferred.push(item);
311
+ continue;
312
+ }
291
313
  // Queue mode never writes the asset: the verdict is staged for a later promote run.
292
314
  if (opts.applyMode !== "promote") {
293
315
  if (opts.dryRun) {
@@ -317,8 +339,7 @@ async function runJudgmentTier(opts, result, pending, acceptBudget, promoteFn, r
317
339
  const outcome = await acceptProposal(opts, proposal, item.id, "judgment-accept", promoteFn, rejectFn, verdict.reason);
318
340
  if (outcome === "promoted") {
319
341
  result.promoted.push(item.id);
320
- if (proposal.source === "consolidate" && proposal.promotionSource !== undefined)
321
- acceptedPromotions.push(proposal);
342
+ acceptedPromotions.push(proposal);
322
343
  acceptBudget -= 1;
323
344
  }
324
345
  else if (outcome === "rejected") {
@@ -339,7 +360,7 @@ async function runJudgmentTier(opts, result, pending, acceptBudget, promoteFn, r
339
360
  }
340
361
  /** The knowledge notes nearest to a consolidate promotion's source memory; none for any other proposal. */
341
362
  function promotionNeighbours(stashDir, proposal, acceptedPromotions) {
342
- if (proposal.source !== "consolidate" || proposal.promotionSource === undefined)
363
+ if (!isPromotionProposal(proposal) || proposal.promotionSource === undefined)
343
364
  return [];
344
365
  try {
345
366
  const parsed = parseRefInput(proposal.promotionSource);
@@ -424,14 +445,24 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
424
445
  if (isEmptyDiff(proposal)) {
425
446
  empties.push(proposal.id);
426
447
  }
448
+ else if (proposal.eligibilitySource === "proactive") {
449
+ // Planned by the proactive lane (no feedback behind it): never accepted
450
+ // here, whatever a judge or an earlier drain staged. A person decides (#1147).
451
+ result.deferred.push({ id: proposal.id, reason: "proactive-needs-review" });
452
+ }
427
453
  else if (decision?.outcome === "staged" && decision.contentHash === proposalContentHash(proposal)) {
454
+ // A judgment accept staged by an earlier drain obeys the same rule: only a promotion.
455
+ if (decision.gate === DRAIN_GATE && !isPromotionProposal(proposal)) {
456
+ result.deferred.push({ id: proposal.id, reason: "needs-judgment" });
457
+ continue;
458
+ }
428
459
  accepts.push({ id: proposal.id, reason: decision.gate === "quality-gate" ? "judge-passed" : "judgment-accept" });
429
460
  }
430
461
  else {
431
462
  result.deferred.push({ id: proposal.id, reason: "needs-judgment" });
432
463
  }
433
464
  }
434
- if (opts.judgment && result.deferred.length > 0) {
465
+ if (opts.judgment && result.deferred.some((d) => d.reason === "needs-judgment")) {
435
466
  // Symbolic credentials, and the runner's model-work tool policy, are checked before any gate, reject or promote.
436
467
  const prepared = resolveExecution({
437
468
  content: "Validate the selected proposal judgment runner before mutation.",
@@ -488,7 +519,9 @@ export async function drainProposals(opts, promoteFn = akmProposalAccept, reject
488
519
  if (!opts.dryRun) {
489
520
  for (const item of result.deferred) {
490
521
  const note = deferNotes.get(item.id);
491
- const reviewReason = note?.reason ?? (opts.judgment ? "judgment-deferred" : "no-judge-configured");
522
+ const reviewReason = item.reason === "proactive-needs-review"
523
+ ? item.reason
524
+ : (note?.reason ?? (opts.judgment ? "judgment-deferred" : "no-judge-configured"));
492
525
  try {
493
526
  recordGateDecision(opts.stashDir, item.id, {
494
527
  outcome: "deferred",
@@ -13,7 +13,6 @@
13
13
  * commands are absorbed here as `extract` and `new` — no top-level
14
14
  * registration remains for either spelling.
15
15
  */
16
- import { getParsedInvocation } from "../../cli/invocation.js";
17
16
  import { parsePositiveIntFlag } from "../../cli/parse-args.js";
18
17
  import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
19
18
  import { resolveStashDir } from "../../core/common.js";
@@ -36,20 +35,6 @@ export function mergeProposalDrainNotices(resolutionNotices, dispatchNotices) {
36
35
  byKey.set(JSON.stringify(notice), notice);
37
36
  return byKey.size > 0 ? Object.freeze([...byKey.values()]) : undefined;
38
37
  }
39
- /**
40
- * `--source` was renamed to `--generator` on `proposal accept`/`proposal
41
- * reject` in 0.9 (WS3/S8 — "Removed in 0.9.0"). citty is non-strict, so the
42
- * retired spelling is silently absorbed rather than rejected — a bulk
43
- * accept/reject invoked with `--source <name>` then has neither `--generator`
44
- * nor a positional id, and falls through to the single-proposal path, which
45
- * throws MISSING_REQUIRED_ARGUMENT instead of running the bulk action the
46
- * caller asked for. Reject it explicitly instead.
47
- */
48
- function rejectRetiredProposalSourceFlag(subcommand) {
49
- if (!getParsedInvocation().hasFlag("--source"))
50
- return;
51
- throw new UsageError(`\`akm proposal ${subcommand} --source\` was renamed to \`--generator\` in 0.9. Use \`--generator <name>\` instead.`, "INVALID_FLAG_VALUE");
52
- }
53
38
  /**
54
39
  * Parse + validate the shared bulk-adjudication filter flags
55
40
  * (`--max-diff-lines`, `--older-than`). Extracted from the two verbatim
@@ -117,7 +102,7 @@ const proposalAcceptCommand = defineJsonCommand({
117
102
  required: false,
118
103
  },
119
104
  queue: { type: "string", description: "Select the proposal queue by source name" },
120
- target: { type: "string", description: "Write destination; must match the proposal's recorded target" },
105
+ bundle: { type: "string", description: "Write destination; must match the proposal's recorded target" },
121
106
  // F-6 / #393: Batch accept by generator, diff size, or age.
122
107
  generator: {
123
108
  type: "string",
@@ -144,7 +129,6 @@ const proposalAcceptCommand = defineJsonCommand({
144
129
  },
145
130
  },
146
131
  async run({ args }) {
147
- rejectRetiredProposalSourceFlag("accept");
148
132
  const generator = args.generator;
149
133
  // F-6 / #393: Bulk-accept when --generator is provided without a positional id.
150
134
  if (generator && !args.id) {
@@ -162,7 +146,7 @@ const proposalAcceptCommand = defineJsonCommand({
162
146
  olderThanMs,
163
147
  dryRun: args["dry-run"],
164
148
  queue: args.queue,
165
- target: args.target,
149
+ target: args.bundle,
166
150
  });
167
151
  output("proposal-accept-batch", {
168
152
  accepted: count,
@@ -182,7 +166,7 @@ const proposalAcceptCommand = defineJsonCommand({
182
166
  const result = await akmProposalAccept({
183
167
  id: args.id,
184
168
  queue: args.queue,
185
- target: args.target,
169
+ target: args.bundle,
186
170
  });
187
171
  output("proposal-accept", result);
188
172
  },
@@ -223,7 +207,6 @@ const proposalRejectCommand = defineJsonCommand({
223
207
  },
224
208
  },
225
209
  async run({ args }) {
226
- rejectRetiredProposalSourceFlag("reject");
227
210
  const generator = args.generator;
228
211
  if (!args.reason || !String(args.reason).trim()) {
229
212
  throw new UsageError("Usage: akm proposal reject <id> --reason '<reason>' OR akm proposal reject --generator <generator> --reason '<reason>'", "MISSING_REQUIRED_ARGUMENT");
@@ -312,10 +295,10 @@ const proposalDiffCommand = defineJsonCommand({
312
295
  required: true,
313
296
  },
314
297
  queue: { type: "string", description: "Select the proposal queue by source name" },
315
- target: { type: "string", description: "Diff destination; must match the proposal's recorded target" },
298
+ bundle: { type: "string", description: "Diff destination; must match the proposal's recorded target" },
316
299
  },
317
300
  run({ args }) {
318
- const result = akmProposalDiff({ id: args.id, queue: args.queue, target: args.target });
301
+ const result = akmProposalDiff({ id: args.id, queue: args.queue, target: args.bundle });
319
302
  output("proposal-diff", result);
320
303
  },
321
304
  });
@@ -342,13 +325,13 @@ const proposalRevertCommand = defineJsonCommand({
342
325
  required: true,
343
326
  },
344
327
  queue: { type: "string", description: "Select the proposal queue by source name" },
345
- target: { type: "string", description: "Write destination; must match the proposal's recorded target" },
328
+ bundle: { type: "string", description: "Write destination; must match the proposal's recorded target" },
346
329
  },
347
330
  async run({ args }) {
348
331
  const result = await akmProposalRevert({
349
332
  id: args.id,
350
333
  queue: args.queue,
351
- target: args.target,
334
+ target: args.bundle,
352
335
  });
353
336
  output("proposal-revert", result);
354
337
  },
@@ -372,7 +355,7 @@ const proposalShowCommand = defineJsonCommand({
372
355
  const proposalDrainCommand = defineJsonCommand({
373
356
  meta: {
374
357
  name: "drain",
375
- description: "Drain the pending proposal backlog: accept what a quality judge passed, reject empty diffs, leave the rest for judgment or review",
358
+ description: "Drain the pending proposal backlog: accept what a quality judge passed, reject empty diffs, leave the rest for judgment or review. The judgment tier accepts only consolidate promotions; it accepted unsafe retirements and unreviewed fixes in testing (#1132), so every other kind waits for a person",
376
359
  },
377
360
  args: {
378
361
  "dry-run": {
@@ -401,7 +384,7 @@ const proposalDrainCommand = defineJsonCommand({
401
384
  },
402
385
  judgment: {
403
386
  type: "boolean",
404
- description: "Enable the judgment tier for this drain (overrides judgment.enabled=false; agent/sdk per config). No-op with a logged triage_deferred summary when no runner is configured.",
387
+ description: "Enable the judgment tier for this drain (overrides judgment.enabled=false; agent/sdk per config). It accepts only consolidate promotions; every other kind it would accept is left for review. No-op with a logged triage_deferred summary when no runner is configured.",
405
388
  default: false,
406
389
  },
407
390
  strategy: {
@@ -409,10 +392,7 @@ const proposalDrainCommand = defineJsonCommand({
409
392
  description: "Read the triage block (applyMode, ceilings, judgment) from this improve strategy; enabled judgment runs the judge.",
410
393
  },
411
394
  },
412
- async run({ args, rawArgs }) {
413
- if (rawArgs.some((arg) => arg === "--profile" || arg.startsWith("--profile="))) {
414
- throw new UsageError("proposal drain: --profile is retired; use --strategy.", "INVALID_FLAG_VALUE");
415
- }
395
+ async run({ args }) {
416
396
  const stashDir = resolveStashDir();
417
397
  const cfg = loadConfig();
418
398
  // Phase 2: read the triage block from the named improve strategy. CLI flags
@@ -423,7 +403,7 @@ const proposalDrainCommand = defineJsonCommand({
423
403
  const applyMode = args.promote === true ? "promote" : (triageConfig?.applyMode ?? "queue");
424
404
  const maxAccepts = parsePositiveIntFlag(args["max-accepts"], "--max-accepts") ??
425
405
  triageConfig?.maxAcceptsPerRun ??
426
- 25;
406
+ 200;
427
407
  const rawOlderThan = parsePositiveIntFlag(args["older-than"], "--older-than");
428
408
  const olderThanMs = rawOlderThan !== undefined ? rawOlderThan * 86_400_000 : undefined;
429
409
  // Promotion in promote mode is destructive (commits to git, no batch revert).
@@ -824,7 +824,7 @@ function resolveRecordedProposalTarget(config, proposalId, binding, explicitTarg
824
824
  const targetBundleId = canonicalBundleIdForTarget(config, target);
825
825
  if (targetBundleId !== binding.source || path.resolve(target.source.path) !== path.resolve(binding.root)) {
826
826
  throw new UsageError(`Proposal ${proposalId} is bound to target "${binding.source}" at ${binding.root}; ` +
827
- `--target "${explicitTarget}" resolves to "${targetBundleId}" at ${target.source.path}.`, "INVALID_FLAG_VALUE");
827
+ `--bundle "${explicitTarget}" resolves to "${targetBundleId}" at ${target.source.path}.`, "INVALID_FLAG_VALUE");
828
828
  }
829
829
  return { ...target, source: { ...target.source, name: targetBundleId } };
830
830
  }
@@ -841,7 +841,7 @@ function resolveProposalWriteTarget(config, proposal, explicitTarget, queueTarge
841
841
  const target = named(resolveBundleWriteTarget(config, identity.bundle));
842
842
  if (explicitTarget !== undefined &&
843
843
  canonicalBundleIdForTarget(config, resolveWriteTarget(config, explicitTarget)) !== identity.bundle) {
844
- throw new UsageError(`Proposal ${proposal.id} ref is bound to bundle "${identity.bundle}", which conflicts with --target "${explicitTarget}".`, "INVALID_FLAG_VALUE");
844
+ throw new UsageError(`Proposal ${proposal.id} ref is bound to bundle "${identity.bundle}", which conflicts with --bundle "${explicitTarget}".`, "INVALID_FLAG_VALUE");
845
845
  }
846
846
  if (queueTarget && canonicalBundleIdForTarget(config, queueTarget) !== identity.bundle) {
847
847
  throw new UsageError(`Proposal ${proposal.id} is bound to a different queue target.`, "INVALID_FLAG_VALUE");
@@ -850,7 +850,7 @@ function resolveProposalWriteTarget(config, proposal, explicitTarget, queueTarge
850
850
  }
851
851
  const target = explicitTarget ? resolveWriteTarget(config, explicitTarget) : queueTarget;
852
852
  if (!target) {
853
- throw new UsageError(`Unbound short proposal ${proposal.id} requires an explicit --target or authenticated --queue context.`, "INVALID_PROPOSAL");
853
+ throw new UsageError(`Unbound short proposal ${proposal.id} requires an explicit --bundle or authenticated --queue context.`, "INVALID_PROPOSAL");
854
854
  }
855
855
  return named(target);
856
856
  }
@@ -491,7 +491,7 @@ export function resolveSupersedesForWrite(rawRefs, target, targetFlag) {
491
491
  : {
492
492
  reason: namedWritableSource
493
493
  ? `resolves outside the write target and the working stash, in writable source "${namedWritableSource}" at ${root}; ` +
494
- `re-run with ${targetFlag ?? "--target"} ${namedWritableSource} to demote it there`
494
+ `re-run with ${targetFlag ?? "--bundle"} ${namedWritableSource} to demote it there`
495
495
  : `resolves outside the write target and the working stash, in a read-only source at ${root}; ` +
496
496
  "demotion only applies to assets in the write target or the working stash",
497
497
  }),
@@ -506,7 +506,7 @@ export function resolveSupersedesForWrite(rawRefs, target, targetFlag) {
506
506
  /**
507
507
  * Write a markdown asset (knowledge or memory) to the resolved write target.
508
508
  *
509
- * Resolves the write target via the v1 precedence chain (`--target` →
509
+ * Resolves the write target via the v1 precedence chain (`--bundle` →
510
510
  * `defaultWriteTarget` → working stash), validates the path is within the
511
511
  * type root, enforces `--force` semantics, and delegates the actual write
512
512
  * to `writeAssetToSource`.
@@ -613,7 +613,7 @@ export async function writeMarkdownAsset(options) {
613
613
  // Write-path indexing: the asset is searchable immediately. Fail-open; reads
614
614
  // no longer trigger reindexes, so keeping the index current is the writer's
615
615
  // job. Demoted files reindex under their own containing root (usually the
616
- // write target itself; the working stash when writing to a --target) so
616
+ // write target itself; the working stash when writing to a --bundle) so
617
617
  // `--belief current` filtering takes effect without waiting for the next
618
618
  // full index. (alpha.4 removed belief weights from ranking; `--belief` is
619
619
  // the only reader of `beliefState` today.)
@@ -1,7 +1,6 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- import { getParsedInvocation } from "../../cli/invocation.js";
5
4
  import { getStringArg } from "../../cli/parse-args.js";
6
5
  import { defineJsonCommand, output, parseAllFlagValues } from "../../cli/shared.js";
7
6
  import { UsageError } from "../../core/errors.js";
@@ -35,17 +34,6 @@ async function fetchSimilarMemories(query, excludeRef, eventSource) {
35
34
  }
36
35
  /** The flag `remember` takes for its destination, as its errors spell it. */
37
36
  const TARGET_FLAG = "--bundle";
38
- /**
39
- * `--target` was renamed to `--bundle` on `remember` in 0.9 (S8). citty is
40
- * non-strict, so the retired spelling is silently absorbed rather than
41
- * rejected — the memory then lands in the default bundle instead of the one
42
- * the caller named, with exit 0 and no error. Reject it explicitly instead.
43
- */
44
- function rejectRetiredTargetFlag() {
45
- if (!getParsedInvocation().hasFlag("--target"))
46
- return;
47
- throw new UsageError("`akm remember --target` was renamed to `--bundle` in 0.9. Use `--bundle <name>` instead.", "INVALID_FLAG_VALUE");
48
- }
49
37
  // ── Command definition ────────────────────────────────────────────────────────
50
38
  export const rememberCommand = defineJsonCommand({
51
39
  meta: {
@@ -140,7 +128,6 @@ export const rememberCommand = defineJsonCommand({
140
128
  },
141
129
  },
142
130
  async run({ args }) {
143
- rejectRetiredTargetFlag();
144
131
  const body = readMemoryContent(args.content);
145
132
  const eventSource = resolveUsageEventSource();
146
133
  // `--name` is a flat name; subdirectory placement is `--path`'s job.
@@ -24,24 +24,6 @@ import { deliverRendered } from "../../output/html-render.js";
24
24
  import { akmCurate, packCuratedHits } from "./curate.js";
25
25
  import { akmSearch, parseBeliefFilterMode, parseScopeFilterFlags, parseSearchSource } from "./search.js";
26
26
  import { akmShowUnified } from "./show.js";
27
- /**
28
- * `--source` was renamed to `--from` on `search`/`curate` in 0.9 (S8). citty
29
- * is non-strict, so the retired spelling is silently absorbed rather than
30
- * rejected — the command then runs against the DEFAULT `--from` value
31
- * (local) instead of the source the caller named, with exit 0 and no error.
32
- * Reject it explicitly instead.
33
- *
34
- * Exported so `akm task list` (`../tasks/tasks-cli.ts`, #951) — a pure
35
- * delegating alias for `akm search --type task` — applies the identical
36
- * guard rather than a second copy of it; a task-list-scoped `--source` must
37
- * fail exactly like `search`'s does, not fall through to citty's silent
38
- * unknown-flag absorption.
39
- */
40
- export function rejectRetiredSourceFlag() {
41
- if (!getParsedInvocation().hasFlag("--source"))
42
- return;
43
- throw new UsageError("`--source` was renamed to `--from` in 0.9. Use `--from local|registry|all` instead.", "INVALID_FLAG_VALUE");
44
- }
45
27
  export const searchCommand = defineJsonCommand({
46
28
  meta: { name: "search", description: "Search the bundle" },
47
29
  args: {
@@ -83,7 +65,6 @@ export const searchCommand = defineJsonCommand({
83
65
  },
84
66
  },
85
67
  async run({ args }) {
86
- rejectRetiredSourceFlag();
87
68
  const query = (args.query ?? "").trim();
88
69
  const type = args.type;
89
70
  const limit = parsePositiveIntFlag(args.limit ?? undefined);
@@ -140,24 +121,9 @@ export const curateCommand = defineJsonCommand({
140
121
  },
141
122
  },
142
123
  async run({ args }) {
143
- rejectRetiredSourceFlag();
144
- if (!args.query || !String(args.query).trim()) {
145
- throw new UsageError('A curate query is required. Usage: akm curate "<task or prompt>" [--type <type>] [--limit <n>]', "MISSING_REQUIRED_ARGUMENT", 'Describe the task you want assets for, e.g. `akm curate "deploy to prod"`.');
146
- }
147
- const type = args.type;
148
- const limitParsed = parsePositiveIntFlag(args.limit ?? undefined);
149
- const limit = limitParsed && limitParsed > 0 ? limitParsed : 4;
150
- const source = parseSearchSource(args.from ?? "local");
151
- const outputMode = getOutputMode();
152
124
  const packBudget = parsePositiveIntFlag(args.pack ?? undefined, "--pack");
153
- const curated = await akmCurate({
154
- query: args.query,
155
- type,
156
- limit,
157
- source,
158
- eventSource: resolveUsageEventSource(),
159
- attributionProjection: outputMode.shape === "agent" ? "agent" : outputMode.detail,
160
- });
125
+ const outputMode = getOutputMode();
126
+ const curated = await runCurate({ query: args.query, type: args.type, limit: args.limit, from: args.from }, outputMode);
161
127
  if (packBudget !== undefined) {
162
128
  const packed = await packCuratedHits(curated, packBudget);
163
129
  deliverRendered(outputMode.format === "text" ? formatCuratePackText(packed) : JSON.stringify(packed.items, null, 2), outputMode.outputPath);
@@ -166,6 +132,27 @@ export const curateCommand = defineJsonCommand({
166
132
  output("curate", curated);
167
133
  },
168
134
  });
135
+ /**
136
+ * Validate the curate arguments and run the curation. The one path both
137
+ * `akm curate` and the in-process `curate()` of `src/api.ts` go through, so
138
+ * argument errors and ranking cannot drift between them. Takes the output
139
+ * mode explicitly (it only picks the attribution projection) instead of
140
+ * reading the process-level singleton.
141
+ */
142
+ export async function runCurate(args, outputMode) {
143
+ if (!args.query || !String(args.query).trim()) {
144
+ throw new UsageError('A curate query is required. Usage: akm curate "<task or prompt>" [--type <type>] [--limit <n>]', "MISSING_REQUIRED_ARGUMENT", 'Describe the task you want assets for, e.g. `akm curate "deploy to prod"`.');
145
+ }
146
+ const limitParsed = parsePositiveIntFlag(args.limit ?? undefined);
147
+ return akmCurate({
148
+ query: args.query,
149
+ type: args.type,
150
+ limit: limitParsed && limitParsed > 0 ? limitParsed : 4,
151
+ source: parseSearchSource(args.from ?? "local"),
152
+ eventSource: resolveUsageEventSource(),
153
+ attributionProjection: outputMode.shape === "agent" ? "agent" : outputMode.detail,
154
+ });
155
+ }
169
156
  /** Human-readable rendering for `akm curate --pack`: concatenated content per hit under a `## <ref>` header. */
170
157
  function formatCuratePackText(packed) {
171
158
  if (packed.items.length === 0) {
@@ -174,62 +161,16 @@ function formatCuratePackText(packed) {
174
161
  return packed.items.map((item) => `## ${item.ref}\n\n${item.content}`).join("\n\n");
175
162
  }
176
163
  /**
177
- * Reject `--scope` (either spelling) on `akm show` (E-3). `--scope` was
178
- * removed in favor of `--filter` (R-047, guardrail 6 — no alias, must keep
179
- * failing loudly, not silently). It is deliberately NOT a declared flag on
180
- * this command, which means citty's default behavior for an undeclared flag
181
- * kicks in — and that default is silent acceptance:
182
- *
183
- * - `--scope=user=nobody` (equals form): citty consumes it as an unknown
184
- * flag's own inline value. It never reaches `args._`, so nothing downstream
185
- * ever notices — the command runs to completion and exits 0, having
186
- * silently ignored the caller's (unsatisfied) scope request. This is the
187
- * dangerous case: the caller believes a read was scoped when it was not,
188
- * and it directly violates guardrail 6's "removed spelling must fail
189
- * loudly, not silently".
190
- * - `--scope user=nobody` (space form): citty treats `--scope` as boolean
191
- * and pushes `user=nobody` into `args._` as a stray positional, which
192
- * incidentally trips `rejectExtraShowPositionals`'s arity check below —
193
- * but with the wrong diagnosis (it blames the unrelated retired
194
- * `toc|section|lines|frontmatter|full` view-mode grammar).
195
- *
196
- * This check runs FIRST, before the positional check, so both spellings are
197
- * caught by one explicit, correctly-worded error — it does NOT make `--scope`
198
- * work, it only makes the failure loud and the diagnosis accurate.
199
- *
200
- * NOTE (general issue, out of scope here): citty silently accepts ANY
201
- * undeclared flag on ANY command (e.g. `akm info --totallybogus` exits 0) —
202
- * this same silent-ignore failure mode applies repo-wide, not just to
203
- * `--scope` on `show`. Fixing that generally is a separate, wide-blast-radius
204
- * owner decision (same family as E-2, `akm list --type skill`); this function
205
- * only closes the `--scope`/`show` instance of it.
206
- */
207
- function rejectRemovedScopeFlag(ref) {
208
- const usedScopeFlag = getParsedInvocation().userArgs.some((token) => token === "--scope" || token.startsWith("--scope="));
209
- if (!usedScopeFlag)
210
- return;
211
- throw new UsageError("akm show has no --scope flag — it was removed in 0.9.0. Use --filter instead: " +
212
- "--filter user=<id> --filter agent=<id> --filter run=<id> --filter channel=<name>.", "INVALID_FLAG_VALUE", `\`akm show ${ref} --filter user=<id>\` narrows resolution to assets whose frontmatter scope matches.`);
213
- }
214
- /**
215
- * Reject any positional after the ref. The
216
- * `akm show <ref> toc|section "H"|lines A B|frontmatter|full` view grammar was
217
- * removed in 0.9.0; its keywords used to be rewritten into hidden flags before
218
- * citty saw argv, so without this guard a stale invocation would silently
219
- * render the whole item instead of the view the caller asked for.
220
- *
221
- * `--scope` is handled separately, and earlier, by {@link rejectRemovedScopeFlag}
222
- * — by the time this runs, a `--scope`-caused stray positional has already
223
- * been intercepted with the correct diagnosis, so this function's generic
224
- * message is reached only by genuine leftover view-grammar tokens.
164
+ * Reject any positional after the ref: `akm show` takes one ref, and a stray
165
+ * token would otherwise silently render the whole item instead of what the
166
+ * caller asked for.
225
167
  */
226
168
  function rejectExtraShowPositionals(positionals, ref) {
227
169
  const extra = (Array.isArray(positionals) ? positionals.map(String) : []).slice(1);
228
170
  if (extra.length === 0)
229
171
  return;
230
172
  throw new UsageError(`akm show takes a single ref, but got ${extra.map((token) => `"${token}"`).join(" ")} after "${ref}". ` +
231
- "The view-mode grammar (toc|section|lines|frontmatter|full) was removed in 0.9.0 — use " +
232
- `\`akm show ${ref}#<heading-slug>\` to read one section, or \`akm show ${ref}\` for the whole item.`, "INVALID_FLAG_VALUE", "An unmatched #fragment lists the available slugs.");
173
+ `Use \`akm show ${ref}#<heading-slug>\` to read one section, or \`akm show ${ref}\` for the whole item.`, "INVALID_FLAG_VALUE", "An unmatched #fragment lists the available slugs.");
233
174
  }
234
175
  export const showCommand = defineJsonCommand({
235
176
  meta: {
@@ -265,10 +206,8 @@ export const showCommand = defineJsonCommand({
265
206
  // direct-read it. (the ref parser would reject the non-type `meta`.)
266
207
  if (!parseMetaRef(args.ref))
267
208
  parseBundleRef(args.ref);
268
- rejectRemovedScopeFlag(args.ref);
269
209
  rejectExtraShowPositionals(args._, args.ref);
270
210
  const invocation = getParsedInvocation();
271
- const cliShape = getOutputMode().shape;
272
211
  // F6/R-021 — `show` deliberately does NOT inherit `output.detail` from
273
212
  // config the way `search`/`curate` do via `getOutputMode().detail`
274
213
  // (which merges an explicit `--detail` flag with the config default,
@@ -282,15 +221,8 @@ export const showCommand = defineJsonCommand({
282
221
  // `--detail full` (explicit or, since it's also the implicit default,
283
222
  // omitted) and any other value fall through to the full response.
284
223
  const explicitDetail = invocation.getFlagValue("--detail");
285
- // `--shape summary` selects the compact metadata projection for show.
286
- // `--detail brief` forces the brief response regardless of shape.
287
- const showDetail = explicitDetail === "brief"
288
- ? "brief"
289
- : explicitDetail === "full"
290
- ? "full"
291
- : cliShape === "summary"
292
- ? "summary"
293
- : undefined;
224
+ // `--detail brief` forces the brief response regardless of projection.
225
+ const showDetail = explicitDetail === "brief" ? "brief" : explicitDetail === "full" ? "full" : undefined;
294
226
  // `--filter` is repeatable — citty only exposes the last value, so read
295
227
  // every occurrence directly from argv (same helper as `akm search`; the two
296
228
  // commands share one spelling for the scope-narrowing axis).
@@ -366,17 +366,6 @@ export function parseSearchSource(source) {
366
366
  return source;
367
367
  if (typeof source === "undefined")
368
368
  return "local";
369
- // 0.9.0 (S8): `--source stash`/`--source both` renamed to `--from
370
- // local`/`--from all`. Reject the retired values explicitly — otherwise
371
- // they fall through to the named-source lookup below and surface as a
372
- // misleading "no source named stash/both" error instead of naming the
373
- // rename.
374
- if (source === "stash") {
375
- throw new UsageError('"stash" was renamed to "local" in 0.9. Use `--from local` instead.', "INVALID_FLAG_VALUE");
376
- }
377
- if (source === "both") {
378
- throw new UsageError('"both" was renamed to "all" in 0.9. Use `--from all` instead.', "INVALID_FLAG_VALUE");
379
- }
380
369
  // Pass through unknown strings — they may be valid named sources.
381
370
  // `akmSearch` will validate against config.sources and throw a UsageError
382
371
  // with a helpful message if the name isn't found.
@@ -67,11 +67,6 @@ export async function akmShowUnified(input) {
67
67
  if (metaRef)
68
68
  return showStashMeta(metaRef);
69
69
  }
70
- const legacyReplacement = legacyColonRefReplacement(ref);
71
- if (legacyReplacement) {
72
- throw new NotFoundError(`The legacy colon ref "${ref}" was removed in 0.9.0. Use the slash form instead: ` +
73
- `akm show ${legacyReplacement}`);
74
- }
75
70
  // Env/secret bodies have no safe fragment surface, and a fragment cannot
76
71
  // widen what the env/secret renderers expose: both always omit the body
77
72
  // (env — key names only; secret — never rendered), fragment or not. Warn
@@ -107,20 +102,6 @@ export async function akmShowUnified(input) {
107
102
  }
108
103
  return result;
109
104
  }
110
- /** Actionable migration guidance for the retired `[bundle//]type:name` spelling. */
111
- function legacyColonRefReplacement(ref) {
112
- const match = /^(?:(?<bundle>[^/#]+)\/\/)?(?<type>[a-z][a-z0-9-]*):(?<name>[^#]+)(?<fragment>#.*)?$/i.exec(ref);
113
- const type = match?.groups?.type?.toLowerCase();
114
- const name = match?.groups?.name;
115
- if (!type || !name)
116
- return undefined;
117
- const stashDir = stashDirFor(type);
118
- if (!stashDir)
119
- return undefined;
120
- const bundle = match?.groups?.bundle;
121
- const fragment = match?.groups?.fragment ?? "";
122
- return `${bundle ? `${bundle}//` : ""}${stashDir}/${name}${fragment}`;
123
- }
124
105
  /**
125
106
  * Resolve a stash `.meta/` doc and return it as a lightweight ShowResponse.
126
107
  *