myagentmemory 0.4.16 → 0.5.1

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.
@@ -19,6 +19,8 @@ agent-memory context --no-search 2>/dev/null
19
19
 
20
20
  This prints your scratchpad, today's log, long-term memory, and yesterday's log. Review it — especially **open scratchpad items** — before starting work.
21
21
 
22
+ Run this even if a SessionStart hook already fired — the hook only injects the narrower "stable" layer (MEMORY.md + scratchpad) to keep per-turn re-injection cheap. Seeing two context blocks in one session is expected; treat this fuller one as authoritative.
23
+
22
24
  If the user's task relates to prior work, search for relevant memories:
23
25
  ```bash
24
26
  agent-memory search --query "<topic>" --mode keyword
@@ -106,6 +108,15 @@ agent-memory search --query "how we handle auth" --mode semantic # Finds related
106
108
  agent-memory search --query "performance" --mode deep --limit 10 # Hybrid + reranking
107
109
  ```
108
110
 
111
+ `search` only looks at what you saved (daily logs, MEMORY.md, topics, scratchpad). For **prior sessions** — things you or the agent said in a past Claude/Codex/pi chat — use `recall`:
112
+
113
+ ```bash
114
+ agent-memory recall "deploy-to-dev label workflow" # Cross-session, verbatim events
115
+ agent-memory recall "auth refresh" --scope current --limit 5 # Restrict to this workspace
116
+ ```
117
+
118
+ When qmd search returns no hits and AgentMemory Pro is installed, `search` automatically falls back to `recall` — but calling `recall` directly is faster and clearer when you know you want session history.
119
+
109
120
  If qmd is not installed, fall back to reading files directly:
110
121
  ```bash
111
122
  agent-memory read --target long_term
@@ -115,7 +126,7 @@ agent-memory read --target daily
115
126
  ### Setup
116
127
 
117
128
  ```bash
118
- agent-memory init # Create dirs, detect qmd, setup collection
129
+ agent-memory setup # Idempotent: memory dir, qmd collection, skills, hooks, MCP
119
130
  agent-memory sync # Re-index and embed all files (requires qmd)
120
131
  agent-memory status # Show config, file counts, qmd status
121
132
  ```
@@ -172,5 +183,6 @@ Distil scans daily logs and topic notes, groups entries by their `#tags`, and ge
172
183
  - Use `--target long_term` sparingly: architecture, preferences, key commands, hard-won lessons
173
184
  - Prefer the scratchpad for any TODOs or follow-ups (persistent, cross-session tracking)
174
185
  - Use `#tags` and `[[links]]` in content to improve search recall
175
- - Use `agent-memory search` to recall past work before starting related tasks
186
+ - Use `agent-memory search` to find things you saved (daily logs, MEMORY.md, topics) before starting related tasks
187
+ - Use `agent-memory recall "<query>"` to find things from prior chat sessions (Pro) — not the same as `search`
176
188
  - All `agent-memory` commands are safe — they read/write only to the memory directory (`~/.agent-memory/` by default)
@@ -24,6 +24,8 @@ If the user's task relates to prior work, search for relevant memories:
24
24
  agent-memory search --query "<topic>" --mode keyword
25
25
  ```
26
26
 
27
+ Run the context command above even if a `sessionStart` hook already fired (via `~/.cursor/hooks.json`, installed by `agent-memory install-hooks`) — both fetch the same full layer, so seeing it twice is redundant but harmless, not a sign of drift.
28
+
27
29
  **Tip:** For project-specific rules (linting, formatting, test conventions), prefer `.cursorrules` or project-level config files. Use agent-memory for cross-project and cross-session knowledge.
28
30
 
29
31
  ## On Session End (After Significant Work)
@@ -108,6 +110,15 @@ agent-memory search --query "how we handle auth" --mode semantic # Finds related
108
110
  agent-memory search --query "performance" --mode deep --limit 10 # Hybrid + reranking
109
111
  ```
110
112
 
113
+ `search` only looks at what you saved (daily logs, MEMORY.md, topics, scratchpad). For **prior sessions** — things you or the agent said in a past chat — use `recall`:
114
+
115
+ ```bash
116
+ agent-memory recall "deploy-to-dev label workflow" # Cross-session, verbatim events
117
+ agent-memory recall "auth refresh" --scope current --limit 5 # Restrict to this workspace
118
+ ```
119
+
120
+ When qmd search returns no hits and AgentMemory Pro is installed, `search` automatically falls back to `recall` — but calling `recall` directly is faster and clearer when you know you want session history.
121
+
111
122
  If qmd is not installed, fall back to reading files directly:
112
123
  ```bash
113
124
  agent-memory read --target long_term
@@ -117,7 +128,7 @@ agent-memory read --target daily
117
128
  ### Setup
118
129
 
119
130
  ```bash
120
- agent-memory init # Create dirs, detect qmd, setup collection
131
+ agent-memory setup # Idempotent: memory dir, qmd collection, skills, hooks, MCP
121
132
  agent-memory sync # Re-index and embed all files (requires qmd)
122
133
  agent-memory status # Show config, file counts, qmd status
123
134
  ```
@@ -174,5 +185,6 @@ Distil scans daily logs and topic notes, groups entries by their `#tags`, and ge
174
185
  - Use `--target long_term` sparingly: architecture, preferences, key commands, hard-won lessons
175
186
  - Prefer the scratchpad for any TODOs or follow-ups (persistent, cross-session tracking)
176
187
  - Use `#tags` and `[[links]]` in content to improve search recall
177
- - Use `agent-memory search` to recall past work before starting related tasks
188
+ - Use `agent-memory search` to find things you saved (daily logs, MEMORY.md, topics) before starting related tasks
189
+ - Use `agent-memory recall "<query>"` to find things from prior chat sessions (Pro) — not the same as `search`
178
190
  - All `agent-memory` commands are safe — they read/write only to the memory directory (`~/.agent-memory/` by default)
package/src/cli-spec.ts CHANGED
@@ -10,20 +10,30 @@ export interface CliOptionSpec {
10
10
 
11
11
  export const COMMANDS = [
12
12
  "context",
13
+ "save",
14
+ "note",
13
15
  "write",
14
16
  "read",
15
17
  "scratchpad",
16
18
  "search",
17
19
  "distil",
18
20
  "sync",
19
- "init",
21
+ "setup",
20
22
  "status",
23
+ "doctor",
24
+ "tutorial",
21
25
  "install-skills",
22
26
  "uninstall-skills",
23
27
  "install-hooks",
24
28
  "uninstall-hooks",
25
29
  "completion",
30
+ "pro",
31
+ "recall",
32
+ "learn",
33
+ "dashboard",
26
34
  "plugin",
35
+ "serve",
36
+ "upgrade",
27
37
  "version",
28
38
  "help",
29
39
  ] as const;
@@ -35,22 +45,32 @@ export const SCRATCHPAD_ACTIONS = ["add", "done", "undo", "clear_done", "list"]
35
45
 
36
46
  export const COMMAND_DESCRIPTIONS: Record<(typeof COMMANDS)[number], string> = {
37
47
  context: "build context from scratchpad, logs, topics, and long-term memory",
48
+ save: 'shortcut: agent-memory save "<text>" → daily memory entry',
49
+ note: 'shortcut: agent-memory note "<text>" → scratchpad checklist item',
38
50
  write: "append or overwrite a daily, topic, or long-term memory entry",
39
51
  read: "read daily, topic, scratchpad, or long-term memory",
40
52
  scratchpad: "add, complete, reopen, list, or clear persistent checklist items",
41
53
  search: "search indexed memory with keyword, semantic, or deep qmd modes",
42
54
  distil: "rebuild a compact MEMORY.md index from logs and topics",
43
55
  sync: "update the qmd index and semantic embeddings",
44
- init: "create memory storage and configure qmd when available",
56
+ setup: "one-shot idempotent installer: init + skills + hooks + plugin + mcp",
45
57
  status: "show memory paths, file counts, qmd, and embedding health",
58
+ doctor: "run a one-shot health check across memory, qmd, skills, hooks, and Pro",
59
+ tutorial: "guided 3-minute walkthrough in a throwaway sandbox",
46
60
  "install-skills": "install core instructions for detected agents",
47
61
  "uninstall-skills": "remove core instructions from detected agents",
48
- "install-hooks": "install automatic SessionStart indexing and context hooks",
49
- "uninstall-hooks": "remove only SessionStart hooks managed by agent-memory",
62
+ "install-hooks": "install managed context and memory-write reminder hooks",
63
+ "uninstall-hooks": "remove only hooks managed by agent-memory",
50
64
  completion: "install or print Bash, Zsh, Fish, or PowerShell completion",
51
- plugin: "index, recall, learn from, and evaluate prior agent sessions",
65
+ pro: "install, inspect, or upgrade AgentMemory Pro",
66
+ recall: "recall decisions and context from prior coding sessions with Pro",
67
+ learn: "find repeated corrections worth remembering with Pro",
68
+ dashboard: "open the private local Memory Dashboard",
69
+ plugin: "discover, install, update, or remove optional official plugins",
70
+ serve: "run as a Model Context Protocol (MCP) server over stdio",
71
+ upgrade: "check for and install newer agent-memory CLI and Pro plugin releases",
52
72
  version: "print the installed agent-memory version",
53
- help: "show top-level, command, or nested plugin help",
73
+ help: "show this command overview",
54
74
  };
55
75
 
56
76
  export const PLUGIN_COMMAND_DESCRIPTIONS: Record<(typeof PLUGIN_COMMANDS)[number], string> = {
@@ -75,20 +95,41 @@ export const SCRATCHPAD_ACTION_DESCRIPTIONS: Record<(typeof SCRATCHPAD_ACTIONS)[
75
95
  export const GLOBAL_OPTIONS = ["--dir", "--json", "--help", "--version", "-h", "-V"] as const;
76
96
 
77
97
  export const COMMAND_OPTIONS: Record<string, readonly string[]> = {
78
- context: ["--query", "--no-search"],
98
+ context: ["--query", "--no-search", "--layer"],
99
+ save: ["--target"],
100
+ note: [],
79
101
  write: ["--content", "--target", "--mode", "--topic", "--date", "--source-uri"],
80
102
  read: ["--target", "--date", "--topic"],
81
103
  scratchpad: ["--text"],
82
104
  search: ["--query", "--mode", "--limit"],
83
105
  distil: ["--dry-run"],
84
106
  sync: [],
85
- init: [],
107
+ init: ["--yes", "--skip-skills", "--skip-hooks"],
108
+ setup: ["--yes", "--skip-skills", "--skip-hooks", "--skip-plugin", "--skip-mcp"],
86
109
  status: ["--probe"],
87
- "install-skills": [],
110
+ doctor: [],
111
+ tutorial: [],
112
+ "install-skills": ["--uninstall"],
88
113
  "uninstall-skills": [],
89
- "install-hooks": ["--yes", "--all", "--only"],
114
+ "install-hooks": ["--yes", "--all", "--only", "--mode"],
90
115
  "uninstall-hooks": ["--only"],
91
116
  completion: ["--stdout"],
117
+ pro: [],
118
+ recall: [
119
+ "--scope",
120
+ "--cwd",
121
+ "--limit",
122
+ "--context",
123
+ "--queries",
124
+ "--multi",
125
+ "--sessions",
126
+ "--events",
127
+ "--per-query",
128
+ ],
129
+ learn: ["--preview"],
130
+ dashboard: ["--no-browser"],
131
+ serve: ["--mcp", "--register", "--only"],
132
+ upgrade: ["--check", "--refresh", "--yes", "--quiet", "--cli", "--plugin"],
92
133
  version: [],
93
134
  help: [],
94
135
  };
@@ -100,6 +141,12 @@ export const PLUGIN_COMMAND_OPTIONS: Record<string, readonly string[]> = {
100
141
  update: ["--channel"],
101
142
  uninstall: ["--yes"],
102
143
  manage: ["--no-browser"],
144
+ // Plugin runtime commands forwarded to the installed bundle
145
+ index: ["--date", "--since"],
146
+ recall: ["--query", "--limit", "--cwd", "--host", "--mode"],
147
+ worker: ["--token"],
148
+ learn: ["--limit", "--dry-run"],
149
+ eval: ["--limit"],
103
150
  };
104
151
 
105
152
  export const WORKER_ACTION_OPTIONS: Record<string, readonly string[]> = {};
@@ -123,7 +170,14 @@ export const OPTION_SPECS: Record<string, CliOptionSpec> = {
123
170
  "--no-search": { description: "build context without invoking qmd" },
124
171
  "--content": { description: "memory entry content to persist", value: { label: "text", kind: "value" } },
125
172
  "--target": { description: "memory destination or collection to read", value: { label: "target", kind: "value" } },
126
- "--mode": { description: "write behavior or qmd search strategy", value: { label: "mode", kind: "value" } },
173
+ "--mode": {
174
+ description: "write behavior, qmd search strategy, or hook mode (stable|per-turn)",
175
+ value: { label: "mode", kind: "value" },
176
+ },
177
+ "--layer": {
178
+ description: "context layer to emit: stable, dynamic, or full (default)",
179
+ value: { label: "layer", kind: "value" },
180
+ },
127
181
  "--topic": { description: "topic name used to resolve a topic file", value: { label: "name", kind: "value" } },
128
182
  "--date": { description: "daily-log date in YYYY-MM-DD form", value: { label: "date", kind: "value" } },
129
183
  "--source-uri": {
@@ -142,8 +196,13 @@ export const OPTION_SPECS: Record<string, CliOptionSpec> = {
142
196
  "--state": { description: "override the plugin state root", value: { label: "directory", kind: "directory" } },
143
197
  "--with-plugin": { description: "include both the core and optional plugin skills" },
144
198
  "--plugin-only": { description: "operate only on the optional plugin skill" },
145
- "--yes": { description: "apply eligible hook changes without confirmation" },
199
+ "--yes": { description: "apply eligible changes without interactive prompts" },
146
200
  "--all": { description: "apply eligible hook changes without confirmation" },
201
+ "--skip-skills": { description: "init: don't prompt to install agent skills" },
202
+ "--skip-hooks": { description: "init: don't prompt to install SessionStart hooks" },
203
+ "--skip-plugin": { description: "setup: don't offer to install the paid plugin bundle" },
204
+ "--skip-mcp": { description: "setup: don't register the MCP server in detected local harnesses" },
205
+ "--preview": { description: "learn: show detected patterns without writing or consuming quota" },
147
206
  "--only": {
148
207
  description: "restrict hook changes to comma-separated agent keys",
149
208
  value: { label: "agents", kind: "value" },
@@ -166,6 +225,23 @@ export const OPTION_SPECS: Record<string, CliOptionSpec> = {
166
225
  description: "surrounding events included with each recall hit",
167
226
  value: { label: "number", kind: "number" },
168
227
  },
228
+ "--queries": {
229
+ description: "JSON array of query strings for one-shot multi-query recall",
230
+ value: { label: "json", kind: "value" },
231
+ },
232
+ "--multi": { description: "treat each positional argument as a separate recall query" },
233
+ "--sessions": {
234
+ description: "maximum sessions returned by multi-query recall",
235
+ value: { label: "number", kind: "number" },
236
+ },
237
+ "--events": {
238
+ description: "top events per session returned by multi-query recall",
239
+ value: { label: "number", kind: "number" },
240
+ },
241
+ "--per-query": {
242
+ description: "candidate hits considered per query before fusion",
243
+ value: { label: "number", kind: "number" },
244
+ },
169
245
  "--pi": { description: "override the Pi session root", value: { label: "directory", kind: "directory" } },
170
246
  "--codex": { description: "override the Codex session root", value: { label: "directory", kind: "directory" } },
171
247
  "--claude": {
@@ -218,6 +294,13 @@ export const OPTION_SPECS: Record<string, CliOptionSpec> = {
218
294
  "--agent": { description: "internal SessionStart host key", value: { label: "agent", kind: "value" } },
219
295
  "--token": { description: "internal session-worker lease token", value: { label: "token", kind: "value" } },
220
296
  "--uninstall": { description: "use install-skills compatibility uninstall mode" },
297
+ "--mcp": { description: "serve as an MCP server over stdio (used by Claude Code)" },
298
+ "--register": { description: "register the MCP server in detected supported agents and exit" },
299
+ "--check": { description: "upgrade: report available updates without installing" },
300
+ "--refresh": { description: "upgrade: force a live registry lookup and rewrite the 24h cache" },
301
+ "--quiet": { description: "upgrade: suppress non-error output (used by the passive session-start refresh)" },
302
+ "--cli": { description: "upgrade: limit action to the CLI binary" },
303
+ "--plugin": { description: "upgrade: limit action to the Pro plugin bundle" },
221
304
  };
222
305
 
223
306
  export const SHELL_DESCRIPTIONS: Record<string, string> = {
@@ -234,3 +317,138 @@ export function optionDescription(option: string): string {
234
317
  export function optionTakesValue(option: string): boolean {
235
318
  return OPTION_SPECS[option]?.value !== undefined;
236
319
  }
320
+
321
+ // One-line usage template per command — shows the shape agents/humans should type.
322
+ const COMMAND_USAGE: Record<string, string> = {
323
+ context: 'agent-memory context [--query "text"] [--no-search] [--json]',
324
+ save: 'agent-memory save "<text>" [--target daily|topic|long_term]',
325
+ note: 'agent-memory note "<text>"',
326
+ write: 'agent-memory write "<text>" [--target daily|topic|long_term] [--mode append|overwrite] [--topic <name>] [--date YYYY-MM-DD]',
327
+ read: "agent-memory read [--target daily|topic|long_term|scratchpad] [--date YYYY-MM-DD] [--topic <name>]",
328
+ scratchpad: 'agent-memory scratchpad <add|done|undo|clear_done|list> [--text "text"]',
329
+ search: 'agent-memory search --query "text" [--mode keyword|semantic|deep] [--limit N]',
330
+ distil: "agent-memory distil [--dry-run]",
331
+ sync: "agent-memory sync",
332
+ init: "agent-memory init [--yes] [--skip-skills] [--skip-hooks]",
333
+ setup: "agent-memory setup [--yes] [--json]",
334
+ status: "agent-memory status [--probe] [--json]",
335
+ doctor: "agent-memory doctor [--json]",
336
+ tutorial: "agent-memory tutorial",
337
+ "install-skills": "agent-memory install-skills [--json]",
338
+ "uninstall-skills": "agent-memory uninstall-skills [--json]",
339
+ "install-hooks":
340
+ "agent-memory install-hooks [--yes] [--all] [--only claude,codex,cursor] [--mode stable|per-turn] [--json]",
341
+ "uninstall-hooks": "agent-memory uninstall-hooks [--only claude,codex,cursor] [--json]",
342
+ completion: "agent-memory completion [bash|zsh|fish|powershell] [--stdout]",
343
+ pro: "agent-memory pro <install|status|upgrade> [--channel stable] [--yes]",
344
+ recall:
345
+ 'agent-memory recall "<query>" [--limit N] [--context N] [--scope current|global] [--cwd <path>] [--json]\n Multi-query: agent-memory recall --queries \'["q1","q2","q3"]\' [--sessions N] [--events N]',
346
+ learn: "agent-memory learn [--preview] [--json]",
347
+ dashboard: "agent-memory dashboard [--no-browser]",
348
+ plugin:
349
+ "agent-memory plugin <list|status|install|update|uninstall|manage> [--channel stable] [--yes] [--no-browser]",
350
+ upgrade: "agent-memory upgrade [--check] [--yes] [--cli|--plugin] [--refresh] [--json]",
351
+ version: "agent-memory version",
352
+ help: "agent-memory help [<command>]",
353
+ };
354
+
355
+ // Concrete examples per command — what an agent or dev is most likely to want.
356
+ const COMMAND_EXAMPLES: Record<string, string[]> = {
357
+ save: [
358
+ 'agent-memory save "shipped the new recall path — bundle at 1.2.3"',
359
+ 'agent-memory save --target long_term "prefer bun test over npm test in example-api"',
360
+ ],
361
+ note: ['agent-memory note "verify the digest backfill against a real corpus"'],
362
+ write: [
363
+ 'agent-memory write "keep sessions in reverse-chron order" --target long_term --mode overwrite',
364
+ 'agent-memory write "half-line summary" --target topic --topic scheduling-machine',
365
+ ],
366
+ read: [
367
+ "agent-memory read --target long_term",
368
+ "agent-memory read --target daily --date 2026-08-24",
369
+ "agent-memory read --target topic --topic scheduling-machine",
370
+ ],
371
+ scratchpad: [
372
+ 'agent-memory scratchpad add --text "chase down the flake in test/web.test.ts"',
373
+ 'agent-memory scratchpad done --text "chase down the flake"',
374
+ "agent-memory scratchpad list",
375
+ ],
376
+ search: [
377
+ 'agent-memory search --query "API test collection credentials"',
378
+ 'agent-memory search --query "grafana alert routing" --mode semantic --limit 10',
379
+ ],
380
+ recall: [
381
+ 'agent-memory recall "API test collection for booking service"',
382
+ 'agent-memory recall --queries \'["API test collection for booking service","booking client integration","client credential setup"]\' --sessions 5',
383
+ 'agent-memory recall "auth refresh" --scope current --limit 5',
384
+ ],
385
+ context: ["agent-memory context", 'agent-memory context --query "grafana alerts" --json'],
386
+ status: ["agent-memory status", "agent-memory status --json"],
387
+ init: ["agent-memory init", "agent-memory init --yes --skip-hooks"],
388
+ setup: ["agent-memory setup", "agent-memory setup --json"],
389
+ "install-hooks": [
390
+ "agent-memory install-hooks",
391
+ "agent-memory install-hooks --yes",
392
+ "agent-memory install-hooks --only claude,codex",
393
+ "agent-memory install-hooks --mode stable # SessionStart only (skip UserPromptSubmit)",
394
+ ],
395
+ distil: ["agent-memory distil", "agent-memory distil --dry-run"],
396
+ learn: ["agent-memory learn", "agent-memory learn --preview # see patterns without consuming quota or writing"],
397
+ };
398
+
399
+ /**
400
+ * Render per-command help using existing spec metadata. Called when the user
401
+ * runs `agent-memory <cmd> --help` — should list the command's real flags with
402
+ * descriptions and one or two concrete examples, not the top-level command list.
403
+ */
404
+ export function renderCommandHelp(command: string): string {
405
+ const canonical = command === "distill" ? "distil" : command;
406
+ const known = new Set<string>([...COMMANDS, "distill", "setup"]);
407
+ if (!known.has(canonical)) {
408
+ return `agent-memory: unknown command '${command}'. Run 'agent-memory help' for the full list.`;
409
+ }
410
+ const description =
411
+ COMMAND_DESCRIPTIONS[canonical as (typeof COMMANDS)[number]] ??
412
+ (canonical === "setup" ? "one-shot idempotent installer: init + skills + hooks + plugin + mcp" : "");
413
+ const usage = COMMAND_USAGE[canonical] ?? `agent-memory ${canonical} [options]`;
414
+ const options = COMMAND_OPTIONS[canonical] ?? [];
415
+ const examples = COMMAND_EXAMPLES[canonical] ?? [];
416
+
417
+ const optLines: string[] = [];
418
+ // Command-specific options
419
+ for (const opt of options) {
420
+ const spec = OPTION_SPECS[opt];
421
+ if (!spec) continue;
422
+ const label = spec.value ? `${opt} <${spec.value.label}>` : opt;
423
+ optLines.push(` ${label.padEnd(24)} ${spec.description}`);
424
+ }
425
+ // Global options that are always available
426
+ const globalKeys = ["--dir", "--json", "--help", "--version"];
427
+ const globalLines = globalKeys
428
+ .map((opt) => {
429
+ const spec = OPTION_SPECS[opt];
430
+ if (!spec) return "";
431
+ const label = spec.value ? `${opt} <${spec.value.label}>` : opt;
432
+ return ` ${label.padEnd(24)} ${spec.description}`;
433
+ })
434
+ .filter(Boolean);
435
+
436
+ // Command-specific subactions
437
+ let subactionLines = "";
438
+ if (canonical === "scratchpad") {
439
+ subactionLines = `\nActions:\n${SCRATCHPAD_ACTIONS.map((a) => ` ${a.padEnd(24)} ${SCRATCHPAD_ACTION_DESCRIPTIONS[a]}`).join("\n")}\n`;
440
+ }
441
+ if (canonical === "plugin" || canonical === "pro") {
442
+ subactionLines = `\nSubcommands:\n${PLUGIN_COMMANDS.map((a) => ` ${a.padEnd(24)} ${PLUGIN_COMMAND_DESCRIPTIONS[a]}`).join("\n")}\n`;
443
+ }
444
+
445
+ const parts = [`agent-memory ${canonical} — ${description}`, "", "Usage:", ` ${usage}`];
446
+ if (subactionLines) parts.push(subactionLines);
447
+ if (optLines.length) parts.push("\nOptions:", ...optLines);
448
+ if (globalLines.length) parts.push("\nGlobal:", ...globalLines);
449
+ if (examples.length) {
450
+ parts.push("\nExamples:");
451
+ for (const ex of examples) parts.push(` $ ${ex}`);
452
+ }
453
+ return parts.join("\n");
454
+ }
@@ -33,6 +33,14 @@ function words(values: readonly string[]): string {
33
33
  return values.join(" ");
34
34
  }
35
35
 
36
+ function shellSingleQuote(value: string): string {
37
+ return `'${value.replace(/'/g, "'\\''")}'`;
38
+ }
39
+
40
+ function powerShellSingleQuote(value: string): string {
41
+ return `'${value.replace(/'/g, "''")}'`;
42
+ }
43
+
36
44
  function zshOptionValue(command: string, option: string): string {
37
45
  if (option === "--target") {
38
46
  return command === "read"
@@ -52,7 +60,7 @@ function zshOptionValue(command: string, option: string): string {
52
60
 
53
61
  function zshOptionSpecs(command: string, options: readonly string[] = COMMAND_OPTIONS[command] ?? []): string {
54
62
  return options
55
- .map((option) => `'${option}[${optionDescription(option)}]${zshOptionValue(command, option)}'`)
63
+ .map((option) => shellSingleQuote(`${option}[${optionDescription(option)}]${zshOptionValue(command, option)}`))
56
64
  .join(" ");
57
65
  }
58
66
 
@@ -71,7 +79,7 @@ function fishOption(command: string, condition: string, option: string): string
71
79
  else if (option === "--only") suggestions = " -a 'claude codex cursor opencode pi'";
72
80
  else if (spec?.value?.kind === "directory") suggestions = " -a '(__fish_complete_directories)'";
73
81
  else if (spec?.value?.kind === "file") suggestions = " -F";
74
- return `complete -c agent-memory -n '${condition}' -l ${option.slice(2)}${value}${suggestions} -d '${optionDescription(option)}'`;
82
+ return `complete -c agent-memory -n ${shellSingleQuote(condition)} -l ${option.slice(2)}${value}${suggestions} -d ${shellSingleQuote(optionDescription(option))}`;
75
83
  }
76
84
 
77
85
  function bashCompletion(): string {
@@ -162,12 +170,12 @@ _agent-memory() {
162
170
  local subcommand="$words[3]"
163
171
  local action="$words[4]"
164
172
  local command_position=$CURRENT
165
- commands=(${COMMANDS.map((command) => `'${command}:${COMMAND_DESCRIPTIONS[command] ?? command}'`).join(" ")})
166
- plugin_commands=(${PLUGIN_COMMANDS.map((command) => `'${command}:${PLUGIN_COMMAND_DESCRIPTIONS[command]}'`).join(" ")})
167
- worker_actions=(${WORKER_ACTIONS.map((action) => `'${action}:${WORKER_ACTION_DESCRIPTIONS[action]}'`).join(" ")})
168
- scratchpad_actions=(${SCRATCHPAD_ACTIONS.map((action) => `'${action}:${SCRATCHPAD_ACTION_DESCRIPTIONS[action]}'`).join(" ")})
173
+ commands=(${COMMANDS.map((command) => shellSingleQuote(`${command}:${COMMAND_DESCRIPTIONS[command] ?? command}`)).join(" ")})
174
+ plugin_commands=(${PLUGIN_COMMANDS.map((command) => shellSingleQuote(`${command}:${PLUGIN_COMMAND_DESCRIPTIONS[command]}`)).join(" ")})
175
+ worker_actions=(${WORKER_ACTIONS.map((action) => shellSingleQuote(`${action}:${WORKER_ACTION_DESCRIPTIONS[action]}`)).join(" ")})
176
+ scratchpad_actions=(${SCRATCHPAD_ACTIONS.map((action) => shellSingleQuote(`${action}:${SCRATCHPAD_ACTION_DESCRIPTIONS[action]}`)).join(" ")})
169
177
  shells=(${Object.entries(SHELL_DESCRIPTIONS)
170
- .map(([shell, description]) => `'${shell}:${description}'`)
178
+ .map(([shell, description]) => shellSingleQuote(`${shell}:${description}`))
171
179
  .join(" ")})
172
180
 
173
181
  _arguments -C \\
@@ -237,23 +245,23 @@ function fishCompletion(): string {
237
245
  "complete -c agent-memory -f",
238
246
  ...COMMANDS.map(
239
247
  (command) =>
240
- `complete -c agent-memory -n '__fish_use_subcommand' -a '${command}' -d '${COMMAND_DESCRIPTIONS[command] ?? command}'`,
248
+ `complete -c agent-memory -n '__fish_use_subcommand' -a '${command}' -d ${shellSingleQuote(COMMAND_DESCRIPTIONS[command] ?? command)}`,
241
249
  ),
242
250
  ...PLUGIN_COMMANDS.map(
243
251
  (command) =>
244
- `complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from ${words(PLUGIN_COMMANDS)}' -a '${command}' -d '${PLUGIN_COMMAND_DESCRIPTIONS[command]}'`,
252
+ `complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from ${words(PLUGIN_COMMANDS)}' -a '${command}' -d ${shellSingleQuote(PLUGIN_COMMAND_DESCRIPTIONS[command])}`,
245
253
  ),
246
254
  ...WORKER_ACTIONS.map(
247
255
  (action) =>
248
- `complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from worker; and not __fish_seen_subcommand_from ${words(WORKER_ACTIONS)}' -a '${action}' -d '${WORKER_ACTION_DESCRIPTIONS[action]}'`,
256
+ `complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from worker; and not __fish_seen_subcommand_from ${words(WORKER_ACTIONS)}' -a '${action}' -d ${shellSingleQuote(WORKER_ACTION_DESCRIPTIONS[action])}`,
249
257
  ),
250
258
  ...SCRATCHPAD_ACTIONS.map(
251
259
  (action) =>
252
- `complete -c agent-memory -n '__fish_seen_subcommand_from scratchpad; and not __fish_seen_subcommand_from ${words(SCRATCHPAD_ACTIONS)}' -a '${action}' -d '${SCRATCHPAD_ACTION_DESCRIPTIONS[action]}'`,
260
+ `complete -c agent-memory -n '__fish_seen_subcommand_from scratchpad; and not __fish_seen_subcommand_from ${words(SCRATCHPAD_ACTIONS)}' -a '${action}' -d ${shellSingleQuote(SCRATCHPAD_ACTION_DESCRIPTIONS[action])}`,
253
261
  ),
254
262
  ...Object.entries(SHELL_DESCRIPTIONS).map(
255
263
  ([shell, description]) =>
256
- `complete -c agent-memory -n '__fish_seen_subcommand_from completion' -a '${shell}' -d '${description}'`,
264
+ `complete -c agent-memory -n '__fish_seen_subcommand_from completion' -a '${shell}' -d ${shellSingleQuote(description)}`,
257
265
  ),
258
266
  "complete -c agent-memory -l dir -r -a '(__fish_complete_directories)' -d 'override the active memory directory'",
259
267
  "complete -c agent-memory -l json -d 'emit command-specific structured JSON'",
@@ -330,32 +338,32 @@ Register-ArgumentCompleter -Native -CommandName agent-memory -ScriptBlock {
330
338
  $shells = @('bash','zsh','fish','powershell')
331
339
  $commandDescriptions = @{
332
340
  ${Object.entries(COMMAND_DESCRIPTIONS)
333
- .map(([command, description]) => ` '${command}' = '${description}'`)
341
+ .map(([command, description]) => ` '${command}' = ${powerShellSingleQuote(description)}`)
334
342
  .join("\n")}
335
343
  }
336
344
  $pluginCommandDescriptions = @{
337
345
  ${Object.entries(PLUGIN_COMMAND_DESCRIPTIONS)
338
- .map(([command, description]) => ` '${command}' = '${description}'`)
346
+ .map(([command, description]) => ` '${command}' = ${powerShellSingleQuote(description)}`)
339
347
  .join("\n")}
340
348
  }
341
349
  $workerActionDescriptions = @{
342
350
  ${Object.entries(WORKER_ACTION_DESCRIPTIONS)
343
- .map(([action, description]) => ` '${action}' = '${description}'`)
351
+ .map(([action, description]) => ` '${action}' = ${powerShellSingleQuote(String(description))}`)
344
352
  .join("\n")}
345
353
  }
346
354
  $scratchpadActionDescriptions = @{
347
355
  ${Object.entries(SCRATCHPAD_ACTION_DESCRIPTIONS)
348
- .map(([action, description]) => ` '${action}' = '${description}'`)
356
+ .map(([action, description]) => ` '${action}' = ${powerShellSingleQuote(description)}`)
349
357
  .join("\n")}
350
358
  }
351
359
  $shellDescriptions = @{
352
360
  ${Object.entries(SHELL_DESCRIPTIONS)
353
- .map(([shell, description]) => ` '${shell}' = '${description}'`)
361
+ .map(([shell, description]) => ` '${shell}' = ${powerShellSingleQuote(description)}`)
354
362
  .join("\n")}
355
363
  }
356
364
  $optionDescriptions = @{
357
365
  ${Object.keys(OPTION_SPECS)
358
- .map((option) => ` '${option}' = '${optionDescription(option)}'`)
366
+ .map((option) => ` '${option}' = ${powerShellSingleQuote(optionDescription(option))}`)
359
367
  .join("\n")}
360
368
  }
361
369
  $globalOptions = @('${GLOBAL_OPTIONS.join("','")}')