akm-cli 0.9.2-alpha.2 → 0.9.2-alpha.3

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.
package/CHANGELOG.md CHANGED
@@ -6,9 +6,53 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.2-alpha.3] - 2026-08-26
10
+
11
+ ### Fixed
12
+
13
+ - **Currency in prose no longer retypes an asset as a command** (#824). The
14
+ smart-Markdown classifier matched `$1`/`$2`/`$3` with a trailing word
15
+ boundary, and that boundary sits between the `2` and the comma in `$2,000` —
16
+ so any note quoting a price was indexed as a `command`, its ref moved to
17
+ `commands/<dir>/<slug>`, and it left its own namespace. Measured on a real
18
+ corpus, 3 of 51 memory documents were affected, and those 3 were exactly the
19
+ 3 whose bodies matched. `$ARGUMENTS` is unambiguous and keeps its existing
20
+ precedence over a directory hint; the numeric placeholders now exclude a
21
+ following digit (or a `.`/`,` followed by one), and where they still
22
+ disagree with a directory that declares a type, the declaration wins. The
23
+ defect is present identically in 0.9.1 — it only became visible once the
24
+ 0.9.2-alpha.2 retrieval work let mistyped assets surface in results.
25
+
26
+ ### Documentation
27
+
28
+ - Recorded the 0.9.2 retrieval measurement in
29
+ `docs/plans/benchmark-tuning-findings.md` §2e (#825). Retrieval-only probes
30
+ with no model in the loop, identical corpora, only the CLI version differing:
31
+ LoCoMo zero-hit 75.0% -> 0.0% and evidence recall@5 0.154 -> 0.590;
32
+ LongMemEval zero-hit 100% -> 0.0% and recall@5 0.000 -> 1.000.
33
+
9
34
  ## [0.9.2-alpha.2] - 2026-08-25
10
35
 
11
- - TODO
36
+ ### Fixed
37
+
38
+ - **Retrieval:** relaxed zero-hit lexical queries centrally, stabilized
39
+ relaxed-retrieval quality, and preserved name quality through relaxed
40
+ ranking. This is the change measured in §2e above — it is what lifted the
41
+ retrieval ceiling that had floored memory-backed evaluation.
42
+ - **Indexing:** verify vec completeness before promotion; compare vec IDs as
43
+ exact sets; materialize vectors for targeted writes; make nested entry
44
+ mutation atomic; reconcile clean before final verification; restore static
45
+ embedding imports.
46
+ - **Markdown projection:** parse nested links safely, parse destination
47
+ phases, and project with stateful delimiters.
48
+ - **Sources:** reconcile local bundle updates and report incomplete filesystem
49
+ reconciliation.
50
+ - **Extract:** keep malformed model output retryable.
51
+
52
+ ### Performance
53
+
54
+ - Keep targeted embedding selection narrow and preserve targeted vec
55
+ degradation.
12
56
 
13
57
  ## [0.9.2-alpha.1] - 2026-08-24
14
58
 
@@ -101,7 +101,21 @@ const DIR_TYPE_MAP = [
101
101
  test: (ext) => ext === ".md",
102
102
  },
103
103
  ];
104
- const COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
104
+ /**
105
+ * `$ARGUMENTS` — unambiguous. Nothing else writes it, so finding it in a body
106
+ * is strong evidence of a command wherever the file lives.
107
+ */
108
+ const ARGUMENTS_PLACEHOLDER_RE = /\$ARGUMENTS/;
109
+ /**
110
+ * `$1` / `$2` / `$3` — AMBIGUOUS, because ordinary prose writes money the same
111
+ * way. The old combined pattern used `\$[123]\b`, and `\b` sits between the
112
+ * `2` and the comma in `$2,000`, so every note quoting a price read as a
113
+ * command (#824). Excluding a following digit, or a `.`/`,` that is itself
114
+ * followed by a digit, rules out `$1,200` / `$2,000` / `$2.50` / `$12` while
115
+ * still matching `$1.` at the end of a sentence — a period with no digit after
116
+ * it is not part of a number.
117
+ */
118
+ const NUMERIC_PLACEHOLDER_RE = /\$[123](?!\d|[.,]\d)/;
105
119
  const SMART_MD_FACTS = {
106
120
  workflow: { type: "workflow", specificity: 19 },
107
121
  toolsAgent: { type: "agent", specificity: 20 },
@@ -141,6 +155,17 @@ function matchDirectoryHint(dirName, ctx, specificity) {
141
155
  }
142
156
  return null;
143
157
  }
158
+ /**
159
+ * True when some ancestor directory already DECLARES this file's type via
160
+ * `DIR_TYPE_MAP` — the same walk `classifyByDirectory` performs. Derived from
161
+ * path fields alone, so `smartMdPathCandidates` can apply it without reading
162
+ * bytes.
163
+ */
164
+ function hasDeclaredDirType(ctx) {
165
+ if (isNestedSkillResource(ctx))
166
+ return false;
167
+ return ctx.ancestorDirs.some((dir) => matchDirectoryHint(dir, ctx, 0) !== null);
168
+ }
144
169
  function classifyByExtension(ctx) {
145
170
  if (ctx.fileName === "SKILL.md") {
146
171
  return { type: "skill", specificity: 25 };
@@ -204,7 +229,19 @@ function classifyBySmartMd(ctx) {
204
229
  return SMART_MD_FACTS.command;
205
230
  }
206
231
  }
207
- if (COMMAND_PLACEHOLDER_RE.test(body)) {
232
+ // `$ARGUMENTS` is unambiguous, so it keeps its long-standing precedence: a
233
+ // command dropped under `knowledge/` is still found as a command.
234
+ if (ARGUMENTS_PLACEHOLDER_RE.test(body)) {
235
+ return SMART_MD_FACTS.command;
236
+ }
237
+ // A NUMERIC placeholder is a guess, and a typed directory is a declaration.
238
+ // Where the two disagree the declaration wins — otherwise a note that merely
239
+ // quotes a price is retyped, which is #824: `memories/*.md` mentioning
240
+ // `$2,000` were indexed as commands, their refs moved to
241
+ // `commands/memories/<slug>`, and they left the `memories/` namespace
242
+ // entirely. Outside a typed directory there is no declaration to defer to,
243
+ // so the guess still stands.
244
+ if (NUMERIC_PLACEHOLDER_RE.test(body) && !hasDeclaredDirType(ctx)) {
208
245
  return SMART_MD_FACTS.command;
209
246
  }
210
247
  if (fm && "model" in fm) {
@@ -14421,7 +14421,8 @@ var DIR_TYPE_MAP = [
14421
14421
  test: (ext) => ext === ".md"
14422
14422
  }
14423
14423
  ];
14424
- var COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
14424
+ var ARGUMENTS_PLACEHOLDER_RE = /\$ARGUMENTS/;
14425
+ var NUMERIC_PLACEHOLDER_RE = /\$[123](?!\d|[.,]\d)/;
14425
14426
  var SMART_MD_FACTS = {
14426
14427
  workflow: { type: "workflow", specificity: 19 },
14427
14428
  toolsAgent: { type: "agent", specificity: 20 },
@@ -14449,6 +14450,11 @@ function matchDirectoryHint(dirName, ctx, specificity) {
14449
14450
  }
14450
14451
  return null;
14451
14452
  }
14453
+ function hasDeclaredDirType(ctx) {
14454
+ if (isNestedSkillResource(ctx))
14455
+ return false;
14456
+ return ctx.ancestorDirs.some((dir) => matchDirectoryHint(dir, ctx, 0) !== null);
14457
+ }
14452
14458
  function classifyByExtension(ctx) {
14453
14459
  if (ctx.fileName === "SKILL.md") {
14454
14460
  return { type: "skill", specificity: 25 };
@@ -14498,7 +14504,10 @@ function classifyBySmartMd(ctx) {
14498
14504
  return SMART_MD_FACTS.command;
14499
14505
  }
14500
14506
  }
14501
- if (COMMAND_PLACEHOLDER_RE.test(body)) {
14507
+ if (ARGUMENTS_PLACEHOLDER_RE.test(body)) {
14508
+ return SMART_MD_FACTS.command;
14509
+ }
14510
+ if (NUMERIC_PLACEHOLDER_RE.test(body) && !hasDeclaredDirType(ctx)) {
14502
14511
  return SMART_MD_FACTS.command;
14503
14512
  }
14504
14513
  if (fm && "model" in fm) {
@@ -14352,7 +14352,8 @@ var DIR_TYPE_MAP = [
14352
14352
  test: (ext) => ext === ".md"
14353
14353
  }
14354
14354
  ];
14355
- var COMMAND_PLACEHOLDER_RE = /\$ARGUMENTS|\$[123]\b/;
14355
+ var ARGUMENTS_PLACEHOLDER_RE = /\$ARGUMENTS/;
14356
+ var NUMERIC_PLACEHOLDER_RE = /\$[123](?!\d|[.,]\d)/;
14356
14357
  var SMART_MD_FACTS = {
14357
14358
  workflow: { type: "workflow", specificity: 19 },
14358
14359
  toolsAgent: { type: "agent", specificity: 20 },
@@ -14380,6 +14381,11 @@ function matchDirectoryHint(dirName, ctx, specificity) {
14380
14381
  }
14381
14382
  return null;
14382
14383
  }
14384
+ function hasDeclaredDirType(ctx) {
14385
+ if (isNestedSkillResource(ctx))
14386
+ return false;
14387
+ return ctx.ancestorDirs.some((dir) => matchDirectoryHint(dir, ctx, 0) !== null);
14388
+ }
14383
14389
  function classifyByExtension(ctx) {
14384
14390
  if (ctx.fileName === "SKILL.md") {
14385
14391
  return { type: "skill", specificity: 25 };
@@ -14429,7 +14435,10 @@ function classifyBySmartMd(ctx) {
14429
14435
  return SMART_MD_FACTS.command;
14430
14436
  }
14431
14437
  }
14432
- if (COMMAND_PLACEHOLDER_RE.test(body)) {
14438
+ if (ARGUMENTS_PLACEHOLDER_RE.test(body)) {
14439
+ return SMART_MD_FACTS.command;
14440
+ }
14441
+ if (NUMERIC_PLACEHOLDER_RE.test(body) && !hasDeclaredDirType(ctx)) {
14433
14442
  return SMART_MD_FACTS.command;
14434
14443
  }
14435
14444
  if (fm && "model" in fm) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akm-cli",
3
- "version": "0.9.2-alpha.2",
3
+ "version": "0.9.2-alpha.3",
4
4
  "type": "module",
5
5
  "description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
6
6
  "keywords": [