@tacone/prosey 0.2.4 → 0.2.6

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
@@ -180,7 +180,7 @@ Use `--reset-config` to restore the defaults at any time.
180
180
  ### `pager`
181
181
 
182
182
  Pager command for transcript and summary output. Defaults to `"auto"`, which
183
- detects the first available of: `bat -lmd` → `glow` → `mdcat -l -p` → `less`.
183
+ detects the first available of: `bat -lmd --style plain` → `glow -p` → `mdcat -l -p` → `less`.
184
184
  Set to a custom command (e.g. `"less -R"`) to override. The `PROSEY_PAGER`
185
185
  environment variable takes precedence over this setting.
186
186
 
package/bin/prosey CHANGED
@@ -94210,9 +94210,9 @@ function detectPager(cfgPager) {
94210
94210
  if (cfgPager !== undefined && cfgPager !== "" && cfgPager !== "auto")
94211
94211
  return cfgPager;
94212
94212
  if (hasCommand("bat"))
94213
- return "bat -lmd";
94213
+ return "bat -lmd --style plain";
94214
94214
  if (hasCommand("glow"))
94215
- return "glow";
94215
+ return "glow -p";
94216
94216
  if (hasCommand("mdcat"))
94217
94217
  return "mdcat -l -p";
94218
94218
  if (hasCommand("less"))
@@ -101603,7 +101603,7 @@ var FALLBACK_CONFIG_TOML = `# Default prosey configuration
101603
101603
  # Created automatically on first run. Edit as needed.
101604
101604
 
101605
101605
  # Pager command for transcript and summary output.
101606
- # Defaults to "auto": bat -lmd → glow → mdcat -l -p → less
101606
+ # Defaults to "auto": bat -lmd --style plain → glow -p → mdcat -l -p → less
101607
101607
  # Set to a custom command (e.g. "less -R") to override.
101608
101608
  # Can also be set via the PROSEY_PAGER env var (takes precedence).
101609
101609
  pager = "auto"
@@ -101759,7 +101759,7 @@ async function writeCache(dir, filename, data) {
101759
101759
  // package.json
101760
101760
  var package_default = {
101761
101761
  name: "@tacone/prosey",
101762
- version: "0.2.4",
101762
+ version: "0.2.6",
101763
101763
  description: "Download YouTube video transcripts from the CLI",
101764
101764
  module: "src/index.ts",
101765
101765
  type: "module",
@@ -101822,6 +101822,31 @@ var package_default = {
101822
101822
  }
101823
101823
  };
101824
101824
 
101825
+ // src/version-check.ts
101826
+ var TIMEOUT_MS = 3000;
101827
+ var registryUrl = `https://registry.npmjs.org/${package_default.name}/latest`;
101828
+ async function checkVersion() {
101829
+ try {
101830
+ const controller = new AbortController;
101831
+ const timer2 = setTimeout(() => controller.abort(), TIMEOUT_MS);
101832
+ const res = await fetch(registryUrl, { signal: controller.signal });
101833
+ clearTimeout(timer2);
101834
+ if (!res.ok) {
101835
+ debug("Version check failed: HTTP", res.status);
101836
+ return null;
101837
+ }
101838
+ const data = await res.json();
101839
+ if (!data.version) {
101840
+ debug("Version check: no version field in response");
101841
+ return null;
101842
+ }
101843
+ return data.version;
101844
+ } catch (err) {
101845
+ debug("Version check error:", err instanceof Error ? err.message : String(err));
101846
+ return null;
101847
+ }
101848
+ }
101849
+
101825
101850
  // node_modules/prettier/index.mjs
101826
101851
  import { createRequire as __prettierCreateRequire } from "module";
101827
101852
  import { fileURLToPath as __prettierFileUrlToPath } from "url";
@@ -120362,6 +120387,16 @@ var debugApis = {
120362
120387
  // src/index.ts
120363
120388
  var NAME2 = "prosey";
120364
120389
  var VERSION2 = package_default.version;
120390
+ var latestVersion = null;
120391
+ var versionCheck = checkVersion().then((v11) => {
120392
+ latestVersion = v11;
120393
+ });
120394
+ function exitProcess(code) {
120395
+ if (useHints && code === 0 && latestVersion && latestVersion !== VERSION2) {
120396
+ hint(`\uD83D\uDCE6 New version available: ${latestVersion} — use npm/pnpm/bun -g i ${package_default.name} to upgrade`);
120397
+ }
120398
+ process.exit(code);
120399
+ }
120365
120400
  function help() {
120366
120401
  return `${NAME2} v${VERSION2}
120367
120402
 
@@ -120504,16 +120539,16 @@ var pagerCmd = null;
120504
120539
  var args = process.argv.slice(2);
120505
120540
  if (args.length === 0 || args.includes("--help")) {
120506
120541
  console.log(help());
120507
- process.exit(0);
120542
+ exitProcess(0);
120508
120543
  }
120509
120544
  if (args.includes("--version")) {
120510
120545
  console.log(VERSION2);
120511
- process.exit(0);
120546
+ exitProcess(0);
120512
120547
  }
120513
120548
  if (args.includes("--reset-config")) {
120514
120549
  const path15 = await resetConfig();
120515
120550
  console.log(`Config reset to defaults: ${path15}`);
120516
- process.exit(0);
120551
+ exitProcess(0);
120517
120552
  }
120518
120553
  var config = await loadConfig().catch(() => ({}));
120519
120554
  var mode = "transcript";
@@ -120543,7 +120578,7 @@ for (let i = 0;i < args.length; i++) {
120543
120578
  lang = args[++i] ?? undefined;
120544
120579
  if (!lang) {
120545
120580
  console.error("Error: --lang requires a language code");
120546
- process.exit(1);
120581
+ exitProcess(1);
120547
120582
  }
120548
120583
  } else if (arg === "--timestamps" || arg === "-t") {
120549
120584
  timestamps = true;
@@ -120553,7 +120588,7 @@ for (let i = 0;i < args.length; i++) {
120553
120588
  outputPath = args[++i] ?? undefined;
120554
120589
  if (!outputPath) {
120555
120590
  console.error("Error: -o/--output requires a file path");
120556
- process.exit(1);
120591
+ exitProcess(1);
120557
120592
  }
120558
120593
  } else if (arg === "--json") {
120559
120594
  outputJson = true;
@@ -120583,7 +120618,7 @@ for (let i = 0;i < args.length; i++) {
120583
120618
  noDecode = true;
120584
120619
  } else if (arg.startsWith("-")) {
120585
120620
  console.error(`Unknown option: ${arg}`);
120586
- process.exit(1);
120621
+ exitProcess(1);
120587
120622
  } else {
120588
120623
  videoId = arg;
120589
120624
  }
@@ -120600,17 +120635,17 @@ if (mode === "config") {
120600
120635
  } else {
120601
120636
  console.log(`Config file: ${path15}`);
120602
120637
  }
120603
- process.exit(0);
120638
+ exitProcess(0);
120604
120639
  }
120605
120640
  if (!videoId) {
120606
120641
  console.error("Error: missing video URL or ID");
120607
120642
  console.log(help());
120608
- process.exit(1);
120643
+ exitProcess(1);
120609
120644
  }
120610
120645
  var extracted = extractVideoId(videoId);
120611
120646
  if (!extracted) {
120612
120647
  console.error("Error: invalid YouTube video URL or ID");
120613
- process.exit(65);
120648
+ exitProcess(65);
120614
120649
  }
120615
120650
  videoId = extracted;
120616
120651
  setLevel(logLevel);
@@ -120626,7 +120661,7 @@ debug("Pager:", pagerCmd ?? "none");
120626
120661
  }
120627
120662
  }
120628
120663
  if (useHints) {
120629
- const hasMarkdownPager = pagerCmd === "bat -lmd" || pagerCmd === "glow" || pagerCmd === "mdcat -l -p";
120664
+ const hasMarkdownPager = pagerCmd === "bat -lmd --style plain" || pagerCmd === "glow -p" || pagerCmd === "mdcat -l -p";
120630
120665
  if (!hasMarkdownPager) {
120631
120666
  hint("Tip: install a markdown highlighter for better output (e.g. bat, glow, mdcat)");
120632
120667
  }
@@ -120636,6 +120671,7 @@ debug("Video ID:", videoId);
120636
120671
  debug("Mode:", mode);
120637
120672
  if (lang)
120638
120673
  debug("Language:", lang);
120674
+ await Promise.race([versionCheck, new Promise((r5) => setTimeout(r5, 1000))]);
120639
120675
  try {
120640
120676
  if (mode === "info") {
120641
120677
  const result = await fetchTranscript(videoId, { videoDetails: true, lang });
@@ -120644,12 +120680,12 @@ try {
120644
120680
  } else {
120645
120681
  printVideoInfo(result.videoDetails);
120646
120682
  }
120647
- process.exit(0);
120683
+ exitProcess(0);
120648
120684
  }
120649
120685
  if (mode === "summarize") {
120650
120686
  if (!config.summarize?.command) {
120651
120687
  console.error("Error: [summarize] section with a command is required in config");
120652
- process.exit(1);
120688
+ exitProcess(1);
120653
120689
  }
120654
120690
  const cacheOpts2 = { lang, mode: "summarize", noDecode };
120655
120691
  const dir2 = cacheDir(videoId, cacheOpts2);
@@ -120694,11 +120730,11 @@ try {
120694
120730
  const formatted = noFormat ? summary : await formatMd(summary);
120695
120731
  await outputText(formatted + `
120696
120732
  `);
120697
- process.exit(0);
120733
+ exitProcess(0);
120698
120734
  } else if (listOnly) {
120699
120735
  const languages2 = await listLanguages(videoId);
120700
120736
  printLanguages(languages2);
120701
- process.exit(0);
120737
+ exitProcess(0);
120702
120738
  }
120703
120739
  const decode = !noDecode;
120704
120740
  const cacheOpts = { lang, timestamps, json: outputJson, noDecode };
@@ -120752,8 +120788,9 @@ try {
120752
120788
  `;
120753
120789
  await outputText(output);
120754
120790
  }
120791
+ exitProcess(0);
120755
120792
  } catch (err) {
120756
120793
  const message = err instanceof Error ? err.message : String(err);
120757
120794
  console.error(`Error: ${message}`);
120758
- process.exit(1);
120795
+ exitProcess(1);
120759
120796
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tacone/prosey",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Download YouTube video transcripts from the CLI",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/config.ts CHANGED
@@ -18,7 +18,7 @@ const FALLBACK_CONFIG_TOML = `# Default prosey configuration
18
18
  # Created automatically on first run. Edit as needed.
19
19
 
20
20
  # Pager command for transcript and summary output.
21
- # Defaults to "auto": bat -lmd → glow → mdcat -l -p → less
21
+ # Defaults to "auto": bat -lmd --style plain → glow -p → mdcat -l -p → less
22
22
  # Set to a custom command (e.g. "less -R") to override.
23
23
  # Can also be set via the PROSEY_PAGER env var (takes precedence).
24
24
  pager = "auto"
@@ -2,12 +2,12 @@
2
2
  # Created automatically on first run. Edit as needed.
3
3
 
4
4
  # Pager command for transcript and summary output.
5
- # Defaults to "auto": bat -lmd → glow → mdcat -l -p → less
5
+ # Defaults to "auto": bat -lmd --style plain → glow -p → mdcat -l -p → less
6
6
  # Set to a custom command (e.g. "less -R") to override.
7
7
  # Can also be set via the PROSEY_PAGER env var (takes precedence).
8
8
  pager = "auto"
9
9
 
10
- # Show hints for missing tools (e.g. markdown highlighter).
10
+ # Show hints when available
11
11
  # Can also be set via PROSEY_HINTS env var (yes, no, 1, 0, true, false).
12
12
  hints = true
13
13
 
@@ -21,19 +21,13 @@ Write a comprehensive summary of the following transcription.
21
21
  """
22
22
 
23
23
  # Command to execute with the prompt and transcript piped via stdin.
24
- # The transcript is appended to the prompt automatically.
24
+ # The transcript will be appended to the prompt automatically.
25
25
  #
26
- # Available options:
26
+ # Examples:
27
27
  #
28
- # opencode run full access (default)
29
- # opencode run --permissions read read-only (view files, no edits)
28
+ # OPENCODE_PERMISSION='{"read":"allow","write":"deny","edit":"deny","bash":"deny","glob":"deny","grep":"deny","webfetch":\"deny\",\"task\":\"deny\",\"todowrite\":\"deny\",\"websearch\":\"deny","lsp":"deny"}' opencode run --pure
29
+ # claude --permission-mode plan --print --read-only
30
+ # copilot -s --deny-all-tools --read-only
31
+ # codex --sandbox read-only --read-only
30
32
  #
31
- # claude -p "" --print — full access (--print for clean output)
32
- # claude --permission-mode plan -p "" --print — read-only (plan/read only)
33
- #
34
- # copilot -sp "" — full access (-s = silent, -p = prompt)
35
- # copilot -sp "" --deny-all-tools — read-only (no shell/write access)
36
- #
37
- # codex --sandbox default -p "" — full access
38
- # codex --sandbox read-only -p "" — read-only
39
33
  command = "OPENCODE_PERMISSION='{\"read\":\"allow\",\"write\":\"deny\",\"edit\":\"deny\",\"bash\":\"deny\",\"glob\":\"deny\",\"grep\":\"deny\",\"webfetch\":\"deny\",\"task\":\"deny\",\"todowrite\":\"deny\",\"websearch\":\"deny\",\"lsp\":\"deny\"}' opencode run --pure"
package/src/index.ts CHANGED
@@ -12,12 +12,27 @@ import { loadConfig, resetConfig, configPath } from "./config";
12
12
  import type { ProseyConfig } from "./config";
13
13
  import { summarize } from "./summarize";
14
14
  import { cacheDir, readCache, writeCache, extractVideoId } from "./cache";
15
+ import { checkVersion } from "./version-check";
15
16
  import pkg from "../package.json";
16
17
  import prettier from "prettier";
17
18
 
18
19
  const NAME = "prosey";
19
20
  const VERSION = pkg.version;
20
21
 
22
+ let latestVersion: string | null = null;
23
+ const versionCheck = checkVersion().then((v) => {
24
+ latestVersion = v;
25
+ });
26
+
27
+ function exitProcess(code: number): never {
28
+ if (useHints && code === 0 && latestVersion && latestVersion !== VERSION) {
29
+ hint(
30
+ `📦 New version available: ${latestVersion} — use npm/pnpm/bun -g i ${pkg.name} to upgrade`,
31
+ );
32
+ }
33
+ process.exit(code);
34
+ }
35
+
21
36
  function help(): string {
22
37
  return `${NAME} v${VERSION}
23
38
 
@@ -180,18 +195,18 @@ const args = process.argv.slice(2);
180
195
 
181
196
  if (args.length === 0 || args.includes("--help")) {
182
197
  console.log(help());
183
- process.exit(0);
198
+ exitProcess(0);
184
199
  }
185
200
 
186
201
  if (args.includes("--version")) {
187
202
  console.log(VERSION);
188
- process.exit(0);
203
+ exitProcess(0);
189
204
  }
190
205
 
191
206
  if (args.includes("--reset-config")) {
192
207
  const path = await resetConfig();
193
208
  console.log(`Config reset to defaults: ${path}`);
194
- process.exit(0);
209
+ exitProcess(0);
195
210
  }
196
211
 
197
212
  const config: ProseyConfig = await loadConfig().catch(() => ({}) as ProseyConfig);
@@ -224,7 +239,7 @@ for (let i = 0; i < args.length; i++) {
224
239
  lang = args[++i] ?? undefined;
225
240
  if (!lang) {
226
241
  console.error("Error: --lang requires a language code");
227
- process.exit(1);
242
+ exitProcess(1);
228
243
  }
229
244
  } else if (arg === "--timestamps" || arg === "-t") {
230
245
  timestamps = true;
@@ -234,7 +249,7 @@ for (let i = 0; i < args.length; i++) {
234
249
  outputPath = args[++i] ?? undefined;
235
250
  if (!outputPath) {
236
251
  console.error("Error: -o/--output requires a file path");
237
- process.exit(1);
252
+ exitProcess(1);
238
253
  }
239
254
  } else if (arg === "--json") {
240
255
  outputJson = true;
@@ -264,7 +279,7 @@ for (let i = 0; i < args.length; i++) {
264
279
  noDecode = true;
265
280
  } else if (arg.startsWith("-")) {
266
281
  console.error(`Unknown option: ${arg}`);
267
- process.exit(1);
282
+ exitProcess(1);
268
283
  } else {
269
284
  videoId = arg;
270
285
  }
@@ -282,23 +297,23 @@ if (mode === "config") {
282
297
  } else {
283
298
  console.log(`Config file: ${path}`);
284
299
  }
285
- process.exit(0);
300
+ exitProcess(0);
286
301
  }
287
302
 
288
303
  if (!videoId) {
289
304
  console.error("Error: missing video URL or ID");
290
305
  console.log(help());
291
- process.exit(1);
306
+ exitProcess(1);
292
307
  }
293
308
 
294
309
  const extracted = extractVideoId(videoId);
295
310
 
296
311
  if (!extracted) {
297
312
  console.error("Error: invalid YouTube video URL or ID");
298
- process.exit(65);
313
+ exitProcess(65);
299
314
  }
300
315
 
301
- videoId = extracted;
316
+ videoId = extracted!;
302
317
 
303
318
  setLevel(logLevel);
304
319
  resetTimer();
@@ -316,7 +331,7 @@ debug("Pager:", pagerCmd ?? "none");
316
331
 
317
332
  if (useHints) {
318
333
  const hasMarkdownPager =
319
- pagerCmd === "bat -lmd" || pagerCmd === "glow" || pagerCmd === "mdcat -l -p";
334
+ pagerCmd === "bat -lmd --style plain" || pagerCmd === "glow -p" || pagerCmd === "mdcat -l -p";
320
335
  if (!hasMarkdownPager) {
321
336
  hint("Tip: install a markdown highlighter for better output (e.g. bat, glow, mdcat)");
322
337
  }
@@ -327,6 +342,9 @@ debug("Video ID:", videoId);
327
342
  debug("Mode:", mode);
328
343
  if (lang) debug("Language:", lang);
329
344
 
345
+ // Give the version check a moment to complete
346
+ await Promise.race([versionCheck, new Promise((r) => setTimeout(r, 1000))]);
347
+
330
348
  try {
331
349
  if (mode === "info") {
332
350
  const result = await fetchTranscript(videoId, { videoDetails: true, lang } as any);
@@ -335,13 +353,13 @@ try {
335
353
  } else {
336
354
  printVideoInfo(result.videoDetails);
337
355
  }
338
- process.exit(0);
356
+ exitProcess(0);
339
357
  }
340
358
 
341
359
  if (mode === "summarize") {
342
360
  if (!config.summarize?.command) {
343
361
  console.error("Error: [summarize] section with a command is required in config");
344
- process.exit(1);
362
+ exitProcess(1);
345
363
  }
346
364
 
347
365
  const cacheOpts = { lang, mode: "summarize", noDecode };
@@ -374,14 +392,14 @@ try {
374
392
  debug("Cache written: transcript.json");
375
393
  }
376
394
 
377
- const prompt = config.summarize.prompt ?? "";
395
+ const prompt = config.summarize!.prompt ?? "";
378
396
  const transcriptText = toText(segments, !noDecode);
379
397
 
380
398
  if (!summary) {
381
399
  info(`Summarizing...`);
382
400
  summary = await summarize({
383
401
  prompt,
384
- command: config.summarize.command,
402
+ command: config.summarize!.command!,
385
403
  transcript: transcriptText,
386
404
  cwd: dir,
387
405
  });
@@ -392,11 +410,11 @@ try {
392
410
 
393
411
  const formatted = noFormat ? summary : await formatMd(summary);
394
412
  await outputText(formatted + "\n");
395
- process.exit(0);
413
+ exitProcess(0);
396
414
  } else if (listOnly) {
397
415
  const languages = await listLanguages(videoId);
398
416
  printLanguages(languages);
399
- process.exit(0);
417
+ exitProcess(0);
400
418
  }
401
419
 
402
420
  const decode = !noDecode;
@@ -463,8 +481,9 @@ try {
463
481
 
464
482
  await outputText(output);
465
483
  }
484
+ exitProcess(0);
466
485
  } catch (err: unknown) {
467
486
  const message = err instanceof Error ? err.message : String(err);
468
487
  console.error(`Error: ${message}`);
469
- process.exit(1);
488
+ exitProcess(1);
470
489
  }
package/src/pager.test.ts CHANGED
@@ -58,7 +58,7 @@ describe("detectPager", () => {
58
58
  // In CI or minimal environments, no pagers may be installed
59
59
  // If a pager IS found, it should be one of the expected ones
60
60
  if (pager !== null) {
61
- const known = ["bat -lmd", "glow", "mdcat -l -p", "less"];
61
+ const known = ["bat -lmd --style plain", "glow -p", "mdcat -l -p", "less"];
62
62
  expect(known).toContain(pager);
63
63
  }
64
64
  });
package/src/pager.ts CHANGED
@@ -15,8 +15,8 @@ export function detectPager(cfgPager?: string): string | null {
15
15
 
16
16
  if (cfgPager !== undefined && cfgPager !== "" && cfgPager !== "auto") return cfgPager;
17
17
 
18
- if (hasCommand("bat")) return "bat -lmd";
19
- if (hasCommand("glow")) return "glow";
18
+ if (hasCommand("bat")) return "bat -lmd --style plain";
19
+ if (hasCommand("glow")) return "glow -p";
20
20
  if (hasCommand("mdcat")) return "mdcat -l -p";
21
21
  if (hasCommand("less")) return "less";
22
22
  return null;
@@ -0,0 +1,32 @@
1
+ import { debug } from "./debug";
2
+ import pkg from "../package.json";
3
+
4
+ const TIMEOUT_MS = 3000;
5
+
6
+ const registryUrl = `https://registry.npmjs.org/${pkg.name}/latest`;
7
+
8
+ export async function checkVersion(): Promise<string | null> {
9
+ try {
10
+ const controller = new AbortController();
11
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
12
+
13
+ const res = await fetch(registryUrl, { signal: controller.signal });
14
+ clearTimeout(timer);
15
+
16
+ if (!res.ok) {
17
+ debug("Version check failed: HTTP", res.status);
18
+ return null;
19
+ }
20
+
21
+ const data = (await res.json()) as { version?: string };
22
+ if (!data.version) {
23
+ debug("Version check: no version field in response");
24
+ return null;
25
+ }
26
+
27
+ return data.version;
28
+ } catch (err) {
29
+ debug("Version check error:", err instanceof Error ? err.message : String(err));
30
+ return null;
31
+ }
32
+ }