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 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
- git clone https://github.com/rgr4y/rtm.git
15
- cd rtm
16
- npm install && npm run build
17
- npm link # makes `rtm` available globally
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.1");
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> <repo>")
151
- .description("Add a GitHub repo as a doc source (repo = owner/repo)")
152
- .option("-b, --branch <branch>", "Branch to index", "main")
153
- .option("-p, --prefix <prefix>", "Path prefix for docs in the repo", "docs/")
154
- .action((name, repo, opts) => {
155
- (0, config_1.addSource)({ name, repo, branch: opts.branch, docsPrefix: opts.prefix });
156
- console.log(`Added source "${name}" ${repo}@${opts.branch} (prefix: ${opts.prefix})`);
157
- console.log(`Run \`rtm ingest -s ${name}\` to index it.`);
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
- console.log(` - ${s.name}: ${s.repo}@${s.branch ?? "main"} (prefix: ${s.docsPrefix ?? "docs/"})${marker}`);
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
@@ -1,8 +1,9 @@
1
1
  export interface SourceConfig {
2
2
  name: string;
3
- repo: string;
3
+ repo?: string;
4
4
  branch?: string;
5
5
  docsPrefix?: string;
6
+ localPath?: string;
6
7
  }
7
8
  export interface Config {
8
9
  sources: SourceConfig[];
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.1",
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": "",