docshark 0.1.12 ā 0.1.16
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/dist/cli-update.d.ts +10 -0
- package/dist/cli-update.js +186 -0
- package/dist/cli.js +254 -42
- package/dist/server.d.ts +7 -7
- package/dist/server.js +123 -113
- package/dist/services/library.d.ts +8 -3
- package/dist/services/library.js +42 -12
- package/dist/storage/db.d.ts +4 -3
- package/dist/storage/db.js +45 -24
- package/dist/tools/list-libraries.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +6 -2
- package/package.json +2 -2
- package/LICENSE +0 -21
- package/README.md +0 -167
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
|
|
|
@@ -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
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// src/cli.ts ā DocShark CLI entry point
|
|
3
|
-
import {
|
|
3
|
+
import { cac } from "cac";
|
|
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
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const useColor = process.stdout.isTTY;
|
|
10
|
+
const color = {
|
|
11
|
+
reset: "\x1b[0m",
|
|
12
|
+
bold: "\x1b[1m",
|
|
13
|
+
dim: "\x1b[2m",
|
|
14
|
+
cyan: "\x1b[36m",
|
|
15
|
+
yellow: "\x1b[33m",
|
|
16
|
+
gray: "\x1b[90m",
|
|
17
|
+
};
|
|
18
|
+
const cli = cac("docshark");
|
|
19
|
+
cli
|
|
20
|
+
.command("", "Start the MCP server")
|
|
21
|
+
.alias("start")
|
|
14
22
|
.alias("s")
|
|
15
|
-
.
|
|
16
|
-
.option("-p, --port <port>", "HTTP server port", "6380")
|
|
23
|
+
.option("-p, --port <port>", "HTTP server port", { default: "6380" })
|
|
17
24
|
.option("-S, --stdio", "Run in STDIO mode (for Claude Desktop, Cursor, etc.)")
|
|
18
|
-
.option("-D, --data-dir <path>", "Data directory"
|
|
25
|
+
.option("-D, --data-dir <path>", "Data directory")
|
|
19
26
|
.action(async (opts) => {
|
|
27
|
+
await maybeNotifyForCommand("start", opts.stdio === true);
|
|
20
28
|
if (opts.dataDir) {
|
|
21
29
|
process.env.DOCSHARK_DATA_DIR = opts.dataDir;
|
|
22
30
|
}
|
|
@@ -30,14 +38,70 @@ program
|
|
|
30
38
|
await startHttpServer(parseInt(opts.port));
|
|
31
39
|
}
|
|
32
40
|
});
|
|
33
|
-
|
|
34
|
-
|
|
41
|
+
const helpCommands = [
|
|
42
|
+
{
|
|
43
|
+
name: "start",
|
|
44
|
+
aliases: ["s", "-s"],
|
|
45
|
+
args: "",
|
|
46
|
+
description: "Start server",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "add",
|
|
50
|
+
aliases: ["a", "-a"],
|
|
51
|
+
args: "<url>",
|
|
52
|
+
description: "Add & crawl library",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "search",
|
|
56
|
+
aliases: ["f", "-f"],
|
|
57
|
+
args: "<query>",
|
|
58
|
+
description: "Search docs",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "list",
|
|
62
|
+
aliases: ["l", "-l"],
|
|
63
|
+
args: "",
|
|
64
|
+
description: "List libraries",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "refresh",
|
|
68
|
+
aliases: ["r", "-r"],
|
|
69
|
+
args: "<name>",
|
|
70
|
+
description: "Refresh library",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "remove",
|
|
74
|
+
aliases: ["rm", "-rm"],
|
|
75
|
+
args: "<name>",
|
|
76
|
+
description: "Remove library",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: "get",
|
|
80
|
+
aliases: ["g", "-g"],
|
|
81
|
+
args: "[url]",
|
|
82
|
+
description: "Get page markdown",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "update",
|
|
86
|
+
aliases: ["u", "-u"],
|
|
87
|
+
args: "",
|
|
88
|
+
description: "Update DocShark",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "info",
|
|
92
|
+
aliases: ["i", "-i"],
|
|
93
|
+
args: "<name>",
|
|
94
|
+
description: "Library info + pages",
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
cli
|
|
98
|
+
.command("add <url>", "Add a documentation library and start crawling")
|
|
35
99
|
.alias("a")
|
|
36
|
-
.description("Add a documentation library and start crawling (aliases: a, -a)")
|
|
37
100
|
.option("-n, --name <name>", "Library name (auto-generated from URL if omitted)")
|
|
38
|
-
.option("-d, --depth <n>", "Max crawl depth", "3")
|
|
101
|
+
.option("-d, --depth <n>", "Max crawl depth", { default: "3" })
|
|
39
102
|
.option("-V, --lib-version <version>", "Library version")
|
|
40
103
|
.action(async (url, opts) => {
|
|
104
|
+
await maybeNotifyForCommand("add");
|
|
41
105
|
db.init();
|
|
42
106
|
try {
|
|
43
107
|
const lib = await libraryService.add({
|
|
@@ -57,13 +121,36 @@ program
|
|
|
57
121
|
process.exit(1);
|
|
58
122
|
}
|
|
59
123
|
});
|
|
60
|
-
|
|
61
|
-
|
|
124
|
+
cli.command("help [command]", "Show help for a command").action((command) => {
|
|
125
|
+
if (command) {
|
|
126
|
+
printCommandHelp(command);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
printRootHelp();
|
|
130
|
+
});
|
|
131
|
+
cli
|
|
132
|
+
.command("rename <current-name> <new-name>", "Rename an existing documentation library")
|
|
133
|
+
.alias("mv")
|
|
134
|
+
.action(async (currentName, newName) => {
|
|
135
|
+
await maybeNotifyForCommand("rename");
|
|
136
|
+
db.init();
|
|
137
|
+
try {
|
|
138
|
+
const library = libraryService.rename({ currentName, newName });
|
|
139
|
+
console.log(`\nā
Renamed library to "${library.display_name}" (${library.name}).\n`);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
143
|
+
console.error(`\nā ${message}\n`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
cli
|
|
148
|
+
.command("search <query>", "Search indexed documentation")
|
|
62
149
|
.alias("f")
|
|
63
|
-
.description("Search indexed documentation (aliases: f, -f)")
|
|
64
150
|
.option("-l, --library <name>", "Filter by library")
|
|
65
|
-
.option("-m, --limit <n>", "Max results", "5")
|
|
151
|
+
.option("-m, --limit <n>", "Max results", { default: "5" })
|
|
66
152
|
.action(async (query, opts) => {
|
|
153
|
+
await maybeNotifyForCommand("search");
|
|
67
154
|
db.init();
|
|
68
155
|
const results = searchEngine.search(query, {
|
|
69
156
|
library: opts.library,
|
|
@@ -80,12 +167,12 @@ program
|
|
|
80
167
|
console.log(`Source: ${r.page_url}\n`);
|
|
81
168
|
}
|
|
82
169
|
});
|
|
83
|
-
|
|
84
|
-
.command("list")
|
|
170
|
+
cli
|
|
171
|
+
.command("list", "List indexed libraries")
|
|
85
172
|
.alias("l")
|
|
86
|
-
.
|
|
87
|
-
.
|
|
88
|
-
|
|
173
|
+
.option("-s, --status <status>", "Filter by status (indexed, crawling, error, all)", { default: "all" })
|
|
174
|
+
.action(async (opts) => {
|
|
175
|
+
await maybeNotifyForCommand("list");
|
|
89
176
|
db.init();
|
|
90
177
|
const libs = db.listLibraries(opts.status);
|
|
91
178
|
if (libs.length === 0) {
|
|
@@ -101,11 +188,11 @@ program
|
|
|
101
188
|
"Last Crawled": l.last_crawled_at || "never",
|
|
102
189
|
})));
|
|
103
190
|
});
|
|
104
|
-
|
|
105
|
-
.command("refresh <name>")
|
|
191
|
+
cli
|
|
192
|
+
.command("refresh <name>", "Refresh an existing documentation library")
|
|
106
193
|
.alias("r")
|
|
107
|
-
.description("Refresh an existing documentation library (aliases: r, -r)")
|
|
108
194
|
.action(async (name) => {
|
|
195
|
+
await maybeNotifyForCommand("refresh");
|
|
109
196
|
db.init();
|
|
110
197
|
try {
|
|
111
198
|
const lib = db.getLibraryByName(name);
|
|
@@ -122,11 +209,11 @@ program
|
|
|
122
209
|
process.exit(1);
|
|
123
210
|
}
|
|
124
211
|
});
|
|
125
|
-
|
|
126
|
-
.command("remove <name>")
|
|
212
|
+
cli
|
|
213
|
+
.command("remove <name>", "Remove a documentation library and its index")
|
|
127
214
|
.alias("rm")
|
|
128
|
-
.
|
|
129
|
-
|
|
215
|
+
.action(async (name) => {
|
|
216
|
+
await maybeNotifyForCommand("remove");
|
|
130
217
|
db.init();
|
|
131
218
|
try {
|
|
132
219
|
const lib = db.getLibraryByName(name);
|
|
@@ -140,13 +227,13 @@ program
|
|
|
140
227
|
process.exit(1);
|
|
141
228
|
}
|
|
142
229
|
});
|
|
143
|
-
|
|
144
|
-
.command("get [url]")
|
|
230
|
+
cli
|
|
231
|
+
.command("get [url]", "Get the full markdown content of a specific indexed page")
|
|
145
232
|
.alias("g")
|
|
146
|
-
.description("Get the full markdown content of a specific indexed page (aliases: g, -g)")
|
|
147
233
|
.option("-l, --library <name>", "Library name to search within")
|
|
148
234
|
.option("-p, --path <path>", "Relative path within the library")
|
|
149
|
-
.action((url, opts) => {
|
|
235
|
+
.action(async (url, opts) => {
|
|
236
|
+
await maybeNotifyForCommand("get");
|
|
150
237
|
if (!url && (!opts.library || !opts.path)) {
|
|
151
238
|
console.error(`\nā Please provide either a URL, or both --library and --path\n`);
|
|
152
239
|
process.exit(1);
|
|
@@ -162,8 +249,20 @@ program
|
|
|
162
249
|
console.log(page.content_markdown);
|
|
163
250
|
console.log("\n");
|
|
164
251
|
});
|
|
252
|
+
cli
|
|
253
|
+
.command("update", "Update the global Bun installation of DocShark")
|
|
254
|
+
.alias("u")
|
|
255
|
+
.option("-c, --check", "Only check whether a newer DocShark version is available")
|
|
256
|
+
.option("-q, --quiet", "Suppress DocShark status output and rely on exit codes")
|
|
257
|
+
.action(async (opts) => {
|
|
258
|
+
await maybeNotifyForCommand("update");
|
|
259
|
+
await runUpdateCommand({
|
|
260
|
+
checkOnly: opts.check,
|
|
261
|
+
quiet: opts.quiet,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
165
264
|
// Intercept manual short flags (e.g., -l instead of l) so they act as command aliases
|
|
166
|
-
const args = process.argv;
|
|
265
|
+
const args = process.argv.slice(2);
|
|
167
266
|
const cmdAliases = {
|
|
168
267
|
"-s": "start",
|
|
169
268
|
"-a": "add",
|
|
@@ -173,15 +272,31 @@ const cmdAliases = {
|
|
|
173
272
|
"-rm": "remove",
|
|
174
273
|
"-g": "get",
|
|
175
274
|
"-i": "info",
|
|
275
|
+
"-u": "update",
|
|
176
276
|
};
|
|
177
|
-
|
|
178
|
-
|
|
277
|
+
const normalizedArgs = [...args];
|
|
278
|
+
if (normalizedArgs[0] && cmdAliases[normalizedArgs[0]]) {
|
|
279
|
+
normalizedArgs[0] = cmdAliases[normalizedArgs[0]];
|
|
280
|
+
}
|
|
281
|
+
const helpRequest = getHelpRequest(normalizedArgs);
|
|
282
|
+
if (helpRequest === "root") {
|
|
283
|
+
printRootHelp();
|
|
284
|
+
process.exit(0);
|
|
285
|
+
}
|
|
286
|
+
if (helpRequest && helpRequest !== "root") {
|
|
287
|
+
printCommandHelp(helpRequest);
|
|
288
|
+
process.exit(0);
|
|
179
289
|
}
|
|
180
|
-
|
|
181
|
-
|
|
290
|
+
if (normalizedArgs.includes("-v") || normalizedArgs.includes("--version")) {
|
|
291
|
+
printVersion();
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
const parseArgv = [process.argv[0], process.argv[1], ...normalizedArgs];
|
|
295
|
+
cli
|
|
296
|
+
.command("info <name>", "Get information about a library and list its pages")
|
|
182
297
|
.alias("i")
|
|
183
|
-
.
|
|
184
|
-
|
|
298
|
+
.action(async (name) => {
|
|
299
|
+
await maybeNotifyForCommand("info");
|
|
185
300
|
db.init();
|
|
186
301
|
const lib = db.getLibraryByName(name);
|
|
187
302
|
if (!lib) {
|
|
@@ -207,7 +322,13 @@ program
|
|
|
207
322
|
console.log(`\nNo pages found for this library.\n`);
|
|
208
323
|
}
|
|
209
324
|
});
|
|
210
|
-
|
|
325
|
+
try {
|
|
326
|
+
cli.parse(parseArgv, { run: false });
|
|
327
|
+
await cli.runMatchedCommand();
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
handleCliError(error);
|
|
331
|
+
}
|
|
211
332
|
/** Helper to wait for a crawl job to finish (CLI blocking mode) */
|
|
212
333
|
async function waitForCrawl(jobId) {
|
|
213
334
|
const { jobManager } = await import("./server.js");
|
|
@@ -232,3 +353,94 @@ async function waitForCrawl(jobId) {
|
|
|
232
353
|
check();
|
|
233
354
|
});
|
|
234
355
|
}
|
|
356
|
+
async function maybeNotifyForCommand(commandName, stdioMode = false) {
|
|
357
|
+
await maybeNotifyAboutUpdate({ commandName, stdioMode });
|
|
358
|
+
}
|
|
359
|
+
function getHelpRequest(args) {
|
|
360
|
+
if (args.length === 0) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
if (args[0] === "-h" || args[0] === "--help") {
|
|
364
|
+
return "root";
|
|
365
|
+
}
|
|
366
|
+
if (args[0] === "help") {
|
|
367
|
+
return args[1] ? normalizeCommandName(args[1]) : "root";
|
|
368
|
+
}
|
|
369
|
+
if (args[1] === "help") {
|
|
370
|
+
return normalizeCommandName(args[0]);
|
|
371
|
+
}
|
|
372
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
373
|
+
return normalizeCommandName(args[0]);
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
function normalizeCommandName(name) {
|
|
378
|
+
return cmdAliases[name] ?? name;
|
|
379
|
+
}
|
|
380
|
+
function printVersion() {
|
|
381
|
+
console.log(`${paint("DocShark", color.cyan)} ${VERSION}`);
|
|
382
|
+
}
|
|
383
|
+
function printRootHelp() {
|
|
384
|
+
printHeader();
|
|
385
|
+
console.log(`${paint("USAGE", color.gray)}`);
|
|
386
|
+
console.log(` docshark [options] [command]\n`);
|
|
387
|
+
console.log(`${paint("OPTIONS", color.gray)}`);
|
|
388
|
+
console.log(` ${paint("-v, --version", color.cyan).padEnd(18)} Show version`);
|
|
389
|
+
console.log(` ${paint("-h, --help", color.cyan).padEnd(18)} Show this help\n`);
|
|
390
|
+
console.log(`${paint("COMMANDS", color.gray)}`);
|
|
391
|
+
const rows = helpCommands.map((command) => ({
|
|
392
|
+
primary: [
|
|
393
|
+
command.name,
|
|
394
|
+
...command.aliases.filter((alias) => !alias.startsWith("-")),
|
|
395
|
+
].join(", "),
|
|
396
|
+
shortAliases: command.aliases.filter((alias) => alias.startsWith("-")),
|
|
397
|
+
args: command.args,
|
|
398
|
+
description: command.description,
|
|
399
|
+
}));
|
|
400
|
+
const primaryWidth = Math.max(...rows.map((row) => row.primary.length));
|
|
401
|
+
const argsWidth = Math.max(...rows.map((row) => row.args.length));
|
|
402
|
+
for (const row of rows) {
|
|
403
|
+
const aliasSuffix = row.shortAliases.length > 0
|
|
404
|
+
? ` [aliases: ${row.shortAliases.join(", ")}]`
|
|
405
|
+
: "";
|
|
406
|
+
const label = `${row.primary.padEnd(primaryWidth)}${row.args ? ` ${row.args.padEnd(argsWidth)}` : `${"".padEnd(argsWidth + 1)}`}${aliasSuffix}`.trimEnd();
|
|
407
|
+
console.log(` ${paint(label.padEnd(36), color.cyan)} ${row.description}`);
|
|
408
|
+
}
|
|
409
|
+
console.log(`\n${paint("Run `docshark help <command>` for more information.", color.dim)}`);
|
|
410
|
+
}
|
|
411
|
+
function printCommandHelp(commandName) {
|
|
412
|
+
const command = helpCommands.find((item) => item.name === normalizeCommandName(commandName));
|
|
413
|
+
printHeader();
|
|
414
|
+
if (!command) {
|
|
415
|
+
console.log(`${paint(`Unknown command: ${commandName}`, color.cyan)}\n`);
|
|
416
|
+
printRootHelp();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
console.log(`${paint("USAGE", color.gray)}`);
|
|
420
|
+
console.log(` docshark ${command.name} ${command.args ? paint(command.args, color.yellow) : ""}`.trimEnd());
|
|
421
|
+
console.log(``);
|
|
422
|
+
console.log(`${paint("ALIASES", color.gray)}`);
|
|
423
|
+
console.log(` ${command.aliases.join(", ")}\n`);
|
|
424
|
+
console.log(`${paint("SUMMARY", color.gray)}`);
|
|
425
|
+
console.log(` ${command.description}\n`);
|
|
426
|
+
console.log(`${paint("Run `docshark help` to see all commands.", color.dim)}`);
|
|
427
|
+
}
|
|
428
|
+
function printHeader() {
|
|
429
|
+
console.log();
|
|
430
|
+
console.log(`${paint("š¦ DocShark", color.cyan)} ${paint("Documentation MCP Server", color.bold)}`);
|
|
431
|
+
console.log(` ${paint("Scrape ⢠Index ⢠Search any docs site", color.dim)}\n`);
|
|
432
|
+
}
|
|
433
|
+
function paint(text, code) {
|
|
434
|
+
if (!useColor) {
|
|
435
|
+
return text;
|
|
436
|
+
}
|
|
437
|
+
return `${code}${text}${color.reset}`;
|
|
438
|
+
}
|
|
439
|
+
function handleCliError(error) {
|
|
440
|
+
const message = error instanceof Error ? error.message : "Unknown command error";
|
|
441
|
+
const prettyMessage = message.startsWith("Unused args:")
|
|
442
|
+
? "Too many arguments passed. Run `docshark help <command>` for usage."
|
|
443
|
+
: message;
|
|
444
|
+
console.error(`\nā ${prettyMessage}\n`);
|
|
445
|
+
process.exit(1);
|
|
446
|
+
}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { McpServer } from
|
|
2
|
-
import * as v from
|
|
3
|
-
import { Database } from
|
|
4
|
-
import { SearchEngine } from
|
|
5
|
-
import { LibraryService } from
|
|
6
|
-
import { JobManager } from
|
|
7
|
-
import { EventBus } from
|
|
1
|
+
import { McpServer } from "tmcp";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import { Database } from "./storage/db.js";
|
|
4
|
+
import { SearchEngine } from "./storage/search.js";
|
|
5
|
+
import { LibraryService } from "./services/library.js";
|
|
6
|
+
import { JobManager } from "./jobs/manager.js";
|
|
7
|
+
import { EventBus } from "./jobs/events.js";
|
|
8
8
|
export declare const db: Database;
|
|
9
9
|
export declare const eventBus: EventBus;
|
|
10
10
|
export declare const searchEngine: SearchEngine;
|