@readwise/cli 0.3.0 → 0.5.0

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
@@ -7,10 +7,7 @@ Commands are auto-discovered from the Readwise API, so the CLI stays up to date
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- git clone <repo-url> && cd readwise
11
- npm install
12
- npm run build
13
- npm link
10
+ npm install -g @readwise/cli
14
11
  ```
15
12
 
16
13
  ## Setup
@@ -67,6 +64,8 @@ readwise reader-get-document-details --document-id <document-id>
67
64
  readwise reader-get-document-highlights --document-id <document-id>
68
65
  ```
69
66
 
67
+ > **Tip: seen vs unseen documents.** In the response, `firstOpenedAt: null` means the document is **unseen** (never opened). A non-null `firstOpenedAt` means it has been opened/seen. Use `reader-edit-document-metadata --seen true` to mark a document as seen.
68
+
70
69
  ### Save a document
71
70
 
72
71
  ```bash
@@ -91,6 +90,7 @@ readwise reader-move-document --document-id <id> --location archive
91
90
 
92
91
  # Edit metadata
93
92
  readwise reader-edit-document-metadata --document-id <id> --title "Better Title"
93
+ readwise reader-edit-document-metadata --document-id <id> --seen true # mark as seen/opened
94
94
  readwise reader-set-document-notes --document-id <id> --notes "Updated notes"
95
95
  ```
96
96
 
@@ -125,16 +125,17 @@ Pipe results to `jq`:
125
125
  readwise reader-list-documents --limit 3 --json | jq '.results[].title'
126
126
  ```
127
127
 
128
+ ## How it works
129
+
130
+ The CLI connects to the [Readwise MCP server](https://mcp2.readwise.io) internally, auto-discovers available tools, and exposes each one as a CLI command. The tool list is cached locally for 24 hours.
131
+
128
132
  ## Development
129
133
 
130
134
  ```bash
135
+ git clone https://github.com/readwise/readwise-cli && cd readwise-cli
136
+ npm install
137
+ npm run build
138
+
131
139
  # Run without building
132
140
  npx tsx src/index.ts --help
133
-
134
- # Build
135
- npm run build
136
141
  ```
137
-
138
- ## How it works
139
-
140
- The CLI connects to the [Readwise MCP server](https://mcp2.readwise.io) internally, auto-discovers available tools, and exposes each one as a CLI command. The tool list is cached locally for 24 hours.
package/dist/config.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface SchemaProperty {
15
15
  enum?: string[];
16
16
  items?: SchemaProperty;
17
17
  default?: unknown;
18
+ examples?: unknown[];
18
19
  anyOf?: SchemaProperty[];
19
20
  $ref?: string;
20
21
  properties?: Record<string, SchemaProperty>;
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { createInterface } from "node:readline";
3
3
  import { Command } from "commander";
4
4
  import { login, loginWithToken, ensureValidToken } from "./auth.js";
5
- import { getTools, callTool } from "./mcp.js";
6
- import { registerTools, displayResult } from "./commands.js";
5
+ import { getTools } from "./mcp.js";
6
+ import { registerTools } from "./commands.js";
7
7
  import { loadConfig } from "./config.js";
8
8
  import { VERSION } from "./version.js";
9
9
  function readHiddenInput(prompt) {
@@ -85,62 +85,14 @@ program
85
85
  process.exitCode = 1;
86
86
  }
87
87
  });
88
- program
89
- .command("search <query>")
90
- .description("Search across Reader documents and Readwise highlights")
91
- .option("--limit <n>", "Max results per source (default: 10)")
92
- .action(async (query, options) => {
93
- try {
94
- const { token, authType } = await ensureValidToken();
95
- const limit = options.limit ? Number(options.limit) : 10;
96
- const json = program.opts().json || false;
97
- const [readerResult, highlightsResult] = await Promise.all([
98
- callTool(token, authType, "reader_search_documents", { query, limit }),
99
- callTool(token, authType, "readwise_search_highlights", { vector_search_term: query, limit }),
100
- ]);
101
- if (json) {
102
- const combined = {};
103
- for (const item of readerResult.content) {
104
- if (item.type === "text" && item.text) {
105
- try {
106
- combined.reader = JSON.parse(item.text);
107
- }
108
- catch {
109
- combined.reader = item.text;
110
- }
111
- }
112
- }
113
- for (const item of highlightsResult.content) {
114
- if (item.type === "text" && item.text) {
115
- try {
116
- combined.highlights = JSON.parse(item.text);
117
- }
118
- catch {
119
- combined.highlights = item.text;
120
- }
121
- }
122
- }
123
- process.stdout.write(JSON.stringify(combined) + "\n");
124
- }
125
- else {
126
- console.log("\x1b[1m\x1b[36m── Reader Documents ──\x1b[0m\n");
127
- displayResult(readerResult, false);
128
- console.log("\n\x1b[1m\x1b[36m── Readwise Highlights ──\x1b[0m\n");
129
- displayResult(highlightsResult, false);
130
- }
131
- }
132
- catch (err) {
133
- process.stderr.write(`\x1b[31m${err.message}\x1b[0m\n`);
134
- process.exitCode = 1;
135
- }
136
- });
137
88
  async function main() {
138
89
  const config = await loadConfig();
139
90
  const forceRefresh = process.argv.includes("--refresh");
140
91
  const positionalArgs = process.argv.slice(2).filter((a) => !a.startsWith("--"));
141
92
  const hasSubcommand = positionalArgs.length > 0;
142
- // If no subcommand, TTY, and authenticated → launch TUI
143
- if (!hasSubcommand && process.stdout.isTTY && config.access_token) {
93
+ const wantsHelp = process.argv.includes("--help") || process.argv.includes("-h");
94
+ // If no subcommand, TTY, and authenticated → launch TUI (unless --help)
95
+ if (!hasSubcommand && !wantsHelp && process.stdout.isTTY && config.access_token) {
144
96
  try {
145
97
  const { token, authType } = await ensureValidToken();
146
98
  const tools = await getTools(token, authType, forceRefresh);
@@ -159,6 +111,12 @@ async function main() {
159
111
  console.log("\nRun `readwise login` or `readwise login-with-token` to authenticate.");
160
112
  return;
161
113
  }
114
+ // If not authenticated and trying a non-login command, tell user to log in
115
+ if (!config.access_token && hasSubcommand && positionalArgs[0] !== "login" && positionalArgs[0] !== "login-with-token") {
116
+ process.stderr.write("\x1b[31mNot logged in.\x1b[0m Run `readwise login` or `readwise login-with-token` to authenticate.\n");
117
+ process.exitCode = 1;
118
+ return;
119
+ }
162
120
  // Try to load tools if we have a token (for subcommand mode)
163
121
  if (config.access_token) {
164
122
  try {
@@ -168,7 +126,6 @@ async function main() {
168
126
  }
169
127
  catch (err) {
170
128
  // Don't fail — login command should still work
171
- // Only warn if user is trying to run a non-login command
172
129
  if (hasSubcommand && positionalArgs[0] !== "login" && positionalArgs[0] !== "login-with-token") {
173
130
  process.stderr.write(`\x1b[33mWarning: Could not fetch tools: ${err.message}\x1b[0m\n`);
174
131
  }