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.
- package/cli/adapters/process.js +9 -0
- package/cli/skills-catalog.js +169 -10
- package/cli/skills.js +46 -3
- package/cli/supercli.js +21 -1
- package/package.json +1 -1
- package/plugins/openhands/plugin.json +6 -6
- package/plugins/openhands/skills/quickstart/SKILL.md +23 -0
- package/plugins/plugins.json +10 -0
- package/plugins/pplx/README.md +95 -0
- package/plugins/pplx/plugin.json +123 -0
- package/plugins/pplx/skills/usage/SKILL.md +121 -0
package/cli/adapters/process.js
CHANGED
|
@@ -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)
|
package/cli/skills-catalog.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
|
|
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
|
@@ -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":
|
|
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":
|
|
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":
|
|
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:
|
package/plugins/plugins.json
CHANGED
|
@@ -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
|