akm-cli 0.9.16-alpha.1 → 0.9.16-alpha.2

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 (144) hide show
  1. package/CHANGELOG.md +40 -132
  2. package/dist/assets/hints/cli-hints-full.md +13 -6
  3. package/dist/assets/tasks/core/index-refresh.yml +1 -1
  4. package/dist/assets/tasks/improve/akm-improve-catchup.yml +3 -6
  5. package/dist/cli/retired-commands.js +0 -4
  6. package/dist/cli/unknown-flags.js +3 -36
  7. package/dist/commands/env/env-binding.js +4 -4
  8. package/dist/commands/env/env-cli.js +3 -3
  9. package/dist/commands/improve/collapse-detector.js +2 -2
  10. package/dist/commands/improve/consolidate.js +4 -6
  11. package/dist/commands/improve/improve-cli.js +20 -15
  12. package/dist/commands/improve/reflect.js +23 -2
  13. package/dist/commands/lint/base-linter.js +9 -0
  14. package/dist/commands/lint/env-key-rules.js +2 -2
  15. package/dist/commands/proposal/propose.js +15 -1
  16. package/dist/commands/proposal/repository.js +3 -12
  17. package/dist/commands/proposal/validators/proposal-quality-validators.js +40 -3
  18. package/dist/commands/proposal/validators/proposal-validators.js +5 -4
  19. package/dist/commands/read/curate.js +44 -34
  20. package/dist/commands/read/search.js +35 -54
  21. package/dist/commands/read/show.js +21 -2
  22. package/dist/commands/registry-cli.js +5 -5
  23. package/dist/commands/sources/add-cli.js +59 -16
  24. package/dist/commands/sources/bundle-cli.js +35 -11
  25. package/dist/commands/sources/bundle-config-ops.js +30 -0
  26. package/dist/commands/sources/dangerous-env-audit.js +4 -4
  27. package/dist/commands/sources/info.js +8 -8
  28. package/dist/commands/sources/installed-stashes.js +55 -61
  29. package/dist/commands/sources/source-add.js +39 -38
  30. package/dist/commands/sources/source-manage.js +34 -12
  31. package/dist/commands/sources/stash-cli.js +111 -119
  32. package/dist/commands/sources/stash-skeleton.js +6 -3
  33. package/dist/commands/tasks/explain.js +4 -1
  34. package/dist/commands/tasks/tasks-cli.js +31 -9
  35. package/dist/commands/tasks/tasks.js +239 -194
  36. package/dist/commands/tasks/validate.js +20 -32
  37. package/dist/core/activation-policy.js +4 -4
  38. package/dist/core/adapter/adapters/akm-adapter.js +8 -35
  39. package/dist/core/adapter/adapters/akm-metadata.js +1 -11
  40. package/dist/core/adapter/execution-source.js +10 -29
  41. package/dist/core/asset/asset-placement.js +0 -35
  42. package/dist/core/config/config-schema.js +64 -8
  43. package/dist/core/config/config-sources.js +96 -2
  44. package/dist/core/config/config.js +190 -24
  45. package/dist/core/config/legacy-source-shape-shim.js +9 -0
  46. package/dist/core/config/schema/embedding.js +30 -7
  47. package/dist/core/config/schema/execution.js +23 -0
  48. package/dist/core/config/schema/experimental.js +1 -1
  49. package/dist/core/config/schema/scheduler.js +20 -0
  50. package/dist/core/config/schema/search.js +10 -12
  51. package/dist/core/config/schema/sources-bundles.js +32 -1
  52. package/dist/core/content-safety.js +52 -0
  53. package/dist/core/errors.js +2 -5
  54. package/dist/core/maintenance-barrier.js +11 -13
  55. package/dist/core/paths.js +11 -0
  56. package/dist/core/run-lock.js +2 -5
  57. package/dist/core/state/migrations.js +1 -26
  58. package/dist/core/state-db.js +27 -63
  59. package/dist/core/type-presentation.js +1 -1
  60. package/dist/core/write-source.js +13 -8
  61. package/dist/indexer/bundle-identity-guard.js +45 -8
  62. package/dist/indexer/ensure-index.js +0 -5
  63. package/dist/indexer/index-db-contention.js +56 -0
  64. package/dist/indexer/index-rebuild-lock.js +73 -0
  65. package/dist/indexer/index-written-assets.js +171 -133
  66. package/dist/indexer/indexer.js +1621 -458
  67. package/dist/indexer/lookup/adapter-concept-owner.js +5 -19
  68. package/dist/indexer/materialize-embeddings.js +785 -0
  69. package/dist/indexer/passes/dir-staleness.js +161 -0
  70. package/dist/indexer/passes/metadata.js +1 -18
  71. package/dist/indexer/scan/drain-dir.js +70 -27
  72. package/dist/indexer/search/db-search.js +89 -373
  73. package/dist/indexer/search/ranking-contributors.js +16 -21
  74. package/dist/indexer/search/ranking.js +57 -135
  75. package/dist/indexer/search/search-source.js +29 -11
  76. package/dist/integrations/agent/execution-lowering.js +3 -2
  77. package/dist/integrations/agent/execution-preparation.js +32 -1
  78. package/dist/integrations/agent/prompts.js +1 -1
  79. package/dist/integrations/agent/request-lowering.js +3 -2
  80. package/dist/llm/client.js +3 -11
  81. package/dist/llm/embedder.js +3 -10
  82. package/dist/llm/embedders/remote.js +104 -133
  83. package/dist/llm/feature-gate.js +2 -4
  84. package/dist/llm/rerank-client.js +3 -3
  85. package/dist/output/shapes/passthrough.js +2 -1
  86. package/dist/output/text/command-format.js +13 -19
  87. package/dist/output/text/helpers.js +1 -1
  88. package/dist/output/text/index.js +2 -5
  89. package/dist/registry/resolve.js +37 -10
  90. package/dist/scripts/akm-migrate-node.js +15197 -11351
  91. package/dist/scripts/akm-migrate.js +15514 -11668
  92. package/dist/setup/semantic-assets.js +2 -2
  93. package/dist/setup/setup.js +3 -3
  94. package/dist/setup/steps/connection.js +2 -3
  95. package/dist/setup/steps/tasks.js +29 -36
  96. package/dist/sources/providers/git-install.js +17 -11
  97. package/dist/sources/providers/git-provider.js +12 -5
  98. package/dist/sources/providers/git-stash.js +38 -16
  99. package/dist/sources/snapshot-fetchers/website-ingest.js +3 -3
  100. package/dist/storage/repositories/embedding-salvage-repository.js +184 -0
  101. package/dist/storage/repositories/index-connection.js +3 -1
  102. package/dist/storage/repositories/index-entries-repository.js +68 -77
  103. package/dist/storage/repositories/index-entry-schema.js +25 -16
  104. package/dist/storage/repositories/index-fts-repository.js +263 -29
  105. package/dist/storage/repositories/index-meta-repository.js +29 -0
  106. package/dist/storage/repositories/index-schema.js +122 -115
  107. package/dist/storage/repositories/index-utility-repository.js +1 -1
  108. package/dist/storage/repositories/index-vec-repository.js +435 -22
  109. package/dist/tasks/activation-config.js +90 -0
  110. package/dist/tasks/backends/cron.js +9 -0
  111. package/dist/tasks/backends/launchd.js +1 -0
  112. package/dist/tasks/backends/schtasks.js +2 -0
  113. package/dist/tasks/embedded.js +4 -5
  114. package/dist/tasks/scheduler-binding.js +2 -2
  115. package/dist/tasks/scheduler-sync-preview.js +8 -1
  116. package/dist/tasks/scheduler-sync.js +19 -10
  117. package/dist/tasks/source/parse-task-source.js +10 -113
  118. package/dist/tasks/source/project-v4.js +2 -2
  119. package/dist/tasks/source/task-source-v4.js +4 -12
  120. package/dist/tasks/source/task-to-v3.js +4 -12
  121. package/dist/tasks/source/task-to-v4.js +40 -7
  122. package/docs/migration/README.md +1 -0
  123. package/docs/migration/release-notes/0.9.15.md +36 -34
  124. package/docs/migration/release-notes/0.9.16.md +60 -98
  125. package/docs/migration/release-notes/README.md +0 -5
  126. package/docs/migration/v0.9.1-to-v0.9.2.md +6 -9
  127. package/docs/reference/cli.md +124 -122
  128. package/docs/reference/configuration.md +137 -133
  129. package/docs/reference/data-and-telemetry.md +1 -2
  130. package/docs/reference/tasks.md +34 -29
  131. package/package.json +1 -1
  132. package/schemas/akm-config.json +170 -6
  133. package/schemas/akm-task.json +1 -2
  134. package/dist/commands/sources/index-status.js +0 -99
  135. package/dist/core/hash.js +0 -18
  136. package/dist/indexer/drain.js +0 -306
  137. package/dist/indexer/embedding-identity.js +0 -20
  138. package/dist/indexer/enrich.js +0 -260
  139. package/dist/indexer/reconcile.js +0 -890
  140. package/dist/indexer/scan/parse-file.js +0 -66
  141. package/dist/indexer/units/unit.js +0 -159
  142. package/dist/llm/embedders/provider-limits.js +0 -288
  143. package/dist/storage/repositories/files-repository.js +0 -181
  144. package/dist/storage/repositories/units-repository.js +0 -510
@@ -8,9 +8,9 @@ import { UsageError } from "../../core/errors.js";
8
8
  import { appendEvent } from "../../core/events.js";
9
9
  import { warn } from "../../core/warn.js";
10
10
  import { auditStashForDangerousKeys } from "./dangerous-env-audit.js";
11
- import { akmRemove } from "./installed-stashes.js";
11
+ import { akmRemove, akmUpdate } from "./installed-stashes.js";
12
12
  import { akmAdd } from "./source-add.js";
13
- import { addStash } from "./source-manage.js";
13
+ import { addStash, removeStash } from "./source-manage.js";
14
14
  // ── Shared website-options helper ──────────
15
15
  export function buildWebsiteOptions(args) {
16
16
  // getStringArg maps absent/blank to undefined; parsePositiveIntFlag maps
@@ -86,28 +86,41 @@ export const addCommand = defineJsonCommand({
86
86
  description: "Mark a git bundle as writable so changes can be pushed back",
87
87
  default: false,
88
88
  },
89
+ before: { type: "string", description: "Insert the new bundle before this configured bundle" },
90
+ after: { type: "string", description: "Insert the new bundle after this configured bundle" },
91
+ credential: {
92
+ type: "string",
93
+ description: "Git HTTPS bearer credential reference ($VAR or secret://name); never a literal token",
94
+ },
89
95
  "max-pages": { type: "string", description: "Maximum pages to crawl for website sources (default: 50)" },
90
96
  "max-depth": { type: "string", description: "Maximum crawl depth for website sources (default: 3)" },
91
- "allow-insecure": {
97
+ "allow-insecure-transport": {
98
+ type: "boolean",
99
+ description: "Allow a plain HTTP source URL after explicitly accepting transport substitution risk.",
100
+ default: false,
101
+ },
102
+ "allow-dangerous-env-keys": {
92
103
  type: "boolean",
93
- description: "Allow a plain HTTP source URL and skip confirmation for dangerous env keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the bundle.",
104
+ description: "Allow reviewed process-hijacking env keys (e.g. LD_PRELOAD, PATH) in the installed bundle.",
94
105
  default: false,
95
106
  },
96
107
  },
97
108
  async run({ args }) {
98
109
  const ref = args.ref.trim();
99
- const allowInsecure = args["allow-insecure"];
100
- const allowDangerousKeys = allowInsecure;
110
+ const allowInsecureTransport = args["allow-insecure-transport"];
111
+ const allowDangerousKeys = args["allow-dangerous-env-keys"];
112
+ if (args.before && args.after)
113
+ throw new UsageError("Only one of --before or --after may be used.");
101
114
  // --provider → declarative bundle source (URL for git/website; bare
102
- // package spec for npm — R-013). Config-only write; content is not
103
- // synced until a later `akm update`.
115
+ // package spec for npm — R-013). Git is materialized below before success;
116
+ // other providers remain declarative until bundle update.
104
117
  if (args.provider) {
105
118
  if (shouldWarnOnPlainHttp(ref)) {
106
- if (!allowInsecure) {
119
+ if (!allowInsecureTransport) {
107
120
  throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
108
- "Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
121
+ "Use https:// or pass --allow-insecure-transport if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure-transport` only after confirming the URL is trusted.");
109
122
  }
110
- warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
123
+ warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure-transport was set; an on-path attacker could substitute a malicious payload.");
111
124
  }
112
125
  let parsedOptions;
113
126
  if (args.options) {
@@ -130,7 +143,34 @@ export const addCommand = defineJsonCommand({
130
143
  providerType: args.provider,
131
144
  options: parsedOptions,
132
145
  writable: args.writable,
146
+ before: args.before,
147
+ after: args.after,
148
+ credential: args.credential,
133
149
  });
150
+ // Declarative git sources must be materialized and indexed before add
151
+ // reports success. This also makes --provider git behave consistently
152
+ // with inferred/registry git installs (#968/#970).
153
+ if (args.provider === "git" && result.added && result.entry?.name) {
154
+ let updated;
155
+ try {
156
+ updated = await akmUpdate({ target: result.entry.name, allowDangerousEnvKeys: allowDangerousKeys });
157
+ }
158
+ catch (error) {
159
+ removeStash(result.entry.name);
160
+ throw error;
161
+ }
162
+ appendEvent({
163
+ eventType: "add",
164
+ metadata: {
165
+ target: ref,
166
+ provider: args.provider,
167
+ name: args.name ?? null,
168
+ writable: args.writable === true,
169
+ },
170
+ });
171
+ output("add", { ...result, index: updated.index });
172
+ return;
173
+ }
134
174
  appendEvent({
135
175
  eventType: "add",
136
176
  metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
@@ -139,11 +179,11 @@ export const addCommand = defineJsonCommand({
139
179
  return;
140
180
  }
141
181
  if (shouldWarnOnPlainHttp(ref)) {
142
- if (!allowInsecure) {
182
+ if (!allowInsecureTransport) {
143
183
  throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
144
- "Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
184
+ "Use https:// or pass --allow-insecure-transport if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure-transport` only after confirming the URL is trusted.");
145
185
  }
146
- warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
186
+ warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure-transport was set; an on-path attacker could substitute a malicious payload.");
147
187
  }
148
188
  const websiteOptions = buildWebsiteOptions(args);
149
189
  const result = await akmAdd({
@@ -152,6 +192,9 @@ export const addCommand = defineJsonCommand({
152
192
  options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
153
193
  writable: args.writable,
154
194
  adapter: args.adapter,
195
+ before: args.before,
196
+ after: args.after,
197
+ credential: args.credential,
155
198
  });
156
199
  appendEvent({
157
200
  eventType: "add",
@@ -165,8 +208,8 @@ export const addCommand = defineJsonCommand({
165
208
  // Resolve the stash root from the install result and scan any env files
166
209
  // for dangerous env var keys. When findings are present the install is
167
210
  // gated: TTY → interactive confirmation prompt; non-TTY without
168
- // --allow-insecure → hard failure (exit 1). Pass
169
- // --allow-insecure to skip the prompt non-interactively.
211
+ // --allow-dangerous-env-keys → hard failure (exit 1). Pass the dedicated
212
+ // flag to skip the prompt non-interactively.
170
213
  const installedStashRoot = result.installed?.stashRoot ??
171
214
  (result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
172
215
  if (installedStashRoot) {
@@ -24,8 +24,9 @@
24
24
  * `proposal-new` shape). `show` is a new shape, `bundle-show`.
25
25
  */
26
26
  import { defineGroupCommand, defineJsonCommand, output } from "../../cli/shared.js";
27
- import { NotFoundError, UsageError } from "../../core/errors.js";
27
+ import { NotFoundError, TransientError, UsageError } from "../../core/errors.js";
28
28
  import { appendEvent } from "../../core/events.js";
29
+ import { warn } from "../../core/warn.js";
29
30
  import { addCommand } from "./add-cli.js";
30
31
  import { akmInit } from "./init.js";
31
32
  import { akmListSources, akmRemove, akmUpdate } from "./installed-stashes.js";
@@ -118,12 +119,20 @@ const removeCommand = defineJsonCommand({
118
119
  },
119
120
  });
120
121
  const updateCommand = defineJsonCommand({
121
- meta: { name: "update", description: "Refresh one or all configured bundles and reconcile their index" },
122
+ meta: {
123
+ name: "update",
124
+ description: "Refresh one or all configured bundles and reconcile their index. Git refresh is explicit; schedule this command for automatic updates.",
125
+ },
122
126
  args: {
123
127
  target: { type: "positional", description: "Bundle to update (id or ref)", required: false },
124
128
  all: { type: "boolean", description: "Update all configured bundles and report each outcome", default: false },
125
129
  force: { type: "boolean", description: "Force fresh download even if version is unchanged", default: false },
126
- "allow-insecure": {
130
+ "skip-if-locked": {
131
+ type: "boolean",
132
+ description: "Exit successfully when index/state database contention shows another akm operation is active",
133
+ default: false,
134
+ },
135
+ "allow-dangerous-env-keys": {
127
136
  type: "boolean",
128
137
  description: "Allow an update containing dangerous env keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the staged bundle.",
129
138
  default: false,
@@ -140,20 +149,31 @@ const updateCommand = defineJsonCommand({
140
149
  },
141
150
  },
142
151
  async run({ args }) {
143
- const result = await akmUpdate({
144
- target: args.target,
145
- all: args.all,
146
- force: args.force,
147
- yes: args.yes,
148
- allowInsecure: args["allow-insecure"],
149
- });
152
+ let result;
153
+ try {
154
+ result = await akmUpdate({
155
+ target: args.target,
156
+ all: args.all,
157
+ force: args.force,
158
+ yes: args.yes,
159
+ allowDangerousEnvKeys: args["allow-dangerous-env-keys"],
160
+ });
161
+ }
162
+ catch (error) {
163
+ const skippable = isSkippableBundleUpdateLock(error);
164
+ if (!args["skip-if-locked"] || !skippable)
165
+ throw error;
166
+ warn(`[bundle update] ${error.message}; skipping (--skip-if-locked)`);
167
+ output("update", { ok: true, skipped: { reason: "lock-held", code: error.code } });
168
+ return;
169
+ }
150
170
  appendEvent({
151
171
  eventType: "update",
152
172
  metadata: {
153
173
  target: args.target ?? null,
154
174
  all: args.all === true,
155
175
  force: args.force === true,
156
- allowInsecure: args["allow-insecure"] === true,
176
+ allowDangerousEnvKeys: args["allow-dangerous-env-keys"] === true,
157
177
  processed: Array.isArray(result.processed)
158
178
  ? result.processed.length
159
179
  : 0,
@@ -162,6 +182,10 @@ const updateCommand = defineJsonCommand({
162
182
  output("update", result);
163
183
  },
164
184
  });
185
+ export function isSkippableBundleUpdateLock(error) {
186
+ return (error instanceof TransientError &&
187
+ ["INDEX_DB_CONTENDED", "STATE_DB_CONTENDED", "MAINTENANCE_BARRIER_BUSY"].includes(error.code));
188
+ }
165
189
  export const bundleCommand = defineGroupCommand({
166
190
  meta: {
167
191
  name: "bundle",
@@ -14,8 +14,38 @@
14
14
  */
15
15
  import path from "node:path";
16
16
  import { bundleKeyForContentRoot, primaryBundlePath } from "../../core/config/config.js";
17
+ import { UsageError } from "../../core/errors.js";
17
18
  import { deriveBundleId } from "../../indexer/installations.js";
18
19
  export { primaryBundlePath };
20
+ /**
21
+ * Insert or move a bundle at an explicit config-map position. Object insertion
22
+ * order is bundle resolution priority after `defaultBundle`, which always
23
+ * remains first. Without a position an existing key keeps its place and a new
24
+ * key is appended.
25
+ */
26
+ export function placeBundle(bundles, key, entry, position = {}) {
27
+ if (position.before && position.after) {
28
+ throw new UsageError("Only one of --before or --after may be used.");
29
+ }
30
+ const target = position.before ?? position.after;
31
+ if (!target)
32
+ return { ...bundles, [key]: entry };
33
+ if (target === key)
34
+ throw new UsageError(`Bundle "${key}" cannot be positioned relative to itself.`);
35
+ if (!(target in bundles))
36
+ throw new UsageError(`Bundle position target "${target}" is not configured.`);
37
+ const ordered = {};
38
+ for (const [existingKey, existingEntry] of Object.entries(bundles)) {
39
+ if (existingKey === key)
40
+ continue;
41
+ if (position.before === existingKey)
42
+ ordered[key] = entry;
43
+ ordered[existingKey] = existingEntry;
44
+ if (position.after === existingKey)
45
+ ordered[key] = entry;
46
+ }
47
+ return ordered;
48
+ }
19
49
  /**
20
50
  * Upsert the primary filesystem bundle (`{ path, writable: true }`) and point
21
51
  * `defaultBundle` at it. Reuses the current default key when it already names a
@@ -85,13 +85,13 @@ export async function auditStashForDangerousKeys(opts) {
85
85
  }
86
86
  const stance = decideDangerousKeyInstall({
87
87
  findingsPresent: allFindings.length > 0,
88
- allowInsecure: opts.allowDangerousKeys,
88
+ allowDangerousEnvKeys: opts.allowDangerousKeys,
89
89
  });
90
90
  if (stance === "allow")
91
91
  return { blocked: false, findings: allFindings };
92
92
  if (stance === "warn-allow") {
93
93
  for (const finding of allFindings) {
94
- warn(`[dangerous-env-key] ${finding.relPath}: key \`${finding.keyName}\` in ${finding.envRef} can hijack process execution via \`akm env run\`. Proceeding because --allow-insecure was set.`);
94
+ warn(`[dangerous-env-key] ${finding.relPath}: key \`${finding.keyName}\` in ${finding.envRef} can hijack process execution via \`akm env run\`. Proceeding because --allow-dangerous-env-keys was set.`);
95
95
  }
96
96
  return { blocked: false, findings: allFindings };
97
97
  }
@@ -113,11 +113,11 @@ export async function auditStashForDangerousKeys(opts) {
113
113
  const confirmed = await p.confirm({ message: `${operationTitle} anyway?`, initialValue: false });
114
114
  if (!p.isCancel(confirmed) && confirmed === true)
115
115
  return { blocked: false, findings: allFindings };
116
- error = `${operationTitle} aborted: stash contains dangerous env keys. Remove the keys or re-run with --allow-insecure to bypass.`;
116
+ error = `${operationTitle} aborted: stash contains dangerous env keys. Remove the keys or re-run with --allow-dangerous-env-keys to bypass.`;
117
117
  }
118
118
  else {
119
119
  const keyList = allFindings.map((finding) => ` - ${finding.keyName} (${finding.envRef})`).join("\n");
120
- error = `${operationTitle} blocked: stash "${opts.ref}" contains dangerous env keys that can hijack process execution via \`akm env run\`:\n${keyList}\nRe-run with --allow-insecure to bypass this check after reviewing the env file.`;
120
+ error = `${operationTitle} blocked: stash "${opts.ref}" contains dangerous env keys that can hijack process execution via \`akm env run\`:\n${keyList}\nRe-run with --allow-dangerous-env-keys to bypass this check after reviewing the env file.`;
121
121
  }
122
122
  const rollbackWarning = await opts.rollback?.();
123
123
  if (opts.renderBlockedError !== false) {
@@ -53,15 +53,15 @@ export function assembleInfo(options) {
53
53
  // Semantic status is read live from the index's own state, not a cached
54
54
  // verdict — a failed embed attempt at search time falls back to FTS and
55
55
  // reports that in the search response, it never disables the mode here.
56
- // "ready-js" (a JS-computed cosine-similarity fallback for when the
57
- // sqlite-vec extension is unavailable) is retired (index redesign, B5):
58
- // the units vector store (`units_vec`) is a vec0 virtual table with no
59
- // BLOB fallback, so there is nothing to have embeddings without also
60
- // having the extension available — `vecAvailable` false and
61
- // `hasEmbeddings` true together is no longer a reachable combination.
62
- const semanticStatus = config.semanticSearchMode === "off" ? "disabled" : !indexStats.hasEmbeddings ? "pending" : "ready-vec";
56
+ const semanticStatus = config.semanticSearchMode === "off"
57
+ ? "disabled"
58
+ : !indexStats.hasEmbeddings
59
+ ? "pending"
60
+ : indexStats.vecAvailable
61
+ ? "ready-vec"
62
+ : "ready-js";
63
63
  const searchModes = ["fts"];
64
- if (semanticStatus === "ready-vec") {
64
+ if (semanticStatus === "ready-js" || semanticStatus === "ready-vec") {
65
65
  searchModes.push("semantic", "hybrid");
66
66
  }
67
67
  return {