docdex 0.2.36 → 0.2.39
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 +9 -0
- package/assets/agents.md +7 -5
- package/lib/install.js +12 -2
- package/lib/paths.js +10 -1
- package/lib/postinstall_setup.js +151 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/assets/agents.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
---- START OF DOCDEX INFO V0.2.
|
|
1
|
+
---- START OF DOCDEX INFO V0.2.39 ----
|
|
2
2
|
Docdex URL: http://127.0.0.1:28491
|
|
3
3
|
Use this base URL for Docdex HTTP endpoints.
|
|
4
4
|
Health check endpoint: `GET /healthz` (not `/v1/health`).
|
|
@@ -6,6 +6,7 @@ Health check endpoint: `GET /healthz` (not `/v1/health`).
|
|
|
6
6
|
## Priority Directives (Mandatory)
|
|
7
7
|
|
|
8
8
|
- Always use both memory lobes early: docdex_get_profile + docdex_memory_recall; save new facts via docdex_memory_save and new preferences via docdex_save_preference.
|
|
9
|
+
- Profile memory is always-on and global; never gate on DOCDEX_ENABLE_MEMORY or repo state. docdex_save_preference must persist (fallback if embeddings/LLM unavailable). If profile calls fail, treat it as a bug.
|
|
9
10
|
- Maintain agent-specific profile memory: use your agent_id with docdex_get_profile/docdex_save_preference; store generic self-knowledge (role, tooling, preferences).
|
|
10
11
|
- Use impact analysis for every code change: prefer MCP tools `docdex_impact_graph` / `docdex_dag_export` (IPC/HTTP). If shell networking is blocked, do not use curl; use MCP/IPC instead. If unavailable, state it and proceed cautiously.
|
|
11
12
|
- Apply DAG reasoning for planning: prefer dependency graph facts (impact results and /v1/dag/export) to choose the right change order and scope.
|
|
@@ -250,7 +251,7 @@ Query params:
|
|
|
250
251
|
|
|
251
252
|
- SSE: `/v1/mcp/sse` + `/v1/mcp/message`. When multiple repos are mounted, initialize with `rootUri` first.
|
|
252
253
|
- HTTP: `/v1/mcp` accepts repo context in the payload or via prior initialize.
|
|
253
|
-
- If HTTP/SSE is unreachable (sandboxed clients), fall back to local IPC: configure `transport = "ipc"` with `socket_path` (Unix) or `pipe_name` (Windows) and send MCP JSON-RPC to `/v1/mcp` over IPC.
|
|
254
|
+
- If HTTP/SSE is unreachable or fails (e.g., connection refused or sandboxed clients), fall back to local IPC: configure `transport = "ipc"` with `socket_path` (Unix) or `pipe_name` (Windows) and send MCP JSON-RPC to `/v1/mcp` over IPC.
|
|
254
255
|
- For stdio-only clients (e.g., Smithery), use the `docdex-mcp-stdio` entrypoint to bridge stdio JSON-RPC to Docdex MCP.
|
|
255
256
|
- For impact/DAG in sandboxed shells, prefer MCP/IPC tools over `curl` to `/v1/graph/impact` or `/v1/dag/export`.
|
|
256
257
|
- MCP tools: `docdex_impact_graph` (impact traversal) and `docdex_dag_export` (DAG export).
|
|
@@ -304,9 +305,10 @@ When answering a complex coding query, follow this "Reasoning Trace":
|
|
|
304
305
|
Save more memories for both lobes during the task, not just at the end.
|
|
305
306
|
|
|
306
307
|
1. Repo memory: After each meaningful discovery or code change, save at least one durable fact (file location, behavior, config, gotcha) via `docdex_memory_save`.
|
|
307
|
-
2.
|
|
308
|
-
3.
|
|
309
|
-
4.
|
|
308
|
+
2. Memory overrides: When a new repo memory replaces older facts, include `metadata.supersedes` with the prior memory id(s). Docdex marks the superseded entries with `supersededBy`/`supersededAtMs`, down-ranks them during recall, and they can be removed via `docdex memory compact` (dry-run unless `--apply`).
|
|
309
|
+
3. Profile memory: When the user expresses a preference, constraint, or workflow correction, call `docdex_save_preference` immediately with the right category.
|
|
310
|
+
4. Keep it crisp: 1-3 short sentences, include file paths when relevant, avoid raw code blobs.
|
|
311
|
+
5. Safety: Never store secrets, tokens, or sensitive user data. Skip transient or speculative info.
|
|
310
312
|
|
|
311
313
|
### 3. Index Health + Diff-Aware Search (Mandatory)
|
|
312
314
|
|
package/lib/install.js
CHANGED
|
@@ -2258,7 +2258,7 @@ async function main() {
|
|
|
2258
2258
|
}
|
|
2259
2259
|
const result = await runInstaller({ env, distBaseDir });
|
|
2260
2260
|
try {
|
|
2261
|
-
const skipDaemon =
|
|
2261
|
+
const skipDaemon = parseEnvBool(env?.DOCDEX_DAEMON_SKIP_SETUP);
|
|
2262
2262
|
await runPostInstallSetup({ binaryPath: result?.binaryPath, env, skipDaemon, distBaseDir });
|
|
2263
2263
|
} catch (err) {
|
|
2264
2264
|
console.warn(`[docdex] postinstall setup failed: ${err?.message || err}`);
|
|
@@ -2286,6 +2286,14 @@ function printPostInstallBanner() {
|
|
|
2286
2286
|
return false;
|
|
2287
2287
|
}
|
|
2288
2288
|
};
|
|
2289
|
+
const writeStderr = (message) => {
|
|
2290
|
+
try {
|
|
2291
|
+
process.stderr.write(message);
|
|
2292
|
+
return true;
|
|
2293
|
+
} catch {
|
|
2294
|
+
return false;
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2289
2297
|
let width = 0;
|
|
2290
2298
|
const content = [
|
|
2291
2299
|
"\x1b[31m _ _ \x1b[0m",
|
|
@@ -2320,7 +2328,9 @@ function printPostInstallBanner() {
|
|
|
2320
2328
|
lines.push(bottom);
|
|
2321
2329
|
const banner = `\r\x1b[2K${lines.join("\n")}\n`;
|
|
2322
2330
|
if (!writeDirect(banner)) {
|
|
2323
|
-
|
|
2331
|
+
if (!writeStderr(banner)) {
|
|
2332
|
+
console.log(banner);
|
|
2333
|
+
}
|
|
2324
2334
|
}
|
|
2325
2335
|
}
|
|
2326
2336
|
|
package/lib/paths.js
CHANGED
|
@@ -102,11 +102,20 @@ function resolveWindowsRunnerPath(options = {}) {
|
|
|
102
102
|
return pathModule.join(resolveDocdexDataDir({ ...options, pathModule }), "run-daemon.cmd");
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
function resolveWindowsSetupRunnerPath(options = {}) {
|
|
106
|
+
const pathModule = options.pathModule || path;
|
|
107
|
+
if (options.distBaseDir) {
|
|
108
|
+
return pathModule.join(pathModule.dirname(options.distBaseDir), "run-setup.cmd");
|
|
109
|
+
}
|
|
110
|
+
return pathModule.join(resolveDocdexDataDir({ ...options, pathModule }), "run-setup.cmd");
|
|
111
|
+
}
|
|
112
|
+
|
|
105
113
|
module.exports = {
|
|
106
114
|
resolveUserDataDir,
|
|
107
115
|
resolveDocdexDataDir,
|
|
108
116
|
resolveDistBaseCandidates,
|
|
109
117
|
resolveDistBaseDir,
|
|
110
118
|
resolveBinDir,
|
|
111
|
-
resolveWindowsRunnerPath
|
|
119
|
+
resolveWindowsRunnerPath,
|
|
120
|
+
resolveWindowsSetupRunnerPath
|
|
112
121
|
};
|
package/lib/postinstall_setup.js
CHANGED
|
@@ -15,7 +15,8 @@ const {
|
|
|
15
15
|
resolveDistBaseDir,
|
|
16
16
|
resolveDistBaseCandidates,
|
|
17
17
|
resolveBinDir,
|
|
18
|
-
resolveWindowsRunnerPath
|
|
18
|
+
resolveWindowsRunnerPath,
|
|
19
|
+
resolveWindowsSetupRunnerPath
|
|
19
20
|
} = require("./paths");
|
|
20
21
|
|
|
21
22
|
const DEFAULT_HOST = "127.0.0.1";
|
|
@@ -313,6 +314,73 @@ function stopDaemonService({ logger } = {}) {
|
|
|
313
314
|
return false;
|
|
314
315
|
}
|
|
315
316
|
|
|
317
|
+
function removeDaemonService({ logger, distBaseDir, env } = {}) {
|
|
318
|
+
if (process.platform === "darwin") {
|
|
319
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
320
|
+
const domain = uid != null ? `gui/${uid}` : null;
|
|
321
|
+
const plistPath = path.join(os.homedir(), "Library", "LaunchAgents", "com.docdex.daemon.plist");
|
|
322
|
+
const systemPlistPath = "/Library/LaunchDaemons/com.docdex.daemon.plist";
|
|
323
|
+
if (domain) {
|
|
324
|
+
spawnSync("launchctl", ["bootout", domain, "com.docdex.daemon"]);
|
|
325
|
+
spawnSync("launchctl", ["bootout", domain, plistPath]);
|
|
326
|
+
} else {
|
|
327
|
+
spawnSync("launchctl", ["bootout", "com.docdex.daemon"]);
|
|
328
|
+
spawnSync("launchctl", ["bootout", plistPath]);
|
|
329
|
+
}
|
|
330
|
+
spawnSync("launchctl", ["remove", "com.docdex.daemon"]);
|
|
331
|
+
spawnSync("launchctl", ["unload", "-w", plistPath]);
|
|
332
|
+
if (fs.existsSync(plistPath)) {
|
|
333
|
+
try {
|
|
334
|
+
fs.unlinkSync(plistPath);
|
|
335
|
+
} catch (err) {
|
|
336
|
+
logger?.warn?.(`[docdex] failed to remove LaunchAgent plist: ${err?.message || err}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (fs.existsSync(systemPlistPath)) {
|
|
340
|
+
spawnSync("launchctl", ["bootout", "system", systemPlistPath]);
|
|
341
|
+
spawnSync("launchctl", ["remove", "com.docdex.daemon"]);
|
|
342
|
+
try {
|
|
343
|
+
fs.unlinkSync(systemPlistPath);
|
|
344
|
+
} catch (err) {
|
|
345
|
+
logger?.warn?.(`[docdex] failed to remove LaunchDaemon plist: ${err?.message || err}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if (process.platform === "linux") {
|
|
351
|
+
const systemdDir = path.join(os.homedir(), ".config", "systemd", "user");
|
|
352
|
+
const unitPath = path.join(systemdDir, "docdexd.service");
|
|
353
|
+
spawnSync("systemctl", ["--user", "stop", "docdexd.service"]);
|
|
354
|
+
spawnSync("systemctl", ["--user", "disable", "--now", "docdexd.service"]);
|
|
355
|
+
spawnSync("systemctl", ["--user", "reset-failed", "docdexd.service"]);
|
|
356
|
+
if (fs.existsSync(unitPath)) {
|
|
357
|
+
try {
|
|
358
|
+
fs.unlinkSync(unitPath);
|
|
359
|
+
} catch (err) {
|
|
360
|
+
logger?.warn?.(`[docdex] failed to remove systemd unit: ${err?.message || err}`);
|
|
361
|
+
}
|
|
362
|
+
spawnSync("systemctl", ["--user", "daemon-reload"]);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
if (process.platform === "win32") {
|
|
367
|
+
const taskName = "Docdex Daemon";
|
|
368
|
+
spawnSync("schtasks", ["/End", "/TN", taskName]);
|
|
369
|
+
spawnSync("schtasks", ["/Delete", "/TN", taskName, "/F"]);
|
|
370
|
+
const resolvedDistBaseDir = distBaseDir || resolveDistBaseDir({ env, fsModule: fs });
|
|
371
|
+
const runnerPath = resolveWindowsRunnerPath({ distBaseDir: resolvedDistBaseDir, pathModule: path });
|
|
372
|
+
if (runnerPath && fs.existsSync(runnerPath)) {
|
|
373
|
+
try {
|
|
374
|
+
fs.unlinkSync(runnerPath);
|
|
375
|
+
} catch (err) {
|
|
376
|
+
logger?.warn?.(`[docdex] failed to remove Windows runner: ${err?.message || err}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
316
384
|
function startDaemonService({ logger } = {}) {
|
|
317
385
|
if (process.platform === "darwin") {
|
|
318
386
|
const uid = typeof process.getuid === "function" ? process.getuid() : null;
|
|
@@ -356,6 +424,21 @@ function stopDaemonByName({ logger } = {}) {
|
|
|
356
424
|
return true;
|
|
357
425
|
}
|
|
358
426
|
|
|
427
|
+
async function cleanupExistingDaemon({ logger, env, distBaseDir, host, port } = {}) {
|
|
428
|
+
const log = logger || console;
|
|
429
|
+
stopDaemonService({ logger: log });
|
|
430
|
+
stopDaemonFromLock({ logger: log });
|
|
431
|
+
stopDaemonByName({ logger: log });
|
|
432
|
+
removeDaemonService({ logger: log, distBaseDir, env });
|
|
433
|
+
clearDaemonLocks();
|
|
434
|
+
if (!host || !port) return true;
|
|
435
|
+
const released = await waitForPortAvailable({ host, port });
|
|
436
|
+
if (!released) {
|
|
437
|
+
log.warn?.(`[docdex] ${host}:${port} still in use after daemon cleanup.`);
|
|
438
|
+
}
|
|
439
|
+
return released;
|
|
440
|
+
}
|
|
441
|
+
|
|
359
442
|
function clearDaemonLocks() {
|
|
360
443
|
let removed = false;
|
|
361
444
|
for (const lockPath of daemonLockPaths()) {
|
|
@@ -2179,6 +2262,27 @@ function writeWindowsRunner({ binaryPath, args, envPairs, workingDir, logger, di
|
|
|
2179
2262
|
}
|
|
2180
2263
|
}
|
|
2181
2264
|
|
|
2265
|
+
function writeWindowsSetupRunner({ binaryPath, args, logger, distBaseDir } = {}) {
|
|
2266
|
+
if (!binaryPath) return null;
|
|
2267
|
+
const runnerPath = resolveWindowsSetupRunnerPath({ distBaseDir });
|
|
2268
|
+
const lines = [
|
|
2269
|
+
"@echo off",
|
|
2270
|
+
"setlocal",
|
|
2271
|
+
"set \"DOCDEX_SETUP_AUTO=1\"",
|
|
2272
|
+
"set \"DOCDEX_SETUP_MODE=auto\""
|
|
2273
|
+
];
|
|
2274
|
+
const argString = (args || []).map((arg) => escapeCmdArg(arg)).join(" ");
|
|
2275
|
+
lines.push(`${escapeCmdArg(binaryPath)} ${argString}`.trim());
|
|
2276
|
+
try {
|
|
2277
|
+
fs.mkdirSync(path.dirname(runnerPath), { recursive: true });
|
|
2278
|
+
fs.writeFileSync(runnerPath, `${lines.join("\r\n")}\r\n`);
|
|
2279
|
+
return runnerPath;
|
|
2280
|
+
} catch (err) {
|
|
2281
|
+
logger?.warn?.(`[docdex] failed to write Windows setup runner: ${err?.message || err}`);
|
|
2282
|
+
return null;
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2182
2286
|
function registerStartup({ binaryPath, port, repoRoot, logger, distBaseDir }) {
|
|
2183
2287
|
if (!binaryPath) return { ok: false, reason: "missing_binary" };
|
|
2184
2288
|
stopDaemonService({ logger });
|
|
@@ -2360,7 +2464,6 @@ function isNpmLifecycle(env = process.env) {
|
|
|
2360
2464
|
|
|
2361
2465
|
function shouldSkipDaemonSideEffects({ env = process.env, skipDaemon } = {}) {
|
|
2362
2466
|
if (skipDaemon) return true;
|
|
2363
|
-
if (isNpmLifecycle(env)) return true;
|
|
2364
2467
|
if (parseEnvBool(env?.DOCDEX_DAEMON_SKIP_SETUP)) return true;
|
|
2365
2468
|
return false;
|
|
2366
2469
|
}
|
|
@@ -2433,7 +2536,8 @@ function launchSetupWizard({
|
|
|
2433
2536
|
spawnFn = spawn,
|
|
2434
2537
|
spawnSyncFn = spawnSync,
|
|
2435
2538
|
platform = process.platform,
|
|
2436
|
-
canPrompt = canPromptWithTty
|
|
2539
|
+
canPrompt = canPromptWithTty,
|
|
2540
|
+
distBaseDir
|
|
2437
2541
|
}) {
|
|
2438
2542
|
if (!binaryPath) return { ok: false, reason: "missing_binary" };
|
|
2439
2543
|
if (shouldSkipSetup(env)) return { ok: false, reason: "skipped" };
|
|
@@ -2454,9 +2558,21 @@ function launchSetupWizard({
|
|
|
2454
2558
|
}
|
|
2455
2559
|
|
|
2456
2560
|
if (platform === "win32") {
|
|
2457
|
-
const
|
|
2458
|
-
|
|
2459
|
-
|
|
2561
|
+
const runnerPath = writeWindowsSetupRunner({
|
|
2562
|
+
binaryPath,
|
|
2563
|
+
args,
|
|
2564
|
+
logger,
|
|
2565
|
+
distBaseDir
|
|
2566
|
+
});
|
|
2567
|
+
if (!runnerPath) return { ok: false, reason: "runner_failed" };
|
|
2568
|
+
const result = spawnSyncFn("cmd", [
|
|
2569
|
+
"/c",
|
|
2570
|
+
"start",
|
|
2571
|
+
"",
|
|
2572
|
+
"cmd",
|
|
2573
|
+
"/c",
|
|
2574
|
+
escapeCmdArg(runnerPath)
|
|
2575
|
+
]);
|
|
2460
2576
|
if (result.status === 0) return { ok: true };
|
|
2461
2577
|
logger?.warn?.(`[docdex] cmd start failed: ${result.stderr || "unknown error"}`);
|
|
2462
2578
|
return { ok: false, reason: "terminal_launch_failed" };
|
|
@@ -2478,6 +2594,22 @@ async function runPostInstallSetup({ binaryPath, logger, env, skipDaemon, distBa
|
|
|
2478
2594
|
}
|
|
2479
2595
|
const port = DEFAULT_DAEMON_PORT;
|
|
2480
2596
|
let portState = { available: true, reuseExisting: false };
|
|
2597
|
+
if (allowDaemon) {
|
|
2598
|
+
const cleaned = await cleanupExistingDaemon({
|
|
2599
|
+
logger: log,
|
|
2600
|
+
env: effectiveEnv,
|
|
2601
|
+
distBaseDir: resolvedDistBaseDir,
|
|
2602
|
+
host: DEFAULT_HOST,
|
|
2603
|
+
port
|
|
2604
|
+
});
|
|
2605
|
+
if (!cleaned) {
|
|
2606
|
+
log.warn?.(
|
|
2607
|
+
`[docdex] ${DEFAULT_HOST}:${port} is still in use after removing the daemon service; skipping daemon startup.`
|
|
2608
|
+
);
|
|
2609
|
+
recordStartupFailure({ reason: "port_in_use", host: DEFAULT_HOST, port });
|
|
2610
|
+
allowDaemon = false;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2481
2613
|
if (allowDaemon) {
|
|
2482
2614
|
portState = await resolveDaemonPortState({
|
|
2483
2615
|
host: DEFAULT_HOST,
|
|
@@ -2584,13 +2716,20 @@ async function runPostInstallSetup({ binaryPath, logger, env, skipDaemon, distBa
|
|
|
2584
2716
|
if (startupOk) {
|
|
2585
2717
|
clearStartupFailure();
|
|
2586
2718
|
}
|
|
2587
|
-
const
|
|
2719
|
+
const skipExplicit = shouldSkipSetup(effectiveEnv);
|
|
2720
|
+
const skipWizard = isNpmLifecycle(effectiveEnv) || skipExplicit;
|
|
2588
2721
|
const setupLaunch = skipWizard
|
|
2589
|
-
? { ok: false, reason: "skipped" }
|
|
2590
|
-
: launchSetupWizard({
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2722
|
+
? { ok: false, reason: skipExplicit ? "skipped" : "npm_lifecycle" }
|
|
2723
|
+
: launchSetupWizard({
|
|
2724
|
+
binaryPath: startupBinaries.binaryPath,
|
|
2725
|
+
logger: log,
|
|
2726
|
+
distBaseDir: resolvedDistBaseDir
|
|
2727
|
+
});
|
|
2728
|
+
if (!setupLaunch.ok) {
|
|
2729
|
+
if (setupLaunch.reason !== "skipped") {
|
|
2730
|
+
log.warn?.("[docdex] setup wizard did not launch. Run `docdex setup`.");
|
|
2731
|
+
recordSetupPending({ reason: setupLaunch.reason, port, repoRoot: daemonRoot });
|
|
2732
|
+
}
|
|
2594
2733
|
}
|
|
2595
2734
|
return { port, url, configPath };
|
|
2596
2735
|
}
|