docshark 0.1.12 → 0.1.13

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,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.13](https://github.com/Michael-Obele/docshark/compare/v0.1.12...v0.1.13) (2026-03-12)
4
+
5
+
6
+ ### ✨ Features
7
+
8
+ * add workflow to cleanup release-please branches on pull request closure ([b250d35](https://github.com/Michael-Obele/docshark/commit/b250d352df810998ed4eb9a1876bf5149c2e9d7d))
9
+ * enhance update command with check and quiet options in CLI ([9f91aba](https://github.com/Michael-Obele/docshark/commit/9f91abafccaa21c9fb43dba5a5548b473a4dc266))
10
+ * implement Bun-first update command and version notification in CLI ([ce31a13](https://github.com/Michael-Obele/docshark/commit/ce31a132d91e3302699afe51f6295a9b0b815e20))
11
+
3
12
  ## [0.1.12](https://github.com/Michael-Obele/docshark/compare/v0.1.11...v0.1.12) (2026-03-12)
4
13
 
5
14
 
package/README.md CHANGED
@@ -64,11 +64,10 @@ bunx docshark search "schema validation"
64
64
 
65
65
  To install DocShark globally as a CLI tool:
66
66
 
67
- ```bash
68
- # Using npm
69
- npm install -g docshark
67
+ DocShark is intended to be installed and run with Bun.
70
68
 
71
- # Using Bun
69
+ ```bash
70
+ # Global Bun installation
72
71
  bun add -g docshark
73
72
  ```
74
73
 
@@ -76,8 +75,18 @@ After installation, you can use the `docshark` command:
76
75
 
77
76
  ```bash
78
77
  docshark list
78
+
79
+ # Update the global Bun installation when a new release is published
80
+ docshark update
81
+
82
+ # Script-friendly update check
83
+ docshark update --check --quiet
79
84
  ```
80
85
 
86
+ Interactive CLI runs will also let you know when a newer version is available. Update notices are intentionally skipped for MCP `stdio` mode so they never interfere with protocol output.
87
+
88
+ For scripts, `docshark update --check` exits `0` when current, `10` when a newer version is available, and `1` when the version check could not be completed.
89
+
81
90
  ## 🔌 MCP Integration
82
91
 
83
92
  ### VS Code (GitHub Copilot / MCP Extension)
@@ -0,0 +1,10 @@
1
+ type RunUpdateOptions = {
2
+ checkOnly?: boolean;
3
+ quiet?: boolean;
4
+ };
5
+ export declare function maybeNotifyAboutUpdate(options: {
6
+ commandName: string;
7
+ stdioMode: boolean;
8
+ }): Promise<void>;
9
+ export declare function runUpdateCommand(options?: RunUpdateOptions): Promise<void>;
10
+ export {};
@@ -0,0 +1,186 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { basename, dirname, join } from "node:path";
5
+ import { VERSION } from "./version.js";
6
+ const PACKAGE_NAME = "docshark";
7
+ const REGISTRY_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
8
+ const UPDATE_CHECK_TTL_MS = 12 * 60 * 60 * 1000;
9
+ const REQUEST_TIMEOUT_MS = 2500;
10
+ export async function maybeNotifyAboutUpdate(options) {
11
+ if (shouldSkipUpdateNotice(options)) {
12
+ return;
13
+ }
14
+ const latestVersion = await getLatestVersion();
15
+ if (!latestVersion || compareVersions(latestVersion, VERSION) <= 0) {
16
+ return;
17
+ }
18
+ console.error(`\nA newer DocShark version is available: ${VERSION} -> ${latestVersion}.`);
19
+ console.error(`Run \"docshark update\" or \"bun add -g ${PACKAGE_NAME}@latest\".\n`);
20
+ }
21
+ export async function runUpdateCommand(options = {}) {
22
+ const latestVersion = await getLatestVersion({ forceRefresh: true });
23
+ if (!latestVersion) {
24
+ printFallbackUpdateCommand("Could not check the npm registry for the latest DocShark release.", options.quiet);
25
+ process.exit(1);
26
+ return;
27
+ }
28
+ const hasUpdate = compareVersions(latestVersion, VERSION) > 0;
29
+ if (options.checkOnly) {
30
+ if (!options.quiet) {
31
+ if (hasUpdate) {
32
+ console.log(`\nUpdate available: ${VERSION} -> ${latestVersion}.\n`);
33
+ }
34
+ else {
35
+ console.log(`\nDocShark is already up to date (${VERSION}).\n`);
36
+ }
37
+ }
38
+ process.exit(hasUpdate ? 10 : 0);
39
+ return;
40
+ }
41
+ if (!hasUpdate) {
42
+ if (!options.quiet) {
43
+ console.log(`\nDocShark is already up to date (${VERSION}).\n`);
44
+ }
45
+ return;
46
+ }
47
+ const bunPath = resolveBunExecutable();
48
+ if (!bunPath) {
49
+ printFallbackUpdateCommand(`A newer version is available (${VERSION} -> ${latestVersion}), but Bun was not detected on PATH.`, options.quiet);
50
+ process.exit(1);
51
+ return;
52
+ }
53
+ if (!options.quiet) {
54
+ console.log(`\nUpdating DocShark ${VERSION} -> ${latestVersion} with Bun...\n`);
55
+ }
56
+ const exitCode = await spawnProcess(bunPath, [
57
+ "add",
58
+ "-g",
59
+ `${PACKAGE_NAME}@latest`,
60
+ ]);
61
+ if (exitCode !== 0) {
62
+ printFallbackUpdateCommand(`The Bun update command exited with code ${exitCode}.`, options.quiet);
63
+ process.exit(exitCode ?? 1);
64
+ }
65
+ if (!options.quiet) {
66
+ console.log(`\nDocShark was updated to ${latestVersion}.\n`);
67
+ }
68
+ }
69
+ function shouldSkipUpdateNotice(options) {
70
+ if (options.stdioMode || options.commandName === "update") {
71
+ return true;
72
+ }
73
+ if (!process.stdout.isTTY || !process.stderr.isTTY || process.env.CI) {
74
+ return true;
75
+ }
76
+ const rawFlag = process.env.DOCSHARK_DISABLE_UPDATE_CHECK?.trim().toLowerCase();
77
+ return rawFlag === "1" || rawFlag === "true" || rawFlag === "yes";
78
+ }
79
+ async function getLatestVersion(options) {
80
+ if (!options?.forceRefresh) {
81
+ const cached = await readCachedUpdateCheck();
82
+ if (cached && Date.now() - cached.checkedAt < UPDATE_CHECK_TTL_MS) {
83
+ return cached.latestVersion;
84
+ }
85
+ }
86
+ const latestVersion = await fetchLatestVersion();
87
+ if (!latestVersion) {
88
+ const cached = await readCachedUpdateCheck();
89
+ return cached?.latestVersion ?? null;
90
+ }
91
+ await writeCachedUpdateCheck({
92
+ latestVersion,
93
+ checkedAt: Date.now(),
94
+ });
95
+ return latestVersion;
96
+ }
97
+ async function fetchLatestVersion() {
98
+ try {
99
+ const response = await fetch(REGISTRY_LATEST_URL, {
100
+ headers: { accept: "application/json" },
101
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
102
+ });
103
+ if (!response.ok) {
104
+ return null;
105
+ }
106
+ const payload = (await response.json());
107
+ return typeof payload.version === "string" ? payload.version : null;
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ }
113
+ async function readCachedUpdateCheck() {
114
+ try {
115
+ const contents = await readFile(getUpdateCachePath(), "utf8");
116
+ const parsed = JSON.parse(contents);
117
+ if (typeof parsed.latestVersion !== "string" ||
118
+ typeof parsed.checkedAt !== "number") {
119
+ return null;
120
+ }
121
+ return {
122
+ latestVersion: parsed.latestVersion,
123
+ checkedAt: parsed.checkedAt,
124
+ };
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ async function writeCachedUpdateCheck(cache) {
131
+ try {
132
+ const cachePath = getUpdateCachePath();
133
+ await mkdir(dirname(cachePath), { recursive: true });
134
+ await writeFile(cachePath, JSON.stringify(cache), "utf8");
135
+ }
136
+ catch {
137
+ // Best-effort cache only.
138
+ }
139
+ }
140
+ function getUpdateCachePath() {
141
+ const baseCacheDir = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
142
+ return join(baseCacheDir, PACKAGE_NAME, "update-check.json");
143
+ }
144
+ function resolveBunExecutable() {
145
+ if (typeof Bun !== "undefined") {
146
+ const bunOnPath = Bun.which("bun");
147
+ if (bunOnPath) {
148
+ return bunOnPath;
149
+ }
150
+ }
151
+ return basename(process.execPath).startsWith("bun") ? process.execPath : null;
152
+ }
153
+ function compareVersions(left, right) {
154
+ const leftParts = left.split(".");
155
+ const rightParts = right.split(".");
156
+ const length = Math.max(leftParts.length, rightParts.length);
157
+ for (let index = 0; index < length; index += 1) {
158
+ const leftValue = parseNumericVersionPart(leftParts[index]);
159
+ const rightValue = parseNumericVersionPart(rightParts[index]);
160
+ if (leftValue !== rightValue) {
161
+ return leftValue - rightValue;
162
+ }
163
+ }
164
+ return 0;
165
+ }
166
+ function parseNumericVersionPart(part) {
167
+ if (!part) {
168
+ return 0;
169
+ }
170
+ const match = part.match(/^(\d+)/);
171
+ return match ? Number.parseInt(match[1], 10) : 0;
172
+ }
173
+ function spawnProcess(command, args) {
174
+ return new Promise((resolve, reject) => {
175
+ const child = spawn(command, args, { stdio: "inherit" });
176
+ child.on("error", reject);
177
+ child.on("exit", (code) => resolve(code));
178
+ });
179
+ }
180
+ function printFallbackUpdateCommand(reason, quiet = false) {
181
+ if (quiet) {
182
+ return;
183
+ }
184
+ console.error(`\n${reason}`);
185
+ console.error(`Run \"bun add -g ${PACKAGE_NAME}@latest\" to update DocShark.\n`);
186
+ }
package/dist/cli.js CHANGED
@@ -4,6 +4,7 @@ import { Command } from "commander";
4
4
  import { startHttpServer } from "./http.js";
5
5
  import { StdioTransport } from "@tmcp/transport-stdio";
6
6
  import { server, db, searchEngine, libraryService } from "./server.js";
7
+ import { maybeNotifyAboutUpdate, runUpdateCommand } from "./cli-update.js";
7
8
  import { VERSION } from "./version.js";
8
9
  const program = new Command()
9
10
  .name("docshark")
@@ -162,6 +163,18 @@ program
162
163
  console.log(page.content_markdown);
163
164
  console.log("\n");
164
165
  });
166
+ program
167
+ .command("update")
168
+ .alias("u")
169
+ .description("Update the global Bun installation of DocShark (aliases: u, -u)")
170
+ .option("-c, --check", "Only check whether a newer DocShark version is available")
171
+ .option("-q, --quiet", "Suppress DocShark status output and rely on exit codes")
172
+ .action(async (opts) => {
173
+ await runUpdateCommand({
174
+ checkOnly: opts.check,
175
+ quiet: opts.quiet,
176
+ });
177
+ });
165
178
  // Intercept manual short flags (e.g., -l instead of l) so they act as command aliases
166
179
  const args = process.argv;
167
180
  const cmdAliases = {
@@ -173,6 +186,7 @@ const cmdAliases = {
173
186
  "-rm": "remove",
174
187
  "-g": "get",
175
188
  "-i": "info",
189
+ "-u": "update",
176
190
  };
177
191
  if (args[2] && cmdAliases[args[2]]) {
178
192
  args[2] = cmdAliases[args[2]];
@@ -207,7 +221,17 @@ program
207
221
  console.log(`\nNo pages found for this library.\n`);
208
222
  }
209
223
  });
210
- program.parse(args);
224
+ program.hook("preAction", async (_thisCommand, actionCommand) => {
225
+ const commandName = actionCommand.name();
226
+ const options = typeof actionCommand.opts === "function"
227
+ ? actionCommand.opts()
228
+ : {};
229
+ await maybeNotifyAboutUpdate({
230
+ commandName,
231
+ stdioMode: commandName === "start" && options.stdio === true,
232
+ });
233
+ });
234
+ await program.parseAsync(args);
211
235
  /** Helper to wait for a crawl job to finish (CLI blocking mode) */
212
236
  async function waitForCrawl(jobId) {
213
237
  const { jobManager } = await import("./server.js");
@@ -5,7 +5,7 @@ export declare function createListLibrariesTool(db: Database): {
5
5
  name: "list_libraries";
6
6
  description: string;
7
7
  schema: v.ObjectSchema<{
8
- readonly status: v.OptionalSchema<v.SchemaWithPipe<readonly [v.PicklistSchema<["indexed", "crawling", "error", "all"], undefined>, v.DescriptionAction<"crawling" | "indexed" | "error" | "all", "Filter by indexing status. Default: \"all\".">]>, "all">;
8
+ readonly status: v.OptionalSchema<v.SchemaWithPipe<readonly [v.PicklistSchema<["indexed", "crawling", "error", "all"], undefined>, v.DescriptionAction<"error" | "crawling" | "indexed" | "all", "Filter by indexing status. Default: \"all\".">]>, "all">;
9
9
  }, undefined>;
10
10
  };
11
11
  handler: ({ status }: {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.12";
1
+ export declare const VERSION = "0.1.13";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is automatically updated by release-please
2
- export const VERSION = '0.1.12'; // x-release-please-version
2
+ export const VERSION = '0.1.13'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docshark",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "🦈 Documentation MCP Server — scrape, index, and search any doc website",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",