Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [0.25.3] - 2026-06-15

### Fixed — the installed `deepdive` command was a silent no-op

- **`npm i -g @askalf/deepdive` then `deepdive …` ran nothing** (exit 0, no output); only `node dist/cli.js …` worked. npm installs the bin as a symlink, so `process.argv[1]` was the symlink path while `import.meta.url` was the resolved module path — the entry-point guard compared them raw, never matched, and skipped `main()`. The guard now compares `realpathSync(argv[1])` to `realpathSync(import.meta.url)`, so `deepdive`, `npx deepdive`, and `node dist/cli.js` all run while imports (tests) still don't. Pinned by `test/cli-entrypoint.test.mjs` (spawns the CLI through a symlink). Went unnoticed because the bench and tests invoke `node dist/cli.js` directly, never the installed bin.

## [0.25.2] - 2026-06-15

### Fixed — synthesis reliability on long / stalling generations
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,12 @@ One command, aggregated health report. Paste the output when filing issues.

```bash
$ deepdive doctor
deepdive doctor — v0.25.2
deepdive doctor — v0.25.3

# environment
OK Node v22.21.1
--- Platform win32 x64
--- deepdive v0.25.2
--- deepdive v0.25.3

# cache
--- dir ~/.deepdive/cache
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@askalf/deepdive",
"version": "0.25.2",
"version": "0.25.3",
"description": "own your research — local agent, cited answers. Part of Own Your Stack.",
"type": "module",
"bin": {
Expand Down
14 changes: 12 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// Prints the cited markdown report to stdout (or JSON with --json). Progress
// events go to stderr when --verbose is set or DEEPDIVE_VERBOSE=1.

import { writeFileSync } from "node:fs";
import { realpathSync, writeFileSync } from "node:fs";
import { resolve, join } from "node:path";
import { tmpdir } from "node:os";
import { spawn } from "node:child_process";
Expand Down Expand Up @@ -1785,7 +1785,17 @@ const isEntryPoint =
process.argv[1] !== undefined &&
(() => {
try {
return process.argv[1] === fileURLToPath(import.meta.url);
// Resolve symlinks before comparing: npm installs the bin as a symlink,
// so `process.argv[1]` is the symlink path (…/bin/deepdive) while
// `import.meta.url` is the real module path (…/dist/cli.js). Comparing
// raw paths made the installed `deepdive` command a silent no-op — it
// never matched, so main() never ran (#109). realpathSync collapses both
// to the canonical file, so `deepdive`, `npx deepdive`, and
// `node dist/cli.js` all run main while imports (tests) still don't.
return (
realpathSync(process.argv[1]) ===
realpathSync(fileURLToPath(import.meta.url))
);
} catch {
return false;
}
Expand Down
36 changes: 36 additions & 0 deletions test/cli-entrypoint.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Regression for #109: npm installs the `deepdive` bin as a symlink to
// dist/cli.js. The entry-point guard must still run main() under symlink
// invocation — comparing raw paths (argv[1] vs import.meta.url) made the
// installed command a silent no-op. We assert the symlinked invocation both
// exits 0 AND prints output (the no-op exited 0 with empty stdout).

import test from "node:test";
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdtempSync, symlinkSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const CLI = resolve(fileURLToPath(import.meta.url), "..", "..", "dist", "cli.js");

test(
"cli entry point: runs main() when invoked through a symlink (#109)",
{ skip: process.platform === "win32" ? "symlinks need privileges on Windows" : false },
() => {
const dir = mkdtempSync(join(tmpdir(), "dd-bin-"));
const link = join(dir, "deepdive"); // mimic the npm bin symlink
try {
symlinkSync(CLI, link);
const r = spawnSync(process.execPath, [link, "--version"], { encoding: "utf8" });
assert.equal(r.status, 0, `exit 0 expected; stderr: ${r.stderr}`);
assert.match(
r.stdout.trim(),
/^\d+\.\d+\.\d+/,
`symlinked invocation must print a version (the #109 bug left this empty), got: ${JSON.stringify(r.stdout)}`,
);
} finally {
rmSync(dir, { recursive: true, force: true });
}
},
);