docdex 0.2.4 → 0.2.5

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
@@ -1,6 +1,6 @@
1
1
  # Changelog
2
2
 
3
- ## 0.2.4
3
+ ## 0.2.5
4
4
  - Added glama support
5
5
 
6
6
  ## 0.1.10
@@ -381,24 +381,46 @@ function hasInteractiveTty(stdin, stdout) {
381
381
  return Boolean((stdin && stdin.isTTY) || (stdout && stdout.isTTY));
382
382
  }
383
383
 
384
- function resolveOllamaInstallMode({ env = process.env, stdin = process.stdin, stdout = process.stdout } = {}) {
384
+ function canPromptWithTty(stdin, stdout) {
385
+ if (hasInteractiveTty(stdin, stdout)) return true;
386
+ const ttyPath = process.platform === "win32" ? "CONIN$" : "/dev/tty";
387
+ try {
388
+ const fd = fs.openSync(ttyPath, "r");
389
+ fs.closeSync(fd);
390
+ return true;
391
+ } catch {
392
+ return false;
393
+ }
394
+ }
395
+
396
+ function resolveOllamaInstallMode({
397
+ env = process.env,
398
+ stdin = process.stdin,
399
+ stdout = process.stdout,
400
+ canPrompt = canPromptWithTty
401
+ } = {}) {
385
402
  const override = parseEnvBool(env.DOCDEX_OLLAMA_INSTALL);
386
403
  if (override === true) return { mode: "install", reason: "env", interactive: false };
387
404
  if (override === false) return { mode: "skip", reason: "env", interactive: false };
388
- if (!hasInteractiveTty(stdin, stdout)) {
405
+ if (!canPrompt(stdin, stdout)) {
389
406
  return { mode: "skip", reason: "non_interactive", interactive: false };
390
407
  }
391
408
  if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
392
409
  return { mode: "prompt", reason: "interactive", interactive: true };
393
410
  }
394
411
 
395
- function resolveOllamaModelPromptMode({ env = process.env, stdin = process.stdin, stdout = process.stdout } = {}) {
412
+ function resolveOllamaModelPromptMode({
413
+ env = process.env,
414
+ stdin = process.stdin,
415
+ stdout = process.stdout,
416
+ canPrompt = canPromptWithTty
417
+ } = {}) {
396
418
  const override = parseEnvBool(env.DOCDEX_OLLAMA_MODEL_PROMPT);
397
419
  if (override === true) return { mode: "prompt", reason: "env", interactive: true };
398
420
  if (override === false) return { mode: "skip", reason: "env", interactive: false };
399
421
  const assumeYes = parseEnvBool(env.DOCDEX_OLLAMA_MODEL_ASSUME_Y);
400
422
  if (assumeYes === true) return { mode: "auto", reason: "env", interactive: false };
401
- if (!hasInteractiveTty(stdin, stdout)) {
423
+ if (!canPrompt(stdin, stdout)) {
402
424
  return { mode: "skip", reason: "non_interactive", interactive: false };
403
425
  }
404
426
  if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
@@ -1050,5 +1072,6 @@ module.exports = {
1050
1072
  upsertLlmDefaultModel,
1051
1073
  pullOllamaModel,
1052
1074
  listOllamaModels,
1053
- hasInteractiveTty
1075
+ hasInteractiveTty,
1076
+ canPromptWithTty
1054
1077
  };
package/lib/uninstall.js CHANGED
@@ -18,6 +18,8 @@ function stateDir() {
18
18
  }
19
19
 
20
20
  function daemonLockPath() {
21
+ const override = process.env.DOCDEX_DAEMON_LOCK_PATH;
22
+ if (override && override.trim()) return override.trim();
21
23
  return path.join(os.homedir(), ".docdex", "daemon.lock");
22
24
  }
23
25
 
@@ -28,21 +30,51 @@ function clientConfigPaths() {
28
30
  switch (process.platform) {
29
31
  case "win32":
30
32
  return {
31
- claude: path.join(appData, "Claude", "claude_desktop_config.json"),
32
- cursor: path.join(userProfile, ".cursor", "mcp.json"),
33
- codex: path.join(userProfile, ".codex", "config.toml")
33
+ json: [
34
+ path.join(appData, "Claude", "claude_desktop_config.json"),
35
+ path.join(userProfile, ".cursor", "mcp.json"),
36
+ path.join(userProfile, ".cursor", "settings.json"),
37
+ path.join(userProfile, ".codeium", "windsurf", "mcp_config.json"),
38
+ path.join(appData, "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
39
+ path.join(appData, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings", "mcp_settings.json"),
40
+ path.join(userProfile, ".continue", "config.json"),
41
+ path.join(userProfile, ".kiro", "settings", "mcp.json"),
42
+ path.join(appData, "Zed", "settings.json")
43
+ ],
44
+ toml: [path.join(userProfile, ".codex", "config.toml")],
45
+ yaml: [path.join(appData, "Aider", "config.yml")]
34
46
  };
35
47
  case "darwin":
36
48
  return {
37
- claude: path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
38
- cursor: path.join(home, ".cursor", "mcp.json"),
39
- codex: path.join(home, ".codex", "config.toml")
49
+ json: [
50
+ path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
51
+ path.join(home, ".cursor", "mcp.json"),
52
+ path.join(home, ".cursor", "settings.json"),
53
+ path.join(home, ".codeium", "windsurf", "mcp_config.json"),
54
+ path.join(home, "Library", "Application Support", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
55
+ path.join(home, "Library", "Application Support", "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings", "mcp_settings.json"),
56
+ path.join(home, ".continue", "config.json"),
57
+ path.join(home, ".kiro", "settings", "mcp.json"),
58
+ path.join(home, ".config", "zed", "settings.json")
59
+ ],
60
+ toml: [path.join(home, ".codex", "config.toml")],
61
+ yaml: [path.join(home, ".config", "aider", "config.yml"), path.join(home, ".aider.conf.yml")]
40
62
  };
41
63
  default:
42
64
  return {
43
- claude: path.join(home, ".config", "Claude", "claude_desktop_config.json"),
44
- cursor: path.join(home, ".cursor", "mcp.json"),
45
- codex: path.join(home, ".codex", "config.toml")
65
+ json: [
66
+ path.join(home, ".config", "Claude", "claude_desktop_config.json"),
67
+ path.join(home, ".cursor", "mcp.json"),
68
+ path.join(home, ".cursor", "settings.json"),
69
+ path.join(home, ".codeium", "windsurf", "mcp_config.json"),
70
+ path.join(home, ".config", "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "settings", "cline_mcp_settings.json"),
71
+ path.join(home, ".config", "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "settings", "mcp_settings.json"),
72
+ path.join(home, ".continue", "config.json"),
73
+ path.join(home, ".kiro", "settings", "mcp.json"),
74
+ path.join(home, ".config", "zed", "settings.json")
75
+ ],
76
+ toml: [path.join(home, ".codex", "config.toml")],
77
+ yaml: [path.join(home, ".config", "aider", "config.yml"), path.join(home, ".aider.conf.yml")]
46
78
  };
47
79
  }
48
80
  }
@@ -67,12 +99,17 @@ function removeMcpServerJson(pathname, name = "docdex") {
67
99
  const { value, exists } = readJson(pathname);
68
100
  if (!exists || typeof value !== "object" || value == null || Array.isArray(value)) return false;
69
101
  const root = value;
70
- if (!root.mcpServers || typeof root.mcpServers !== "object" || Array.isArray(root.mcpServers)) {
71
- return false;
102
+ const keys = ["mcpServers", "mcp_servers"];
103
+ let changed = false;
104
+ for (const key of keys) {
105
+ const section = root[key];
106
+ if (!section || typeof section !== "object" || Array.isArray(section)) continue;
107
+ if (!Object.prototype.hasOwnProperty.call(section, name)) continue;
108
+ delete section[name];
109
+ changed = true;
110
+ if (Object.keys(section).length === 0) delete root[key];
72
111
  }
73
- if (!Object.prototype.hasOwnProperty.call(root.mcpServers, name)) return false;
74
- delete root.mcpServers[name];
75
- if (Object.keys(root.mcpServers).length === 0) delete root.mcpServers;
112
+ if (!changed) return false;
76
113
  writeJson(pathname, root);
77
114
  return true;
78
115
  }
@@ -180,6 +217,71 @@ function removeCodexConfig(pathname, name = "docdex") {
180
217
  return false;
181
218
  }
182
219
 
220
+ function removeMcpServerYaml(pathname, name = "docdex") {
221
+ if (!fs.existsSync(pathname)) return false;
222
+ const original = fs.readFileSync(pathname, "utf8");
223
+ const lines = original.split(/\r?\n/);
224
+ const output = [];
225
+ let inSection = false;
226
+ let sectionIndent = null;
227
+ let skipIndent = null;
228
+ let changed = false;
229
+
230
+ const indentSize = (line) => (line.match(/^\s*/)?.[0].length ?? 0);
231
+
232
+ for (const line of lines) {
233
+ if (skipIndent != null) {
234
+ if (line.trim() && indentSize(line) <= skipIndent) {
235
+ skipIndent = null;
236
+ } else {
237
+ changed = true;
238
+ continue;
239
+ }
240
+ }
241
+
242
+ if (!inSection) {
243
+ if (/^\s*mcp_servers\s*:\s*$/.test(line)) {
244
+ inSection = true;
245
+ sectionIndent = indentSize(line);
246
+ }
247
+ output.push(line);
248
+ continue;
249
+ }
250
+
251
+ if (line.trim() && indentSize(line) <= sectionIndent) {
252
+ inSection = false;
253
+ output.push(line);
254
+ continue;
255
+ }
256
+
257
+ if (new RegExp(`^\\s*${name}\\s*:`).test(line)) {
258
+ changed = true;
259
+ skipIndent = indentSize(line);
260
+ continue;
261
+ }
262
+
263
+ const listName = line.match(/^\s*-\s*name\s*:\s*(.+)\s*$/);
264
+ if (listName && listName[1].replace(/["']/g, "").trim() === name) {
265
+ changed = true;
266
+ skipIndent = indentSize(line);
267
+ continue;
268
+ }
269
+
270
+ const listValue = line.match(/^\s*-\s*([^\s#]+)\s*$/);
271
+ if (listValue && listValue[1].replace(/["']/g, "").trim() === name) {
272
+ changed = true;
273
+ continue;
274
+ }
275
+
276
+ output.push(line);
277
+ }
278
+
279
+ if (changed) {
280
+ fs.writeFileSync(pathname, output.join("\n"));
281
+ }
282
+ return changed;
283
+ }
284
+
183
285
  function killPid(pid) {
184
286
  if (!pid) return false;
185
287
  try {
@@ -209,6 +311,16 @@ function stopDaemonFromLock() {
209
311
  }
210
312
  }
211
313
 
314
+ function stopDaemonByName() {
315
+ if (process.platform === "win32") {
316
+ spawnSync("taskkill", ["/IM", "docdexd.exe", "/T", "/F"]);
317
+ return true;
318
+ }
319
+ spawnSync("pkill", ["-TERM", "-x", "docdexd"]);
320
+ spawnSync("pkill", ["-TERM", "-f", "docdexd daemon"]);
321
+ return true;
322
+ }
323
+
212
324
  function unregisterStartup() {
213
325
  if (process.platform === "darwin") {
214
326
  const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.docdex.daemon.plist");
@@ -259,23 +371,34 @@ function clearStartupFailure() {
259
371
 
260
372
  function removeDaemonRootNotice() {
261
373
  const root = daemonRootPath();
262
- const readme = path.join(root, "README.txt");
263
- if (fs.existsSync(readme)) {
264
- try {
265
- fs.unlinkSync(readme);
266
- } catch {}
374
+ const readmes = [path.join(root, "README.txt"), path.join(root, "README.md")];
375
+ for (const readme of readmes) {
376
+ if (fs.existsSync(readme)) {
377
+ try {
378
+ fs.unlinkSync(readme);
379
+ } catch {}
380
+ }
267
381
  }
268
382
  }
269
383
 
270
384
  function removeClientConfigs() {
271
385
  const paths = clientConfigPaths();
272
- removeMcpServerJson(paths.claude);
273
- removeMcpServerJson(paths.cursor);
274
- removeCodexConfig(paths.codex);
386
+ for (const pathname of paths.json || []) {
387
+ removeMcpServerJson(pathname);
388
+ }
389
+ for (const pathname of paths.toml || []) {
390
+ removeCodexConfig(pathname);
391
+ }
392
+ for (const pathname of paths.yaml || []) {
393
+ removeMcpServerYaml(pathname);
394
+ }
275
395
  }
276
396
 
277
397
  async function main() {
278
- stopDaemonFromLock();
398
+ const stopped = stopDaemonFromLock();
399
+ if (!stopped) {
400
+ stopDaemonByName();
401
+ }
279
402
  unregisterStartup();
280
403
  removeClientConfigs();
281
404
  clearStartupFailure();
@@ -289,7 +412,9 @@ if (require.main === module) {
289
412
  module.exports = {
290
413
  removeMcpServerJson,
291
414
  removeCodexConfig,
415
+ removeMcpServerYaml,
292
416
  stopDaemonFromLock,
417
+ stopDaemonByName,
293
418
  unregisterStartup,
294
419
  removeClientConfigs
295
420
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docdex",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Docdex CLI as an npm-installable binary wrapper.",
5
5
  "bin": {
6
6
  "docdex": "bin/docdex.js",