Add calibre-search extension#27295
Conversation
- Apply Prettier formatting to all source files - Add ESLint flat config and remove unused import for store validation - Add store screenshots and fix author handle for Raycast Store submission - Add README with commands, configuration, and installation instructions - Exclude CLAUDE.md from version control - Add "Add to Calibre Library" command and fix open-in-Calibre navigation - Initial release: Calibre Library Raycast extension
|
Congratulations on your new Raycast extension! 🚀 We're currently experiencing a high volume of incoming requests. As a result, the initial review may take up to 10-15 business days. Once the PR is approved and merged, the extension will be available on our Store. |
Greptile SummaryThis PR adds a new
Confidence Score: 3/5Not safe to merge until the blocking Two real defects in the changed code — a synchronous extensions/calibre-search/src/add-to-library.tsx requires the most attention (blocking call + missing error handler). Important Files Changed
Prompt To Fix All With AIThis is a comment left during a code review.
Path: extensions/calibre-search/src/add-to-library.tsx
Line: 255-262
Comment:
**`spawnSync` blocks the event loop**
`spawnSync` is synchronous and will freeze the entire Node.js event loop — including Raycast's UI — while `calibredb` is running. Adding a book to Calibre can take several seconds, making the interface completely unresponsive despite the animated toast. Replace with the async `execFile` (or `spawnAsync` via `util.promisify`) so the event loop stays free.
```typescript
import { execFile } from "child_process";
import { promisify } from "util";
const execFileAsync = promisify(execFile);
// inside handleAdd:
const { stdout = "", stderr = "" } = await execFileAsync(
CALIBREDB,
buildCalibredbArgs(file.path, libraryPath),
).catch((err) => ({ stdout: "", stderr: err.message ?? "" }));
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/calibre-search/src/add-to-library.tsx
Line: 181-188
Comment:
**Missing error handler on `qlmanage` process**
If `qlmanage` is not found or fails to spawn, the child process emits an `'error'` event with no listener, resulting in an uncaught exception that can crash the extension. Add an error listener:
```suggestion
const proc = spawn("qlmanage", ["-t", "-s", "256", "-o", qlDir, file.path]);
proc.on("error", () => {});
proc.on("close", () => {
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/calibre-search/CHANGELOG.md
Line: 3
Comment:
**Changelog date should use `{PR_MERGE_DATE}` placeholder**
The Raycast convention is to use the `{PR_MERGE_DATE}` template variable for initial release entries; the merge date is substituted automatically when the PR lands.
```suggestion
## [Initial Release] - {PR_MERGE_DATE}
```
**Rule Used:** What: In Raycast extension changelogs, `{PR_MERGE_... ([source](https://app.greptile.com/review/custom-context?memory=799af734-ebd9-4b40-9ffd-97a70fc71c8a))
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/calibre-search/src/types.ts
Line: 34-36
Comment:
**Manual `Preferences` interface should not be defined**
Preference types are auto-generated in `raycast-env.d.ts` when the extension runs. Manually defining `Preferences` (here) and `AddPreferences extends Preferences` (in `add-to-library.tsx`) can drift out of sync with `package.json`, causing silent type mismatches. Remove both manual interfaces and use `getPreferenceValues<Preferences>()` directly — the generated types will include all defined preferences.
**Rule Used:** What: Don't manually define `Preferences` for `get... ([source](https://app.greptile.com/review/custom-context?memory=d93fc9fb-a45d-4479-a6a4-b1b4af98ebc8))
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/calibre-search/eslint.config.js
Line: 1
Comment:
**Use `defineConfig` from `eslint/config` subpath**
The current pattern manually calls `.flat()` instead of using the canonical ESLint v9 helper. The preferred pattern is:
```suggestion
const { defineConfig } = require("eslint/config");
const raycastConfig = require("@raycast/eslint-config");
module.exports = defineConfig([...raycastConfig]);
```
**Rule Used:** What: Enforce importing `defineConfig` from `"esli... ([source](https://app.greptile.com/review/custom-context?memory=645a7150-4078-490e-a70c-d6aad94e0cf5))
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: extensions/calibre-search/package.json
Line: 44-63
Comment:
**Preference titles should use Title Case**
`"Add books"` and `"Add books (optional)"` are lowercase. Raycast guidelines require Title Case for all user-facing preference titles.
```suggestion
"title": "Add Books",
```
Apply the same fix to the two `"Add books (optional)"` entries on lines 52 and 58.
**Rule Used:** What: Use title case for titles in `package.json`.... ([source](https://app.greptile.com/review/custom-context?memory=a44fb089-4d03-4b60-a4ff-03431cdf0eb4))
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "Add CHANGELOG for store submission" | Re-trigger Greptile |
| const result = spawnSync( | ||
| CALIBREDB, | ||
| buildCalibredbArgs(file.path, libraryPath), | ||
| { encoding: "utf8" }, | ||
| ); | ||
| const output = result.stdout ?? ""; | ||
| const stderr = result.stderr?.trim() ?? ""; | ||
| const { addedIds } = parseCalibredbOutput(output); |
There was a problem hiding this comment.
spawnSync blocks the event loop
spawnSync is synchronous and will freeze the entire Node.js event loop — including Raycast's UI — while calibredb is running. Adding a book to Calibre can take several seconds, making the interface completely unresponsive despite the animated toast. Replace with the async execFile (or spawnAsync via util.promisify) so the event loop stays free.
import { execFile } from "child_process";
import { promisify } from "util";
const execFileAsync = promisify(execFile);
// inside handleAdd:
const { stdout = "", stderr = "" } = await execFileAsync(
CALIBREDB,
buildCalibredbArgs(file.path, libraryPath),
).catch((err) => ({ stdout: "", stderr: err.message ?? "" }));Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/src/add-to-library.tsx
Line: 255-262
Comment:
**`spawnSync` blocks the event loop**
`spawnSync` is synchronous and will freeze the entire Node.js event loop — including Raycast's UI — while `calibredb` is running. Adding a book to Calibre can take several seconds, making the interface completely unresponsive despite the animated toast. Replace with the async `execFile` (or `spawnAsync` via `util.promisify`) so the event loop stays free.
```typescript
import { execFile } from "child_process";
import { promisify } from "util";
const execFileAsync = promisify(execFile);
// inside handleAdd:
const { stdout = "", stderr = "" } = await execFileAsync(
CALIBREDB,
buildCalibredbArgs(file.path, libraryPath),
).catch((err) => ({ stdout: "", stderr: err.message ?? "" }));
```
How can I resolve this? If you propose a fix, please make it concise.| const proc = spawn("qlmanage", ["-t", "-s", "256", "-o", qlDir, file.path]); | ||
| proc.on("close", () => { | ||
| if (!cancelled && existsSync(expectedThumb)) onReady(expectedThumb); | ||
| }); | ||
| return () => { | ||
| cancelled = true; | ||
| proc.kill(); | ||
| }; |
There was a problem hiding this comment.
Missing error handler on
qlmanage process
If qlmanage is not found or fails to spawn, the child process emits an 'error' event with no listener, resulting in an uncaught exception that can crash the extension. Add an error listener:
| const proc = spawn("qlmanage", ["-t", "-s", "256", "-o", qlDir, file.path]); | |
| proc.on("close", () => { | |
| if (!cancelled && existsSync(expectedThumb)) onReady(expectedThumb); | |
| }); | |
| return () => { | |
| cancelled = true; | |
| proc.kill(); | |
| }; | |
| const proc = spawn("qlmanage", ["-t", "-s", "256", "-o", qlDir, file.path]); | |
| proc.on("error", () => {}); | |
| proc.on("close", () => { |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/src/add-to-library.tsx
Line: 181-188
Comment:
**Missing error handler on `qlmanage` process**
If `qlmanage` is not found or fails to spawn, the child process emits an `'error'` event with no listener, resulting in an uncaught exception that can crash the extension. Add an error listener:
```suggestion
const proc = spawn("qlmanage", ["-t", "-s", "256", "-o", qlDir, file.path]);
proc.on("error", () => {});
proc.on("close", () => {
```
How can I resolve this? If you propose a fix, please make it concise.| @@ -0,0 +1,13 @@ | |||
| # Calibre Library Changelog | |||
|
|
|||
| ## [1.0.0] - 2026-04-20 | |||
There was a problem hiding this comment.
Changelog date should use
{PR_MERGE_DATE} placeholder
The Raycast convention is to use the {PR_MERGE_DATE} template variable for initial release entries; the merge date is substituted automatically when the PR lands.
| ## [1.0.0] - 2026-04-20 | |
| ## [Initial Release] - {PR_MERGE_DATE} |
Rule Used: What: In Raycast extension changelogs, `{PR_MERGE_... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/CHANGELOG.md
Line: 3
Comment:
**Changelog date should use `{PR_MERGE_DATE}` placeholder**
The Raycast convention is to use the `{PR_MERGE_DATE}` template variable for initial release entries; the merge date is substituted automatically when the PR lands.
```suggestion
## [Initial Release] - {PR_MERGE_DATE}
```
**Rule Used:** What: In Raycast extension changelogs, `{PR_MERGE_... ([source](https://app.greptile.com/review/custom-context?memory=799af734-ebd9-4b40-9ffd-97a70fc71c8a))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| export interface Preferences { | ||
| libraryPath: string; | ||
| } |
There was a problem hiding this comment.
Manual
Preferences interface should not be defined
Preference types are auto-generated in raycast-env.d.ts when the extension runs. Manually defining Preferences (here) and AddPreferences extends Preferences (in add-to-library.tsx) can drift out of sync with package.json, causing silent type mismatches. Remove both manual interfaces and use getPreferenceValues<Preferences>() directly — the generated types will include all defined preferences.
Rule Used: What: Don't manually define Preferences for `get... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/src/types.ts
Line: 34-36
Comment:
**Manual `Preferences` interface should not be defined**
Preference types are auto-generated in `raycast-env.d.ts` when the extension runs. Manually defining `Preferences` (here) and `AddPreferences extends Preferences` (in `add-to-library.tsx`) can drift out of sync with `package.json`, causing silent type mismatches. Remove both manual interfaces and use `getPreferenceValues<Preferences>()` directly — the generated types will include all defined preferences.
**Rule Used:** What: Don't manually define `Preferences` for `get... ([source](https://app.greptile.com/review/custom-context?memory=d93fc9fb-a45d-4479-a6a4-b1b4af98ebc8))
How can I resolve this? If you propose a fix, please make it concise.| @@ -0,0 +1 @@ | |||
| module.exports = require("@raycast/eslint-config").flat(); | |||
There was a problem hiding this comment.
Use
defineConfig from eslint/config subpath
The current pattern manually calls .flat() instead of using the canonical ESLint v9 helper. The preferred pattern is:
| module.exports = require("@raycast/eslint-config").flat(); | |
| const { defineConfig } = require("eslint/config"); | |
| const raycastConfig = require("@raycast/eslint-config"); | |
| module.exports = defineConfig([...raycastConfig]); |
Rule Used: What: Enforce importing defineConfig from `"esli... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/eslint.config.js
Line: 1
Comment:
**Use `defineConfig` from `eslint/config` subpath**
The current pattern manually calls `.flat()` instead of using the canonical ESLint v9 helper. The preferred pattern is:
```suggestion
const { defineConfig } = require("eslint/config");
const raycastConfig = require("@raycast/eslint-config");
module.exports = defineConfig([...raycastConfig]);
```
**Rule Used:** What: Enforce importing `defineConfig` from `"esli... ([source](https://app.greptile.com/review/custom-context?memory=645a7150-4078-490e-a70c-d6aad94e0cf5))
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| "title": "Add books", | ||
| "description": "Primary folder to scan for ebook files", | ||
| "type": "directory", | ||
| "required": false, | ||
| "default": "~/Downloads" | ||
| }, | ||
| { | ||
| "name": "searchPath2", | ||
| "title": "Add books (optional)", | ||
| "description": "Additional folder to scan", | ||
| "type": "directory", | ||
| "required": false | ||
| }, | ||
| { | ||
| "name": "searchPath3", | ||
| "title": "Add books (optional)", | ||
| "description": "Additional folder to scan", | ||
| "type": "directory", | ||
| "required": false | ||
| } |
There was a problem hiding this comment.
Preference titles should use Title Case
"Add books" and "Add books (optional)" are lowercase. Raycast guidelines require Title Case for all user-facing preference titles.
| "title": "Add books", | |
| "description": "Primary folder to scan for ebook files", | |
| "type": "directory", | |
| "required": false, | |
| "default": "~/Downloads" | |
| }, | |
| { | |
| "name": "searchPath2", | |
| "title": "Add books (optional)", | |
| "description": "Additional folder to scan", | |
| "type": "directory", | |
| "required": false | |
| }, | |
| { | |
| "name": "searchPath3", | |
| "title": "Add books (optional)", | |
| "description": "Additional folder to scan", | |
| "type": "directory", | |
| "required": false | |
| } | |
| "title": "Add Books", |
Apply the same fix to the two "Add books (optional)" entries on lines 52 and 58.
Rule Used: What: Use title case for titles in package.json.... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/calibre-search/package.json
Line: 44-63
Comment:
**Preference titles should use Title Case**
`"Add books"` and `"Add books (optional)"` are lowercase. Raycast guidelines require Title Case for all user-facing preference titles.
```suggestion
"title": "Add Books",
```
Apply the same fix to the two `"Add books (optional)"` entries on lines 52 and 58.
**Rule Used:** What: Use title case for titles in `package.json`.... ([source](https://app.greptile.com/review/custom-context?memory=a44fb089-4d03-4b60-a4ff-03431cdf0eb4))
How can I resolve this? If you propose a fix, please make it concise.- Update build artifacts - Address Raycast store review feedback
|
Addressed all Greptile feedback: replaced spawnSync with async execFileAsync, added error handler on qlmanage, removed manual Preferences interface, fixed eslint.config.js, Title Case on preference titles, and {PR_MERGE_DATE} in CHANGELOG. |
|
This pull request has been automatically marked as stale because it did not have any recent activity. It will be closed if no further activity occurs in the next 7 days to keep our backlog clean 😊 |
|
All feedback from the Greptile review has been addressed in commit 4580d07:
Would appreciate a re-review when time allows. Thanks! |
Description
Screencast
Checklist
npm run buildand tested this distribution build in Raycastassetsfolder are used by the extension itselfREADMEare placed outside of themetadatafolder