superacli 1.1.8 → 1.1.10

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.
@@ -171,16 +171,25 @@ async function execute(cmd, flags, context = {}) {
171
171
  }
172
172
 
173
173
  if (passthroughMode) {
174
+ // Passthrough mode: use __rawArgs from flags (collected from remaining positional args)
174
175
  const passthroughArgs = Array.isArray(flags.__rawArgs) ? flags.__rawArgs : []
175
176
  args.push(...passthroughArgs)
176
177
  } else {
178
+ // Normal mode: collect positional values from flags OR from __positionalArgs
177
179
  const positionalValues = []
180
+
181
+ // First, try to get positional values from flags (defined args)
178
182
  for (const name of positionalNames) {
179
183
  if (remainingFlags[name] !== undefined) {
180
184
  positionalValues.push(String(remainingFlags[name]))
181
185
  delete remainingFlags[name]
182
186
  }
183
187
  }
188
+
189
+ // Also support __positionalArgs array for commands that need raw positional values
190
+ if (Array.isArray(flags.__positionalArgs)) {
191
+ positionalValues.push(...flags.__positionalArgs)
192
+ }
184
193
 
185
194
  const flagArgs = []
186
195
  if (includeJsonFlag) flagArgs.push(includeJsonFlag)
@@ -7,17 +7,27 @@ function dcliDir() {
7
7
  return process.env.SUPERCLI_HOME || path.join(os.homedir(), ".dcli")
8
8
  }
9
9
 
10
+ function supercliDir() {
11
+ return process.env.SUPERCLI_HOME || path.join(os.homedir(), ".supercli")
12
+ }
13
+
10
14
  function providersFile() {
11
- return path.join(dcliDir(), "skills-providers.json")
15
+ const supercliPath = path.join(supercliDir(), "skills-providers.json")
16
+ const dcliPath = path.join(dcliDir(), "skills-providers.json")
17
+ return fs.existsSync(supercliPath) ? supercliPath : dcliPath
12
18
  }
13
19
 
14
20
  function indexFile() {
15
- return path.join(dcliDir(), "skills-index.json")
21
+ const supercliPath = path.join(supercliDir(), "skills-index.json")
22
+ const dcliPath = path.join(dcliDir(), "skills-index.json")
23
+ return fs.existsSync(supercliPath) ? supercliPath : dcliPath
16
24
  }
17
25
 
18
26
  function ensureDir() {
19
27
  const dir = dcliDir()
20
28
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
29
+ const supercliDirPath = supercliDir()
30
+ if (!fs.existsSync(supercliDirPath)) fs.mkdirSync(supercliDirPath, { recursive: true })
21
31
  }
22
32
 
23
33
  function defaultProviders() {
@@ -135,7 +145,7 @@ function syncCatalog() {
135
145
  for (const entry of entries) {
136
146
  if (!entry || !entry.id || !entry.source_url) continue
137
147
  skills.push({
138
- id: `${provider.name}:${entry.id}`,
148
+ id: provider.name + ":" + entry.id,
139
149
  provider: provider.name,
140
150
  name: entry.name || entry.id,
141
151
  description: entry.description || "",
@@ -147,6 +157,34 @@ function syncCatalog() {
147
157
  continue
148
158
  }
149
159
 
160
+ if (provider.type === "plugin_fs") {
161
+ const pluginDir = provider.plugin_dir
162
+ if (!pluginDir || !fs.existsSync(pluginDir)) continue
163
+ const skillsDir = path.join(pluginDir, "skills")
164
+ if (fs.existsSync(skillsDir)) {
165
+ const files = walkDir(skillsDir)
166
+ for (const filePath of files) {
167
+ const markdown = fs.readFileSync(filePath, "utf-8")
168
+ const { frontmatter, body } = parseFrontmatter(markdown)
169
+ const baseId = baseSkillId(filePath, frontmatter, skillsDir)
170
+ const heading = body.split("\n").find(l => l.startsWith("# "))
171
+ const name = frontmatter.skill_name || (heading ? heading.slice(2).trim() : baseId)
172
+ const description = frontmatter.description || ""
173
+ const tags = typeof frontmatter.tags === "string" ? frontmatter.tags.split(",").map(t => t.trim()).filter(Boolean) : []
174
+ skills.push({
175
+ id: provider.name + ":" + baseId,
176
+ provider: provider.name,
177
+ name,
178
+ description,
179
+ source_path: filePath,
180
+ tags,
181
+ updated_at: new Date().toISOString()
182
+ })
183
+ }
184
+ }
185
+ continue
186
+ }
187
+
150
188
  const roots = Array.isArray(provider.roots) ? provider.roots : []
151
189
  for (const root of roots) {
152
190
  const files = walkDir(root)
@@ -158,9 +196,8 @@ function syncCatalog() {
158
196
  const name = frontmatter.skill_name || (heading ? heading.slice(2).trim() : baseId)
159
197
  const description = frontmatter.description || ""
160
198
  const tags = typeof frontmatter.tags === "string" ? frontmatter.tags.split(",").map(t => t.trim()).filter(Boolean) : []
161
-
162
199
  skills.push({
163
- id: `${provider.name}:${baseId}`,
200
+ id: provider.name + ":" + baseId,
164
201
  provider: provider.name,
165
202
  name,
166
203
  description,
@@ -206,7 +243,6 @@ function getCatalogSkill(providerId) {
206
243
  const hit = (idx.skills || []).find(s => s.id === providerId)
207
244
  if (!hit) return null
208
245
  let markdown = null
209
-
210
246
  if (hit.source_path) {
211
247
  if (!fs.existsSync(hit.source_path)) return null
212
248
  markdown = fs.readFileSync(hit.source_path, "utf-8")
@@ -215,15 +251,136 @@ function getCatalogSkill(providerId) {
215
251
  if (res.error || res.status !== 0) return null
216
252
  markdown = (res.stdout || "").trim()
217
253
  }
218
-
219
254
  if (!markdown) return null
255
+ return { ...hit, markdown }
256
+ }
220
257
 
258
+
259
+
260
+ /**
261
+ * Get catalog information with provider breakdown
262
+ */
263
+ function getCatalogInfo() {
264
+ const idx = readIndex()
265
+ const providers = listProviders()
266
+
267
+ const providerStats = providers.map(provider => {
268
+ if (!provider.enabled) {
269
+ return { name: provider.name, type: provider.type, enabled: false, status: 'disabled' }
270
+ }
271
+
272
+ let skillsCount = 0
273
+ let status = 'active'
274
+ let details = {}
275
+
276
+ if (provider.type === 'plugin_fs') {
277
+ const pluginDir = provider.plugin_dir
278
+ if (!pluginDir || !fs.existsSync(pluginDir)) {
279
+ status = 'missing'
280
+ details.error = 'Plugin directory not found'
281
+ } else {
282
+ const skillsDir = path.join(pluginDir, 'skills')
283
+ if (fs.existsSync(skillsDir)) {
284
+ const files = walkDir(skillsDir)
285
+ skillsCount = files.length
286
+ details.plugin_dir = pluginDir
287
+ details.skills_dir = skillsDir
288
+ } else {
289
+ status = 'no_skills_dir'
290
+ details.skills_dir = skillsDir
291
+ }
292
+ }
293
+ } else if (provider.type === 'local_fs' || provider.type === 'repo_fs') {
294
+ const roots = Array.isArray(provider.roots) ? provider.roots : []
295
+ for (const root of roots) {
296
+ if (fs.existsSync(root)) {
297
+ const files = walkDir(root)
298
+ skillsCount += files.length
299
+ }
300
+ }
301
+ details.roots = roots
302
+ } else if (provider.type === 'remote_static') {
303
+ skillsCount = Array.isArray(provider.entries) ? provider.entries.length : 0
304
+ details.entries_count = skillsCount
305
+ details.source_repo = provider.source_repo
306
+ }
307
+
308
+ return {
309
+ name: provider.name,
310
+ type: provider.type,
311
+ enabled: provider.enabled,
312
+ status,
313
+ skills_count: skillsCount,
314
+ ...details
315
+ }
316
+ })
317
+
221
318
  return {
222
- ...hit,
223
- markdown
319
+ index: {
320
+ version: idx.version,
321
+ updated_at: idx.updated_at,
322
+ total_skills: idx.skills.length
323
+ },
324
+ providers: providerStats,
325
+ recent_skills: idx.skills.slice(-5).map(s => ({
326
+ id: s.id,
327
+ name: s.name,
328
+ provider: s.provider,
329
+ added_at: s.updated_at
330
+ }))
224
331
  }
225
332
  }
226
333
 
334
+ /**
335
+ * Describe all provider types with examples
336
+ */
337
+ function describeProviderTypes() {
338
+ return {
339
+ provider_types: [
340
+ {
341
+ name: 'local_fs',
342
+ description: 'Scans local directories for SKILL.md files',
343
+ example: {
344
+ name: 'my-skills',
345
+ type: 'local_fs',
346
+ roots: ['~/.config/opencode/skills'],
347
+ enabled: true
348
+ }
349
+ },
350
+ {
351
+ name: 'repo_fs',
352
+ description: 'Scans repository directories for SKILL.md files',
353
+ example: {
354
+ name: 'repo',
355
+ type: 'repo_fs',
356
+ roots: ['.agents/skills', 'docs/skills'],
357
+ enabled: true
358
+ }
359
+ },
360
+ {
361
+ name: 'remote_static',
362
+ description: 'Pre-indexed skills from remote repositories (GitHub)',
363
+ example: {
364
+ name: 'visual-explainer',
365
+ type: 'remote_static',
366
+ source_repo: 'https://github.com/user/repo',
367
+ entries: [{ id: 'skill-1', source_url: 'https://raw.githubusercontent.com/...' }]
368
+ }
369
+ },
370
+ {
371
+ name: 'plugin_fs',
372
+ description: 'Auto-discovers SKILL.md files from installed plugin skills/ directory',
373
+ example: {
374
+ name: 'superbackend',
375
+ type: 'plugin_fs',
376
+ plugin_dir: '/path/to/plugins/superbackend'
377
+ }
378
+ }
379
+ ]
380
+ }
381
+ }
382
+
383
+
227
384
  module.exports = {
228
385
  listProviders,
229
386
  addProvider,
@@ -233,5 +390,7 @@ module.exports = {
233
390
  syncCatalog,
234
391
  listCatalogSkills,
235
392
  searchCatalog,
236
- getCatalogSkill
393
+ getCatalogSkill,
394
+ getCatalogInfo,
395
+ describeProviderTypes
237
396
  }
package/cli/skills.js CHANGED
@@ -13,7 +13,9 @@ const {
13
13
  syncCatalog,
14
14
  listCatalogSkills,
15
15
  searchCatalog,
16
- getCatalogSkill
16
+ getCatalogSkill,
17
+ getCatalogInfo,
18
+ describeProviderTypes
17
19
  } = require("./skills-catalog")
18
20
 
19
21
  function normalizeSkillId(input) {
@@ -179,7 +181,7 @@ function buildTeachSkillMarkdown(options = {}) {
179
181
  ]
180
182
  }
181
183
 
182
- return `---\n${renderYamlObject(frontmatter)}\n---\n\n# Instruction\n\nThis skill teaches LLMs how to discover and use SuperCLI capabilities and skill documents:\n\n1. List available capability docs and catalog skills:\n\n\`\`\`bash\nsupercli skills list --json\n\`\`\`\n\n2. Fetch documentation for a specific capability:\n\n\`\`\`bash\nsupercli skills get <namespace.resource.action> --format skill.md\n\`\`\`\n\n3. Parse YAML frontmatter to understand command, arguments, output schema, and metadata.\n\n4. Execute the capability with validated arguments:\n\n\`\`\`bash\nsupercli <namespace> <resource> <action> --arg value --json\n\`\`\`\n\n5. For plugin discovery and remote plugin installs, use:\n\n\`\`\`bash\nsupercli skills get ${PLUGINS_USAGE_SKILL_ID} --format skill.md\n\`\`\`\n\n6. To index skill documents from a local directory (e.g., a project with docs/skills):\n\n\`\`\`bash\n# List current providers\nsupercli skills providers list --json\n\n# Add a local provider for a project\nsupercli skills providers add --name myproject --type local_fs --roots ./myproject/docs/skills\n\n# Sync the catalog to index new skill documents\nsupercli skills sync\n\n# Search skill documents from the new provider\nsupercli skills search <query> --provider myproject\n\n# Remove a provider if needed\nsupercli skills providers remove --name myproject\n\`\`\`\n\n# Examples\n\n\`\`\`bash\nsupercli skills teach --format skill.md\nsupercli skills teach --format skill.md --show-dag\nsupercli skills providers add --name myproject --type local_fs --roots ./myproject/docs/skills\nsupercli skills sync\nsupercli skills search btc --provider myproject\n\`\`\``
184
+ return `---\n${renderYamlObject(frontmatter)}\n---\n\n# Instruction\n\nThis skill teaches LLMs how to discover and use SuperCLI capabilities and skill documents:\n\n1. List available capability docs and catalog skills:\n\n\`\`\`bash\nsupercli skills list --json\n\`\`\`\n\n2. Fetch documentation for a specific capability:\n\n\`\`\`bash\nsupercli skills get <namespace.resource.action> --format skill.md\n\`\`\`\n\n3. Parse YAML frontmatter to understand command, arguments, output schema, and metadata.\n\n4. Execute the capability with validated arguments:\n\n\`\`\`bash\nsupercli <namespace> <resource> <action> --arg value --json\n\`\`\`\n\n5. For plugin discovery and remote plugin installs, use:\n\n\`\`\`bash\nsupercli skills get ${PLUGINS_USAGE_SKILL_ID} --format skill.md\n\`\`\`\n\n6. To index skill documents from a local directory (e.g., a project with docs/skills):\n\n\`\`\`bash\n# List current providers\nsupercli skills providers list --json\n\n# Add a local provider for a project\nsupercli skills providers add --name myproject --type local_fs --roots ./myproject/docs/skills\n\n# Sync the catalog to index new skill documents\nsupercli skills sync\n\n# Search skill documents from the new provider\nsupercli skills search <query> --provider myproject\n\n# Remove a provider if needed\nsupercli skills providers remove --name myproject\n\`\`\`\n\n7. **Using Positional Arguments** (for commands that require positional values):\n\n\`\`\`bash\n# Some commands require positional arguments instead of named flags\nsupercli <namespace> <resource> <action> <positional1> <positional2> --json\n\n# Example: Delete by ID (positional argument)\nsupercli superbackend waiting-list delete --id <objectId> --quiet\n\n# Example: Passthrough to plugin CLI\nsupercli superbackend _ _ <resource> <command> <args...> --quiet\n\`\`\`\n\n# Examples\n\n\`\`\`bash\nsupercli skills teach --format skill.md\nsupercli skills teach --format skill.md --show-dag\nsupercli skills providers add --name myproject --type local_fs --roots ./myproject/docs/skills\nsupercli skills sync\nsupercli skills search btc --provider myproject\nsupercli superbackend waiting-list delete --id <id> --quiet\nsupercli superbackend _ _ users list --value 10 --quiet\n\`\`\``
183
185
  }
184
186
 
185
187
  function buildPluginsUsageSkillMarkdown(options = {}) {
@@ -339,7 +341,48 @@ function handleSkillsCommand(options) {
339
341
  return true
340
342
  }
341
343
 
342
- outputError({ code: 85, type: "invalid_argument", message: "Unknown providers subcommand. Use: list, add, remove, show", recoverable: false })
344
+ if (action === "describe") {
345
+ const types = describeProviderTypes()
346
+ if (humanMode && !flags.json) {
347
+ console.log("\n ⚡ Skill Provider Types\n")
348
+ for (const t of types.provider_types) {
349
+ console.log(" " + t.name + ":")
350
+ console.log(" " + t.description)
351
+ console.log(" Example:")
352
+ console.log(" " + JSON.stringify(t.example, null, 6))
353
+ console.log("")
354
+ }
355
+ } else {
356
+ output(types)
357
+ }
358
+ return true
359
+ }
360
+
361
+ outputError({ code: 85, type: "invalid_argument", message: "Unknown providers subcommand. Use: list, add, remove, show, describe", recoverable: false })
362
+ return true
363
+ }
364
+
365
+ if (subcommand === "catalog") {
366
+ const action = positional[2]
367
+ if (action === "info") {
368
+ const info = getCatalogInfo()
369
+ if (humanMode && !flags.json) {
370
+ console.log("\n ⚡ Skills Catalog Info\n")
371
+ console.log(" Index:")
372
+ console.log(" Version:", info.index.version)
373
+ console.log(" Updated:", info.index.updated_at)
374
+ console.log(" Total Skills:", info.index.total_skills)
375
+ console.log("\n Providers:")
376
+ for (const p of info.providers) {
377
+ console.log(" - " + p.name + " (" + p.type + "): " + p.skills_count + " skills [" + p.status + "]")
378
+ }
379
+ console.log("")
380
+ } else {
381
+ output({ catalog: info })
382
+ }
383
+ return true
384
+ }
385
+ outputError({ code: 85, type: "invalid_argument", message: "Usage: supercli skills catalog info [--json]", recoverable: false })
343
386
  return true
344
387
  }
345
388
 
package/cli/supercli.js CHANGED
@@ -226,7 +226,7 @@ function renderTopLevelHelp(config) {
226
226
  " MCP: supercli mcp list | supercli mcp add <name> --url <url> | supercli mcp tools --mcp-server <name> | supercli mcp call --mcp-server <name> --tool <tool> | supercli mcp bind --mcp-server <name> --tool <tool> --as <ns.res.act> | supercli mcp doctor --mcp-server <name> | supercli mcp remove <name>",
227
227
  );
228
228
  console.log(
229
- " Skill Docs: supercli skills list | supercli skills get <id> | supercli skills search --query <q> | supercli skills sync",
229
+ " Skill Docs: supercli skills list | supercli skills get <id> | supercli skills catalog info | supercli skills providers describe | supercli skills search --query <q> | supercli skills sync",
230
230
  );
231
231
  if (config.features?.ask || process.env.OPENAI_BASE_URL) {
232
232
  console.log(' AI: supercli ask "<your natural language query>"');
@@ -859,6 +859,26 @@ async function main() {
859
859
  return;
860
860
  }
861
861
 
862
+ // Handle passthrough positional arguments
863
+ const passthroughArgs = [];
864
+ if (cmd.adapterConfig && cmd.adapterConfig.passthrough === true) {
865
+ // For passthrough commands, collect remaining positional args after namespace.resource.action
866
+ const cmdPositionalIndex = positional.findIndex((p, i) =>
867
+ i >= 0 &&
868
+ positional[i] === namespace &&
869
+ positional[i + 1] === resource &&
870
+ positional[i + 2] === action
871
+ );
872
+ if (cmdPositionalIndex >= 0) {
873
+ // Collect all args after namespace resource action
874
+ for (let i = cmdPositionalIndex + 3; i < positional.length; i++) {
875
+ passthroughArgs.push(positional[i]);
876
+ }
877
+ }
878
+ // Pass to adapter via __rawArgs
879
+ uFlags.__rawArgs = passthroughArgs;
880
+ }
881
+
862
882
  const start = Date.now();
863
883
  const result = await execute(cmd, uFlags, {
864
884
  server: SERVER || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "superacli",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "Config-driven, AI-friendly dynamic CLI",
5
5
  "license": "MIT",
6
6
  "main": "server/app.js",
@@ -43,10 +43,10 @@
43
43
  "adapter": "process",
44
44
  "adapterConfig": {
45
45
  "command": "openhands",
46
- "baseArgs": ["--headless", "-t"],
46
+ "baseArgs": ["--headless", "--always-approve", "--override-with-envs", "-t"],
47
47
  "positionalArgs": ["task"],
48
48
  "parseJson": false,
49
- "timeout_ms": 15000,
49
+ "timeout_ms": 180000,
50
50
  "missingDependencyHelp": "Please install OpenHands CLI to use this command."
51
51
  },
52
52
  "args": [
@@ -64,10 +64,10 @@
64
64
  "adapter": "process",
65
65
  "adapterConfig": {
66
66
  "command": "openhands",
67
- "baseArgs": ["--headless", "-f"],
67
+ "baseArgs": ["--headless", "--always-approve", "--override-with-envs", "-f"],
68
68
  "positionalArgs": ["file"],
69
69
  "parseJson": false,
70
- "timeout_ms": 15000,
70
+ "timeout_ms": 180000,
71
71
  "missingDependencyHelp": "Please install OpenHands CLI to use this command."
72
72
  },
73
73
  "args": [
@@ -85,11 +85,11 @@
85
85
  "adapter": "process",
86
86
  "adapterConfig": {
87
87
  "command": "openhands",
88
- "baseArgs": ["--headless", "--json", "-t"],
88
+ "baseArgs": ["--headless", "--always-approve", "--override-with-envs", "--json", "-t"],
89
89
  "positionalArgs": ["task"],
90
90
  "stream": "jsonl",
91
91
  "parseJson": false,
92
- "timeout_ms": 15000,
92
+ "timeout_ms": 180000,
93
93
  "missingDependencyHelp": "Please install OpenHands CLI to use this command."
94
94
  },
95
95
  "args": [
@@ -8,6 +8,16 @@ Use this plugin to run OpenHands in headless automation mode.
8
8
  - Verify CLI: `openhands --version`
9
9
  - Install plugin: `supercli plugins install openhands`
10
10
 
11
+ ## Environment Setup
12
+
13
+ Set these environment variables before running headless tasks:
14
+
15
+ ```bash
16
+ export LLM_API_KEY="your-api-key"
17
+ export LLM_MODEL="openai/gpt-4o-mini" # or another model
18
+ export LLM_BASE_URL="https://openrouter.ai/api/v1" # optional
19
+ ```
20
+
11
21
  ## Common Commands
12
22
 
13
23
  - Version: `supercli openhands self version`
@@ -15,6 +25,19 @@ Use this plugin to run OpenHands in headless automation mode.
15
25
  - Headless task from file: `supercli openhands task file --file ./task.txt`
16
26
  - JSONL stream mode: `supercli openhands task json --task "Add tests"`
17
27
 
28
+ ## Smoke Test Example
29
+
30
+ ```bash
31
+ # Set environment variables
32
+ export LLM_API_KEY="your-openrouter-api-key"
33
+ export LLM_MODEL="openai/gpt-4o-mini"
34
+ export LLM_BASE_URL="https://openrouter.ai/api/v1"
35
+ export PATH="/path/to/venv/bin:$PATH"
36
+
37
+ # Run a simple task
38
+ supercli openhands task run --task "Create a file called test.txt with 'hello world'"
39
+ ```
40
+
18
41
  ## Passthrough
19
42
 
20
43
  For any unsupported subcommand, use passthrough:
@@ -581,6 +581,16 @@
581
581
  "type": "bundled",
582
582
  "manifest_path": "plugins/boxlite/plugin.json"
583
583
  }
584
+ },
585
+ {
586
+ "name": "pplx",
587
+ "description": "Perplexity API CLI for online search, research, and Q&A with citations",
588
+ "tags": ["perplexity", "ai", "search", "research", "llm", "citations", "online"],
589
+ "has_learn": true,
590
+ "source": {
591
+ "type": "bundled",
592
+ "manifest_path": "plugins/pplx/plugin.json"
593
+ }
584
594
  }
585
595
  ]
586
596
  }
@@ -0,0 +1,95 @@
1
+ # pplx Plugin
2
+
3
+ Perplexity API CLI plugin for SuperCLI - Fast, non-interactive, agent-first CLI for online search and research with citations.
4
+
5
+ ## Source
6
+
7
+ - **CLI Repository**: https://github.com/javimosch/pplx
8
+ - **Skill Sync**: Auto-syncs from `.agents/skills/pplx-usage/SKILL.md`
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Learn about the plugin
14
+ sc plugins learn pplx
15
+
16
+ # Install from registry
17
+ sc plugins install pplx
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ Set your Perplexity API key:
23
+
24
+ ```bash
25
+ export PERPLEXITY_API_KEY=pplx-xxxxxxxxxxxxxxxxxxxxxxxx
26
+ ```
27
+
28
+ Or use `--api-key` flag with each command.
29
+
30
+ Get API key: https://www.perplexity.ai/settings/api
31
+
32
+ ## Commands
33
+
34
+ | Command | Description |
35
+ |---------|-------------|
36
+ | `sc pplx chat send <message>` | Send a chat completion request |
37
+ | `sc pplx ask send <question>` | Ask a question (simplified chat) |
38
+ | `sc pplx models list` | List available Perplexity models |
39
+ | `sc pplx help json` | Get machine-readable help schema |
40
+ | `sc pplx _ <any>` | Passthrough for any pplx command |
41
+
42
+ ## Examples
43
+
44
+ ```bash
45
+ # Quick question
46
+ sc pplx ask send "What is quantum computing?" --json
47
+
48
+ # Research with citations
49
+ sc pplx chat send "Latest AI developments 2025" --citations --json
50
+
51
+ # Deep research
52
+ sc pplx chat send "Climate change research" \
53
+ --model sonar-deep-research \
54
+ --citations \
55
+ --related-questions \
56
+ --json
57
+
58
+ # List models
59
+ sc pplx models list --json
60
+
61
+ # Passthrough mode
62
+ sc pplx _ chat "Question" --model sonar-pro --temperature 0.7
63
+ ```
64
+
65
+ ## Available Models
66
+
67
+ - `sonar` - Fast online search
68
+ - `sonar-pro` - Advanced online search
69
+ - `sonar-deep-research` - Deep research with citations
70
+ - `sonar-reasoning` - Reasoning with search
71
+ - `sonar-reasoning-pro` - Advanced reasoning
72
+
73
+ ## Error Handling
74
+
75
+ | Exit Code | Type | Action |
76
+ |-----------|------|--------|
77
+ | 0 | success | Parse response |
78
+ | 85 | missing_api_key | Set PERPLEXITY_API_KEY |
79
+ | 87 | authentication_failed | Check API key |
80
+ | 106 | rate_limited | Wait 60s and retry |
81
+ | 105 | timeout | Retry immediately |
82
+
83
+ ## Skills
84
+
85
+ This plugin includes learn skills:
86
+
87
+ ```bash
88
+ sc plugins learn pplx
89
+ ```
90
+
91
+ Skills are synced from the remote repository automatically.
92
+
93
+ ## License
94
+
95
+ MIT
@@ -0,0 +1,123 @@
1
+ {
2
+ "name": "pplx",
3
+ "version": "1.0.0",
4
+ "description": "Perplexity API CLI - Fast, non-interactive, agent-first CLI for online search and research with citations",
5
+ "source": "https://github.com/javimosch/pplx",
6
+ "tags": ["perplexity", "ai", "search", "research", "llm", "citations"],
7
+ "has_learn": true,
8
+ "learn": {
9
+ "file": "skills/usage/SKILL.md",
10
+ "remote": {
11
+ "repo": "https://github.com/javimosch/pplx",
12
+ "path": ".agents/skills/pplx-usage/SKILL.md",
13
+ "sync": true
14
+ }
15
+ },
16
+ "checks": [
17
+ { "type": "binary", "name": "node" },
18
+ { "type": "env", "name": "PERPLEXITY_API_KEY", "required": false }
19
+ ],
20
+ "commands": [
21
+ {
22
+ "namespace": "pplx",
23
+ "resource": "chat",
24
+ "action": "send",
25
+ "description": "Send a chat completion request to Perplexity API",
26
+ "adapter": "process",
27
+ "adapterConfig": {
28
+ "command": "node",
29
+ "baseArgs": ["cli/bin/pplx.js", "chat"],
30
+ "positionalArgs": ["message"],
31
+ "jsonFlag": "--json",
32
+ "parseJson": true,
33
+ "missingDependencyHelp": "Set PERPLEXITY_API_KEY env var or use --api-key flag",
34
+ "pluginDir": "plugins/pplx"
35
+ },
36
+ "args": [
37
+ { "name": "message", "type": "string", "required": true, "description": "The message to send" },
38
+ { "name": "model", "type": "string", "required": false, "description": "Model to use (sonar, sonar-pro, sonar-deep-research, sonar-reasoning, sonar-reasoning-pro)", "flag": "--model", "default": "sonar" },
39
+ { "name": "api-key", "type": "string", "required": false, "description": "API key override (overrides PERPLEXITY_API_KEY env var)", "flag": "--api-key" },
40
+ { "name": "citations", "type": "boolean", "required": false, "description": "Return citations for verification", "flag": "--citations" },
41
+ { "name": "images", "type": "boolean", "required": false, "description": "Return images", "flag": "--images" },
42
+ { "name": "related-questions", "type": "boolean", "required": false, "description": "Return related questions", "flag": "--related-questions" },
43
+ { "name": "search-queries", "type": "boolean", "required": false, "description": "Return search queries used", "flag": "--search-queries" },
44
+ { "name": "system", "type": "string", "required": false, "description": "System prompt", "flag": "--system" },
45
+ { "name": "temperature", "type": "number", "required": false, "description": "Temperature (0-1)", "flag": "--temperature" },
46
+ { "name": "max-tokens", "type": "integer", "required": false, "description": "Maximum tokens in response", "flag": "--max-tokens" },
47
+ { "name": "json", "type": "boolean", "required": false, "description": "Output in JSON format", "flag": "--json" }
48
+ ]
49
+ },
50
+ {
51
+ "namespace": "pplx",
52
+ "resource": "ask",
53
+ "action": "send",
54
+ "description": "Ask a question (simplified chat for Q&A)",
55
+ "adapter": "process",
56
+ "adapterConfig": {
57
+ "command": "node",
58
+ "baseArgs": ["cli/bin/pplx.js", "ask"],
59
+ "positionalArgs": ["question"],
60
+ "jsonFlag": "--json",
61
+ "parseJson": true,
62
+ "missingDependencyHelp": "Set PERPLEXITY_API_KEY env var or use --api-key flag",
63
+ "pluginDir": "plugins/pplx"
64
+ },
65
+ "args": [
66
+ { "name": "question", "type": "string", "required": true, "description": "The question to ask" },
67
+ { "name": "model", "type": "string", "required": false, "description": "Model to use", "flag": "--model", "default": "sonar" },
68
+ { "name": "api-key", "type": "string", "required": false, "description": "API key override", "flag": "--api-key" },
69
+ { "name": "citations", "type": "boolean", "required": false, "description": "Return citations", "flag": "--citations" },
70
+ { "name": "system", "type": "string", "required": false, "description": "System prompt", "flag": "--system" },
71
+ { "name": "json", "type": "boolean", "required": false, "description": "Output in JSON format", "flag": "--json" }
72
+ ]
73
+ },
74
+ {
75
+ "namespace": "pplx",
76
+ "resource": "models",
77
+ "action": "list",
78
+ "description": "List available Perplexity models",
79
+ "adapter": "process",
80
+ "adapterConfig": {
81
+ "command": "node",
82
+ "baseArgs": ["cli/bin/pplx.js", "models"],
83
+ "jsonFlag": "--json",
84
+ "parseJson": true,
85
+ "missingDependencyHelp": "Set PERPLEXITY_API_KEY env var or use --api-key flag",
86
+ "pluginDir": "plugins/pplx"
87
+ },
88
+ "args": [
89
+ { "name": "api-key", "type": "string", "required": false, "description": "API key override", "flag": "--api-key" },
90
+ { "name": "json", "type": "boolean", "required": false, "description": "Output in JSON format", "flag": "--json" }
91
+ ]
92
+ },
93
+ {
94
+ "namespace": "pplx",
95
+ "resource": "help",
96
+ "action": "json",
97
+ "description": "Get machine-readable help schema",
98
+ "adapter": "process",
99
+ "adapterConfig": {
100
+ "command": "node",
101
+ "baseArgs": ["cli/bin/pplx.js", "help-json"],
102
+ "parseJson": true,
103
+ "pluginDir": "plugins/pplx"
104
+ },
105
+ "args": []
106
+ },
107
+ {
108
+ "namespace": "pplx",
109
+ "resource": "_",
110
+ "action": "_",
111
+ "description": "Passthrough to execute any pplx CLI command directly",
112
+ "adapter": "process",
113
+ "adapterConfig": {
114
+ "command": "node",
115
+ "baseArgs": ["cli/bin/pplx.js"],
116
+ "passthrough": true,
117
+ "parseJson": false,
118
+ "pluginDir": "plugins/pplx"
119
+ },
120
+ "args": []
121
+ }
122
+ ]
123
+ }
@@ -0,0 +1,121 @@
1
+ ---
2
+ skill_name: "pplx_usage"
3
+ description: "Teaches agents how to use the Perplexity API CLI for online search, research, and Q&A with citations."
4
+ command: "sc skills get pplx.usage"
5
+ tags: ["perplexity", "ai", "search", "research", "llm", "citations", "online"]
6
+ ---
7
+
8
+ # pplx Usage Skill
9
+
10
+ Use this skill when you need real-time information, research with citations, or online search capabilities through the Perplexity API.
11
+
12
+ ## 1) Learn and Install
13
+
14
+ ```bash
15
+ # Learn about the plugin
16
+ sc plugins learn pplx
17
+
18
+ # Install the plugin
19
+ sc plugins install pplx
20
+ ```
21
+
22
+ ## 2) Configure API Key
23
+
24
+ ```bash
25
+ # Set environment variable (recommended)
26
+ export PERPLEXITY_API_KEY=pplx-xxxxxxxxxxxxxxxxxxxxxxxx
27
+
28
+ # Or use --api-key flag with each command
29
+ sc pplx chat send "Question" --api-key pplx-xxx
30
+ ```
31
+
32
+ Get API key: https://www.perplexity.ai/settings/api
33
+
34
+ ## 3) Validate Setup
35
+
36
+ ```bash
37
+ # Check version
38
+ sc pplx _ version
39
+
40
+ # List available models
41
+ sc pplx models list --json
42
+ ```
43
+
44
+ ## 4) Common Workflows
45
+
46
+ ### Quick Question
47
+ ```bash
48
+ sc pplx ask send "What is quantum computing?" --json
49
+ ```
50
+
51
+ ### Research with Citations
52
+ ```bash
53
+ sc pplx chat send "Latest developments in AI regulation 2025" --citations --json
54
+ ```
55
+
56
+ ### Deep Research
57
+ ```bash
58
+ sc pplx chat send "Comprehensive analysis of renewable energy trends" \
59
+ --model sonar-deep-research \
60
+ --citations \
61
+ --related-questions \
62
+ --search-queries \
63
+ --json
64
+ ```
65
+
66
+ ### List Models
67
+ ```bash
68
+ sc pplx models list --json
69
+ ```
70
+
71
+ ### Get Help Schema
72
+ ```bash
73
+ sc pplx help json
74
+ ```
75
+
76
+ ## 5) Available Models
77
+
78
+ | Model | Use Case |
79
+ |-------|----------|
80
+ | `sonar` | Fast online search |
81
+ | `sonar-pro` | Advanced online search |
82
+ | `sonar-deep-research` | Deep research with citations |
83
+ | `sonar-reasoning` | Reasoning with search |
84
+ | `sonar-reasoning-pro` | Advanced reasoning |
85
+
86
+ ## 6) Error Handling
87
+
88
+ Exit codes for agent decision-making:
89
+
90
+ | Code | Type | Action |
91
+ |------|------|--------|
92
+ | 0 | success | Parse response |
93
+ | 85 | missing_api_key | Set PERPLEXITY_API_KEY |
94
+ | 87 | authentication_failed | Check API key validity |
95
+ | 106 | rate_limited | Wait 60s and retry |
96
+ | 105 | timeout | Retry immediately |
97
+
98
+ ## 7) Passthrough Mode
99
+
100
+ Use passthrough for advanced options:
101
+
102
+ ```bash
103
+ sc pplx _ chat "Question" --model sonar-pro --temperature 0.7 --max-tokens 500
104
+ ```
105
+
106
+ ## 8) Remote Skill Sync
107
+
108
+ This plugin syncs skills from the remote repository:
109
+
110
+ ```bash
111
+ # Skills are auto-synced from https://github.com/javimosch/pplx
112
+ # Manual sync if needed:
113
+ sc skills sync pplx
114
+ ```
115
+
116
+ ## Caveats
117
+
118
+ - Always use `--json` for programmatic parsing
119
+ - API key required (env var or --api-key flag)
120
+ - Rate limits apply (exit code 106)
121
+ - Prefer `sonar-deep-research` for academic/research tasks