coding-friend-cli 1.18.0 → 1.20.0

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 (35) hide show
  1. package/README.md +24 -1
  2. package/dist/{chunk-YC6MBHCT.js → chunk-JFGLNTZI.js} +13 -0
  3. package/dist/{chunk-VUAUAO2R.js → chunk-NEQZP5D4.js} +10 -6
  4. package/dist/{chunk-A427XMWE.js → chunk-PHQK2MMO.js} +4 -2
  5. package/dist/{chunk-KTX4MGMR.js → chunk-QMD7P67N.js} +24 -4
  6. package/dist/chunk-WEMDLEK5.js +331 -0
  7. package/dist/{config-6SBGNTAQ.js → config-UQXY45DN.js} +6 -244
  8. package/dist/{dev-MC6TGHRT.js → dev-7DLYIXBO.js} +2 -2
  9. package/dist/{disable-R6K5YJN4.js → disable-XYZRE3TD.js} +1 -1
  10. package/dist/{enable-HF4PYVJN.js → enable-3NZBQWLQ.js} +1 -1
  11. package/dist/{host-SYZH3FVC.js → host-QDWBFJB2.js} +1 -1
  12. package/dist/index.js +37 -28
  13. package/dist/{init-2HLPKYXB.js → init-ONUC6QMM.js} +1 -1
  14. package/dist/{install-3QCRGPTY.js → install-35IWHBIS.js} +3 -3
  15. package/dist/{mcp-TBEDYELW.js → mcp-GFIOFXOL.js} +1 -1
  16. package/dist/{memory-BQK2R7BV.js → memory-47RXG7VL.js} +158 -26
  17. package/dist/postinstall.js +1 -1
  18. package/dist/{session-H4XW2WXH.js → session-JGRF5SNX.js} +1 -1
  19. package/dist/status-SENJZQ3G.js +226 -0
  20. package/dist/{uninstall-5LRHXFSF.js → uninstall-NNCEKPIE.js} +2 -2
  21. package/dist/{update-4YUSCBCB.js → update-NZ2HRWEN.js} +6 -2
  22. package/lib/cf-memory/CHANGELOG.md +4 -0
  23. package/lib/cf-memory/README.md +29 -1
  24. package/lib/cf-memory/package.json +1 -1
  25. package/lib/cf-memory/src/__tests__/markdown-backend.test.ts +41 -1
  26. package/lib/cf-memory/src/backends/markdown.ts +28 -12
  27. package/lib/cf-memory/src/lib/types.ts +1 -0
  28. package/lib/cf-memory/src/tools/store.ts +23 -5
  29. package/lib/learn-host/CHANGELOG.md +5 -0
  30. package/lib/learn-host/package.json +1 -1
  31. package/lib/learn-host/src/app/[category]/[slug]/page.tsx +4 -3
  32. package/lib/learn-host/src/components/Breadcrumbs.tsx +1 -1
  33. package/lib/learn-host/src/components/DocCard.tsx +4 -1
  34. package/lib/learn-host/src/components/Sidebar.tsx +1 -1
  35. package/package.json +1 -1
package/README.md CHANGED
@@ -5,8 +5,26 @@ CLI companion for the [coding-friend](https://github.com/dinhanhthi/coding-frien
5
5
  ## Requirements
6
6
 
7
7
  - Node.js >= 18
8
+ - npm (included with Node.js, but on some Linux distros you may need to install it separately)
8
9
  - The [coding-friend plugin](https://github.com/dinhanhthi/coding-friend) installed in Claude Code
9
10
 
11
+ ### Additional requirements for `cf memory init`
12
+
13
+ The memory system's Tier 1 (SQLite) uses native Node.js modules that require compilation. On **Linux** (Ubuntu/Debian), install the following system packages before running `cf memory init`:
14
+
15
+ ```bash
16
+ sudo apt update
17
+ sudo apt install -y build-essential python3
18
+ ```
19
+
20
+ On **macOS**, install Xcode Command Line Tools:
21
+
22
+ ```bash
23
+ xcode-select --install
24
+ ```
25
+
26
+ Without these, `cf memory init` will fail when installing SQLite dependencies (`better-sqlite3`, `sqlite-vec`). If you don't need Tier 1, you can choose the **lite** (Tier 2) or **markdown** (Tier 3) tier during init — these have no native dependencies.
27
+
10
28
  ## Install
11
29
 
12
30
  ```bash
@@ -55,6 +73,8 @@ cf permission --project # Save to project-level settings (.claude/settings.lo
55
73
  cf permission --all --user # Apply all recommended permissions to user settings
56
74
  cf statusline # Setup coding-friend statusline
57
75
  cf update # Update plugin + CLI + statusline
76
+ # 💡 If update fails, open Claude Code (`claude`) and run:
77
+ # /plugins → Installed → coding-friend → Update now → /reload-plugins
58
78
  cf update --cli # Update only the CLI (npm package)
59
79
  cf update --plugin # Update only the Claude Code plugin
60
80
  cf update --statusline # Update only the statusline
@@ -76,8 +96,11 @@ cf memory rm --prune # Remove orphaned projects (source dir missing or 0
76
96
  cf memory start-daemon # Start memory daemon (enables Tier 2 search)
77
97
  cf memory stop-daemon # Stop memory daemon
78
98
  cf memory rebuild # Rebuild search index from markdown files
79
- cf memory init # Initialize SQLite backend (Tier 1) and import memories
99
+ cf memory init # Initialize memory system (interactive wizard)
100
+ cf memory config # Configure memory system settings
80
101
  cf memory mcp # Show MCP server config for clients
102
+ cf status # Show comprehensive Coding Friend status
103
+ # (versions, plugin, memory, config)
81
104
  cf session save # Save current Claude Code session to docs/sessions/
82
105
  cf session load # Load a saved session from docs/sessions/
83
106
  cf help # Show all commands
@@ -66,6 +66,18 @@ function enableMarketplaceAutoUpdate() {
66
66
  return false;
67
67
  }
68
68
  }
69
+ function detectPluginScope() {
70
+ const scopeOrder = ["local", "project", "user"];
71
+ for (const scope of scopeOrder) {
72
+ const settings = readJson(
73
+ settingsPathForScope(scope)
74
+ );
75
+ if (!settings) continue;
76
+ const enabled = settings.enabledPlugins;
77
+ if (enabled && PLUGIN_ID in enabled) return scope;
78
+ }
79
+ return "user";
80
+ }
69
81
  function setPluginEnabled(scope, enabled) {
70
82
  const filePath = settingsPathForScope(scope);
71
83
  const settings = readJson(filePath) ?? {};
@@ -89,5 +101,6 @@ export {
89
101
  isMarketplaceRegistered,
90
102
  isPluginDisabled,
91
103
  enableMarketplaceAutoUpdate,
104
+ detectPluginScope,
92
105
  setPluginEnabled
93
106
  };
@@ -21,7 +21,7 @@ ${MARKER_START}
21
21
  _cf_completions() {
22
22
  local cur="\${COMP_WORDS[COMP_CWORD]}"
23
23
  local prev="\${COMP_WORDS[COMP_CWORD-1]}"
24
- local commands="install uninstall disable enable init config host mcp memory permission statusline update dev session"
24
+ local commands="install uninstall disable enable init config host mcp memory permission statusline update status dev session"
25
25
  local scope_flags="--user --global --project --local"
26
26
  local update_flags="--cli --plugin --statusline --user --global --project --local"
27
27
 
@@ -33,7 +33,7 @@ _cf_completions() {
33
33
 
34
34
  # Subcommands for 'memory'
35
35
  if [[ "\${COMP_WORDS[1]}" == "memory" && \${COMP_CWORD} -eq 2 ]]; then
36
- COMPREPLY=($(compgen -W "status search list rm init start-daemon stop-daemon rebuild mcp" -- "$cur"))
36
+ COMPREPLY=($(compgen -W "status search list rm init config start-daemon stop-daemon rebuild mcp" -- "$cur"))
37
37
  return
38
38
  fi
39
39
 
@@ -88,6 +88,7 @@ var ZSH_FUNCTION_BODY = `_cf() {
88
88
  'statusline:Setup coding-friend statusline in Claude Code'
89
89
  'update:Update coding-friend plugin and refresh statusline'
90
90
  'dev:Switch between local and remote plugin for development'
91
+ 'status:Show comprehensive Coding Friend status'
91
92
  'session:Save and load Claude Code sessions across machines'
92
93
  )
93
94
 
@@ -142,7 +143,8 @@ var ZSH_FUNCTION_BODY = `_cf() {
142
143
  'search:Search memories by query'
143
144
  'list:List memories (--projects for all DBs)'
144
145
  'rm:Remove a project database'
145
- 'init:Initialize Tier 1 (SQLite + Hybrid Search)'
146
+ 'init:Initialize memory system (interactive wizard)'
147
+ 'config:Configure memory system settings'
146
148
  'start-daemon:Start the memory daemon (Tier 2)'
147
149
  'stop-daemon:Stop the memory daemon'
148
150
  'rebuild:Rebuild the daemon search index'
@@ -185,6 +187,7 @@ complete -c cf -n "__fish_use_subcommand" -a permission -d "Manage Claude Code p
185
187
  complete -c cf -n "__fish_use_subcommand" -a statusline -d "Setup coding-friend statusline in Claude Code"
186
188
  complete -c cf -n "__fish_use_subcommand" -a update -d "Update coding-friend plugin and refresh statusline"
187
189
  complete -c cf -n "__fish_use_subcommand" -a dev -d "Switch between local and remote plugin for development"
190
+ complete -c cf -n "__fish_use_subcommand" -a status -d "Show comprehensive Coding Friend status"
188
191
  complete -c cf -n "__fish_use_subcommand" -a session -d "Save and load Claude Code sessions across machines"
189
192
  # Scope flags for install/uninstall/disable/enable
190
193
  complete -c cf -n "__fish_seen_subcommand_from install uninstall disable enable" -l user -d "User scope (all projects)"
@@ -215,7 +218,8 @@ complete -c cf -n "__fish_seen_subcommand_from memory" -a status -d "Show memory
215
218
  complete -c cf -n "__fish_seen_subcommand_from memory" -a search -d "Search memories by query"
216
219
  complete -c cf -n "__fish_seen_subcommand_from memory" -a list -d "List memories (--projects for all DBs)"
217
220
  complete -c cf -n "__fish_seen_subcommand_from memory" -a rm -d "Remove a project database"
218
- complete -c cf -n "__fish_seen_subcommand_from memory" -a init -d "Initialize Tier 1 (SQLite + Hybrid Search)"
221
+ complete -c cf -n "__fish_seen_subcommand_from memory" -a init -d "Initialize memory system (interactive wizard)"
222
+ complete -c cf -n "__fish_seen_subcommand_from memory" -a config -d "Configure memory system settings"
219
223
  complete -c cf -n "__fish_seen_subcommand_from memory" -a start-daemon -d "Start the memory daemon (Tier 2)"
220
224
  complete -c cf -n "__fish_seen_subcommand_from memory" -a stop-daemon -d "Stop the memory daemon"
221
225
  complete -c cf -n "__fish_seen_subcommand_from memory" -a rebuild -d "Rebuild the daemon search index"
@@ -229,9 +233,9 @@ var POWERSHELL_BLOCK = `
229
233
  ${MARKER_START}
230
234
  Register-ArgumentCompleter -Native -CommandName cf -ScriptBlock {
231
235
  param($wordToComplete, $commandAst, $cursorPosition)
232
- $commands = @('install','uninstall','disable','enable','init','config','host','mcp','memory','permission','statusline','update','dev','session')
236
+ $commands = @('install','uninstall','disable','enable','init','config','host','mcp','memory','permission','statusline','update','status','dev','session')
233
237
  $devSubcommands = @('on','off','status','restart','sync','update')
234
- $memorySubcommands = @('status','search','list','rm','init','start-daemon','stop-daemon','rebuild','mcp')
238
+ $memorySubcommands = @('status','search','list','rm','init','config','start-daemon','stop-daemon','rebuild','mcp')
235
239
  $sessionSubcommands = @('save','load')
236
240
  $scopeFlags = @('--user','--global','--project','--local')
237
241
  $updateFlags = @('--cli','--plugin','--statusline','--user','--global','--project','--local')
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ORACWEDN.js";
5
5
  import {
6
6
  ensureShellCompletion
7
- } from "./chunk-VUAUAO2R.js";
7
+ } from "./chunk-NEQZP5D4.js";
8
8
  import {
9
9
  resolveScope
10
10
  } from "./chunk-C5LYVVEI.js";
@@ -198,7 +198,7 @@ async function updateCommand(opts) {
198
198
  log.dim(`stderr: ${result.stderr}`);
199
199
  }
200
200
  log.dim(
201
- "Try manually: claude plugin update coding-friend@coding-friend-marketplace"
201
+ "Try manually in Claude Code: /plugins \u2192 Installed \u2192 coding-friend \u2192 Update now \u2192 /reload-plugins"
202
202
  );
203
203
  }
204
204
  }
@@ -255,6 +255,8 @@ async function updateCommand(opts) {
255
255
 
256
256
  export {
257
257
  semverCompare,
258
+ getCliVersion,
259
+ getLatestCliVersion,
258
260
  getLatestVersion,
259
261
  updateCommand
260
262
  };
@@ -9,10 +9,33 @@ import {
9
9
  } from "./chunk-RWUTFVRB.js";
10
10
 
11
11
  // src/lib/config.ts
12
+ function deepMerge(base, override) {
13
+ const result = { ...base };
14
+ for (const key of Object.keys(override)) {
15
+ const baseVal = result[key];
16
+ const overVal = override[key];
17
+ if (baseVal && overVal && typeof baseVal === "object" && typeof overVal === "object" && !Array.isArray(baseVal) && !Array.isArray(overVal)) {
18
+ result[key] = deepMerge(
19
+ baseVal,
20
+ overVal
21
+ );
22
+ } else {
23
+ result[key] = overVal;
24
+ }
25
+ }
26
+ return result;
27
+ }
12
28
  function loadConfig() {
13
29
  const global = readJson(globalConfigPath());
14
30
  const local = readJson(localConfigPath());
15
- return { ...DEFAULT_CONFIG, ...global, ...local };
31
+ const base = deepMerge(
32
+ DEFAULT_CONFIG,
33
+ global ?? {}
34
+ );
35
+ return deepMerge(
36
+ base,
37
+ local ?? {}
38
+ );
16
39
  }
17
40
  function resolveDocsDir(explicitPath) {
18
41
  if (explicitPath) {
@@ -33,9 +56,6 @@ function resolveMemoryDir(explicitPath) {
33
56
  return resolvePath(explicitPath);
34
57
  }
35
58
  const config = loadConfig();
36
- if (config.memory?.docsDir) {
37
- return resolvePath(config.memory.docsDir);
38
- }
39
59
  if (config.docsDir) {
40
60
  return resolvePath(`${config.docsDir}/memory`);
41
61
  }
@@ -0,0 +1,331 @@
1
+ import {
2
+ getLibPath
3
+ } from "./chunk-RZRT7NGT.js";
4
+ import {
5
+ BACK,
6
+ askScope,
7
+ formatScopeLabel,
8
+ injectBackChoice
9
+ } from "./chunk-C5LYVVEI.js";
10
+ import {
11
+ globalConfigPath,
12
+ localConfigPath,
13
+ mergeJson,
14
+ readJson
15
+ } from "./chunk-RWUTFVRB.js";
16
+ import {
17
+ log
18
+ } from "./chunk-W5CD7WTX.js";
19
+
20
+ // src/lib/memory-prompts.ts
21
+ import { confirm, input, select } from "@inquirer/prompts";
22
+ import { join } from "path";
23
+ function getMemoryFieldScope(field, globalCfg, localCfg) {
24
+ const globalSection = globalCfg?.memory;
25
+ const localSection = localCfg?.memory;
26
+ const inGlobal = globalSection?.[field] !== void 0;
27
+ const inLocal = localSection?.[field] !== void 0;
28
+ if (inGlobal && inLocal) return "both";
29
+ if (inGlobal) return "global";
30
+ if (inLocal) return "local";
31
+ return "-";
32
+ }
33
+ function getMergedMemoryValue(field, globalCfg, localCfg) {
34
+ const localSection = localCfg?.memory;
35
+ if (localSection?.[field] !== void 0) return localSection[field];
36
+ const globalSection = globalCfg?.memory;
37
+ return globalSection?.[field];
38
+ }
39
+ function writeMemoryField(scope, field, value) {
40
+ const targetPath = scope === "global" ? globalConfigPath() : localConfigPath();
41
+ const existingConfig = readJson(targetPath);
42
+ const existingSection = existingConfig?.memory ?? {};
43
+ const updated = { ...existingSection, [field]: value };
44
+ mergeJson(targetPath, { memory: updated });
45
+ log.success(`Saved to ${targetPath}`);
46
+ }
47
+ async function editMemoryTier(globalCfg, localCfg) {
48
+ const currentValue = getMergedMemoryValue("tier", globalCfg, localCfg);
49
+ if (currentValue) {
50
+ log.dim(`Current: ${currentValue}`);
51
+ }
52
+ const choice = await select({
53
+ message: "Memory search tier:",
54
+ choices: injectBackChoice(
55
+ [
56
+ {
57
+ name: "auto \u2014 detect best available (recommended)",
58
+ value: "auto"
59
+ },
60
+ {
61
+ name: "full \u2014 SQLite + FTS5 + vector embeddings (Tier 1)",
62
+ value: "full"
63
+ },
64
+ {
65
+ name: "lite \u2014 MiniSearch daemon, in-memory BM25 + fuzzy (Tier 2)",
66
+ value: "lite"
67
+ },
68
+ {
69
+ name: "markdown \u2014 file-based substring search (Tier 3)",
70
+ value: "markdown"
71
+ }
72
+ ],
73
+ "Back"
74
+ )
75
+ });
76
+ if (choice === BACK) return;
77
+ const scope = await askScope();
78
+ if (scope === "back") return;
79
+ writeMemoryField(scope, "tier", choice);
80
+ }
81
+ async function editMemoryAutoCapture(globalCfg, localCfg) {
82
+ const currentValue = getMergedMemoryValue(
83
+ "autoCapture",
84
+ globalCfg,
85
+ localCfg
86
+ );
87
+ if (currentValue !== void 0) {
88
+ log.dim(`Current: ${currentValue}`);
89
+ }
90
+ const value = await confirm({
91
+ message: "Auto-capture session context to memory on PreCompact (context window compression)?",
92
+ default: currentValue ?? false
93
+ });
94
+ const scope = await askScope();
95
+ if (scope === "back") return;
96
+ writeMemoryField(scope, "autoCapture", value);
97
+ }
98
+ async function editMemoryAutoStart(globalCfg, localCfg) {
99
+ const currentValue = getMergedMemoryValue(
100
+ "autoStart",
101
+ globalCfg,
102
+ localCfg
103
+ );
104
+ if (currentValue !== void 0) {
105
+ log.dim(`Current: ${currentValue}`);
106
+ }
107
+ const value = await confirm({
108
+ message: "Auto-start memory daemon when MCP server connects?",
109
+ default: currentValue ?? false
110
+ });
111
+ const scope = await askScope();
112
+ if (scope === "back") return;
113
+ writeMemoryField(scope, "autoStart", value);
114
+ }
115
+ async function editMemoryEmbedding(globalCfg, localCfg) {
116
+ const currentEmbedding = getMergedMemoryValue(
117
+ "embedding",
118
+ globalCfg,
119
+ localCfg
120
+ );
121
+ if (currentEmbedding) {
122
+ const parts = [`provider: ${currentEmbedding.provider ?? "transformers"}`];
123
+ if (currentEmbedding.model) parts.push(`model: ${currentEmbedding.model}`);
124
+ if (currentEmbedding.ollamaUrl)
125
+ parts.push(`url: ${currentEmbedding.ollamaUrl}`);
126
+ log.dim(`Current: ${parts.join(", ")}`);
127
+ }
128
+ const provider = await select({
129
+ message: "Embedding provider:",
130
+ choices: injectBackChoice(
131
+ [
132
+ {
133
+ name: "transformers \u2014 Transformers.js, runs in-process (no external deps)",
134
+ value: "transformers"
135
+ },
136
+ {
137
+ name: "ollama \u2014 Local Ollama server (faster, GPU support, wider model selection)",
138
+ value: "ollama"
139
+ }
140
+ ],
141
+ "Back"
142
+ )
143
+ });
144
+ if (provider === BACK) return;
145
+ let model;
146
+ let ollamaUrl;
147
+ if (provider === "ollama") {
148
+ ollamaUrl = await input({
149
+ message: "Ollama server URL:",
150
+ default: currentEmbedding?.ollamaUrl ?? "http://localhost:11434"
151
+ });
152
+ if (ollamaUrl === "http://localhost:11434") ollamaUrl = void 0;
153
+ const mcpDir = getLibPath("cf-memory");
154
+ try {
155
+ const { isOllamaRunning, hasOllamaEmbeddingModel } = await import(join(mcpDir, "dist/lib/ollama.js"));
156
+ const url = ollamaUrl ?? "http://localhost:11434";
157
+ const running = await isOllamaRunning(url);
158
+ if (!running) {
159
+ console.log();
160
+ log.warn("Ollama is not running at " + url);
161
+ log.dim(
162
+ "Install Ollama: https://ollama.ai \xB7 Docs: https://cf.dinhanhthi.com/cli/cf-memory"
163
+ );
164
+ console.log();
165
+ const fallback = await confirm({
166
+ message: "Fall back to Transformers.js instead?",
167
+ default: true
168
+ });
169
+ if (fallback) {
170
+ const scope2 = await askScope();
171
+ if (scope2 === "back") return;
172
+ writeMemoryField(scope2, "embedding", { provider: "transformers" });
173
+ return;
174
+ }
175
+ } else {
176
+ model = await input({
177
+ message: "Ollama model name:",
178
+ default: currentEmbedding?.model ?? "all-minilm:l6-v2"
179
+ });
180
+ const hasModel = await hasOllamaEmbeddingModel(model, url);
181
+ if (!hasModel) {
182
+ console.log();
183
+ log.warn(`Model "${model}" not found in Ollama.`);
184
+ log.dim(`Pull it with: ollama pull ${model}`);
185
+ console.log();
186
+ const proceed = await confirm({
187
+ message: "Save this config anyway? (you can pull the model later)",
188
+ default: true
189
+ });
190
+ if (!proceed) return;
191
+ }
192
+ }
193
+ } catch {
194
+ model = await input({
195
+ message: "Ollama model name:",
196
+ default: currentEmbedding?.model ?? "all-minilm:l6-v2"
197
+ });
198
+ }
199
+ } else {
200
+ model = await input({
201
+ message: "Transformers.js model:",
202
+ default: currentEmbedding?.model ?? "Xenova/all-MiniLM-L6-v2"
203
+ });
204
+ if (model === "Xenova/all-MiniLM-L6-v2") model = void 0;
205
+ }
206
+ const scope = await askScope();
207
+ if (scope === "back") return;
208
+ const embedding = { provider };
209
+ if (model) embedding.model = model;
210
+ if (ollamaUrl) embedding.ollamaUrl = ollamaUrl;
211
+ writeMemoryField(scope, "embedding", embedding);
212
+ }
213
+ async function editMemoryDaemonTimeout(globalCfg, localCfg) {
214
+ const currentDaemon = getMergedMemoryValue("daemon", globalCfg, localCfg);
215
+ const currentMs = currentDaemon?.idleTimeout;
216
+ const currentMin = currentMs ? currentMs / 6e4 : void 0;
217
+ if (currentMin !== void 0) {
218
+ log.dim(`Current: ${currentMin} minutes`);
219
+ }
220
+ const value = await input({
221
+ message: "Daemon idle timeout (minutes):",
222
+ default: String(currentMin ?? 30),
223
+ validate: (val) => {
224
+ const n = Number(val);
225
+ if (isNaN(n) || n < 1) return "Must be a positive number";
226
+ return true;
227
+ }
228
+ });
229
+ const scope = await askScope();
230
+ if (scope === "back") return;
231
+ writeMemoryField(scope, "daemon", {
232
+ ...currentDaemon,
233
+ idleTimeout: Number(value) * 6e4
234
+ });
235
+ }
236
+ async function memoryConfigMenu(opts) {
237
+ while (true) {
238
+ const globalCfg = readJson(globalConfigPath());
239
+ const localCfg = readJson(localConfigPath());
240
+ const tierScope = getMemoryFieldScope("tier", globalCfg, localCfg);
241
+ const tierVal = getMergedMemoryValue("tier", globalCfg, localCfg);
242
+ const autoCaptureScope = getMemoryFieldScope(
243
+ "autoCapture",
244
+ globalCfg,
245
+ localCfg
246
+ );
247
+ const autoCaptureVal = getMergedMemoryValue(
248
+ "autoCapture",
249
+ globalCfg,
250
+ localCfg
251
+ );
252
+ const autoStartScope = getMemoryFieldScope(
253
+ "autoStart",
254
+ globalCfg,
255
+ localCfg
256
+ );
257
+ const autoStartVal = getMergedMemoryValue(
258
+ "autoStart",
259
+ globalCfg,
260
+ localCfg
261
+ );
262
+ const embeddingScope = getMemoryFieldScope(
263
+ "embedding",
264
+ globalCfg,
265
+ localCfg
266
+ );
267
+ const embeddingVal = getMergedMemoryValue(
268
+ "embedding",
269
+ globalCfg,
270
+ localCfg
271
+ );
272
+ const daemonScope = getMemoryFieldScope("daemon", globalCfg, localCfg);
273
+ const daemonVal = getMergedMemoryValue("daemon", globalCfg, localCfg);
274
+ const embeddingLabel = embeddingVal?.provider ? embeddingVal.model ? `${embeddingVal.model} (${embeddingVal.provider})` : embeddingVal.provider : "";
275
+ const choice = await select({
276
+ message: "Memory settings:",
277
+ choices: injectBackChoice(
278
+ [
279
+ {
280
+ name: `Tier ${formatScopeLabel(tierScope)}${tierVal ? ` (${tierVal})` : ""}`,
281
+ value: "tier"
282
+ },
283
+ {
284
+ name: `Auto-capture ${formatScopeLabel(autoCaptureScope)}${autoCaptureVal !== void 0 ? ` (${autoCaptureVal})` : ""}`,
285
+ value: "autoCapture"
286
+ },
287
+ {
288
+ name: `Auto-start daemon ${formatScopeLabel(autoStartScope)}${autoStartVal !== void 0 ? ` (${autoStartVal})` : ""}`,
289
+ value: "autoStart"
290
+ },
291
+ {
292
+ name: `Embedding ${formatScopeLabel(embeddingScope)}${embeddingLabel ? ` (${embeddingLabel})` : ""}`,
293
+ value: "embedding"
294
+ },
295
+ {
296
+ name: `Daemon timeout ${formatScopeLabel(daemonScope)}${daemonVal?.idleTimeout ? ` (${daemonVal.idleTimeout / 6e4}min)` : ""}`,
297
+ value: "daemon"
298
+ }
299
+ ],
300
+ opts?.exitLabel ?? "Back"
301
+ )
302
+ });
303
+ if (choice === BACK) return;
304
+ switch (choice) {
305
+ case "tier":
306
+ await editMemoryTier(globalCfg, localCfg);
307
+ break;
308
+ case "autoCapture":
309
+ await editMemoryAutoCapture(globalCfg, localCfg);
310
+ break;
311
+ case "autoStart":
312
+ await editMemoryAutoStart(globalCfg, localCfg);
313
+ break;
314
+ case "embedding":
315
+ await editMemoryEmbedding(globalCfg, localCfg);
316
+ break;
317
+ case "daemon":
318
+ await editMemoryDaemonTimeout(globalCfg, localCfg);
319
+ break;
320
+ }
321
+ }
322
+ }
323
+
324
+ export {
325
+ editMemoryTier,
326
+ editMemoryAutoCapture,
327
+ editMemoryAutoStart,
328
+ editMemoryEmbedding,
329
+ editMemoryDaemonTimeout,
330
+ memoryConfigMenu
331
+ };