askpplx 1.2.2 → 1.3.1

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
@@ -29,6 +29,15 @@ askpplx "Explain Raft vs Paxos in simple terms"
29
29
 
30
30
  # web-enabled search with local context
31
31
  askpplx "What are breaking changes in React 19 that affect this code? $(cat src/app.tsx)"
32
+
33
+ # read prompt from stdin (filter style)
34
+ cat article.txt | askpplx -S "Summarize this article"
35
+
36
+ # extract plain text from JSON response
37
+ askpplx "Node.js LTS version" --json | jq -r '.text'
38
+
39
+ # list cited source URLs
40
+ askpplx "Latest TypeScript release notes" --json | jq -r '.sources[].url' | sort -u
32
41
  ```
33
42
 
34
43
  ## Agent Rule
@@ -36,11 +45,20 @@ askpplx "What are breaking changes in React 19 that affect this code? $(cat src/
36
45
  Add this rule to your `CLAUDE.md` or `AGENTS.md` to enable automatic Perplexity lookups, no need to configure MCPs:
37
46
 
38
47
  ```markdown
39
- # Rule: askpplx CLI Usage
48
+ # Rule: `askpplx` CLI Usage
49
+
50
+ **MANDATORY:** Run `npx -y askpplx --help` at the start of every agent session to learn available options and confirm the tool is working.
51
+
52
+ Use `askpplx` to query Perplexity, an AI search engine combining real-time web search with advanced language models.
53
+
54
+ ## Why This Matters
40
55
 
41
- Use `askpplx` to query Perplexity, an AI search engine combining real-time web search with advanced language models. Run it via `npx -y askpplx`.
56
+ - **Ground your knowledge:** Your training data has a cutoff date. Real-time search ensures you work with current information—correct API signatures, latest versions, up-to-date best practices.
57
+ - **Save time and resources:** A quick lookup is far cheaper than debugging hallucinated code or explaining why an approach failed. When in doubt, verify first.
58
+ - **Reduce false confidence:** Even when you feel certain, external verification catches subtle errors before they compound into larger problems.
59
+ - **Stay current:** Libraries change, APIs deprecate, patterns evolve. What was correct six months ago may be wrong today.
42
60
 
43
- Use concise prompts for quick facts and focused questions for deeper topics. If results are unexpected, refine your query and ask again.
61
+ ## Usage Guidelines
44
62
 
45
- Verification is fast and cheap, so prefer looking up information over making assumptions. Before first use, run `npx -y askpplx --help`.
63
+ Use concise prompts for quick facts and focused questions for deeper topics. If results are unexpected, refine your query and ask again. Verification is fast and cheapprefer looking up information over making assumptions.
46
64
  ```
package/dist/cli.js CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from "commander";
2
+ import { Command, Option } from "@commander-js/extra-typings";
3
3
  import packageJson from "../package.json" with { type: "json" };
4
4
  import { clearPerplexityApiKey, getConfigPath, getPerplexityApiKey, maskApiKey, setPerplexityApiKey, } from "./config.js";
5
5
  import { runCli } from "./run-cli.js";
6
+ import { collectStdinText } from "./collect-stdin-text.js";
7
+ import { resolveCliPrompt } from "./resolve-cli-prompt.js";
6
8
  const usageExamples = `
7
9
  About Perplexity:
8
10
  Perplexity AI is an AI-powered search engine and answer engine that delivers
@@ -28,28 +30,51 @@ Examples:
28
30
  askpplx "Latest news on AI" -c medium
29
31
  askpplx "$(cat article.txt)" -s ./summarize.md
30
32
  askpplx "$(cat article.txt)" -S "Summarize this article"
33
+ cat article.txt | askpplx -S "Summarize this article"
31
34
  askpplx "Node.js LTS version" --json | jq -r '.text'
32
35
  askpplx "Show reasoning" --show-thinking`;
33
36
  const program = new Command()
34
- .name("askpplx")
37
+ .name(packageJson.name)
35
38
  .description(packageJson.description)
36
39
  .version(packageJson.version)
40
+ .showHelpAfterError("(add --help for additional information)")
41
+ .showSuggestionAfterError()
37
42
  .argument("[prompt]", "The prompt to send to Perplexity Sonar")
38
43
  .option("-m, --model <model>", "Model to use", "sonar-reasoning-pro")
39
44
  .option("-s, --system <path>", "Path to custom system prompt file")
40
45
  .option("-S, --system-text <text>", "System prompt text (overrides -s)")
41
- .option("-c, --context <size>", "Search context size: low, medium, high", "high")
46
+ .addOption(new Option("-c, --context <size>", "Search context size: low, medium, high")
47
+ .choices(["low", "medium", "high"])
48
+ .default("high"))
42
49
  .option("--json", "Output full API response as JSON (text, sources, usage)")
43
50
  .option("--show-thinking", "Show model thinking/reasoning blocks")
44
- .option("--no-stream, --no-streaming", "Disable streaming output")
51
+ .option("--no-stream", "Disable streaming output")
52
+ .addOption(new Option("--no-streaming", "Alias for --no-stream").hideHelp())
45
53
  .addHelpText("after", usageExamples)
46
54
  .action(async (prompt, options) => {
47
- if (!prompt) {
48
- program.help();
49
- return;
50
- }
51
55
  try {
52
- await runCli(prompt, options);
56
+ let stdinText;
57
+ if (!prompt && !process.stdin.isTTY) {
58
+ process.stdin.setEncoding("utf8");
59
+ stdinText = await collectStdinText(process.stdin);
60
+ }
61
+ const effectivePrompt = resolveCliPrompt(prompt, stdinText);
62
+ if (!effectivePrompt) {
63
+ program.error("Missing prompt.\n" +
64
+ 'Usage: askpplx <prompt> OR cat <file> | askpplx -S "Summarize this article"\n' +
65
+ "(Note: use -S to provide an instruction with stdin input)", { exitCode: 1 });
66
+ return;
67
+ }
68
+ const cliOptions = {
69
+ model: options.model,
70
+ json: Boolean(options.json),
71
+ system: options.system,
72
+ systemText: options.systemText,
73
+ context: options.context,
74
+ showThinking: Boolean(options.showThinking),
75
+ stream: options.stream && options.streaming,
76
+ };
77
+ await runCli(effectivePrompt, cliOptions);
53
78
  }
54
79
  catch (error) {
55
80
  const message = error instanceof Error ? error.message : "An unexpected error occurred";
@@ -57,7 +82,7 @@ const program = new Command()
57
82
  process.exitCode = 1;
58
83
  }
59
84
  });
60
- program
85
+ const configCommand = program
61
86
  .command("config")
62
87
  .description("Manage stored configuration")
63
88
  .option("--set-api-key <key>", "Store Perplexity API key")
@@ -68,7 +93,7 @@ program
68
93
  try {
69
94
  if (options.setApiKey) {
70
95
  setPerplexityApiKey(options.setApiKey);
71
- console.log("API key stored successfully.");
96
+ console.error("API key stored successfully.");
72
97
  }
73
98
  else if (options.showApiKey) {
74
99
  const key = getPerplexityApiKey();
@@ -77,14 +102,13 @@ program
77
102
  }
78
103
  else if (options.clearApiKey) {
79
104
  clearPerplexityApiKey();
80
- console.log("API key cleared.");
105
+ console.error("API key cleared.");
81
106
  }
82
107
  else if (options.path) {
83
108
  console.log(getConfigPath());
84
109
  }
85
110
  else {
86
- const configCmd = program.commands.find((c) => c.name() === "config");
87
- configCmd?.help();
111
+ configCommand.help({ error: true });
88
112
  }
89
113
  }
90
114
  catch (error) {
@@ -95,4 +119,4 @@ program
95
119
  process.exitCode = 1;
96
120
  }
97
121
  });
98
- program.parse();
122
+ await program.parseAsync();
@@ -0,0 +1 @@
1
+ export declare function collectStdinText(input: AsyncIterable<string>, maxBytes?: number): Promise<string>;
@@ -0,0 +1,15 @@
1
+ import { Buffer } from "node:buffer";
2
+ const DEFAULT_MAX_STDIN_BYTES = 10 * 1024 * 1024;
3
+ export async function collectStdinText(input, maxBytes = DEFAULT_MAX_STDIN_BYTES) {
4
+ const chunks = [];
5
+ let totalBytes = 0;
6
+ for await (const chunk of input) {
7
+ totalBytes += Buffer.byteLength(chunk, "utf8");
8
+ if (totalBytes > maxBytes) {
9
+ const limitMegabytes = Math.round(maxBytes / 1024 / 1024);
10
+ throw new Error(`Input too large: exceeds ${String(limitMegabytes)}MB limit`);
11
+ }
12
+ chunks.push(chunk);
13
+ }
14
+ return chunks.join("");
15
+ }
@@ -0,0 +1 @@
1
+ export declare function resolveCliPrompt(promptArgument: string | undefined, stdinText?: string): string | undefined;
@@ -0,0 +1,9 @@
1
+ export function resolveCliPrompt(promptArgument, stdinText) {
2
+ const candidate = (promptArgument ?? stdinText)?.trimEnd();
3
+ if (candidate === undefined)
4
+ return undefined;
5
+ const trimmedCandidate = candidate.trim();
6
+ if (trimmedCandidate.length === 0)
7
+ return undefined;
8
+ return candidate;
9
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "askpplx",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "1.2.2",
5
+ "version": "1.3.1",
6
6
  "description": "Minimal Unix-style CLI for querying Perplexity Sonar API.",
7
7
  "repository": {
8
8
  "type": "git",
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@ai-sdk/perplexity": "^2.0.21",
45
+ "@commander-js/extra-typings": "^14.0.0",
45
46
  "ai": "^5.0.108",
46
47
  "commander": "^14.0.2",
47
48
  "conf": "^15.0.2",