docdex 0.2.4 → 0.2.6

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.6
4
4
  - Added glama support
5
5
 
6
6
  ## 0.1.10
@@ -381,27 +381,53 @@ 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 isWindows = process.platform === "win32";
387
+ const inputPath = isWindows ? "CONIN$" : "/dev/tty";
388
+ const outputPath = isWindows ? "CONOUT$" : "/dev/tty";
389
+ try {
390
+ const readFd = fs.openSync(inputPath, "r");
391
+ const writeFd = fs.openSync(outputPath, "w");
392
+ fs.closeSync(readFd);
393
+ fs.closeSync(writeFd);
394
+ return true;
395
+ } catch {
396
+ return false;
397
+ }
398
+ }
399
+
400
+ function resolveOllamaInstallMode({
401
+ env = process.env,
402
+ stdin = process.stdin,
403
+ stdout = process.stdout,
404
+ canPrompt = canPromptWithTty
405
+ } = {}) {
385
406
  const override = parseEnvBool(env.DOCDEX_OLLAMA_INSTALL);
386
407
  if (override === true) return { mode: "install", reason: "env", interactive: false };
387
408
  if (override === false) return { mode: "skip", reason: "env", interactive: false };
388
- if (!hasInteractiveTty(stdin, stdout)) {
409
+ if (!canPrompt(stdin, stdout)) {
410
+ if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
389
411
  return { mode: "skip", reason: "non_interactive", interactive: false };
390
412
  }
391
- if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
392
413
  return { mode: "prompt", reason: "interactive", interactive: true };
393
414
  }
394
415
 
395
- function resolveOllamaModelPromptMode({ env = process.env, stdin = process.stdin, stdout = process.stdout } = {}) {
416
+ function resolveOllamaModelPromptMode({
417
+ env = process.env,
418
+ stdin = process.stdin,
419
+ stdout = process.stdout,
420
+ canPrompt = canPromptWithTty
421
+ } = {}) {
396
422
  const override = parseEnvBool(env.DOCDEX_OLLAMA_MODEL_PROMPT);
397
423
  if (override === true) return { mode: "prompt", reason: "env", interactive: true };
398
424
  if (override === false) return { mode: "skip", reason: "env", interactive: false };
399
425
  const assumeYes = parseEnvBool(env.DOCDEX_OLLAMA_MODEL_ASSUME_Y);
400
426
  if (assumeYes === true) return { mode: "auto", reason: "env", interactive: false };
401
- if (!hasInteractiveTty(stdin, stdout)) {
427
+ if (!canPrompt(stdin, stdout)) {
428
+ if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
402
429
  return { mode: "skip", reason: "non_interactive", interactive: false };
403
430
  }
404
- if (env.CI) return { mode: "skip", reason: "ci", interactive: false };
405
431
  return { mode: "prompt", reason: "interactive", interactive: true };
406
432
  }
407
433
 
@@ -563,10 +589,19 @@ function resolvePromptStreams(stdin, stdout) {
563
589
  return { input: stdin, output: stdout, close: null };
564
590
  }
565
591
  const isWindows = process.platform === "win32";
566
- const ttyPath = isWindows ? "CONIN$" : "/dev/tty";
592
+ const inputPath = isWindows ? "CONIN$" : "/dev/tty";
593
+ const outputPath = isWindows ? "CONOUT$" : "/dev/tty";
567
594
  try {
568
- const input = fs.createReadStream(ttyPath, { autoClose: true });
569
- return { input, output: stdout, close: () => input.close() };
595
+ const input = fs.createReadStream(inputPath, { autoClose: true });
596
+ const output = fs.createWriteStream(outputPath, { autoClose: true });
597
+ return {
598
+ input,
599
+ output,
600
+ close: () => {
601
+ input.close();
602
+ output.end();
603
+ }
604
+ };
570
605
  } catch {
571
606
  return { input: stdin, output: stdout, close: null };
572
607
  }
@@ -1050,5 +1085,6 @@ module.exports = {
1050
1085
  upsertLlmDefaultModel,
1051
1086
  pullOllamaModel,
1052
1087
  listOllamaModels,
1053
- hasInteractiveTty
1088
+ hasInteractiveTty,
1089
+ canPromptWithTty
1054
1090
  };
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.6",
4
4
  "description": "Docdex CLI as an npm-installable binary wrapper.",
5
5
  "bin": {
6
6
  "docdex": "bin/docdex.js",