akm-cli 0.7.5 → 0.8.0-rc.6

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/{.github/CHANGELOG.md → CHANGELOG.md} +113 -2
  2. package/README.md +20 -4
  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.js +1995 -551
  9. package/dist/commands/agent-dispatch.js +110 -0
  10. package/dist/commands/agent-support.js +68 -0
  11. package/dist/commands/completions.js +3 -0
  12. package/dist/commands/config-cli.js +130 -534
  13. package/dist/commands/consolidate.js +1531 -0
  14. package/dist/commands/curate.js +44 -3
  15. package/dist/commands/db-cli.js +23 -0
  16. package/dist/commands/distill-promotion-policy.js +660 -0
  17. package/dist/commands/distill.js +990 -75
  18. package/dist/commands/eval-cases.js +43 -0
  19. package/dist/commands/events.js +5 -23
  20. package/dist/commands/graph.js +477 -0
  21. package/dist/commands/health.js +400 -0
  22. package/dist/commands/help/help-accept.md +9 -0
  23. package/dist/commands/help/help-improve.md +77 -0
  24. package/dist/commands/help/help-proposals.md +15 -0
  25. package/dist/commands/help/help-propose.md +17 -0
  26. package/dist/commands/help/help-reject.md +8 -0
  27. package/dist/commands/history.js +54 -46
  28. package/dist/commands/improve-profiles.js +146 -0
  29. package/dist/commands/improve-result-file.js +103 -0
  30. package/dist/commands/improve.js +2175 -0
  31. package/dist/commands/info.js +5 -2
  32. package/dist/commands/init.js +50 -2
  33. package/dist/commands/installed-stashes.js +102 -139
  34. package/dist/commands/knowledge.js +136 -0
  35. package/dist/commands/lint/agent-linter.js +49 -0
  36. package/dist/commands/lint/base-linter.js +479 -0
  37. package/dist/commands/lint/command-linter.js +49 -0
  38. package/dist/commands/lint/default-linter.js +16 -0
  39. package/dist/commands/lint/index.js +183 -0
  40. package/dist/commands/lint/knowledge-linter.js +16 -0
  41. package/dist/commands/lint/markdown-insertion.js +343 -0
  42. package/dist/commands/lint/memory-linter.js +61 -0
  43. package/dist/commands/lint/registry.js +36 -0
  44. package/dist/commands/lint/skill-linter.js +45 -0
  45. package/dist/commands/lint/task-linter.js +50 -0
  46. package/dist/commands/lint/types.js +4 -0
  47. package/dist/commands/lint/vault-key-rules.js +139 -0
  48. package/dist/commands/lint/workflow-linter.js +56 -0
  49. package/dist/commands/lint.js +4 -0
  50. package/dist/commands/migration-help.js +5 -2
  51. package/dist/commands/proposal.js +66 -12
  52. package/dist/commands/propose.js +86 -31
  53. package/dist/commands/reflect.js +1119 -73
  54. package/dist/commands/registry-search.js +5 -2
  55. package/dist/commands/remember.js +69 -6
  56. package/dist/commands/schema-repair.js +203 -0
  57. package/dist/commands/search.js +115 -14
  58. package/dist/commands/self-update.js +3 -0
  59. package/dist/commands/show.js +144 -25
  60. package/dist/commands/source-add.js +17 -45
  61. package/dist/commands/source-clone.js +3 -0
  62. package/dist/commands/source-manage.js +14 -19
  63. package/dist/commands/tasks.js +438 -0
  64. package/dist/commands/url-checker.js +42 -0
  65. package/dist/commands/vault.js +130 -77
  66. package/dist/core/action-contributors.js +28 -0
  67. package/dist/core/asset-ref.js +7 -0
  68. package/dist/core/asset-registry.js +7 -16
  69. package/dist/core/asset-serialize.js +88 -0
  70. package/dist/core/asset-spec.js +22 -0
  71. package/dist/core/common.js +157 -0
  72. package/dist/core/concurrent.js +25 -0
  73. package/dist/core/config-io.js +347 -0
  74. package/dist/core/config-migration.js +625 -0
  75. package/dist/core/config-schema.js +501 -0
  76. package/dist/core/config-sources.js +108 -0
  77. package/dist/core/config-types.js +4 -0
  78. package/dist/core/config-walker.js +337 -0
  79. package/dist/core/config.js +327 -987
  80. package/dist/core/errors.js +40 -19
  81. package/dist/core/events.js +91 -138
  82. package/dist/core/file-lock.js +104 -0
  83. package/dist/core/frontmatter.js +3 -6
  84. package/dist/core/lesson-lint.js +3 -0
  85. package/dist/core/markdown.js +20 -0
  86. package/dist/core/memory-belief.js +62 -0
  87. package/dist/core/memory-contradiction-detect.js +274 -0
  88. package/dist/core/memory-improve.js +806 -0
  89. package/dist/core/parse.js +158 -0
  90. package/dist/core/paths.js +326 -14
  91. package/dist/core/proposal-quality-validators.js +364 -0
  92. package/dist/core/proposal-validators.js +69 -0
  93. package/dist/core/proposals.js +498 -42
  94. package/dist/core/state-db.js +927 -0
  95. package/dist/core/text-truncation.js +107 -0
  96. package/dist/core/time.js +54 -0
  97. package/dist/core/warn.js +62 -1
  98. package/dist/core/write-source.js +3 -0
  99. package/dist/indexer/db-backup.js +391 -0
  100. package/dist/indexer/db-search.js +152 -253
  101. package/dist/indexer/db.js +933 -103
  102. package/dist/indexer/ensure-index.js +64 -0
  103. package/dist/indexer/file-context.js +3 -0
  104. package/dist/indexer/graph-boost.js +376 -101
  105. package/dist/indexer/graph-db.js +391 -0
  106. package/dist/indexer/graph-dedup.js +95 -0
  107. package/dist/indexer/graph-extraction.js +550 -124
  108. package/dist/indexer/index-context.js +4 -0
  109. package/dist/indexer/indexer.js +506 -291
  110. package/dist/indexer/llm-cache.js +47 -0
  111. package/dist/indexer/manifest.js +3 -0
  112. package/dist/indexer/matchers.js +148 -160
  113. package/dist/indexer/memory-inference.js +99 -74
  114. package/dist/indexer/metadata-contributors.js +29 -0
  115. package/dist/indexer/metadata.js +255 -196
  116. package/dist/indexer/path-resolver.js +92 -0
  117. package/dist/indexer/project-context.js +192 -0
  118. package/dist/indexer/ranking-contributors.js +331 -0
  119. package/dist/indexer/ranking.js +81 -0
  120. package/dist/indexer/search-fields.js +5 -9
  121. package/dist/indexer/search-hit-enrichers.js +111 -0
  122. package/dist/indexer/search-source.js +44 -10
  123. package/dist/indexer/semantic-status.js +5 -16
  124. package/dist/indexer/staleness-detect.js +447 -0
  125. package/dist/indexer/usage-events.js +12 -9
  126. package/dist/indexer/walker.js +28 -0
  127. package/dist/integrations/agent/builders.js +135 -0
  128. package/dist/integrations/agent/config.js +122 -230
  129. package/dist/integrations/agent/detect.js +3 -0
  130. package/dist/integrations/agent/index.js +7 -13
  131. package/dist/integrations/agent/model-aliases.js +55 -0
  132. package/dist/integrations/agent/profiles.js +70 -5
  133. package/dist/integrations/agent/prompts.js +150 -74
  134. package/dist/integrations/agent/runner.js +151 -0
  135. package/dist/integrations/agent/sdk-runner.js +126 -0
  136. package/dist/integrations/agent/spawn.js +118 -23
  137. package/dist/integrations/github.js +3 -0
  138. package/dist/integrations/lockfile.js +32 -69
  139. package/dist/integrations/session-logs/index.js +68 -0
  140. package/dist/integrations/session-logs/providers/claude-code.js +59 -0
  141. package/dist/integrations/session-logs/providers/opencode.js +55 -0
  142. package/dist/integrations/session-logs/types.js +4 -0
  143. package/dist/llm/call-ai.js +62 -0
  144. package/dist/llm/client.js +72 -124
  145. package/dist/llm/embedder.js +3 -19
  146. package/dist/llm/embedders/cache.js +3 -7
  147. package/dist/llm/embedders/local.js +3 -0
  148. package/dist/llm/embedders/remote.js +20 -8
  149. package/dist/llm/embedders/types.js +3 -7
  150. package/dist/llm/feature-gate.js +89 -48
  151. package/dist/llm/graph-extract.js +676 -70
  152. package/dist/llm/index-passes.js +9 -23
  153. package/dist/llm/memory-infer.js +52 -71
  154. package/dist/llm/metadata-enhance.js +42 -29
  155. package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
  156. package/dist/output/cli-hints-full.md +281 -0
  157. package/dist/output/cli-hints-short.md +65 -0
  158. package/dist/output/cli-hints.js +5 -318
  159. package/dist/output/context.js +3 -0
  160. package/dist/output/renderers.js +223 -256
  161. package/dist/output/shapes.js +150 -105
  162. package/dist/output/text.js +318 -30
  163. package/dist/registry/build-index.js +3 -0
  164. package/dist/registry/create-provider-registry.js +3 -0
  165. package/dist/registry/factory.js +3 -0
  166. package/dist/registry/origin-resolve.js +3 -0
  167. package/dist/registry/providers/index.js +3 -0
  168. package/dist/registry/providers/skills-sh.js +70 -49
  169. package/dist/registry/providers/static-index.js +53 -48
  170. package/dist/registry/providers/types.js +3 -24
  171. package/dist/registry/resolve.js +11 -16
  172. package/dist/registry/types.js +3 -0
  173. package/dist/scripts/migrate-storage.js +17307 -0
  174. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
  175. package/dist/scripts/migrations/v16-to-v17.js +141 -0
  176. package/dist/setup/detect.js +3 -0
  177. package/dist/setup/ripgrep-install.js +3 -0
  178. package/dist/setup/ripgrep-resolve.js +3 -0
  179. package/dist/setup/setup.js +775 -37
  180. package/dist/setup/steps.js +3 -15
  181. package/dist/sources/include.js +3 -0
  182. package/dist/sources/provider-factory.js +5 -12
  183. package/dist/sources/provider.js +3 -20
  184. package/dist/sources/providers/filesystem.js +19 -23
  185. package/dist/sources/providers/git.js +7 -5
  186. package/dist/sources/providers/index.js +3 -0
  187. package/dist/sources/providers/install-types.js +3 -13
  188. package/dist/sources/providers/npm.js +3 -4
  189. package/dist/sources/providers/provider-utils.js +3 -0
  190. package/dist/sources/providers/sync-from-ref.js +3 -11
  191. package/dist/sources/providers/tar-utils.js +3 -0
  192. package/dist/sources/providers/website.js +18 -22
  193. package/dist/sources/resolve.js +3 -0
  194. package/dist/sources/types.js +3 -0
  195. package/dist/sources/website-ingest.js +7 -0
  196. package/dist/tasks/backends/cron.js +203 -0
  197. package/dist/tasks/backends/exec-utils.js +28 -0
  198. package/dist/tasks/backends/index.js +24 -0
  199. package/dist/tasks/backends/launchd-template.xml +19 -0
  200. package/dist/tasks/backends/launchd.js +187 -0
  201. package/dist/tasks/backends/schtasks-template.xml +29 -0
  202. package/dist/tasks/backends/schtasks.js +215 -0
  203. package/dist/tasks/parser.js +211 -0
  204. package/dist/tasks/resolveAkmBin.js +87 -0
  205. package/dist/tasks/runner.js +458 -0
  206. package/dist/tasks/schedule.js +211 -0
  207. package/dist/tasks/schema.js +15 -0
  208. package/dist/tasks/validator.js +62 -0
  209. package/dist/version.js +3 -0
  210. package/dist/wiki/index-template.md +12 -0
  211. package/dist/wiki/ingest-workflow-template.md +54 -0
  212. package/dist/wiki/log-template.md +8 -0
  213. package/dist/wiki/schema-template.md +61 -0
  214. package/dist/wiki/wiki-templates.js +15 -0
  215. package/dist/wiki/wiki.js +13 -61
  216. package/dist/workflows/authoring.js +8 -25
  217. package/dist/workflows/cli.js +3 -0
  218. package/dist/workflows/db.js +140 -10
  219. package/dist/workflows/document-cache.js +3 -10
  220. package/dist/workflows/parser.js +3 -0
  221. package/dist/workflows/renderer.js +11 -3
  222. package/dist/workflows/runs.js +62 -91
  223. package/dist/workflows/schema.js +3 -0
  224. package/dist/workflows/scope-key.js +3 -0
  225. package/dist/workflows/validator.js +4 -8
  226. package/dist/workflows/workflow-template.md +24 -0
  227. package/docs/README.md +9 -2
  228. package/docs/data-and-telemetry.md +225 -0
  229. package/docs/migration/release-notes/0.7.0.md +1 -1
  230. package/docs/migration/release-notes/0.7.5.md +2 -2
  231. package/docs/migration/release-notes/0.8.0.md +48 -0
  232. package/docs/migration/v0.7-to-v0.8.md +1307 -0
  233. package/package.json +20 -8
  234. package/.github/LICENSE +0 -374
  235. package/dist/commands/install-audit.js +0 -381
  236. package/dist/templates/wiki-templates.js +0 -100
@@ -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
  * `akm show` — entry point.
3
6
  *
@@ -16,14 +19,17 @@
16
19
  import fs from "node:fs";
17
20
  import path from "node:path";
18
21
  import { parseAssetRef } from "../core/asset-ref";
22
+ import { asNonEmptyString } from "../core/common";
19
23
  import { loadConfig } from "../core/config";
20
- import { NotFoundError, UsageError } from "../core/errors";
24
+ import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../core/errors";
21
25
  import { appendEvent, readEvents } from "../core/events";
22
- import { parseFrontmatter, toStringOrUndefined } from "../core/frontmatter";
26
+ import { parseFrontmatter } from "../core/frontmatter";
23
27
  import { closeDatabase, findEntryIdByRef, openExistingDatabase } from "../indexer/db";
24
28
  import { ensureIndex } from "../indexer/ensure-index";
25
29
  import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../indexer/file-context";
30
+ import { listRelatedPathsForFile } from "../indexer/graph-boost";
26
31
  import { lookup } from "../indexer/indexer";
32
+ import { resolveAssetPath } from "../indexer/path-resolver";
27
33
  import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../indexer/search-source";
28
34
  import { insertUsageEvent } from "../indexer/usage-events";
29
35
  import { resolveSourcesForOrigin } from "../registry/origin-resolve";
@@ -139,7 +145,7 @@ export async function akmShowUnified(input) {
139
145
  }
140
146
  // Count prior shows of this ref before logging the current one.
141
147
  const priorShowCount = recentShowCount(ref);
142
- logShowEvent(ref);
148
+ logShowEvent(ref, undefined, input.eventSource);
143
149
  if (priorShowCount >= 2) {
144
150
  // Agent has shown this same asset 3+ times — inject a loop-break hint.
145
151
  result.showLoopWarning = priorShowCount + 1;
@@ -177,7 +183,7 @@ function enforceScopeOrThrow(filePath, ref, scope) {
177
183
  for (const [key, expectedValue] of expected) {
178
184
  if (expectedValue === undefined)
179
185
  continue;
180
- const actual = toStringOrUndefined(fm[`scope_${key}`]);
186
+ const actual = asNonEmptyString(fm[`scope_${key}`]);
181
187
  if (actual !== expectedValue) {
182
188
  throw new NotFoundError(`Asset "${ref}" exists but is out of scope (expected scope_${key}="${expectedValue}").`);
183
189
  }
@@ -189,18 +195,51 @@ function enforceScopeOrThrow(filePath, ref, scope) {
189
195
  */
190
196
  function recentShowCount(ref) {
191
197
  try {
192
- const { events } = readEvents({ type: "show", ref });
198
+ const { events } = readEvents({
199
+ type: "show",
200
+ ref,
201
+ since: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
202
+ });
193
203
  return events.length;
194
204
  }
195
205
  catch {
196
206
  return 0;
197
207
  }
198
208
  }
199
- function logShowEvent(ref, existingDb) {
209
+ function logShowEvent(ref, existingDb, eventSource = "user") {
200
210
  // Emit a structured event to events.jsonl so workflow-trace consumers
201
211
  // detect akm show invocations without relying on stdout scraping.
202
212
  const parsed = parseAssetRef(ref);
203
213
  appendEvent({ eventType: "show", ref, metadata: { type: parsed.type, name: parsed.name } });
214
+ // Detect if this show is a selection from a recent search result.
215
+ try {
216
+ // D7: bound the query to the last 60 s so we never scan unbounded history
217
+ const { events: recentSearches } = readEvents({
218
+ type: "search",
219
+ since: new Date(Date.now() - 60_000).toISOString(),
220
+ });
221
+ const cutoffMs = Date.now() - 60_000;
222
+ const matchingSearch = [...recentSearches].reverse().find((e) => {
223
+ if (!e.ts || new Date(e.ts).getTime() < cutoffMs)
224
+ return false;
225
+ const refs = e.metadata?.resultRefs ?? [];
226
+ return refs.includes(ref);
227
+ });
228
+ if (matchingSearch) {
229
+ appendEvent({
230
+ eventType: "select",
231
+ ref,
232
+ metadata: {
233
+ query: matchingSearch.metadata?.query,
234
+ searchTs: matchingSearch.ts,
235
+ rankPosition: (matchingSearch.metadata?.resultRefs ?? []).indexOf(ref),
236
+ },
237
+ });
238
+ }
239
+ }
240
+ catch {
241
+ /* fire-and-forget — select is best-effort */
242
+ }
204
243
  try {
205
244
  const db = existingDb ?? openExistingDatabase();
206
245
  try {
@@ -208,6 +247,7 @@ function logShowEvent(ref, existingDb) {
208
247
  event_type: "show",
209
248
  entry_ref: ref,
210
249
  entry_id: findEntryIdByRef(db, ref),
250
+ source: eventSource,
211
251
  });
212
252
  }
213
253
  finally {
@@ -215,22 +255,11 @@ function logShowEvent(ref, existingDb) {
215
255
  closeDatabase(db);
216
256
  }
217
257
  }
218
- catch {
258
+ catch (err) {
259
+ rethrowIfTestIsolationError(err);
219
260
  /* fire-and-forget */
220
261
  }
221
262
  }
222
- /**
223
- * Resolve an asset path via the FTS5 index only. Spec §6.2's primary path.
224
- *
225
- * Returns `undefined` if the index has no matching row.
226
- */
227
- async function resolvePathViaIndex(parsed) {
228
- const entry = await lookup(parsed);
229
- if (entry) {
230
- return { assetPath: entry.filePath };
231
- }
232
- return undefined;
233
- }
234
263
  /** @internal Use akmShowUnified() for all external callers. */
235
264
  export async function showLocal(input) {
236
265
  const parsed = parseAssetRef(input.ref);
@@ -251,10 +280,11 @@ export async function showLocal(input) {
251
280
  }
252
281
  }
253
282
  if (!assetPath) {
254
- const resolved = await resolvePathViaIndex(parsed);
255
- if (resolved?.assetPath) {
256
- assetPath = resolved.assetPath;
257
- }
283
+ const resolvedAssetPath = await resolveAssetPath(parsed, {
284
+ stashDir: input.stashDir,
285
+ mode: "index-first",
286
+ });
287
+ assetPath = resolvedAssetPath ?? undefined;
258
288
  }
259
289
  if (!assetPath && parsed.origin && searchSources.length === 0) {
260
290
  const installCmd = `akm add ${parsed.origin}`;
@@ -293,8 +323,24 @@ export async function showLocal(input) {
293
323
  origin: source?.registryId ?? null,
294
324
  editable,
295
325
  ...(!editable ? { editHint: buildEditHint(assetPath, parsed.type, parsed.name, source?.registryId) } : {}),
326
+ related: (() => {
327
+ let db;
328
+ try {
329
+ db = openExistingDatabase();
330
+ const related = listRelatedPathsForFile(sourceStashDir, assetPath, 5, db);
331
+ return { total: related.length, hits: related };
332
+ }
333
+ catch (err) {
334
+ rethrowIfTestIsolationError(err);
335
+ return { total: 0, hits: [] };
336
+ }
337
+ finally {
338
+ if (db)
339
+ closeDatabase(db);
340
+ }
341
+ })(),
296
342
  };
297
- const activeRun = getActiveWorkflowRun(getCurrentWorkflowScopeKey());
343
+ const activeRun = await getActiveWorkflowRun(getCurrentWorkflowScopeKey());
298
344
  if (activeRun) {
299
345
  fullResponse.activeRun = activeRun;
300
346
  }
@@ -354,7 +400,7 @@ function buildSummaryResponse(full, assetPath) {
354
400
  const textContent = full.content ?? full.template ?? full.prompt;
355
401
  if (textContent && !description) {
356
402
  const parsed = parseFrontmatter(textContent);
357
- description = toStringOrUndefined(parsed.data.description);
403
+ description = asNonEmptyString(parsed.data.description);
358
404
  }
359
405
  }
360
406
  const summary = {
@@ -371,3 +417,76 @@ function buildSummaryResponse(full, assetPath) {
371
417
  };
372
418
  return summary;
373
419
  }
420
+ // ── argv normalisation ───────────────────────────────────────────────────────
421
+ const SHOW_VIEW_MODES = new Set(["toc", "frontmatter", "full", "section", "lines"]);
422
+ /**
423
+ * Normalize argv so positional view-mode arguments after the asset ref
424
+ * are rewritten into internal flags that citty can parse.
425
+ *
426
+ * Converts:
427
+ * akm show knowledge:guide.md toc → akm show knowledge:guide.md --akmView toc
428
+ * akm show knowledge:guide.md section Auth → akm show knowledge:guide.md --akmView section --akmHeading Auth
429
+ * akm show knowledge:guide.md lines 1 50 → akm show knowledge:guide.md --akmView lines --akmStart 1 --akmEnd 50
430
+ *
431
+ * Legacy `--view` is intentionally unsupported.
432
+ * Returns a new array; the input is never modified.
433
+ */
434
+ export function normalizeShowArgv(argv) {
435
+ // argv[0]=bun argv[1]=script argv[2]=subcommand argv[3]=ref argv[4..]=rest
436
+ if (argv[2] !== "show")
437
+ return argv;
438
+ if (argv[3] === "proposal")
439
+ return argv;
440
+ if (argv.includes("--view") || argv.includes("--heading") || argv.includes("--start") || argv.includes("--end")) {
441
+ throw new UsageError('Legacy show flags are no longer supported. Use positional syntax like `akm show knowledge:guide toc` or `akm show knowledge:guide section "Auth"`.');
442
+ }
443
+ // Separate global flags from positional/show-specific args
444
+ const prefix = argv.slice(0, 3); // [bun, script, show]
445
+ const rest = argv.slice(3);
446
+ const globalFlags = [];
447
+ const showArgs = [];
448
+ for (let i = 0; i < rest.length; i++) {
449
+ const arg = rest[i];
450
+ if (arg === "--quiet" || arg === "-q" || arg === "--for-agent" || arg === "--for-agent=true") {
451
+ globalFlags.push(arg);
452
+ continue;
453
+ }
454
+ if (arg.startsWith("--format=") || arg.startsWith("--detail=")) {
455
+ globalFlags.push(arg);
456
+ continue;
457
+ }
458
+ if (arg === "--format" || arg === "--detail") {
459
+ globalFlags.push(arg);
460
+ if (rest[i + 1] !== undefined) {
461
+ globalFlags.push(rest[i + 1]);
462
+ i++;
463
+ }
464
+ continue;
465
+ }
466
+ showArgs.push(arg);
467
+ }
468
+ // showArgs[0] = ref, showArgs[1] = potential view mode, showArgs[2..] = view params
469
+ const ref = showArgs[0];
470
+ const viewMode = showArgs[1];
471
+ if (!ref || !viewMode || !SHOW_VIEW_MODES.has(viewMode)) {
472
+ return argv;
473
+ }
474
+ const result = [...prefix, ref, "--akmView", viewMode];
475
+ if (viewMode === "section") {
476
+ // Next arg is the heading name; pass empty string when missing so the
477
+ // show handler can produce a clear "section not found" error.
478
+ const heading = showArgs[2] ?? "";
479
+ result.push("--akmHeading", heading);
480
+ }
481
+ else if (viewMode === "lines") {
482
+ // Next two args are start and end
483
+ const start = showArgs[2];
484
+ const end = showArgs[3];
485
+ if (start)
486
+ result.push("--akmStart", start);
487
+ if (end)
488
+ result.push("--akmEnd", end);
489
+ }
490
+ result.push(...globalFlags);
491
+ return result;
492
+ }
@@ -1,9 +1,11 @@
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
  import fs from "node:fs";
2
5
  import path from "node:path";
3
6
  import { isHttpUrl, resolveStashDir } from "../core/common";
4
- import { loadConfig, loadUserConfig, saveConfig } from "../core/config";
7
+ import { getSources, loadConfig, loadUserConfig, saveConfig } from "../core/config";
5
8
  import { ConfigError, UsageError } from "../core/errors";
6
- import { warn } from "../core/warn";
7
9
  import { akmIndex } from "../indexer/indexer";
8
10
  import { upsertLockEntry } from "../integrations/lockfile";
9
11
  import { parseRegistryRef } from "../registry/resolve";
@@ -11,7 +13,6 @@ import { detectStashRoot } from "../sources/providers/provider-utils";
11
13
  import { syncFromRef } from "../sources/providers/sync-from-ref";
12
14
  import { ensureWebsiteMirror, validateWebsiteInputUrl } from "../sources/website-ingest";
13
15
  import { ensureWikiNameAvailable, validateWikiName } from "../wiki/wiki";
14
- import { auditInstallCandidate, deriveRegistryLabels, enforceRegistryInstallPolicy, formatInstallAuditFailure, } from "./install-audit";
15
16
  const VALID_OVERRIDE_TYPES = new Set(["wiki"]);
16
17
  export async function akmAdd(input) {
17
18
  const ref = input.ref.trim();
@@ -38,16 +39,13 @@ export async function akmAdd(input) {
38
39
  try {
39
40
  const parsed = parseRegistryRef(ref);
40
41
  if (parsed.source === "local") {
41
- if (input.trustThisInstall) {
42
- warn("--trust has no effect on local directory sources; the install audit is not run for local paths.");
43
- }
44
42
  return addLocalSource(ref, parsed.sourcePath, stashDir, wikiName, input.name);
45
43
  }
46
44
  }
47
45
  catch {
48
46
  // Not a local ref — fall through to registry install
49
47
  }
50
- return addRegistryStash(ref, stashDir, input.trustThisInstall, input.writable, wikiName);
48
+ return addRegistryStash(ref, stashDir, input.writable, wikiName);
51
49
  }
52
50
  export async function registerWikiSource(input) {
53
51
  const stashDir = resolveStashDir();
@@ -59,7 +57,6 @@ export async function registerWikiSource(input) {
59
57
  name,
60
58
  overrideType: "wiki",
61
59
  options: input.options,
62
- trustThisInstall: input.trustThisInstall,
63
60
  writable: input.writable,
64
61
  });
65
62
  }
@@ -74,7 +71,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
74
71
  // Derive the canonical name: explicit --name wins, then wiki name, then readable path.
75
72
  const derivedName = explicitName ?? wikiName ?? toReadableId(resolvedPath);
76
73
  // Check for duplicates in sources[]
77
- const sources = [...(config.sources ?? config.stashes ?? [])];
74
+ const sources = [...getSources(config)];
78
75
  const existing = sources.find((s) => s.type === "filesystem" && s.path && path.resolve(s.path) === resolvedPath);
79
76
  let persistedEntry;
80
77
  if (!existing) {
@@ -85,7 +82,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
85
82
  ...(wikiName ? { wikiName } : {}),
86
83
  };
87
84
  sources.push(persistedEntry);
88
- saveConfig({ ...config, sources, stashes: undefined });
85
+ saveConfig({ ...config, sources });
89
86
  }
90
87
  else {
91
88
  let changed = false;
@@ -99,7 +96,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
99
96
  changed = true;
100
97
  }
101
98
  if (changed)
102
- saveConfig({ ...config, sources, stashes: undefined });
99
+ saveConfig({ ...config, sources });
103
100
  persistedEntry = existing;
104
101
  }
105
102
  const index = await akmIndex({ stashDir });
@@ -116,7 +113,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
116
113
  ...(persistedEntry.wikiName ? { wiki: persistedEntry.wikiName } : {}),
117
114
  },
118
115
  config: {
119
- sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
116
+ sourceCount: getSources(updatedConfig).length,
120
117
  installedKitCount: updatedConfig.installed?.length ?? 0,
121
118
  },
122
119
  index: {
@@ -131,7 +128,7 @@ async function addLocalSource(ref, sourcePath, stashDir, wikiName, explicitName)
131
128
  async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
132
129
  const normalizedUrl = validateWebsiteInputUrl(ref);
133
130
  const config = loadUserConfig();
134
- const sources = [...(config.sources ?? config.stashes ?? [])];
131
+ const sources = [...getSources(config)];
135
132
  let entry = sources.find((stash) => stash.type === "website" && stash.url === normalizedUrl);
136
133
  if (!entry) {
137
134
  entry = {
@@ -142,7 +139,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
142
139
  ...(wikiName ? { wikiName } : {}),
143
140
  };
144
141
  sources.push(entry);
145
- saveConfig({ ...config, sources, stashes: undefined });
142
+ saveConfig({ ...config, sources });
146
143
  }
147
144
  else {
148
145
  let changed = false;
@@ -155,7 +152,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
155
152
  changed = true;
156
153
  }
157
154
  if (changed)
158
- saveConfig({ ...config, sources, stashes: undefined });
155
+ saveConfig({ ...config, sources });
159
156
  }
160
157
  const cachePaths = await ensureWebsiteMirror(entry, { requireStashDir: true });
161
158
  const index = await akmIndex({ stashDir });
@@ -172,7 +169,7 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
172
169
  ...(entry.wikiName ? { wiki: entry.wikiName } : {}),
173
170
  },
174
171
  config: {
175
- sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
172
+ sourceCount: getSources(updatedConfig).length,
176
173
  installedKitCount: updatedConfig.installed?.length ?? 0,
177
174
  },
178
175
  index: {
@@ -186,38 +183,14 @@ async function addWebsiteSource(ref, stashDir, name, options, wikiName) {
186
183
  }
187
184
  /**
188
185
  * Install a stash from a registry (npm, github, git) by dispatching to the
189
- * matching syncable provider, then running the post-sync install audit and
190
- * persisting the lock entry.
186
+ * matching syncable provider and persisting the lock entry.
191
187
  */
192
- async function addRegistryStash(ref, stashDir, trustThisInstall, writable, wikiName) {
188
+ async function addRegistryStash(ref, stashDir, writable, wikiName) {
193
189
  const parsedRef = parseRegistryRef(ref);
194
190
  if (writable === true && parsedRef.source !== "git") {
195
191
  throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
196
192
  }
197
- // Pre-sync registry-policy enforcement uses just the parsed ref (no fetch needed),
198
- // so we keep parity with the historical behavior where `enforceRegistryInstallPolicy`
199
- // ran before `extractTarGzSecure` etc.
200
- const config = loadConfig();
201
- const synced = await syncFromRef(ref, { trustThisInstall, writable });
202
- const registryLabels = deriveRegistryLabels({
203
- source: synced.source,
204
- ref: synced.ref,
205
- artifactUrl: synced.artifactUrl,
206
- });
207
- enforceRegistryInstallPolicy(registryLabels, config, ref);
208
- // Post-sync hook: install audit. Throws when blocked unless `--trust` is set
209
- // (in which case the audit report still surfaces in the response).
210
- const audit = auditInstallCandidate({
211
- rootDir: synced.extractedDir,
212
- source: synced.source,
213
- ref: synced.ref,
214
- registryLabels,
215
- config,
216
- trustThisInstall,
217
- });
218
- if (audit.blocked) {
219
- throw new Error(formatInstallAuditFailure(synced.ref, audit));
220
- }
193
+ const synced = await syncFromRef(ref, { writable });
221
194
  const replaced = (loadConfig().installed ?? []).find((entry) => entry.id === synced.id);
222
195
  const updatedConfig = upsertInstalledRegistryEntry({
223
196
  id: synced.id,
@@ -265,10 +238,9 @@ async function addRegistryStash(ref, stashDir, trustThisInstall, writable, wikiN
265
238
  cacheDir: synced.cacheDir,
266
239
  extractedDir: synced.extractedDir,
267
240
  installedAt: synced.syncedAt,
268
- audit,
269
241
  },
270
242
  config: {
271
- sourceCount: (updatedConfig.sources ?? updatedConfig.stashes ?? []).length,
243
+ sourceCount: getSources(updatedConfig).length,
272
244
  installedKitCount: updatedConfig.installed?.length ?? 0,
273
245
  },
274
246
  index: {
@@ -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
  import fs from "node:fs";
2
5
  import path from "node:path";
3
6
  import { makeAssetRef, parseAssetRef } from "../core/asset-ref";
@@ -1,5 +1,9 @@
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
  import path from "node:path";
2
- import { loadConfig, loadUserConfig, saveConfig } from "../core/config";
5
+ import { isRemoteUrl } from "../core/common";
6
+ import { getSources, loadConfig, loadUserConfig, saveConfig } from "../core/config";
3
7
  import { ConfigError, UsageError } from "../core/errors";
4
8
  import { resolveSourceEntries } from "../indexer/search-source";
5
9
  // ── Operations ──────────────────────────────────────────────────────────────
@@ -19,14 +23,9 @@ export function addStash(opts) {
19
23
  throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
20
24
  }
21
25
  const config = loadUserConfig();
22
- const sources = [...(config.sources ?? config.stashes ?? [])];
23
- const isRemoteUrl = target.startsWith("http://") ||
24
- target.startsWith("https://") ||
25
- target.startsWith("git@") ||
26
- target.startsWith("ssh://") ||
27
- target.startsWith("git://");
26
+ const sources = [...getSources(config)];
28
27
  let entry;
29
- if (isRemoteUrl) {
28
+ if (isRemoteUrl(target)) {
30
29
  if (!providerType) {
31
30
  throw new UsageError("--provider is required for URL sources (e.g. --provider git --provider website)");
32
31
  }
@@ -53,7 +52,7 @@ export function addStash(opts) {
53
52
  entry.name = name;
54
53
  }
55
54
  sources.push(entry);
56
- saveConfig({ ...config, sources, stashes: undefined });
55
+ saveConfig({ ...config, sources });
57
56
  return { sources, added: true, entry };
58
57
  }
59
58
  /**
@@ -62,16 +61,12 @@ export function addStash(opts) {
62
61
  */
63
62
  export function removeStash(target) {
64
63
  const config = loadUserConfig();
65
- const sources = [...(config.sources ?? config.stashes ?? [])];
66
- const isUrl = target.startsWith("http://") ||
67
- target.startsWith("https://") ||
68
- target.startsWith("git@") ||
69
- target.startsWith("ssh://") ||
70
- target.startsWith("git://");
71
- const resolvedPath = !isUrl ? path.resolve(target) : undefined;
64
+ const sources = [...getSources(config)];
65
+ const isUrlTarget = isRemoteUrl(target);
66
+ const resolvedPath = !isUrlTarget ? path.resolve(target) : undefined;
72
67
  // Try URL match first, then path, then name (most specific → least specific)
73
68
  let idx = -1;
74
- if (isUrl) {
69
+ if (isUrlTarget) {
75
70
  idx = sources.findIndex((s) => s.url === target);
76
71
  }
77
72
  if (idx === -1 && resolvedPath) {
@@ -84,7 +79,7 @@ export function removeStash(target) {
84
79
  return { sources, removed: false, message: "No matching source found" };
85
80
  }
86
81
  const removed = sources.splice(idx, 1)[0];
87
- saveConfig({ ...config, sources, stashes: undefined });
82
+ saveConfig({ ...config, sources });
88
83
  return { sources, removed: true, entry: removed };
89
84
  }
90
85
  /**
@@ -93,6 +88,6 @@ export function removeStash(target) {
93
88
  export function listStashes() {
94
89
  const config = loadConfig();
95
90
  const localSources = resolveSourceEntries();
96
- const sources = config.sources ?? config.stashes ?? [];
91
+ const sources = getSources(config);
97
92
  return { localSources, sources };
98
93
  }