readthemanual 0.0.1 → 0.0.2
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/README.md +12 -4
- package/dist/cli.js +72 -10
- package/dist/config.d.ts +2 -1
- package/dist/ingest.js +49 -0
- package/package.json +8 -2
- package/docs-search/package-lock.json +0 -1090
- package/docs-search/package.json +0 -30
- package/docs-search/src/cli.ts +0 -129
- package/docs-search/src/db.ts +0 -192
- package/docs-search/src/ingest.ts +0 -168
- package/docs-search/src/server.ts +0 -145
- package/docs-search/tsconfig.json +0 -17
- package/src/cli.ts +0 -221
- package/src/config.ts +0 -143
- package/src/db.ts +0 -207
- package/src/ingest.ts +0 -200
- package/src/mcp.ts +0 -124
- package/src/server.ts +0 -145
- package/tsconfig.json +0 -17
package/README.md
CHANGED
|
@@ -11,15 +11,23 @@ Documentation lives in repos. Searching it means context-switching to a browser,
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
# Run it without installing globally
|
|
15
|
+
npx readthemanual --help
|
|
16
|
+
|
|
17
|
+
# Or install the short command globally
|
|
18
|
+
npm install -g readthemanual
|
|
19
|
+
rtm --help
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
## Quick start
|
|
21
23
|
|
|
22
24
|
```bash
|
|
25
|
+
# Run directly from npm
|
|
26
|
+
npx readthemanual add react facebook/react -p docs/
|
|
27
|
+
npx readthemanual ingest -s react -v
|
|
28
|
+
npx readthemanual "concurrent rendering"
|
|
29
|
+
|
|
30
|
+
# Or use the global short alias after installing
|
|
23
31
|
# Add a source (any public GitHub repo with markdown docs)
|
|
24
32
|
rtm add react facebook/react -p docs/
|
|
25
33
|
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
3
36
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
37
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
38
|
};
|
|
@@ -11,13 +44,14 @@ const mcp_1 = require("./mcp");
|
|
|
11
44
|
const db_1 = require("./db");
|
|
12
45
|
const config_1 = require("./config");
|
|
13
46
|
const path_1 = __importDefault(require("path"));
|
|
47
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
48
|
const config = (0, config_1.loadConfig)();
|
|
15
49
|
const defaultDb = (0, config_1.getDbPath)(config);
|
|
16
50
|
const program = new commander_1.Command();
|
|
17
51
|
program
|
|
18
52
|
.name("rtm")
|
|
19
53
|
.description("Read the Manual — fast local full-text search for documentation")
|
|
20
|
-
.version("0.0.
|
|
54
|
+
.version("0.0.2");
|
|
21
55
|
program
|
|
22
56
|
.command("ingest")
|
|
23
57
|
.description("Fetch and index docs from configured GitHub sources")
|
|
@@ -147,14 +181,39 @@ program
|
|
|
147
181
|
console.log(`\nShowing ${result.docs.length} of ${result.total} total sections`);
|
|
148
182
|
});
|
|
149
183
|
program
|
|
150
|
-
.command("add <name> <
|
|
151
|
-
.description("Add a
|
|
152
|
-
.option("-b, --branch <branch>", "Branch
|
|
153
|
-
.option("-p, --prefix <prefix>", "
|
|
154
|
-
.action((name,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
184
|
+
.command("add <name> <source>")
|
|
185
|
+
.description("Add a doc source: local path, or owner/repo for GitHub")
|
|
186
|
+
.option("-b, --branch <branch>", "Branch (GitHub sources only)", "main")
|
|
187
|
+
.option("-p, --prefix <prefix>", "Docs path prefix (GitHub sources only)", "docs/")
|
|
188
|
+
.action(async (name, source, opts) => {
|
|
189
|
+
// Strip optional git: prefix
|
|
190
|
+
const stripped = source.startsWith("git:") ? source.slice(4) : source;
|
|
191
|
+
// 1. Try local path first
|
|
192
|
+
const resolved = path_1.default.resolve(stripped);
|
|
193
|
+
if (fs_1.default.existsSync(resolved)) {
|
|
194
|
+
(0, config_1.addSource)({ name, localPath: resolved });
|
|
195
|
+
console.log(`Added local source "${name}" → ${resolved}`);
|
|
196
|
+
console.log(`Run \`rtm ingest -s ${name}\` to index it.`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
// 2. Looks like owner/repo?
|
|
200
|
+
if (/^[\w.-]+\/[\w.-]+$/.test(stripped)) {
|
|
201
|
+
const repo = stripped;
|
|
202
|
+
const { createInterface } = await Promise.resolve().then(() => __importStar(require("readline")));
|
|
203
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
204
|
+
const answer = await new Promise((resolve) => rl.question(`Add GitHub source ${repo}@${opts.branch} (prefix: ${opts.prefix})? [y/N] `, resolve));
|
|
205
|
+
rl.close();
|
|
206
|
+
if (answer.trim().toLowerCase() !== "y") {
|
|
207
|
+
console.log("Aborted.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
(0, config_1.addSource)({ name, repo, branch: opts.branch, docsPrefix: opts.prefix });
|
|
211
|
+
console.log(`Added source "${name}" → ${repo}@${opts.branch} (prefix: ${opts.prefix})`);
|
|
212
|
+
console.log(`Run \`rtm ingest -s ${name}\` to index it.`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
console.error(`Cannot resolve "${source}" as a local path or a GitHub owner/repo.`);
|
|
216
|
+
process.exit(1);
|
|
158
217
|
});
|
|
159
218
|
program
|
|
160
219
|
.command("remove <name>")
|
|
@@ -194,7 +253,10 @@ program
|
|
|
194
253
|
console.log(`Sources (${cfg.sources.length}):`);
|
|
195
254
|
for (const s of cfg.sources) {
|
|
196
255
|
const marker = s.name === (0, config_1.getDefaultSource)(cfg) ? " *" : "";
|
|
197
|
-
|
|
256
|
+
const location = s.localPath
|
|
257
|
+
? `local:${s.localPath}`
|
|
258
|
+
: `${s.repo}@${s.branch ?? "main"} (prefix: ${s.docsPrefix ?? "docs/"})`;
|
|
259
|
+
console.log(` - ${s.name}: ${location}${marker}`);
|
|
198
260
|
}
|
|
199
261
|
});
|
|
200
262
|
// Default: bare args with no subcommand → search
|
package/dist/config.d.ts
CHANGED
package/dist/ingest.js
CHANGED
|
@@ -65,7 +65,56 @@ function splitByHeadings(markdown) {
|
|
|
65
65
|
}
|
|
66
66
|
return sections;
|
|
67
67
|
}
|
|
68
|
+
function walkMarkdownFiles(dir) {
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
71
|
+
const full = path_1.default.join(dir, entry.name);
|
|
72
|
+
if (entry.isDirectory()) {
|
|
73
|
+
results.push(...walkMarkdownFiles(full));
|
|
74
|
+
}
|
|
75
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
76
|
+
results.push(full);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return results;
|
|
80
|
+
}
|
|
81
|
+
async function ingestLocalSource(db, source, opts) {
|
|
82
|
+
const log = opts.verbose ? console.log : () => { };
|
|
83
|
+
const localPath = source.localPath;
|
|
84
|
+
if (!fs_1.default.existsSync(localPath)) {
|
|
85
|
+
throw new Error(`Local path does not exist: ${localPath}`);
|
|
86
|
+
}
|
|
87
|
+
const mdFiles = walkMarkdownFiles(localPath);
|
|
88
|
+
log(`[${source.name}] Found ${mdFiles.length} markdown files in ${localPath}`);
|
|
89
|
+
const allSections = [];
|
|
90
|
+
let filesProcessed = 0;
|
|
91
|
+
for (const filePath of mdFiles) {
|
|
92
|
+
const content = fs_1.default.readFileSync(filePath, "utf-8");
|
|
93
|
+
const relativePath = path_1.default.relative(localPath, filePath);
|
|
94
|
+
const sections = splitByHeadings(content);
|
|
95
|
+
for (const section of sections) {
|
|
96
|
+
allSections.push({
|
|
97
|
+
file_path: `${source.name}/${relativePath}`,
|
|
98
|
+
heading: section.heading,
|
|
99
|
+
content: section.content,
|
|
100
|
+
language: "en",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
filesProcessed++;
|
|
104
|
+
log(` [${filesProcessed}/${mdFiles.length}] ${relativePath} (${sections.length} sections)`);
|
|
105
|
+
}
|
|
106
|
+
const insertMany = db.transaction((sections) => {
|
|
107
|
+
for (const s of sections) {
|
|
108
|
+
(0, db_1.insertSection)(db, s);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
insertMany(allSections);
|
|
112
|
+
return { filesProcessed, sectionsInserted: allSections.length };
|
|
113
|
+
}
|
|
68
114
|
async function ingestSource(db, source, opts) {
|
|
115
|
+
if (source.localPath) {
|
|
116
|
+
return ingestLocalSource(db, source, opts);
|
|
117
|
+
}
|
|
69
118
|
const log = opts.verbose ? console.log : () => { };
|
|
70
119
|
const repo = source.repo;
|
|
71
120
|
const branch = source.branch ?? "main";
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "readthemanual",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Read the Manual — fast local full-text search for any project's docs",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
7
|
+
"readthemanual": "dist/cli.js",
|
|
7
8
|
"rtm": "dist/cli.js"
|
|
8
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
9
14
|
"scripts": {
|
|
10
15
|
"build": "tsc",
|
|
11
16
|
"dev": "tsx src/cli.ts",
|
|
12
|
-
"start": "node dist/cli.js"
|
|
17
|
+
"start": "node dist/cli.js",
|
|
18
|
+
"prepare": "npm run build"
|
|
13
19
|
},
|
|
14
20
|
"keywords": [],
|
|
15
21
|
"author": "",
|